respond_with
also allow you to pass options that are forwarded
+ # to the underlying render call. Those options are only applied success
+ # scenarios. For instance, you can do the following in the create method above:
+ #
+ # def create
+ # @project = Project.find(params[:project_id])
+ # @task = @project.comments.build(params[:task])
+ # flash[:notice] = 'Task was successfully created.' if @task.save
+ # respond_with(@project, @task, :status => 201)
+ # end
+ #
+ # This will return status 201 if the task was saved with success. If not,
+ # it will simply ignore the given options and return status 422 and the
+ # resource errors. To customize the failure scenario, you can pass a
+ # a block to respond_with
:
+ #
+ # def create
+ # @project = Project.find(params[:project_id])
+ # @task = @project.comments.build(params[:task])
+ # respond_with(@project, @task, :status => 201) do |format|
+ # if @task.save
+ # flash[:notice] = 'Task was successfully created.'
+ # else
+ # format.html { render "some_special_template" }
+ # end
+ # end
+ # end
+ #
+ # Using respond_with
with a block follows the same syntax as respond_to
.
class Responder
attr_reader :controller, :request, :format, :resource, :resources, :options
@@ -131,7 +162,11 @@ def to_html
# responds to :to_format and display it.
#
def to_format
- default_render
+ if get? || !has_errors?
+ default_render
+ else
+ display_errors
+ end
rescue ActionView::MissingTemplate => e
api_behavior(e)
end
@@ -155,8 +190,6 @@ def api_behavior(error)
if get?
display resource
- elsif has_errors?
- display resource.errors, :status => :unprocessable_entity
elsif post?
display resource, :status => :created, :location => api_location
elsif has_empty_resource_definition?
@@ -185,7 +218,7 @@ def resource_location
# controller.
#
def default_render
- @default_response.call
+ @default_response.call(options)
end
# Display is just a shortcut to render a resource with the current format.
@@ -209,6 +242,10 @@ def display(resource, given_options={})
controller.render given_options.merge!(options).merge!(format => resource)
end
+ def display_errors
+ controller.render format => resource.errors, :status => :unprocessable_entity
+ end
+
# Check whether the resource has errors.
#
def has_errors?
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 49971fc9f88a1..7f972fc2816d6 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -60,6 +60,7 @@ module ActionDispatch
autoload :Static
end
+ autoload :ClosedError, 'action_dispatch/middleware/closed_error'
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
autoload :Routing
diff --git a/actionpack/lib/action_dispatch/middleware/closed_error.rb b/actionpack/lib/action_dispatch/middleware/closed_error.rb
new file mode 100644
index 0000000000000..0a4db47f4b87b
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/closed_error.rb
@@ -0,0 +1,7 @@
+module ActionDispatch
+ class ClosedError < StandardError #:nodoc:
+ def initialize(kind)
+ super "Cannot modify #{kind} because it was closed. This means it was already streamed back to the client or converted to HTTP headers."
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 7ac608f0a8977..24ebb8fed71bc 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -83,7 +83,7 @@ class Cookies
# Raised when storing more than 4K of session data.
class CookieOverflow < StandardError; end
- class CookieJar < Hash #:nodoc:
+ class CookieJar #:nodoc:
# This regular expression is used to split the levels of a domain.
# The top level domain can be any string without a period or
@@ -115,13 +115,22 @@ def initialize(secret = nil, host = nil, secure = false)
@delete_cookies = {}
@host = host
@secure = secure
-
- super()
+ @closed = false
+ @cookies = {}
end
+ attr_reader :closed
+ alias :closed? :closed
+ def close!; @closed = true end
+
# Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
def [](name)
- super(name.to_s)
+ @cookies[name.to_s]
+ end
+
+ def update(other_hash)
+ @cookies.update other_hash
+ self
end
def handle_options(options) #:nodoc:
@@ -145,6 +154,7 @@ def handle_options(options) #:nodoc:
# Sets the cookie named +name+. The second argument may be the very cookie
# value, or a hash of options as documented above.
def []=(key, options)
+ raise ClosedError, :cookies if closed?
if options.is_a?(Hash)
options.symbolize_keys!
value = options[:value]
@@ -153,7 +163,7 @@ def []=(key, options)
options = { :value => value }
end
- value = super(key.to_s, value)
+ value = @cookies[key.to_s] = value
handle_options(options)
@@ -170,7 +180,7 @@ def delete(key, options = {})
handle_options(options)
- value = super(key.to_s)
+ value = @cookies.delete(key.to_s)
@delete_cookies[key] = options
value
end
@@ -225,6 +235,7 @@ def initialize(parent_jar, secret)
end
def []=(key, options)
+ raise ClosedError, :cookies if closed?
if options.is_a?(Hash)
options.symbolize_keys!
else
@@ -263,6 +274,7 @@ def [](name)
end
def []=(key, options)
+ raise ClosedError, :cookies if closed?
if options.is_a?(Hash)
options.symbolize_keys!
options[:value] = @verifier.generate(options[:value])
@@ -305,6 +317,7 @@ def initialize(app)
end
def call(env)
+ cookie_jar = nil
status, headers, body = @app.call(env)
if cookie_jar = env['action_dispatch.cookies']
@@ -315,6 +328,9 @@ def call(env)
end
[status, headers, body]
+ ensure
+ cookie_jar = ActionDispatch::Request.new(env).cookie_jar unless cookie_jar
+ cookie_jar.close!
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 21aeeb217afec..027ff7f8ac5d1 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -43,9 +43,15 @@ class Flash
class FlashNow #:nodoc:
def initialize(flash)
@flash = flash
+ @closed = false
end
+ attr_reader :closed
+ alias :closed? :closed
+ def close!; @closed = true end
+
def []=(k, v)
+ raise ClosedError, :flash if closed?
@flash[k] = v
@flash.discard(k)
v
@@ -66,27 +72,70 @@ def notice=(message)
end
end
- class FlashHash < Hash
+ class FlashHash
+ include Enumerable
+
def initialize #:nodoc:
- super
- @used = Set.new
+ @used = Set.new
+ @closed = false
+ @flashes = {}
end
+ attr_reader :closed
+ alias :closed? :closed
+ def close!; @closed = true end
+
def []=(k, v) #:nodoc:
+ raise ClosedError, :flash if closed?
keep(k)
- super
+ @flashes[k] = v
+ end
+
+ def [](k)
+ @flashes[k]
end
def update(h) #:nodoc:
h.keys.each { |k| keep(k) }
- super
+ @flashes.update h
+ self
+ end
+
+ def keys
+ @flashes.keys
+ end
+
+ def key?(name)
+ @flashes.key? name
+ end
+
+ def delete(key)
+ @flashes.delete key
+ self
+ end
+
+ def to_hash
+ @flashes.dup
+ end
+
+ def empty?
+ @flashes.empty?
+ end
+
+ def clear
+ @flashes.clear
+ end
+
+ def each(&block)
+ @flashes.each(&block)
end
alias :merge! :update
def replace(h) #:nodoc:
@used = Set.new
- super
+ @flashes.replace h
+ self
end
# Sets a flash that will not be available to the next action, only to the current.
@@ -100,7 +149,7 @@ def replace(h) #:nodoc:
#
# Entries set via now are accessed the same way as standard entries: flash['my-key'].
def now
- FlashNow.new(self)
+ @now ||= FlashNow.new(self)
end
# Keeps either the entire current flash or a specific flash entry available for the next action:
@@ -184,8 +233,11 @@ def call(env)
session = env['rack.session'] || {}
flash_hash = env['action_dispatch.request.flash_hash']
- if flash_hash && (!flash_hash.empty? || session.key?('flash'))
+ if flash_hash
+ if !flash_hash.empty? || session.key?('flash')
session["flash"] = flash_hash
+ end
+ flash_hash.close!
end
if session.key?('flash') && session['flash'].empty?
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 35be0b3a27982..13aef18ee1268 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,6 +1,7 @@
require 'erb'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/object/inclusion'
require 'active_support/inflector'
require 'action_dispatch/routing/redirection'
@@ -1345,11 +1346,11 @@ def scope_action_options #:nodoc:
end
def resource_scope? #:nodoc:
- [:resource, :resources].include?(@scope[:scope_level])
+ @scope[:scope_level].among?(:resource, :resources)
end
def resource_method_scope? #:nodoc:
- [:collection, :member, :new].include?(@scope[:scope_level])
+ @scope[:scope_level].among?(:collection, :member, :new)
end
def with_exclusive_scope
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 77a15f3e97943..16a6b93ce8e38 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/inclusion'
+
module ActionDispatch
module Assertions
# A small suite of assertions that test responses from \Rails applications.
@@ -33,7 +35,7 @@ module ResponseAssertions
def assert_response(type, message = nil)
validate_request!
- if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
+ if type.among?(:success, :missing, :redirect, :error) && @response.send("#{type}?")
assert_block("") { true } # to count the assertion
elsif type.is_a?(Fixnum) && @response.response_code == type
assert_block("") { true } # to count the assertion
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index 2b862fb7d68ed..f41d3e5ddba6a 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -1,4 +1,5 @@
require 'action_controller/vendor/html-scanner'
+require 'active_support/core_ext/object/inclusion'
#--
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
@@ -441,7 +442,7 @@ def assert_select_rjs(*args, &block)
if matches
assert_block("") { true } # to count the assertion
- if block_given? && !([:remove, :show, :hide, :toggle].include? rjs_type)
+ if block_given? && !rjs_type.among?(:remove, :show, :hide, :toggle)
begin
@selected ||= nil
in_scope, @selected = @selected, matches
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 5c6416a19e37b..174babb9aae6c 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -1,6 +1,7 @@
require 'stringio'
require 'uri'
require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/core_ext/object/inclusion'
require 'active_support/core_ext/object/try'
require 'rack/test'
require 'test/unit/assertions'
@@ -26,31 +27,31 @@ module RequestHelpers
# object's @response instance variable will point to the same
# response object.
#
- # You can also perform POST, PUT, DELETE, and HEAD requests with +post+,
- # +put+, +delete+, and +head+.
+ # You can also perform POST, PUT, DELETE, and HEAD requests with +#post+,
+ # +#put+, +#delete+, and +#head+.
def get(path, parameters = nil, headers = nil)
process :get, path, parameters, headers
end
- # Performs a POST request with the given parameters. See get() for more
+ # Performs a POST request with the given parameters. See +#get+ for more
# details.
def post(path, parameters = nil, headers = nil)
process :post, path, parameters, headers
end
- # Performs a PUT request with the given parameters. See get() for more
+ # Performs a PUT request with the given parameters. See +#get+ for more
# details.
def put(path, parameters = nil, headers = nil)
process :put, path, parameters, headers
end
- # Performs a DELETE request with the given parameters. See get() for
+ # Performs a DELETE request with the given parameters. See +#get+ for
# more details.
def delete(path, parameters = nil, headers = nil)
process :delete, path, parameters, headers
end
- # Performs a HEAD request with the given parameters. See get() for more
+ # Performs a HEAD request with the given parameters. See +#get+ for more
# details.
def head(path, parameters = nil, headers = nil)
process :head, path, parameters, headers
@@ -59,7 +60,7 @@ def head(path, parameters = nil, headers = nil)
# Performs an XMLHttpRequest request with the given parameters, mirroring
# a request from the Prototype library.
#
- # The request_method is :get, :post, :put, :delete or :head; the
+ # The request_method is +:get+, +:post+, +:put+, +:delete+ or +:head+; the
# parameters are +nil+, a hash, or a url-encoded or multipart string;
# the headers are a hash. Keys are automatically upcased and prefixed
# with 'HTTP_' if not already.
@@ -243,7 +244,8 @@ def _mock_session
end
# Performs the actual request.
- def process(method, path, parameters = nil, rack_environment = nil)
+ def process(method, path, parameters = nil, env = nil)
+ env ||= {}
if path =~ %r{://}
location = URI.parse(path)
https! URI::HTTPS === location if location.scheme
@@ -259,7 +261,7 @@ def process(method, path, parameters = nil, rack_environment = nil)
hostname, port = host.split(':')
- env = {
+ default_env = {
:method => method,
:params => parameters,
@@ -277,9 +279,7 @@ def process(method, path, parameters = nil, rack_environment = nil)
session = Rack::Test::Session.new(_mock_session)
- (rack_environment || {}).each do |key, value|
- env[key] = value
- end
+ env.reverse_merge!(default_env)
# NOTE: rack-test v0.5 doesn't build a default uri correctly
# Make sure requested path is always a full uri
@@ -321,7 +321,7 @@ def reset!
define_method(method) do |*args|
reset! unless integration_session
# reset the html_document variable, but only for new get/post calls
- @html_document = nil unless %w(cookies assigns).include?(method)
+ @html_document = nil unless method.among?("cookies", "assigns")
integration_session.__send__(method, *args).tap do
copy_session_variables!
end
@@ -384,7 +384,7 @@ def integration_session
end
end
- # An test that spans multiple controllers and actions,
+ # An integration test spans multiple controllers and actions,
# tying them all together to ensure they work together as expected. It tests
# more completely than either unit or functional tests do, exercising the
# entire stack, from the dispatcher to the database.
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index ab8c6259c5461..55191036276d3 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -8,13 +8,13 @@
module ActionView #:nodoc:
# = Action View Base
#
- # Action View templates can be written in three ways. If the template file has a .erb (or .rhtml) extension then it uses a mixture of ERb
+ # Action View templates can be written in three ways. If the template file has a .erb (or .rhtml) extension then it uses a mixture of ERB
# (included in Ruby) and HTML. If the template file has a .builder (or .rxml) extension then Jim Weirich's Builder::XmlMarkup library is used.
# If the template file has a .rjs extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
#
- # == ERb
+ # == ERB
#
- # You trigger ERb by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
+ # You trigger ERB by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
# following loop for names:
#
# Names of all the people
@@ -23,7 +23,7 @@ module ActionView #:nodoc:
# <% end %>
#
# The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
- # is not just a usage suggestion. Regular output functions like print or puts won't work with ERb templates. So this would be wrong:
+ # is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong:
#
# <%# WRONG %>
# Hi, Mr. <% puts "Frodo" %>
@@ -81,7 +81,7 @@ module ActionView #:nodoc:
#
# == Builder
#
- # Builder templates are a more programmatic alternative to ERb. They are especially useful for generating XML content. An XmlMarkup object
+ # Builder templates are a more programmatic alternative to ERB. They are especially useful for generating XML content. An XmlMarkup object
# named +xml+ is automatically made available to templates with a .builder extension.
#
# Here are some basic examples:
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index db9d7a08ff637..96e572225285a 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -4,7 +4,7 @@ module ActionView
# = Action View Atom Feed Helpers
module Helpers #:nodoc:
module AtomFeedHelper
- # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERb or any other
+ # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
# template languages).
#
# Full usage example:
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index c88bd1efd5b2a..9ac7dff1ec5cd 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -14,7 +14,7 @@ module CaptureHelper
# variable. You can then use this variable anywhere in your templates or layout.
#
# ==== Examples
- # The capture method can be used in ERb templates...
+ # The capture method can be used in ERB templates...
#
# <% @greeting = capture do %>
# Welcome to my shiny new web page! The date and time is
diff --git a/actionpack/lib/action_view/helpers/csrf_helper.rb b/actionpack/lib/action_view/helpers/csrf_helper.rb
index 65c8debc76bbf..1f2bc28cac5f8 100644
--- a/actionpack/lib/action_view/helpers/csrf_helper.rb
+++ b/actionpack/lib/action_view/helpers/csrf_helper.rb
@@ -17,10 +17,12 @@ module CsrfHelper
# Note that regular forms generate hidden fields, and that Ajax calls are whitelisted,
# so they do not use these tags.
def csrf_meta_tags
- <<-METAS.strip_heredoc.chomp.html_safe if protect_against_forgery?
-
-
- METAS
+ if protect_against_forgery?
+ [
+ tag('meta', :name => 'csrf-param', :content => request_forgery_protection_token),
+ tag('meta', :name => 'csrf-token', :content => form_authenticity_token)
+ ].join("\n").html_safe
+ end
end
# For backwards compatibility.
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 18e303778c219..506db24dc236d 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -584,7 +584,7 @@ def update_page(&block)
# Works like update_page but wraps the generated JavaScript in a
# \ tag. Use this to include generated JavaScript in an
- # ERb template. See JavaScriptGenerator for more information.
+ # ERB template. See JavaScriptGenerator for more information.
#
# +html_options+ may be a hash of \ attributes to be
# passed to ActionView::Helpers::JavaScriptHelper#javascript_tag.
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 2d3c5fe7e78a8..bdda1df437933 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -303,7 +303,7 @@ def simple_format(text, html_options={}, options={})
# # => "Welcome to my new blog at http://www.myblog.com.
# Please e-mail me at me@email.com."
def auto_link(text, *args, &block)#link = :all, html = {}, &block)
- return ''.html_safe if text.blank?
+ return '' if text.blank?
options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter
unless args.empty?
@@ -507,7 +507,7 @@ def auto_link_urls(text, html_options = {}, options = {})
end
content_tag(:a, link_text, link_attributes.merge('href' => href), !!options[:sanitize]) + punctuation.reverse.join('')
end
- end.html_safe
+ end
end
# Turns all email addresses into clickable links. If a block is given,
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 2cd2dca71160e..de75488e72ad8 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -81,9 +81,12 @@ def url_options
# # => /workshops
#
# <%= url_for(@workshop) %>
- # # calls @workshop.to_s
+ # # calls @workshop.to_param which by default returns the id
# # => /workshops/5
#
+ # # to_param can be re-defined in a model to provide different URL names:
+ # # => /workshops/1-workshop-name
+ #
# <%= url_for("http://www.example.com") %>
# # => http://www.example.com
#
@@ -183,7 +186,7 @@ def url_for(options = {})
# link_to "Profiles", :controller => "profiles"
# # => Profiles
#
- # You can use a block as well if your link target is hard to fit into the name parameter. ERb example:
+ # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
#
# <%= link_to(@profile) do %>
# <%= @profile.name %> -- Check it out!
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index a36837afc8ea4..0443b1b0f5315 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -55,7 +55,7 @@ def add_postamble(src)
class ERB
# Specify trim mode for the ERB compiler. Defaults to '-'.
- # See ERb documentation for suitable values.
+ # See ERB documentation for suitable values.
class_attribute :erb_trim_mode
self.erb_trim_mode = '-'
diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb
new file mode 100644
index 0000000000000..9b69a2648ea39
--- /dev/null
+++ b/actionpack/test/controller/flash_hash_test.rb
@@ -0,0 +1,90 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ class FlashHashTest < ActiveSupport::TestCase
+ def setup
+ @hash = Flash::FlashHash.new
+ end
+
+ def test_set_get
+ @hash[:foo] = 'zomg'
+ assert_equal 'zomg', @hash[:foo]
+ end
+
+ def test_keys
+ assert_equal [], @hash.keys
+
+ @hash['foo'] = 'zomg'
+ assert_equal ['foo'], @hash.keys
+
+ @hash['bar'] = 'zomg'
+ assert_equal ['foo', 'bar'].sort, @hash.keys.sort
+ end
+
+ def test_update
+ @hash['foo'] = 'bar'
+ @hash.update('foo' => 'baz', 'hello' => 'world')
+
+ assert_equal 'baz', @hash['foo']
+ assert_equal 'world', @hash['hello']
+ end
+
+ def test_delete
+ @hash['foo'] = 'bar'
+ @hash.delete 'foo'
+
+ assert !@hash.key?('foo')
+ assert_nil @hash['foo']
+ end
+
+ def test_to_hash
+ @hash['foo'] = 'bar'
+ assert_equal({'foo' => 'bar'}, @hash.to_hash)
+
+ @hash.to_hash['zomg'] = 'aaron'
+ assert !@hash.key?('zomg')
+ assert_equal({'foo' => 'bar'}, @hash.to_hash)
+ end
+
+ def test_empty?
+ assert @hash.empty?
+ @hash['zomg'] = 'bears'
+ assert !@hash.empty?
+ @hash.clear
+ assert @hash.empty?
+ end
+
+ def test_each
+ @hash['hello'] = 'world'
+ @hash['foo'] = 'bar'
+
+ things = []
+ @hash.each do |k,v|
+ things << [k,v]
+ end
+
+ assert_equal([%w{ hello world }, %w{ foo bar }].sort, things.sort)
+ end
+
+ def test_replace
+ @hash['hello'] = 'world'
+ @hash.replace('omg' => 'aaron')
+ assert_equal({'omg' => 'aaron'}, @hash.to_hash)
+ end
+
+ def test_discard_no_args
+ @hash['hello'] = 'world'
+ @hash.discard
+ @hash.sweep
+ assert_equal({}, @hash.to_hash)
+ end
+
+ def test_discard_one_arg
+ @hash['hello'] = 'world'
+ @hash['omg'] = 'world'
+ @hash.discard 'hello'
+ @hash.sweep
+ assert_equal({'omg' => 'world'}, @hash.to_hash)
+ end
+ end
+end
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index 3569a2f213f1f..9c89f1334d681 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -174,13 +174,13 @@ def test_keep_and_discard_return_values
assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed
assert_nil flash.discard(:unknown) # non existant key passed
- assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard()) # nothing passed
- assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil)) # nothing passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard().to_hash) # nothing passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed
assert_equal(:foo_indeed, flash.keep(:foo)) # valid key passed
assert_nil flash.keep(:unknown) # non existant key passed
- assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep()) # nothing passed
- assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil)) # nothing passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep().to_hash) # nothing passed
+ assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed
end
def test_redirect_to_with_alert
@@ -214,11 +214,20 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33'
class TestController < ActionController::Base
+ def dont_set_flash
+ head :ok
+ end
+
def set_flash
flash["that"] = "hello"
head :ok
end
+ def set_flash_now
+ flash.now["that"] = "hello"
+ head :ok
+ end
+
def use_flash
render :inline => "flash: #{flash["that"]}"
end
@@ -245,6 +254,47 @@ def test_just_using_flash_does_not_stream_a_cookie_back
end
end
+ def test_setting_flash_raises_after_stream_back_to_client
+ with_test_route_set do
+ env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
+ get '/set_flash', nil, env
+ assert_raise(ActionDispatch::ClosedError) {
+ @request.flash['alert'] = 'alert'
+ }
+ end
+ end
+
+ def test_setting_flash_raises_after_stream_back_to_client_even_with_an_empty_flash
+ with_test_route_set do
+ env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
+ get '/dont_set_flash', nil, env
+ assert_raise(ActionDispatch::ClosedError) {
+ @request.flash['alert'] = 'alert'
+ }
+ end
+ end
+
+ def test_setting_flash_now_raises_after_stream_back_to_client
+ with_test_route_set do
+ env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
+ get '/set_flash_now', nil, env
+ assert_raise(ActionDispatch::ClosedError) {
+ @request.flash.now['alert'] = 'alert'
+ }
+ end
+ end
+
+ def test_setting_flash_now_raises_after_stream_back_to_client_even_with_an_empty_flash
+ with_test_route_set do
+ env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
+ get '/dont_set_flash', nil, env
+ assert_raise(ActionDispatch::ClosedError) {
+ @request.flash.now['alert'] = 'alert'
+ }
+ end
+ end
+
+
private
# Overwrite get to send SessionSecret in env hash
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index f0d62b0b1372d..01dc2f2091865 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -521,4 +521,12 @@ def app
get '/foo'
assert_raise(NameError) { missing_path }
end
+
+ test "process reuse the env we pass as argument" do
+ env = { :SERVER_NAME => 'server', 'action_dispatch.custom' => 'custom' }
+ get '/foo', nil, env
+ assert_equal :get, env[:method]
+ assert_equal 'server', env[:SERVER_NAME]
+ assert_equal 'custom', env['action_dispatch.custom']
+ end
end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 5debf96232e17..be5866e5aa4cc 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'controller/fake_models'
require 'active_support/core_ext/hash/conversions'
+require 'active_support/core_ext/object/inclusion'
class StarStarMimeController < ActionController::Base
layout nil
@@ -158,7 +159,7 @@ def rescue_action(e)
protected
def set_layout
- if ["all_types_with_layout", "iphone_with_html_response_type"].include?(action_name)
+ if action_name.among?("all_types_with_layout", "iphone_with_html_response_type")
"respond_to/layouts/standard"
elsif action_name == "iphone_with_html_response_type_without_layout"
"respond_to/layouts/missing"
@@ -558,6 +559,15 @@ def using_resource_with_status_and_location
respond_with(resource, :location => "http://test.host/", :status => :created)
end
+ def using_invalid_resource_with_template
+ respond_with(resource)
+ end
+
+ def using_options_with_template
+ @customer = resource
+ respond_with(@customer, :status => 123, :location => "http://test.host/")
+ end
+
def using_resource_with_responder
responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" }
respond_with(resource, :responder => responder)
@@ -953,6 +963,54 @@ def test_using_resource_with_status_and_location
assert_equal 201, @response.status
end
+ def test_using_resource_with_status_and_location_with_invalid_resource
+ errors = { :name => :invalid }
+ Customer.any_instance.stubs(:errors).returns(errors)
+
+ @request.accept = "text/xml"
+
+ post :using_resource_with_status_and_location
+ assert_equal errors.to_xml, @response.body
+ assert_equal 422, @response.status
+ assert_equal nil, @response.location
+
+ put :using_resource_with_status_and_location
+ assert_equal errors.to_xml, @response.body
+ assert_equal 422, @response.status
+ assert_equal nil, @response.location
+ end
+
+ def test_using_invalid_resource_with_template
+ errors = { :name => :invalid }
+ Customer.any_instance.stubs(:errors).returns(errors)
+
+ @request.accept = "text/xml"
+
+ post :using_invalid_resource_with_template
+ assert_equal errors.to_xml, @response.body
+ assert_equal 422, @response.status
+ assert_equal nil, @response.location
+
+ put :using_invalid_resource_with_template
+ assert_equal errors.to_xml, @response.body
+ assert_equal 422, @response.status
+ assert_equal nil, @response.location
+ end
+
+ def test_using_options_with_template
+ @request.accept = "text/xml"
+
+ post :using_options_with_template
+ assert_equal ""
result << $2.strip
@@ -33,7 +35,7 @@ def plusplus(body)
def code(body)
body.gsub!(%r{<(yaml|shell|ruby|erb|html|sql|plain)>(.*?)\1>}m) do |m|
es = ERB::Util.h($2)
- css_class = ['erb', 'shell'].include?($1) ? 'html' : $1
+ css_class = $1.among?('erb', 'shell') ? 'html' : $1
%{
- You're encouraged to help in keeping the quality of this guide.
+ You're encouraged to help improve the quality of this guide.
If you see any typos or factual errors you are confident to
diff --git a/railties/guides/source/ruby_on_rails_guides_guidelines.textile b/railties/guides/source/ruby_on_rails_guides_guidelines.textile
index 657675885640b..8e55780dcae81 100644
--- a/railties/guides/source/ruby_on_rails_guides_guidelines.textile
+++ b/railties/guides/source/ruby_on_rails_guides_guidelines.textile
@@ -10,10 +10,10 @@ Guides are written in "Textile":http://www.textism.com/tools/textile/. There's c
h3. Prologue
-Each guide should start with motivational text at the top. That's the little introduction in the blue area. The prologue should tell the readers what's the guide about, and what will they learn. See for example the "Routing Guide":routing.html.
+Each guide should start with motivational text at the top (that's the little introduction in the blue area.) The prologue should tell the reader what the guide is about, and what they will learn. See for example the "Routing Guide":routing.html.
h3. Titles
-
+
The title of every guide uses +h2+, guide sections use +h3+, subsections +h4+, etc.
Capitalize all words except for internal articles, prepositions, conjunctions, and forms of the verb to be:
@@ -23,7 +23,7 @@ h5. Middleware Stack is an Array
h5. When are Objects Saved?
-Use same typography as in regular text:
+Use the same typography as in regular text:
#{es}
Listing posts
@@ -584,8 +584,8 @@ def new
@post = Post.new
respond_to do |format|
- format.html # new.html.erb
- format.xml { render :xml => @post }
+ format.html # new.html.erb
+ format.json { render :json => @post }
end
end
@@ -653,13 +653,13 @@ def create
respond_to do |format|
if @post.save
- format.html { redirect_to(@post,
+ format.html { redirect_to(@post,
:notice => 'Post was successfully created.') }
- format.xml { render :xml => @post,
+ format.json { render :json => @post,
:status => :created, :location => @post }
else
- format.html { render :action => "new" }
- format.xml { render :xml => @post.errors,
+ format.html { render :action => "new" }
+ format.json { render :json => @post.errors,
:status => :unprocessable_entity }
end
end
@@ -681,8 +681,8 @@ def show
@post = Post.find(params[:id])
respond_to do |format|
- format.html # show.html.erb
- format.xml { render :xml => @post }
+ format.html # show.html.erb
+ format.json { render :json => @post }
end
end
@@ -743,12 +743,12 @@ def update
respond_to do |format|
if @post.update_attributes(params[:post])
- format.html { redirect_to(@post,
+ format.html { redirect_to(@post,
:notice => 'Post was successfully updated.') }
- format.xml { head :ok }
+ format.json { render :json => {}, :status => :ok }
else
- format.html { render :action => "edit" }
- format.xml { render :xml => @post.errors,
+ format.html { render :action => "edit" }
+ format.json { render :json => @post.errors,
:status => :unprocessable_entity }
end
end
@@ -767,8 +767,8 @@ def destroy
@post.destroy
respond_to do |format|
- format.html { redirect_to(posts_url) }
- format.xml { head :ok }
+ format.html { redirect_to(posts_url) }
+ format.json { render :json => {}, :status => :ok }
end
end
@@ -1201,36 +1201,19 @@ h3. Security
If you were to publish your blog online, anybody would be able to add, edit and delete posts or delete comments.
-Rails provides a very simple HTTP authentication system that will work nicely in this situation. First, we enable simple HTTP based authentication in our app/controllers/application_controller.rb:
+Rails provides a very simple HTTP authentication system that will work nicely in this situation.
-
-class ApplicationController < ActionController::Base
- protect_from_forgery
-
- private
-
- def authenticate
- authenticate_or_request_with_http_basic do |user_name, password|
- user_name == 'admin' && password == 'password'
- end
- end
-
-end
-
-
-You can obviously change the username and password to whatever you want. We put this method inside of +ApplicationController+ so that it is available to all of our controllers.
-
-Then in the +PostsController+ we need to have a way to block access to the various actions if the person is not authenticated, here we can use the Rails before_filter method, which allows us to specify that Rails must run a method and only then allow access to the requested action if that method allows it.
+In the +PostsController+ we need to have a way to block access to the various actions if the person is not authenticated, here we can use the Rails http_basic_authenticate_with method, allowing access to the requested action if that method allows it.
-To use the before filter, we specify it at the top of our +PostsController+, in this case, we want the user to be authenticated on every action, except for +index+ and +show+, so we write that:
+To use the authentication system, we specify it at the top of our +PostsController+, in this case, we want the user to be authenticated on every action, except for +index+ and +show+, so we write that:
class PostsController < ApplicationController
- before_filter :authenticate, :except => [:index, :show]
+ http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
# GET /posts
- # GET /posts.xml
+ # GET /posts.json
def index
@posts = Post.all
respond_to do |format|
@@ -1242,7 +1225,7 @@ We also only want to allow authenticated users to delete comments, so in the +Co
class CommentsController < ApplicationController
- before_filter :authenticate, :only => :destroy
+ http_basic_authenticate_with :name => "dhh", :password => "secret", :only => :destroy
def create
@post = Post.find(params[:post_id])
@@ -1475,6 +1458,7 @@ Two very common sources of data that are not UTF-8:
h3. Changelog
+* April 11, 2011: Changed scaffold_controller generator to create format block for JSON instead of XML "Sebastian Martinez":http://www.wyeworks.com
* August 30, 2010: Minor editing after Rails 3 release by "Joost Baaij":http://www.spacebabies.nl
* July 12, 2010: Fixes, editing and updating of code samples by "Jaime Iniesta":http://jaimeiniesta.com
* May 16, 2010: Added a section on configuration gotchas to address common encoding problems that people might have by "Yehuda Katz":http://www.yehudakatz.com
diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index 0cbbe1f389383..7c01f01b2472e 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -478,8 +478,7 @@ The next line in +config/application.rb+ is:
require 'rails/all'
-h4 +railties/lib/rails/all.rb+
-
+h4. +railties/lib/rails/all.rb+
This file is responsible for requiring all the individual parts of Rails like so:
@@ -591,7 +590,7 @@ h4. +activesupport/lib/active_support/deprecation/behaviors.rb+
This file defines the behavior of the +ActiveSupport::Deprecation+ module, setting up the +DEFAULT_BEHAVIORS+ hash constant which contains the three defaults to outputting deprecation warnings: +:stderr+, +:log+ and +:notify+. This file begins by requiring +activesupport/notifications+ and +activesupport/core_ext/array/wrap+.
-h4 +activesupport/lib/active_support/notifications.rb+
+h4. +activesupport/lib/active_support/notifications.rb+
TODO: document +ActiveSupport::Notifications+.
diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb
index f2681c64612e6..911655e0f4cbb 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -111,7 +111,7 @@
Feedback
<%%= link_to 'Show', <%= singular_table_name %> %>
<%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %>
- <%%= link_to 'Destroy', <%= singular_table_name %>, :confirm => 'Are you sure?', :method => :delete %>
+ <%%= link_to 'Destroy', <%= singular_table_name %>, <%= key_value :confirm, "'Are you sure?'" %>, <%= key_value :method, ":delete" %> %>
<%% end %>
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index 64273e4ca429c..f85375b2a37a8 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -1,4 +1,5 @@
require 'active_support/time'
+require 'active_support/core_ext/object/inclusion'
module Rails
module Generators
@@ -44,7 +45,7 @@ def human_name
end
def reference?
- [ :references, :belongs_to ].include?(self.type)
+ self.type.among?(:references, :belongs_to)
end
end
end
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index 2af7f85463096..36bc9e055c0df 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -8,6 +8,9 @@ class NamedBase < Base
class_option :skip_namespace, :type => :boolean, :default => false,
:desc => "Skip namespace (affects only isolated applications)"
+ class_option :old_style_hash, :type => :boolean, :default => false,
+ :desc => "Force using old style hash (:foo => 'bar') on Ruby >= 1.9"
+
def initialize(args, *options) #:nodoc:
# Unfreeze name in case it's given as a frozen string
args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen?
@@ -181,6 +184,16 @@ def self.check_class_collision(options={})
class_collisions "#{options[:prefix]}#{name}#{options[:suffix]}"
end
end
+
+ # Returns Ruby 1.9 style key-value pair if current code is running on
+ # Ruby 1.9.x. Returns the old-style (with hash rocket) otherwise.
+ def key_value(key, value)
+ if options[:old_style_hash] || RUBY_VERSION < '1.9'
+ ":#{key} => #{value}"
+ else
+ "#{key}: #{value}"
+ end
+ end
end
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index c383d4842ff18..61daefef9010c 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -23,9 +23,13 @@ source 'http://rubygems.org'
# Bundle gems for the local environment. Make sure to
# put test-only gems in this group so their generators
# and rake tasks are available in development mode:
-# group :development, :test do
-# gem 'webrat'
-# end
+
+group :development, :test do
+ # Depend "turn" for pretty printing test output, but disable autorequire.
+ gem 'turn', :require => false
+
+ # gem 'webrat'
+end
# Needed for guides generation
# gem "RedCloth", "~> 4.2"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
index 62aa06dc3e91d..ddfe4ba1e1d36 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
@@ -1,6 +1,6 @@
# Be sure to restart your server when you modify this file.
-<%= app_const %>.config.session_store :cookie_store, :key => '_<%= app_name %>_session'
+<%= app_const %>.config.session_store :cookie_store, <%= key_value :key, "'_#{app_name}_session'" %>
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
diff --git a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb
index 664d8c74c8819..9a2efa68a7a8b 100644
--- a/railties/lib/rails/generators/rails/app/templates/db/seeds.rb
+++ b/railties/lib/rails/generators/rails/app/templates/db/seeds.rb
@@ -3,5 +3,5 @@
#
# Examples:
#
-# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
-# Mayor.create(:name => 'Daley', :city => cities.first)
+# cities = City.create([{ <%= key_value :name, "'Chicago'" %> }, { <%= key_value :name, "'Copenhagen'" %> }])
+# Mayor.create(<%= key_value :name, "'Daley'" %>, <%= key_value :city, "cities.first" %>)
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
index b5317a055b3ca..32b961d9fc259 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
@@ -1,35 +1,35 @@
<% module_namespacing do -%>
class <%= controller_class_name %>Controller < ApplicationController
# GET <%= route_url %>
- # GET <%= route_url %>.xml
+ # GET <%= route_url %>.json
def index
@<%= plural_table_name %> = <%= orm_class.all(class_name) %>
respond_to do |format|
format.html # index.html.erb
- format.xml { render :xml => @<%= plural_table_name %> }
+ format.json { render <%= key_value :json, "@#{plural_table_name}" %> }
end
end
# GET <%= route_url %>/1
- # GET <%= route_url %>/1.xml
+ # GET <%= route_url %>/1.json
def show
@<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
respond_to do |format|
format.html # show.html.erb
- format.xml { render :xml => @<%= singular_table_name %> }
+ format.json { render <%= key_value :json, "@#{singular_table_name}" %> }
end
end
# GET <%= route_url %>/new
- # GET <%= route_url %>/new.xml
+ # GET <%= route_url %>/new.json
def new
@<%= singular_table_name %> = <%= orm_class.build(class_name) %>
respond_to do |format|
format.html # new.html.erb
- format.xml { render :xml => @<%= singular_table_name %> }
+ format.json { render <%= key_value :json, "@#{singular_table_name}" %> }
end
end
@@ -39,46 +39,46 @@ def edit
end
# POST <%= route_url %>
- # POST <%= route_url %>.xml
+ # POST <%= route_url %>.json
def create
@<%= singular_table_name %> = <%= orm_class.build(class_name, "params[:#{singular_table_name}]") %>
respond_to do |format|
if @<%= orm_instance.save %>
- format.html { redirect_to(@<%= singular_table_name %>, :notice => '<%= human_name %> was successfully created.') }
- format.xml { render :xml => @<%= singular_table_name %>, :status => :created, :location => @<%= singular_table_name %> }
+ format.html { redirect_to @<%= singular_table_name %>, <%= key_value :notice, "'#{human_name} was successfully created.'" %> }
+ format.json { render <%= key_value :json, "@#{singular_table_name}" %>, <%= key_value :status, ':created' %>, <%= key_value :location, "@#{singular_table_name}" %> }
else
- format.html { render :action => "new" }
- format.xml { render :xml => @<%= orm_instance.errors %>, :status => :unprocessable_entity }
+ format.html { render <%= key_value :action, '"new"' %> }
+ format.json { render <%= key_value :json, "@#{orm_instance.errors}" %>, <%= key_value :status, ':unprocessable_entity' %> }
end
end
end
# PUT <%= route_url %>/1
- # PUT <%= route_url %>/1.xml
+ # PUT <%= route_url %>/1.json
def update
@<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
respond_to do |format|
if @<%= orm_instance.update_attributes("params[:#{singular_table_name}]") %>
- format.html { redirect_to(@<%= singular_table_name %>, :notice => '<%= human_name %> was successfully updated.') }
- format.xml { head :ok }
+ format.html { redirect_to @<%= singular_table_name %>, <%= key_value :notice, "'#{human_name} was successfully updated.'" %> }
+ format.json { head :ok }
else
- format.html { render :action => "edit" }
- format.xml { render :xml => @<%= orm_instance.errors %>, :status => :unprocessable_entity }
+ format.html { render <%= key_value :action, '"edit"' %> }
+ format.json { render <%= key_value :json, "@#{orm_instance.errors}" %>, <%= key_value :status, ':unprocessable_entity' %> }
end
end
end
# DELETE <%= route_url %>/1
- # DELETE <%= route_url %>/1.xml
+ # DELETE <%= route_url %>/1.json
def destroy
@<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
@<%= orm_instance.destroy %>
respond_to do |format|
- format.html { redirect_to(<%= index_helper %>_url) }
- format.xml { head :ok }
+ format.html { redirect_to <%= index_helper %>_url }
+ format.json { head :ok }
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
index 964d59d84c66b..01fe6dda7a2ee 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
@@ -19,30 +19,30 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
test "should create <%= singular_table_name %>" do
assert_difference('<%= class_name %>.count') do
- post :create, :<%= singular_table_name %> => @<%= singular_table_name %>.attributes
+ post :create, <%= key_value singular_table_name, "@#{singular_table_name}.attributes" %>
end
assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>))
end
test "should show <%= singular_table_name %>" do
- get :show, :id => @<%= singular_table_name %>.to_param
+ get :show, <%= key_value :id, "@#{singular_table_name}.to_param" %>
assert_response :success
end
test "should get edit" do
- get :edit, :id => @<%= singular_table_name %>.to_param
+ get :edit, <%= key_value :id, "@#{singular_table_name}.to_param" %>
assert_response :success
end
test "should update <%= singular_table_name %>" do
- put :update, :id => @<%= singular_table_name %>.to_param, :<%= singular_table_name %> => @<%= singular_table_name %>.attributes
+ put :update, <%= key_value :id, "@#{singular_table_name}.to_param" %>, <%= key_value singular_table_name, "@#{singular_table_name}.attributes" %>
assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>))
end
test "should destroy <%= singular_table_name %>" do
assert_difference('<%= class_name %>.count', -1) do
- delete :destroy, :id => @<%= singular_table_name %>.to_param
+ delete :destroy, <%= key_value :id, "@#{singular_table_name}.to_param" %>
end
assert_redirected_to <%= index_helper %>_path
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 591fd6f6bd33c..302206bbcdeac 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -11,7 +11,7 @@
#
# Annotations are looked for in comments and modulus whitespace they have to
# start with the tag optionally followed by a colon. Everything up to the end
-# of the line (or closing ERb comment tag) is considered to be their text.
+# of the line (or closing ERB comment tag) is considered to be their text.
class SourceAnnotationExtractor
class Annotation < Struct.new(:line, :tag, :text)
@@ -99,4 +99,4 @@ def display(results, options={})
puts
end
end
-end
\ No newline at end of file
+end
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 00029e627e32d..41485c8bac76b 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -13,6 +13,18 @@
Test::Unit::Util::BacktraceFilter.module_eval { include Rails::BacktraceFilterForTestUnit }
end
+if defined?(MiniTest)
+ # Enable turn if it is available
+ begin
+ require 'turn'
+
+ if MiniTest::Unit.respond_to?(:use_natural_language_case_names=)
+ MiniTest::Unit.use_natural_language_case_names = true
+ end
+ rescue LoadError
+ end
+end
+
if defined?(ActiveRecord)
require 'active_record/test_case'
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index c51fe856bea9f..cd0646b8ed8ec 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -17,7 +17,6 @@ Gem::Specification.new do |s|
s.require_path = 'lib'
s.rdoc_options << '--exclude' << '.'
- s.has_rdoc = false
s.add_dependency('rake', '>= 0.8.7')
s.add_dependency('thor', '~> 0.14.4')
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 044fd2a278293..62697b1bf9cb4 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -1,5 +1,18 @@
require "isolation/abstract_unit"
+class ::MyMailInterceptor
+ def self.delivering_email(email); email; end
+end
+
+class ::MyOtherMailInterceptor < ::MyMailInterceptor; end
+
+class ::MyMailObserver
+ def self.delivered_email(email); email; end
+end
+
+class ::MyOtherMailObserver < ::MyMailObserver; end
+
+
module ApplicationTests
class ConfigurationTest < Test::Unit::TestCase
include ActiveSupport::Testing::Isolation
@@ -245,6 +258,80 @@ def index
assert_equal res, last_response.body # value should be unchanged
end
+ test "registers interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.interceptors = MyMailInterceptor
+ RUBY
+
+ require "#{app_path}/config/environment"
+ require "mail"
+
+ ActionMailer::Base
+
+ assert_equal [::MyMailInterceptor], ::Mail.send(:class_variable_get, "@@delivery_interceptors")
+ end
+
+ test "registers multiple interceptors with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.interceptors = [MyMailInterceptor, "MyOtherMailInterceptor"]
+ RUBY
+
+ require "#{app_path}/config/environment"
+ require "mail"
+
+ ActionMailer::Base
+
+ assert_equal [::MyMailInterceptor, ::MyOtherMailInterceptor], ::Mail.send(:class_variable_get, "@@delivery_interceptors")
+ end
+
+ test "registers observers with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.observers = MyMailObserver
+ RUBY
+
+ require "#{app_path}/config/environment"
+ require "mail"
+
+ ActionMailer::Base
+
+ assert_equal [::MyMailObserver], ::Mail.send(:class_variable_get, "@@delivery_notification_observers")
+ end
+
+ test "registers multiple observers with ActionMailer" do
+ add_to_config <<-RUBY
+ config.action_mailer.observers = [MyMailObserver, "MyOtherMailObserver"]
+ RUBY
+
+ require "#{app_path}/config/environment"
+ require "mail"
+
+ ActionMailer::Base
+
+ assert_equal [::MyMailObserver, ::MyOtherMailObserver], ::Mail.send(:class_variable_get, "@@delivery_notification_observers")
+ end
+
+ test "valid timezone is setup correctly" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.time_zone = "Wellington"
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ assert_equal "Wellington", Rails.application.config.time_zone
+ end
+
+ test "raises when an invalid timezone is defined in the config" do
+ add_to_config <<-RUBY
+ config.root = "#{app_path}"
+ config.time_zone = "That big hill over yonder hill"
+ RUBY
+
+ assert_raise(ArgumentError) do
+ require "#{app_path}/config/environment"
+ end
+ end
+
test "config.action_controller.perform_caching = false" do
make_basic_app do |app|
app.config.action_controller.perform_caching = false
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 018c2fa6bf7fb..43f2fbd71c990 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -216,6 +216,24 @@ def test_test_unit_is_removed_from_frameworks_if_skip_test_unit_is_given
end
end
+ def test_new_hash_style
+ run_generator [destination_root]
+ assert_file "config/initializers/session_store.rb" do |file|
+ if RUBY_VERSION < "1.9"
+ assert_match /config.session_store :cookie_store, :key => '_.+_session'/, file
+ else
+ assert_match /config.session_store :cookie_store, key: '_.+_session'/, file
+ end
+ end
+ end
+
+ def test_force_old_style_hash
+ run_generator [destination_root, "--old-style-hash"]
+ assert_file "config/initializers/session_store.rb" do |file|
+ assert_match /config.session_store :cookie_store, :key => '_.+_session'/, file
+ end
+ end
+
protected
def action(*args, &block)
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index d55ed22975844..c7f45a807d27b 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -122,4 +122,22 @@ def self.all(klass)
ensure
Unknown::Generators.send :remove_const, :ActiveModel
end
+
+ def test_new_hash_style
+ run_generator
+ assert_file "app/controllers/users_controller.rb" do |content|
+ if RUBY_VERSION < "1.9"
+ assert_match /\{ render :action => "new" \}/, content
+ else
+ assert_match /\{ render action: "new" \}/, content
+ end
+ end
+ end
+
+ def test_force_old_style_hash
+ run_generator ["User", "--old-style-hash"]
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_match /\{ render :action => "new" \}/, content
+ end
+ end
end