diff options
Diffstat (limited to 'actionpack')
26 files changed, 381 insertions, 39 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 4d7035e63f..6917d0e06c 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,77 @@ +## Rails 3.2.11 (unreleased) ## + +* Clear url helper methods when routes are reloaded by removing the methods + explicitly rather than just clearing the module because it didn't work + properly and could be the source of a memory leak. + + *Andrew White* + +* Fix a bug in ActionDispatch::Request#raw_post that caused env['rack.input'] + to be read but not rewound. + + *Matt Venables* + +* More descriptive error messages when calling `render :partial` with + an invalid `:layout` argument. + #8376 + + render :partial => 'partial', :layout => true + + # results in ActionView::MissingTemplate: Missing partial /true + + *Yves Senn* + +* Accept symbols as #send_data :disposition value. [Backport #8329] *Elia Schito* + +* Add i18n scope to distance_of_time_in_words. [Backport #7997] *Steve Klabnik* + +* Fix side effect of `url_for` changing the `:controller` string option. [Backport #6003] + Before: + + controller = '/projects' + url_for :controller => controller, :action => 'status' + + puts controller #=> 'projects' + + After + + puts controller #=> '/projects' + + [Nikita Beloglazov + Andrew White] + +* Introduce `ActionView::Template::Handlers::ERB.escape_whitelist`. This is a list + of mime types where template text is not html escaped by default. It prevents `Jack & Joe` + from rendering as `Jack & Joe` for the whitelisted mime types. The default whitelist + contains text/plain. Fix #7976 [Backport #8235] + + *Joost Baaij* + +* `BestStandardsSupport` middleware now appends it's `X-UA-Compatible` value to app's + returned value if any. Fix #8086 [Backport #8093] + + *Nikita Afanasenko* + +* prevent double slashes in engine urls when `Rails.application.default_url_options[:trailing_slash] = true` is set + Fix #7842 + + *Yves Senn* + +* Fix input name when `:multiple => true` and `:index` are set. + + Before: + + check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1) + #=> <input name=\"post[foo][comment_ids]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids]\" type=\"checkbox\" value=\"1\" /> + + After: + + check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1) + #=> <input name=\"post[foo][comment_ids][]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids][]\" type=\"checkbox\" value=\"1\" /> + + Fix #8108 + + *Daniel Fox, Grant Hutchins & Trace Wax* + ## Rails 3.2.10 ## ## Rails 3.2.9 (Nov 12, 2012) ## diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb index 62e16ea047..95c058fc86 100644 --- a/actionpack/lib/action_controller/metal/data_streaming.rb +++ b/actionpack/lib/action_controller/metal/data_streaming.rb @@ -141,7 +141,7 @@ module ActionController #:nodoc: raise ArgumentError, ":#{arg} option required" if options[arg].nil? end - disposition = options[:disposition] + disposition = options[:disposition].to_s disposition += %(; filename="#{options[:filename]}") if options[:filename] content_type = options[:type] diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index afc0496ef9..0413346d94 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -179,8 +179,9 @@ module ActionDispatch # work with raw requests directly. def raw_post unless @env.include? 'RAW_POST_DATA' - @env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i) - body.rewind if body.respond_to?(:rewind) + raw_post_body = body + @env['RAW_POST_DATA'] = raw_post_body.read(@env['CONTENT_LENGTH'].to_i) + raw_post_body.rewind if raw_post_body.respond_to?(:rewind) end @env['RAW_POST_DATA'] end diff --git a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb index 69adcc419f..d338996240 100644 --- a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb +++ b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb @@ -15,7 +15,13 @@ module ActionDispatch def call(env) status, headers, body = @app.call(env) - headers["X-UA-Compatible"] = @header + + if headers["X-UA-Compatible"] && @header + headers["X-UA-Compatible"] << "," << @header.to_s + else + headers["X-UA-Compatible"] = @header + end + [status, headers, body] end end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 952219631a..9a474d2e3a 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -451,7 +451,7 @@ module ActionDispatch # we must actually delete prefix segment keys to avoid passing them to next url_for _route.segment_keys.each { |k| options.delete(k) } prefix = _routes.url_helpers.send("#{name}_path", prefix_options) - prefix = '' if prefix == '/' + prefix = prefix.gsub(%r{/\z}, '') prefix end end diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb index dc28389360..50b20a2a25 100644 --- a/actionpack/lib/action_dispatch/routing/redirection.rb +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -78,10 +78,10 @@ module ActionDispatch # params, depending of how many arguments your block accepts. A string is required as a # return value. # - # match 'jokes/:number', :to => redirect do |params, request| - # path = (params[:number].to_i.even? ? "/wheres-the-beef" : "/i-love-lamp") + # match 'jokes/:number', :to => redirect { |params, request| + # path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp") # "http://#{request.host_with_port}/#{path}" - # end + # } # # The options version of redirect allows you to supply only the parts of the url which need # to change, it also supports interpolation of the path similar to the first example. diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 667094c469..a993699e05 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -94,7 +94,12 @@ module ActionDispatch attr_reader :routes, :helpers, :module def initialize - clear! + @routes = {} + @helpers = [] + + @module = Module.new do + instance_methods.each { |selector| remove_method(selector) } + end end def helper_names @@ -102,12 +107,14 @@ module ActionDispatch end def clear! + @helpers.each do |helper| + @module.module_eval do + remove_possible_method helper + end + end + @routes = {} @helpers = [] - - @module ||= Module.new do - instance_methods.each { |selector| remove_method(selector) } - end end def add(name, route) @@ -291,7 +298,6 @@ module ActionDispatch def clear! @finalized = false - @url_helpers = nil named_routes.clear set.clear formatter.clear @@ -442,12 +448,12 @@ module ActionDispatch normalize_options! normalize_controller_action_id! use_relative_controller! - controller.sub!(%r{^/}, '') if controller + normalize_controller! handle_nil_action! end def controller - @controller ||= @options[:controller] + @options[:controller] end def current_controller @@ -504,10 +510,15 @@ module ActionDispatch old_parts = current_controller.split('/') size = controller.count("/") + 1 parts = old_parts[0...-size] << controller - @controller = @options[:controller] = parts.join("/") + @options[:controller] = parts.join("/") end end + # Remove leading slashes from controllers + def normalize_controller! + @options[:controller] = controller.sub(%r{^/}, '') if controller + end + # This handles the case of :action => nil being explicitly passed. # It is identical to :action => "index" def handle_nil_action! diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 87e2332c42..99aa144d3a 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -65,12 +65,17 @@ module ActionView # distance_of_time_in_words(Time.now, Time.now) # => less than a minute # def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {}) + options = { + :scope => :'datetime.distance_in_words', + }.merge!(options) + from_time = from_time.to_time if from_time.respond_to?(:to_time) to_time = to_time.to_time if to_time.respond_to?(:to_time) - distance_in_minutes = (((to_time - from_time).abs)/60).round - distance_in_seconds = ((to_time - from_time).abs).round + distance = (to_time.to_f - from_time.to_f).abs + distance_in_minutes = (distance / 60.0).round + distance_in_seconds = distance.round - I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale| + I18n.with_options :locale => options[:locale], :scope => options[:scope] do |locale| case distance_in_minutes when 0..1 return distance_in_minutes == 0 ? @@ -129,8 +134,8 @@ module ActionView # from_time = Time.now - 3.days - 14.minutes - 25.seconds # time_ago_in_words(from_time) # => 3 days # - def time_ago_in_words(from_time, include_seconds = false) - distance_of_time_in_words(from_time, Time.now, include_seconds) + def time_ago_in_words(from_time, include_seconds = false, options = {}) + distance_of_time_in_words(from_time, Time.now, include_seconds, options) end alias_method :distance_of_time_in_words_to_now, :time_ago_in_words diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index a3409ee3c7..d00bad7608 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1205,9 +1205,11 @@ module ActionView options["name"] ||= tag_name_with_index(@auto_index) options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) } else - options["name"] ||= tag_name + (options['multiple'] ? '[]' : '') + options["name"] ||= tag_name options["id"] = options.fetch("id"){ tag_id } end + + options["name"] += "[]" if options["multiple"] options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence end diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 670ff18a66..f0573437ca 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -45,7 +45,7 @@ module ActionView # # => <form action="/posts" method="post"> # # form_tag('/posts/1', :method => :put) - # # => <form action="/posts/1" method="put"> + # # => <form action="/posts/1" method="post"> ... <input name="_method" type="hidden" value="put" /> ... # # form_tag('/upload', :multipart => true) # # => <form action="/upload" method="post" enctype="multipart/form-data"> @@ -53,7 +53,7 @@ module ActionView # <%= form_tag('/posts') do -%> # <div><%= submit_tag 'Save' %></div> # <% end -%> - # # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form> + # # => <form action="/posts" method="post"><div><input type="submit" name="commit" value="Save" /></div></form> # # <%= form_tag('/posts', :remote => true) %> # # => <form action="/posts" method="post" data-remote="true"> diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 3093aca91e..71fa05ab3e 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -256,7 +256,7 @@ module ActionView object, as = @object, @variable if !block && (layout = @options[:layout]) - layout = find_template(layout) + layout = find_template(layout.to_s) end object ||= locals[as] diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index ea495ea9ca..118b18a81c 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -15,6 +15,17 @@ module ActionView src << "@output_buffer.safe_concat('" << escape_text(text) << "');" end + # Erubis toggles <%= and <%== behavior when escaping is enabled. + # We override to always treat <%== as escaped. + def add_expr(src, code, indicator) + case indicator + when '==' + add_expr_escaped(src, code) + else + super + end + end + BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/ def add_expr_literal(src, code) @@ -48,6 +59,10 @@ module ActionView class_attribute :erb_implementation self.erb_implementation = Erubis + # Do not escape templates of these mime types. + class_attribute :escape_whitelist + self.escape_whitelist = ["text/plain"] + ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*") def self.call(template) @@ -83,6 +98,7 @@ module ActionView self.class.erb_implementation.new( erb, + :escape => (self.class.escape_whitelist.include? template.mime_type), :trim => (self.class.erb_trim_mode == "-") ).src end diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index 29c8885d9f..c727f03346 100644 --- a/actionpack/test/controller/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -9,7 +9,8 @@ module RenderTemplate "locals.html.erb" => "The secret is <%= secret %>", "xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend", "with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>", - "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %>", + "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a html template", + "with_implicit_raw.text.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a text template", "test/with_json.html.erb" => "<%= render :template => 'test/with_json', :formats => [:json] %>", "test/with_json.json.erb" => "<%= render :template => 'test/final', :formats => [:json] %>", "test/final.json.erb" => "{ final: json }", @@ -113,7 +114,12 @@ module RenderTemplate get :with_implicit_raw - assert_body "Hello <strong>this is also raw</strong>" + assert_body "Hello <strong>this is also raw</strong> in a html template" + assert_status 200 + + get :with_implicit_raw, :format => 'text' + + assert_body "Hello <strong>this is also raw</strong> in a text template" assert_status 200 end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 8f885ff28e..4e4dd8d005 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -118,6 +118,18 @@ class SendFileTest < ActionController::TestCase assert_equal 'private', h['Cache-Control'] end + def test_send_file_headers_with_disposition_as_a_symbol + options = { + :type => Mime::PNG, + :disposition => :disposition, + :filename => 'filename' + } + + @controller.headers = {} + @controller.send(:send_file_headers!, options) + assert_equal 'disposition; filename="filename"', @controller.headers['Content-Disposition'] + end + def test_send_file_headers_with_mime_lookup_with_symbol options = { :type => :png @@ -138,7 +150,7 @@ class SendFileTest < ActionController::TestCase @controller.headers = {} assert_raise(ArgumentError){ @controller.send(:send_file_headers!, options) } end - + def test_send_file_headers_guess_type_from_extension { 'image.png' => 'image/png', diff --git a/actionpack/test/dispatch/best_standards_support_test.rb b/actionpack/test/dispatch/best_standards_support_test.rb new file mode 100644 index 0000000000..0737c40a39 --- /dev/null +++ b/actionpack/test/dispatch/best_standards_support_test.rb @@ -0,0 +1,34 @@ +require 'abstract_unit' + +class BestStandardsSupportTest < ActiveSupport::TestCase + def test_with_best_standards_support + _, headers, _ = app(true, {}).call({}) + assert_equal "IE=Edge,chrome=1", headers["X-UA-Compatible"] + end + + def test_with_builtin_best_standards_support + _, headers, _ = app(:builtin, {}).call({}) + assert_equal "IE=Edge", headers["X-UA-Compatible"] + end + + def test_without_best_standards_support + _, headers, _ = app(false, {}).call({}) + assert_equal nil, headers["X-UA-Compatible"] + end + + def test_appends_to_app_headers + app_headers = { "X-UA-Compatible" => "requiresActiveX=true" } + _, headers, _ = app(true, app_headers).call({}) + + expects = "requiresActiveX=true,IE=Edge,chrome=1" + assert_equal expects, headers["X-UA-Compatible"] + end + + private + + def app(type, headers) + app = proc { [200, headers, "response"] } + ActionDispatch::BestStandardsSupport.new(app, type) + end + +end diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb index bd5b5edab0..88dc2c093b 100644 --- a/actionpack/test/dispatch/prefix_generation_test.rb +++ b/actionpack/test/dispatch/prefix_generation_test.rb @@ -248,6 +248,11 @@ module TestGenerationPrefix assert_equal "/something/", app_object.root_path end + test "[OBJECT] generating application's route includes default_url_options[:trailing_slash]" do + RailsApplication.routes.default_url_options[:trailing_slash] = true + assert_equal "/awesome/blog/posts", engine_object.posts_path + end + test "[OBJECT] generating engine's route with url_for" do path = engine_object.url_for(:controller => "inside_engine_generating", :action => "show", diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 5b3d38c48c..56431b4daf 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -530,6 +530,13 @@ class RequestTest < ActiveSupport::TestCase assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV]) end + test "raw_post rewinds rack.input if RAW_POST_DATA is nil" do + request = stub_request('rack.input' => StringIO.new("foo"), + 'CONTENT_LENGTH' => 3) + assert_equal "foo", request.raw_post + assert_equal "foo", request.env['rack.input'].read + end + test "process parameter filter" do test_hashes = [ [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'], diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb new file mode 100644 index 0000000000..629b8ed8bd --- /dev/null +++ b/actionpack/test/dispatch/routing/route_set_test.rb @@ -0,0 +1,86 @@ +require 'abstract_unit' + +module ActionDispatch + module Routing + class RouteSetTest < ActiveSupport::TestCase + class SimpleApp + def initialize(response) + @response = response + end + + def call(env) + [ 200, { 'Content-Type' => 'text/plain' }, [response] ] + end + end + + setup do + @set = RouteSet.new + end + + test "url helpers are added when route is added" do + draw do + get 'foo', :to => SimpleApp.new('foo#index') + end + + assert_equal '/foo', url_helpers.foo_path + assert_raises NoMethodError do + assert_equal '/bar', url_helpers.bar_path + end + + draw do + get 'foo', :to => SimpleApp.new('foo#index') + get 'bar', :to => SimpleApp.new('bar#index') + end + + assert_equal '/foo', url_helpers.foo_path + assert_equal '/bar', url_helpers.bar_path + end + + test "url helpers are updated when route is updated" do + draw do + get 'bar', :to => SimpleApp.new('bar#index'), :as => :bar + end + + assert_equal '/bar', url_helpers.bar_path + + draw do + get 'baz', :to => SimpleApp.new('baz#index'), :as => :bar + end + + assert_equal '/baz', url_helpers.bar_path + end + + test "url helpers are removed when route is removed" do + draw do + get 'foo', :to => SimpleApp.new('foo#index') + get 'bar', :to => SimpleApp.new('bar#index') + end + + assert_equal '/foo', url_helpers.foo_path + assert_equal '/bar', url_helpers.bar_path + + draw do + get 'foo', :to => SimpleApp.new('foo#index') + end + + assert_equal '/foo', url_helpers.foo_path + assert_raises NoMethodError do + assert_equal '/bar', url_helpers.bar_path + end + end + + private + def clear! + @set.clear! + end + + def draw(&block) + @set.draw(&block) + end + + def url_helpers + @set.url_helpers + end + end + end +end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index e61b4e2d8f..46d16598f7 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -887,6 +887,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal original_options, options end + def test_url_for_does_not_modify_controller + controller = '/projects' + options = {:controller => controller, :action => 'status', :only_path => true} + url = url_for(options) + + assert_equal '/projects/status', url + assert_equal '/projects', controller + end + # tests the arguments modification free version of define_hash_access def test_named_route_with_no_side_effects original_options = { :host => 'test.host' } diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb index d45215acfd..b0b407b473 100644 --- a/actionpack/test/template/date_helper_i18n_test.rb +++ b/actionpack/test/template/date_helper_i18n_test.rb @@ -36,16 +36,25 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase end end - def assert_distance_of_time_in_words_translates_key(passed, expected) + def test_distance_of_time_in_words_calls_i18n_with_custom_scope + { + [30.days, false] => [:'about_x_months', 1], + [60.days, false] => [:'x_months', 2], + }.each do |passed, expected| + assert_distance_of_time_in_words_translates_key(passed, expected, {:scope => :'datetime.distance_in_words_ago'}) + end + end + + def assert_distance_of_time_in_words_translates_key(passed, expected, options = {}) diff, include_seconds = *passed key, count = *expected to = @from + diff - options = {:locale => 'en', :scope => :'datetime.distance_in_words'} + options = {:locale => 'en', :scope => :'datetime.distance_in_words'}.merge(options) options[:count] = count if count I18n.expects(:t).with(key, options) - distance_of_time_in_words(@from, to, include_seconds, :locale => 'en') + distance_of_time_in_words(@from, to, include_seconds, options) end def test_distance_of_time_pluralizations diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index cf707e1de5..e4f84f8dd7 100644 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -128,12 +128,39 @@ class DateHelperTest < ActionView::TestCase end def test_distance_in_words_with_integers - assert_equal "less than a minute", distance_of_time_in_words(59) + assert_equal "1 minute", distance_of_time_in_words(59) assert_equal "about 1 hour", distance_of_time_in_words(60*60) - assert_equal "less than a minute", distance_of_time_in_words(0, 59) + assert_equal "1 minute", distance_of_time_in_words(0, 59) assert_equal "about 1 hour", distance_of_time_in_words(60*60, 0) end + def test_distance_in_words_with_times + assert_equal "1 minute", distance_of_time_in_words(30.seconds) + assert_equal "1 minute", distance_of_time_in_words(59.seconds) + assert_equal "2 minutes", distance_of_time_in_words(119.seconds) + assert_equal "2 minutes", distance_of_time_in_words(1.minute + 59.seconds) + assert_equal "3 minutes", distance_of_time_in_words(2.minute + 30.seconds) + assert_equal "44 minutes", distance_of_time_in_words(44.minutes + 29.seconds) + assert_equal "about 1 hour", distance_of_time_in_words(44.minutes + 30.seconds) + assert_equal "about 1 hour", distance_of_time_in_words(60.minutes) + + # include seconds + assert_equal "half a minute", distance_of_time_in_words(39.seconds, 0, true) + assert_equal "less than a minute", distance_of_time_in_words(40.seconds, 0, true) + assert_equal "less than a minute", distance_of_time_in_words(59.seconds, 0, true) + assert_equal "1 minute", distance_of_time_in_words(60.seconds, 0, true) + end + + def test_distance_in_words_with_offset_datetimes + start_date = DateTime.new 1975, 1, 31, 0, 0, 0, '+6' + end_date = DateTime.new 1977, 1, 31, 0, 0, 0, '+6' + assert_equal("about 2 years", distance_of_time_in_words(start_date, end_date)) + + start_date = DateTime.new 1982, 12, 3, 0, 0, 0, '+6' + end_date = DateTime.new 2010, 11, 30, 0, 0, 0, '+6' + assert_equal("almost 28 years", distance_of_time_in_words(start_date, end_date)) + end + def test_time_ago_in_words assert_equal "about 1 year", time_ago_in_words(1.year.ago - 1.day) end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 19af01e2c8..49a325af79 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -393,6 +393,19 @@ class FormHelperTest < ActionView::TestCase ) end + def test_check_box_with_multiple_behavior_and_index + @post.comment_ids = [2,3] + assert_dom_equal( + '<input name="post[foo][comment_ids][]" type="hidden" value="0" /><input id="post_foo_comment_ids_1" name="post[foo][comment_ids][]" type="checkbox" value="1" />', + check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1) + ) + assert_dom_equal( + '<input name="post[bar][comment_ids][]" type="hidden" value="0" /><input checked="checked" id="post_bar_comment_ids_3" name="post[bar][comment_ids][]" type="checkbox" value="3" />', + check_box("post", "comment_ids", { :multiple => true, :index => "bar" }, 3) + ) + + end + def test_checkbox_disabled_disables_hidden_field assert_dom_equal( '<input name="post[secret]" type="hidden" value="0" disabled="disabled"/><input checked="checked" disabled="disabled" id="post_secret" name="post[secret]" type="checkbox" value="1" />', diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index bfeb6aee1a..b907e3297b 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -336,7 +336,7 @@ module RenderTestCases ActionView::Template.register_template_handler :foo, CustomHandler assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo) end - + def test_render_knows_about_types_registered_when_extensions_are_checked_earlier_in_initialization ActionView::Template::Handlers.extensions ActionView::Template.register_template_handler :foo, CustomHandler @@ -406,6 +406,11 @@ module RenderTestCases @view.render(:partial => 'test/partial_with_layout_block_content', :layout => 'test/layout_for_partial', :locals => { :name => 'Foo!'}) end + def test_render_partial_with_layout_raises_descriptive_error + e = assert_raises(ActionView::MissingTemplate) { @view.render(:partial => 'test/partial', :layout => true) } + assert_match "Missing partial /true with", e.message + end + def test_render_with_nested_layout assert_equal %(<title>title</title>\n\n<div id="column">column</div>\n<div id="content">content</div>\n), @view.render(:file => "test/nested_layout", :layout => "layouts/yield") diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb index 72d03e43e9..e944cfaee3 100644 --- a/actionpack/test/template/sprockets_helper_test.rb +++ b/actionpack/test/template/sprockets_helper_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' require 'sprockets' require 'sprockets/helpers/rails_helper' -require 'mocha' +require 'mocha/setup' class SprocketsHelperTest < ActionView::TestCase include Sprockets::Helpers::RailsHelper diff --git a/actionpack/test/template/sprockets_helper_with_routes_test.rb b/actionpack/test/template/sprockets_helper_with_routes_test.rb index bcbd81a7dd..bc253ea0fd 100644 --- a/actionpack/test/template/sprockets_helper_with_routes_test.rb +++ b/actionpack/test/template/sprockets_helper_with_routes_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' require 'sprockets' require 'sprockets/helpers/rails_helper' -require 'mocha' +require 'mocha/setup' class SprocketsHelperWithRoutesTest < ActionView::TestCase include Sprockets::Helpers::RailsHelper @@ -54,4 +54,4 @@ class SprocketsHelperWithRoutesTest < ActionView::TestCase stylesheet_link_tag(:application) end -end
\ No newline at end of file +end diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb index 5880eb2bd4..56943381d8 100644 --- a/actionpack/test/template/template_test.rb +++ b/actionpack/test/template/template_test.rb @@ -25,6 +25,10 @@ class TestERBTemplate < ActiveSupport::TestCase "Hello" end + def apostrophe + "l'apostrophe" + end + def partial ActionView::Template.new( "<%= @virtual_path %>", @@ -47,7 +51,7 @@ class TestERBTemplate < ActiveSupport::TestCase end end - def new_template(body = "<%= hello %>", details = {}) + def new_template(body = "<%= hello %>", details = { :format => :html }) ActionView::Template.new(body, "hello template", ERBHandler, {:virtual_path => "hello"}.merge!(details)) end @@ -64,6 +68,16 @@ class TestERBTemplate < ActiveSupport::TestCase assert_equal "Hello", render end + def test_basic_template_does_html_escape + @template = new_template("<%= apostrophe %>") + assert_equal "l'apostrophe", render + end + + def test_text_template_does_not_html_escape + @template = new_template("<%= apostrophe %> <%== apostrophe %>", :format => :text) + assert_equal "l'apostrophe l'apostrophe", render + end + def test_template_loses_its_source_after_rendering @template = new_template render |