diff options
32 files changed, 336 insertions, 52 deletions
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index a073018b42..bd75a27335 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,4 +1,4 @@ -## Rails 3.2.3 (unreleased) ## +## Rails 3.2.3 (March 30, 2012) ## * Upgrade mail version to 2.4.3 *ML* diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 566b6af378..4acd39eb72 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,4 +1,11 @@ -## Rails 3.2.3 (unreleased) ## +## Rails 3.2.4 (unreleased) ## + +* Deprecate `button_to_function` and `link_to_function` helpers. *Rafael Mendonça França* + + +## Rails 3.2.3 (March 30, 2012) ## + +* Allow to lazy load `default_form_builder` by passing a `String` instead of a constant. *Piotr Sarnacki* * Fix #5632, render :inline set the proper rendered format. *Santiago Pastorino* diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 5c28a8074f..1ab436a205 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -167,8 +167,9 @@ module ActionController unless options[:include] || options[:exclude] model ||= _default_wrap_model - if model.respond_to?(:accessible_attributes) && model.accessible_attributes.present? - options[:include] = model.accessible_attributes.to_a + role = options.has_key?(:as) ? options[:as] : :default + if model.respond_to?(:accessible_attributes) && model.accessible_attributes(role).present? + options[:include] = model.accessible_attributes(role).to_a elsif model.respond_to?(:attribute_names) && model.attribute_names.present? options[:include] = model.attribute_names end diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 3ffb7ef426..9abb86caf8 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -45,6 +45,16 @@ module ActionController # integer, or a symbol representing the downcased, underscored and symbolized description. # Note that the status code must be a 3xx HTTP code, or redirection will not occur. # + # If you are using XHR requests other than GET or POST and redirecting after the + # request then some browsers will follow the redirect using the original request + # method. This may lead to undesirable behavior such as a double DELETE. To work + # around this you can return a <tt>303 See Other</tt> status code which will be + # followed using a GET request. + # + # Examples: + # redirect_to posts_url, :status => :see_other + # redirect_to :action => 'index', :status => 303 + # # It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names # +alert+ and +notice+ as well as a general purpose +flash+ bucket. # diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index d641fc6345..67c55a7f40 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -147,17 +147,23 @@ module ActionController extra_keys = routes.extra_keys(parameters) non_path_parameters = get? ? query_parameters : request_parameters parameters.each do |key, value| - if value.is_a? Fixnum - value = value.to_s - elsif value.is_a? Array - value = Result.new(value.map { |v| v.is_a?(String) ? v.dup : v }) - elsif value.is_a? String + if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?)) + value = value.map{ |v| v.duplicable? ? v.dup : v } + elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? }) + value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }] + elsif value.frozen? && value.duplicable? value = value.dup end if extra_keys.include?(key.to_sym) non_path_parameters[key] = value else + if value.is_a?(Array) + value = Result.new(value.map(&:to_param)) + else + value = value.to_param + end + path_parameters[key.to_s] = value end end @@ -426,7 +432,7 @@ module ActionController def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') # Ensure that numbers and symbols passed as params are converted to # proper params, as is the case when engaging rack. - parameters = paramify_values(parameters) + parameters = paramify_values(parameters) if html_format?(parameters) # Sanity check for required instance variables so we can give an # understandable error message. @@ -454,7 +460,6 @@ module ActionController @request.session = ActionController::TestSession.new(session) if session @request.session["flash"] = @request.flash.update(flash || {}) - @request.session["flash"].sweep @controller.request = @request build_request_uri(action, parameters) @@ -507,6 +512,12 @@ module ActionController @request.env["QUERY_STRING"] = query_string || "" end end + + def html_format?(parameters) + return true unless parameters.is_a?(Hash) + format = Mime[parameters[:format]] + format.nil? || format.html? + end end # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index ef5d207b26..cba98544bb 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -35,6 +35,10 @@ module ActionDispatch @env["action_dispatch.request.path_parameters"] ||= {} end + def reset_parameters #:nodoc: + @env.delete("action_dispatch.request.parameters") + end + private # TODO: Validate that the characters are UTF-8. If they aren't, diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index bc5b163931..6f97c06b6b 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -4,7 +4,7 @@ module ActionDispatch # read a notice you put there or <tt>flash["notice"] = "hello"</tt> # to put a new one. def flash - @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new) + @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new).tap(&:sweep) end end @@ -235,10 +235,6 @@ module ActionDispatch end def call(env) - if (session = env['rack.session']) && (flash = session['flash']) - flash.sweep - end - @app.call(env) ensure session = env['rack.session'] || {} @@ -255,7 +251,8 @@ module ActionDispatch env[KEY] = new_hash end - if session.key?('flash') && session['flash'].empty? + if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?) + session.key?('flash') && session['flash'].empty? session.delete('flash') end end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 4c018968bf..82062c0cd5 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -34,6 +34,8 @@ module ActionDispatch } return true + ensure + req.reset_parameters end def call(env) diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb index 4d98f20826..dc28389360 100644 --- a/actionpack/lib/action_dispatch/routing/redirection.rb +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -1,4 +1,6 @@ require 'action_dispatch/http/request' +require 'active_support/core_ext/uri' +require 'rack/utils' module ActionDispatch module Routing @@ -46,8 +48,17 @@ module ActionDispatch :params => request.query_parameters }.merge options + if !params.empty? && url_options[:path].match(/%\{\w*\}/) + url_options[:path] = (url_options[:path] % escape_path(params)) + end + ActionDispatch::Http::URL.url_for url_options end + + private + def escape_path(params) + Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }] + end end module Redirection @@ -93,7 +104,7 @@ module ActionDispatch path = args.shift block = lambda { |params, request| - (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) + (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % escape(params)) } if String === path block = path if path.respond_to? :call @@ -110,6 +121,11 @@ module ActionDispatch Redirect.new status, block end + + private + def escape(params) + Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }] + end end end end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb index dd4e9ae4cc..35f91cec18 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb @@ -1,5 +1,6 @@ require 'thread' require 'active_support/core_ext/file' +require 'active_support/core_ext/module/attribute_accessors' module ActionView module Helpers diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 252fe1ed03..a3409ee3c7 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -9,6 +9,7 @@ require 'active_support/core_ext/module/method_names' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/array/extract_options' +require 'active_support/core_ext/string/inflections' module ActionView # = Action View Form Helpers @@ -959,9 +960,14 @@ module ActionView object_name = ActiveModel::Naming.param_key(object) end - builder = options[:builder] || ActionView::Base.default_form_builder + builder = options[:builder] || default_form_builder builder.new(object_name, object, self, options, block) end + + def default_form_builder + builder = ActionView::Base.default_form_builder + builder.respond_to?(:constantize) ? builder.constantize : builder + end end class InstanceTag diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 842f4c23a3..042ac4e9ee 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -82,6 +82,8 @@ module ActionView # # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" /> # def button_to_function(name, function=nil, html_options={}) + ActiveSupport::Deprecation.warn("button_to_function is deprecated and will be removed from Rails 4.0") + onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick)) @@ -100,6 +102,8 @@ module ActionView # # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a> # def link_to_function(name, function, html_options={}) + ActiveSupport::Deprecation.warn("link_to_function is deprecated and will be removed from Rails 4.0") + onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" href = html_options[:href] || '#' diff --git a/actionpack/lib/sprockets/static_compiler.rb b/actionpack/lib/sprockets/static_compiler.rb index 32a9d66e6e..2e2db4b760 100644 --- a/actionpack/lib/sprockets/static_compiler.rb +++ b/actionpack/lib/sprockets/static_compiler.rb @@ -16,6 +16,9 @@ module Sprockets def compile manifest = {} env.each_logical_path do |logical_path| + if File.basename(logical_path)[/[^\.]+/, 0] == 'index' + logical_path.sub!(/\/index\./, '.') + end next unless compile_path?(logical_path) if asset = env.find_asset(logical_path) manifest[logical_path] = write_asset(asset) diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index c4d2614200..fa1608b9df 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -174,7 +174,7 @@ class ParamsWrapperTest < ActionController::TestCase def test_accessible_wrapped_keys_from_matching_model User.expects(:respond_to?).with(:accessible_attributes).returns(true) - User.expects(:accessible_attributes).twice.returns(["username"]) + User.expects(:accessible_attributes).with(:default).twice.returns(["username"]) with_default_wrapper_options do @request.env['CONTENT_TYPE'] = 'application/json' @@ -186,7 +186,7 @@ class ParamsWrapperTest < ActionController::TestCase def test_accessible_wrapped_keys_from_specified_model with_default_wrapper_options do Person.expects(:respond_to?).with(:accessible_attributes).returns(true) - Person.expects(:accessible_attributes).twice.returns(["username"]) + Person.expects(:accessible_attributes).with(:default).twice.returns(["username"]) UsersController.wrap_parameters Person @@ -195,6 +195,19 @@ class ParamsWrapperTest < ActionController::TestCase assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'person' => { 'username' => 'sikachu' }}) end end + + def test_accessible_wrapped_keys_with_role_from_specified_model + with_default_wrapper_options do + Person.expects(:respond_to?).with(:accessible_attributes).returns(true) + Person.expects(:accessible_attributes).with(:admin).twice.returns(["username"]) + + UsersController.wrap_parameters Person, :as => :admin + + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, { 'username' => 'sikachu', 'title' => 'Developer' } + assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'person' => { 'username' => 'sikachu' }}) + end + end def test_not_wrapping_abstract_model User.expects(:respond_to?).with(:accessible_attributes).returns(false) diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index b64e275363..6a5843f9d7 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -46,6 +46,10 @@ class TestTest < ActionController::TestCase render :text => request.fullpath end + def test_format + render :text => request.format + end + def test_query_string render :text => request.query_string end @@ -527,14 +531,34 @@ XML ) end + def test_params_passing_with_fixnums_when_not_html_request + get :test_params, :format => 'json', :count => 999 + parsed_params = eval(@response.body) + assert_equal( + {'controller' => 'test_test/test', 'action' => 'test_params', + 'format' => 'json', 'count' => 999 }, + parsed_params + ) + end + + def test_params_passing_path_parameter_is_string_when_not_html_request + get :test_params, :format => 'json', :id => 1 + parsed_params = eval(@response.body) + assert_equal( + {'controller' => 'test_test/test', 'action' => 'test_params', + 'format' => 'json', 'id' => '1' }, + parsed_params + ) + end + def test_params_passing_with_frozen_values assert_nothing_raised do - get :test_params, :frozen => 'icy'.freeze, :frozens => ['icy'.freeze].freeze + get :test_params, :frozen => 'icy'.freeze, :frozens => ['icy'.freeze].freeze, :deepfreeze => { :frozen => 'icy'.freeze }.freeze end parsed_params = eval(@response.body) assert_equal( {'controller' => 'test_test/test', 'action' => 'test_params', - 'frozen' => 'icy', 'frozens' => ['icy']}, + 'frozen' => 'icy', 'frozens' => ['icy'], 'deepfreeze' => { 'frozen' => 'icy' }}, parsed_params ) end @@ -635,6 +659,20 @@ XML assert_equal "http://", @response.body end + def test_request_format + get :test_format, :format => 'html' + assert_equal 'text/html', @response.body + + get :test_format, :format => 'json' + assert_equal 'application/json', @response.body + + get :test_format, :format => 'xml' + assert_equal 'application/xml', @response.body + + get :test_format + assert_equal 'text/html', @response.body + end + def test_should_have_knowledge_of_client_side_cookie_state_even_if_they_are_not_set cookies['foo'] = 'bar' get :no_op diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 50b959cc39..5a6be058ec 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -63,8 +63,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match 'secure', :to => redirect("/secure/login") match 'mobile', :to => redirect(:subdomain => 'mobile') + match 'documentation', :to => redirect(:domain => 'example-documentation.com', :path => '') + match 'new_documentation', :to => redirect(:path => '/documentation/new') match 'super_new_documentation', :to => redirect(:host => 'super-docs.com') + match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}') + match 'stores/:name(*rest)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{rest}') + match 'youtube_favorites/:youtube_id/:name', :to => redirect(YoutubeFavoritesRedirector) constraints(lambda { |req| true }) do @@ -727,6 +732,20 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_redirect_hash_with_domain_and_path + with_test_routes do + get '/documentation' + verify_redirect 'http://www.example-documentation.com' + end + end + + def test_redirect_hash_with_path + with_test_routes do + get '/new_documentation' + verify_redirect 'http://www.example.com/documentation/new' + end + end + def test_redirect_hash_with_host with_test_routes do get '/super_new_documentation?section=top' @@ -734,6 +753,20 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_redirect_hash_path_substitution + with_test_routes do + get '/stores/iernest' + verify_redirect 'http://stores.example.com/iernest' + end + end + + def test_redirect_hash_path_substitution_with_catch_all + with_test_routes do + get '/stores/iernest/products' + verify_redirect 'http://stores.example.com/iernest/products' + end + end + def test_redirect_class with_test_routes do get '/youtube_favorites/oHg5SJYRHA0/rick-rolld' @@ -2635,3 +2668,56 @@ class TestMultipleNestedController < ActionDispatch::IntegrationTest end +class TestRedirectInterpolation < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] } + + get "/foo/:id" => redirect("/foo/bar/%{id}") + get "/bar/:id" => redirect(:path => "/foo/bar/%{id}") + get "/foo/bar/:id" => ok + end + end + + def app; Routes end + + test "redirect escapes interpolated parameters with redirect proc" do + get "/foo/1%3E" + verify_redirect "http://www.example.com/foo/bar/1%3E" + end + + test "redirect escapes interpolated parameters with option proc" do + get "/bar/1%3E" + verify_redirect "http://www.example.com/foo/bar/1%3E" + end + +private + def verify_redirect(url, status=301) + assert_equal status, @response.status + assert_equal url, @response.headers['Location'] + assert_equal expected_redirect_body(url), @response.body + end + + def expected_redirect_body(url) + %(<html><body>You are being <a href="#{ERB::Util.h(url)}">redirected</a>.</body></html>) + end +end + +class TestConstraintsAccessingParameters < ActionDispatch::IntegrationTest + Routes = ActionDispatch::Routing::RouteSet.new.tap do |app| + app.draw do + ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] } + + get "/:foo" => ok, :constraints => lambda { |r| r.params[:foo] == 'foo' } + get "/:bar" => ok + end + end + + def app; Routes end + + test "parameters are reset between constraint checks" do + get "/bar" + assert_equal nil, @request.params[:foo] + assert_equal "bar", @request.params[:bar] + end +end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 7eeede7087..19af01e2c8 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -2033,6 +2033,23 @@ class FormHelperTest < ActionView::TestCase ActionView::Base.default_form_builder = old_default_form_builder end + def test_lazy_loading_default_form_builder + old_default_form_builder, ActionView::Base.default_form_builder = + ActionView::Base.default_form_builder, "FormHelperTest::LabelledFormBuilder" + + form_for(@post) do |f| + concat f.text_field(:title) + end + + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do + "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" + end + + assert_dom_equal expected, output_buffer + ensure + ActionView::Base.default_form_builder = old_default_form_builder + end + def test_fields_for_with_labelled_builder output_buffer = fields_for(:post, @post, :builder => LabelledFormBuilder) do |f| concat f.text_field(:title) diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index 4b9c3c97b1..91968861fa 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -46,33 +46,45 @@ class JavaScriptHelperTest < ActionView::TestCase end def test_button_to_function - assert_dom_equal %(<input type="button" onclick="alert('Hello world!');" value="Greeting" />), - button_to_function("Greeting", "alert('Hello world!')") + assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.0" do + assert_dom_equal %(<input type="button" onclick="alert('Hello world!');" value="Greeting" />), + button_to_function("Greeting", "alert('Hello world!')") + end end def test_button_to_function_with_onclick - assert_dom_equal "<input onclick=\"alert('Goodbye World :('); alert('Hello world!');\" type=\"button\" value=\"Greeting\" />", - button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')") + assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.0" do + assert_dom_equal "<input onclick=\"alert('Goodbye World :('); alert('Hello world!');\" type=\"button\" value=\"Greeting\" />", + button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')") + end end def test_button_to_function_without_function - assert_dom_equal "<input onclick=\";\" type=\"button\" value=\"Greeting\" />", - button_to_function("Greeting") + assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.0" do + assert_dom_equal "<input onclick=\";\" type=\"button\" value=\"Greeting\" />", + button_to_function("Greeting") + end end def test_link_to_function - assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>), - link_to_function("Greeting", "alert('Hello world!')") + assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.0" do + assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>), + link_to_function("Greeting", "alert('Hello world!')") + end end def test_link_to_function_with_existing_onclick - assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>), - link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')") + assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.0" do + assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>), + link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')") + end end def test_function_with_href - assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>), - link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/') + assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.0" do + assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>), + link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/') + end end def test_javascript_tag diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index b4db6d9a71..f33471ab28 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -103,7 +103,7 @@ class RecordTagHelperTest < ActionView::TestCase def test_content_tag_for_does_not_change_options_hash options = { :class => "important" } - result = content_tag_for(:li, @post, options) { } + content_tag_for(:li, @post, options) { } assert_equal({ :class => "important" }, options) end end diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index d3ea4c85dc..aeefc1723a 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,4 +1,4 @@ -## Rails 3.2.3 (unreleased) ## +## Rails 3.2.3 (March 30, 2012) ## * No changes. diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index ffd74893be..2f4a0873b0 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -4,7 +4,7 @@ This could cause infinite recursion and potentially other problems. See GH #5667. *Jon Leighton* -## Rails 3.2.3 (unreleased) ## +## Rails 3.2.3 (March 30, 2012) ## * Added find_or_create_by_{attribute}! dynamic method. *Andrew White* diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index a0e2900120..c3aba63e1c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -269,6 +269,12 @@ module ActiveRecord # remove_column(:suppliers, :qualification) # remove_columns(:suppliers, :qualification, :experience) def remove_column(table_name, *column_names) + if column_names.first.kind_of?(Enumerable) + message = 'Passing array to remove_columns is deprecated, please use ' + + 'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`' + ActiveSupport::Deprecation.warn message, caller + end + columns_for_remove(table_name, *column_names).each {|column_name| execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}" } end alias :remove_columns :remove_column diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 55bffc6518..91929f80d2 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -407,6 +407,13 @@ module ActiveRecord def remove_column(table_name, *column_names) #:nodoc: raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty? + + if column_names.first.kind_of?(Enumerable) + message = 'Passing array to remove_columns is deprecated, please use ' + + 'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`' + ActiveSupport::Deprecation.warn message, caller + end + column_names.flatten.each do |column_name| alter_table(table_name) do |definition| definition.columns.delete(definition[column_name]) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 5c5c872993..8dde6c6b5a 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -873,6 +873,12 @@ if ActiveRecord::Base.connection.supports_migrations? assert_raise(ArgumentError) { Person.connection.remove_column("funny") } end + def test_remove_column_with_array_as_an_argument_is_deprecated + assert_deprecated /Passing array to remove_columns is deprecated/ do + Person.connection.remove_column("people", ["last_name", "description"]) + end + end + def test_change_type_of_not_null_column assert_nothing_raised do Topic.connection.change_column "topics", "written_on", :datetime, :null => false diff --git a/activeresource/CHANGELOG.md b/activeresource/CHANGELOG.md index ef957c68ed..e09cd2a772 100644 --- a/activeresource/CHANGELOG.md +++ b/activeresource/CHANGELOG.md @@ -1,4 +1,4 @@ -## Rails 3.2.3 (unreleased) ## +## Rails 3.2.3 (March 30, 2012) ## * No changes. diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index f35c147e60..169f4b8077 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,4 +1,4 @@ -## Rails 3.2.3 (unreleased) ## +## Rails 3.2.3 (March 30, 2012) ## * No changes. diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 35f400f9df..c7d8fc2e17 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -28,7 +28,7 @@ module ActiveSupport MAPPING = { "International Date Line West" => "Pacific/Midway", "Midway Island" => "Pacific/Midway", - "Samoa" => "Pacific/Pago_Pago", + "American Samoa" => "Pacific/Pago_Pago", "Hawaii" => "Pacific/Honolulu", "Alaska" => "America/Juneau", "Pacific Time (US & Canada)" => "America/Los_Angeles", @@ -167,7 +167,9 @@ module ActiveSupport "Marshall Is." => "Pacific/Majuro", "Auckland" => "Pacific/Auckland", "Wellington" => "Pacific/Auckland", - "Nuku'alofa" => "Pacific/Tongatapu" + "Nuku'alofa" => "Pacific/Tongatapu", + "Tokelau Is." => "Pacific/Fakaofo", + "Samoa" => "Pacific/Apia" }.each { |name, zone| name.freeze; zone.freeze } MAPPING.freeze diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile index 6419d32c13..0e811a2527 100644 --- a/railties/guides/source/caching_with_rails.textile +++ b/railties/guides/source/caching_with_rails.textile @@ -257,7 +257,9 @@ However, it's important to note that query caches are created at the start of an h3. Cache Stores -Rails provides different stores for the cached data created by action and fragment caches. Page caches are always stored on disk. +Rails provides different stores for the cached data created by <b>action</b> and <b>fragment</b> caches. + +TIP: Page caches are always stored on disk. h4. Configuration @@ -267,7 +269,7 @@ You can set up your application's default cache store by calling +config.cache_s config.cache_store = :memory_store </ruby> -Alternatively, you can call +ActionController::Base.cache_store+ outside of a configuration block. +NOTE: Alternatively, you can call +ActionController::Base.cache_store+ outside of a configuration block. You can access the cache by calling +Rails.cache+. @@ -294,7 +296,7 @@ h4. ActiveSupport::Cache::MemoryStore This cache store keeps entries in memory in the same Ruby process. The cache store has a bounded size specified by the +:size+ options to the initializer (default is 32Mb). When the cache exceeds the allotted size, a cleanup will occur and the least recently used entries will be removed. <ruby> -ActionController::Base.cache_store = :memory_store, :size => 64.megabytes +config.cache_store = :memory_store, :size => 64.megabytes </ruby> If you're running multiple Ruby on Rails server processes (which is the case if you're using mongrel_cluster or Phusion Passenger), then your Rails server process instances won't be able to share cache data with each other. This cache store is not appropriate for large application deployments, but can work well for small, low traffic sites with only a couple of server processes or for development and test environments. @@ -306,7 +308,7 @@ h4. ActiveSupport::Cache::FileStore This cache store uses the file system to store entries. The path to the directory where the store files will be stored must be specified when initializing the cache. <ruby> -ActionController::Base.cache_store = :file_store, "/path/to/cache/directory" +config.cache_store = :file_store, "/path/to/cache/directory" </ruby> With this cache store, multiple server processes on the same host can share a cache. Servers processes running on different hosts could share a cache by using a shared file system, but that set up would not be ideal and is not recommended. The cache store is appropriate for low to medium traffic sites that are served off one or two hosts. @@ -322,7 +324,7 @@ When initializing the cache, you need to specify the addresses for all memcached The +write+ and +fetch+ methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify +:raw+ to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operation like +increment+ and +decrement+ only on raw values. You can also specify +:unless_exist+ if you don't want memcached to overwrite an existing entry. <ruby> -ActionController::Base.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com" +config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com" </ruby> h4. ActiveSupport::Cache::EhcacheStore @@ -330,7 +332,7 @@ h4. ActiveSupport::Cache::EhcacheStore If you are using JRuby you can use Terracotta's Ehcache as the cache store for your application. Ehcache is an open source Java cache that also offers an enterprise version with increased scalability, management, and commercial support. You must first install the jruby-ehcache-rails3 gem (version 1.1.0 or later) to use this cache store. <ruby> -ActionController::Base.cache_store = :ehcache_store +config.cache_store = :ehcache_store </ruby> When initializing the cache, you may use the +:ehcache_config+ option to specify the Ehcache config file to use (where the default is "ehcache.xml" in your Rails config directory), and the :cache_name option to provide a custom name for your cache (the default is rails_cache). @@ -359,7 +361,7 @@ h4. ActiveSupport::Cache::NullStore This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with +Rails.cache+, but caching may interfere with being able to see the results of code changes. With this cache store, all +fetch+ and +read+ operations will result in a miss. <ruby> -ActionController::Base.cache_store = :null +config.cache_store = :null_store </ruby> h4. Custom Cache Stores @@ -369,7 +371,7 @@ You can create your own custom cache store by simply extending +ActiveSupport::C To use a custom cache store, simple set the cache store to a new instance of the class. <ruby> -ActionController::Base.cache_store = MyCacheStore.new +config.cache_store = MyCacheStore.new </ruby> h4. Cache Keys diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 60034cdda5..3466e1c3f4 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -345,7 +345,7 @@ There are only a few configuration options for Action View, starting with three Proc.new { |html_tag, instance| %Q(<div class="field_with_errors">#{html_tag}</div>).html_safe } </ruby> -* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+. +* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+. If you want your form builder class to be loaded after initialization (so it's reloaded on each request in development), you can pass it as a +String+ * +config.action_view.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information. diff --git a/railties/railties.gemspec b/railties/railties.gemspec index a7b9407daf..6101b8647a 100644 --- a/railties/railties.gemspec +++ b/railties/railties.gemspec @@ -21,8 +21,12 @@ Gem::Specification.new do |s| s.rdoc_options << '--exclude' << '.' s.add_dependency('rake', '>= 0.8.7') - s.add_dependency('thor', '~> 0.14.6') s.add_dependency('rack-ssl', '~> 1.3.2') + + # The current API of the Thor gem (0.14) will remain stable at least until Thor 2.0. Because + # Thor is so heavily used by other gems, we will accept Thor's semver guarantee to reduce + # the possibility of conflicts. + s.add_dependency('thor', '>= 0.14.6', '< 2.0') s.add_dependency('rdoc', '~> 3.4') s.add_dependency('activesupport', version) s.add_dependency('actionpack', version) diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index c864d70fa2..673a403fd3 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -110,6 +110,15 @@ module ApplicationTests assert !File.exists?("#{app_path}/public/assets/something.else.css") end + test "precompile something.js for directory containing index file" do + add_to_config "config.assets.precompile = [ 'something.js' ]" + app_file "app/assets/javascripts/something/index.js.erb", "alert();" + + precompile! + + assert File.exists?("#{app_path}/public/assets/something.js") + end + test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do add_to_config "config.assets.digest = true" add_to_config "config.action_controller.perform_caching = false" diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index 9f39e18a74..aee2417abc 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -26,5 +26,25 @@ module ApplicationTests require "#{app_path}/config/environment" assert app.config.session_options[:secure], "Expected session to be marked as secure" end + + test "session is not loaded if it's not used" do + make_basic_app + + class ::OmgController < ActionController::Base + def index + if params[:flash] + flash[:notice] = "notice" + end + + render :nothing => true + end + end + + get "/?flash=true" + get "/" + + assert last_request.env["HTTP_COOKIE"] + assert !last_response.headers["Set-Cookie"] + end end end |