diff options
Diffstat (limited to 'actionview')
-rw-r--r-- | actionview/CHANGELOG.md | 16 | ||||
-rw-r--r-- | actionview/lib/action_view/digestor.rb | 64 | ||||
-rw-r--r-- | actionview/lib/action_view/gem_version.rb | 15 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/cache_helper.rb | 2 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/date_helper.rb | 12 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/form_helper.rb | 4 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/form_tag_helper.rb | 18 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/rendering_helper.rb | 5 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/tag_helper.rb | 10 | ||||
-rw-r--r-- | actionview/lib/action_view/rendering.rb | 5 | ||||
-rw-r--r-- | actionview/lib/action_view/version.rb | 11 | ||||
-rw-r--r-- | actionview/test/fixtures/digestor/messages/new.html+iphone.erb | 15 | ||||
-rw-r--r-- | actionview/test/template/digestor_test.rb | 35 | ||||
-rw-r--r-- | actionview/test/template/form_tag_helper_test.rb | 5 |
14 files changed, 167 insertions, 50 deletions
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 28508a5dfa..e46f55a875 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1 +1,17 @@ +* Fixed a problem where the default options for the `button_tag` helper is not + applied correctly. + + Fixes #14254. + + *Sergey Prikhodko* + +* Take variants into account when calculating template digests in ActionView::Digestor. + + The arguments to ActionView::Digestor#digest are now being passed as a hash + to support variants and allow more flexibility in the future. The support for + regular (required) arguments is deprecated and will be removed in Rails 5.0 or later. + + *Piotr Chmolowski, Łukasz Strzałkowski* + + Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/actionview/CHANGELOG.md) for previous changes. diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb index 5570e2a8dc..da43fef2e3 100644 --- a/actionview/lib/action_view/digestor.rb +++ b/actionview/lib/action_view/digestor.rb @@ -9,23 +9,56 @@ module ActionView @@digest_monitor = Monitor.new class << self - def digest(name, format, finder, options = {}) + # Supported options: + # + # * <tt>name</tt> - Template name + # * <tt>format</tt> - Template format + # * <tt>variant</tt> - Variant of +format+ (optional) + # * <tt>finder</tt> - An instance of ActionView::LookupContext + # * <tt>dependencies</tt> - An array of dependent views + # * <tt>partial</tt> - Specifies whether the template is a partial + def digest(*args) + options = _setup_options(*args) + + name = options[:name] + format = options[:format] + variant = options[:variant] + finder = options[:finder] + details_key = finder.details_key.hash dependencies = Array.wrap(options[:dependencies]) - cache_key = ([name, details_key, format] + dependencies).join('.') + cache_key = ([name, details_key, format, variant].compact + dependencies).join('.') # this is a correctly done double-checked locking idiom # (ThreadSafe::Cache's lookups have volatile semantics) @@cache[cache_key] || @@digest_monitor.synchronize do @@cache.fetch(cache_key) do # re-check under lock - compute_and_store_digest(cache_key, name, format, finder, options) + compute_and_store_digest(cache_key, options) end end end + def _setup_options(*args) + unless args.first.is_a?(Hash) + ActiveSupport::Deprecation.warn("Arguments to ActionView::Digestor should be provided as a hash. The support for regular arguments will be removed in Rails 5.0 or later") + + { + name: args.first, + format: args.second, + finder: args.third, + }.merge(args.fourth || {}) + else + options = args.first + options.assert_valid_keys(:name, :format, :variant, :finder, :dependencies, :partial) + + options + end + end + private - def compute_and_store_digest(cache_key, name, format, finder, options) # called under @@digest_monitor lock - klass = if options[:partial] || name.include?("/_") + + def compute_and_store_digest(cache_key, options) # called under @@digest_monitor lock + klass = if options[:partial] || options[:name].include?("/_") # Prevent re-entry or else recursive templates will blow the stack. # There is no need to worry about other threads seeing the +false+ value, # as they will then have to wait for this thread to let go of the @@digest_monitor lock. @@ -35,20 +68,25 @@ module ActionView Digestor end - digest = klass.new(name, format, finder, options).digest + digest = klass.new(options).digest # Store the actual digest if config.cache_template_loading is true @@cache[cache_key] = stored_digest = digest if ActionView::Resolver.caching? digest ensure # something went wrong or ActionView::Resolver.caching? is false, make sure not to corrupt the @@cache - @@cache.delete_pair(cache_key, false) if pre_stored && !stored_digest + @@cache.delete_pair(cache_key, false) if pre_stored && !stored_digest end end - attr_reader :name, :format, :finder, :options + attr_reader :name, :format, :variant, :finder, :options + + def initialize(*args) + @options = self.class._setup_options(*args) - def initialize(name, format, finder, options={}) - @name, @format, @finder, @options = name, format, finder, options + @name = @options.delete(:name) + @format = @options.delete(:format) + @variant = @options.delete(:variant) + @finder = @options.delete(:finder) end def digest @@ -68,7 +106,7 @@ module ActionView def nested_dependencies dependencies.collect do |dependency| - dependencies = PartialDigestor.new(dependency, format, finder).nested_dependencies + dependencies = PartialDigestor.new(name: dependency, format: format, finder: finder).nested_dependencies dependencies.any? ? { dependency => dependencies } : dependency end end @@ -88,7 +126,7 @@ module ActionView end def template - @template ||= finder.find(logical_name, [], partial?, formats: [ format ]) + @template ||= finder.find(logical_name, [], partial?, formats: [ format ], variants: [ variant ]) end def source @@ -97,7 +135,7 @@ module ActionView def dependency_digest template_digests = dependencies.collect do |template_name| - Digestor.digest(template_name, format, finder, partial: true) + Digestor.digest(name: template_name, format: format, finder: finder, partial: true) end (template_digests + injected_dependencies).join("-") diff --git a/actionview/lib/action_view/gem_version.rb b/actionview/lib/action_view/gem_version.rb new file mode 100644 index 0000000000..9266e55c47 --- /dev/null +++ b/actionview/lib/action_view/gem_version.rb @@ -0,0 +1,15 @@ +module ActionView + # Returns the version of the currently loaded ActionView as a <tt>Gem::Version</tt> + def self.gem_version + Gem::Version.new VERSION::STRING + end + + module VERSION + MAJOR = 4 + MINOR = 2 + TINY = 0 + PRE = "alpha" + + STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") + end +end diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb index b3af1d4da4..30e4e5e329 100644 --- a/actionview/lib/action_view/helpers/cache_helper.rb +++ b/actionview/lib/action_view/helpers/cache_helper.rb @@ -167,7 +167,7 @@ module ActionView if @virtual_path [ *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name), - Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context, dependencies: view_cache_dependencies) + Digestor.digest(name: @virtual_path, format: formats.last.to_sym, variant: request.variant, finder: lookup_context, dependencies: view_cache_dependencies) ] else name diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb index 698f0ca31c..1738df9cac 100644 --- a/actionview/lib/action_view/helpers/date_helper.rb +++ b/actionview/lib/action_view/helpers/date_helper.rb @@ -19,6 +19,10 @@ module ActionView # the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead # of \date[month]. module DateHelper + MINUTES_IN_YEAR = 525600 + MINUTES_IN_QUARTER_YEAR = 131400 + MINUTES_IN_THREE_QUARTERS_YEAR = 394200 + # Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds. # Pass <tt>include_seconds: true</tt> if you want more detailed approximations when distance < 1 min, 29 secs. # Distances are reported based on the following table: @@ -120,11 +124,11 @@ module ActionView else minutes_with_offset = distance_in_minutes end - remainder = (minutes_with_offset % 525600) - distance_in_years = (minutes_with_offset.div 525600) - if remainder < 131400 + remainder = (minutes_with_offset % MINUTES_IN_YEAR) + distance_in_years = (minutes_with_offset.div MINUTES_IN_YEAR) + if remainder < MINUTES_IN_QUARTER_YEAR locale.t(:about_x_years, :count => distance_in_years) - elsif remainder < 394200 + elsif remainder < MINUTES_IN_THREE_QUARTERS_YEAR locale.t(:over_x_years, :count => distance_in_years) else locale.t(:almost_x_years, :count => distance_in_years + 1) diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index 5235962f9f..1ff090f244 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -457,7 +457,7 @@ module ActionView # doesn't create the form tags themselves. This makes fields_for suitable # for specifying additional model objects in the same form. # - # Although the usage and purpose of +field_for+ is similar to +form_for+'s, + # Although the usage and purpose of +fields_for+ is similar to +form_for+'s, # its method signature is slightly different. Like +form_for+, it yields # a FormBuilder object associated with a particular model object to a block, # and within the block allows methods to be called on the builder to @@ -1268,7 +1268,7 @@ module ActionView # doesn't create the form tags themselves. This makes fields_for suitable # for specifying additional model objects in the same form. # - # Although the usage and purpose of +field_for+ is similar to +form_for+'s, + # Although the usage and purpose of +fields_for+ is similar to +form_for+'s, # its method signature is slightly different. Like +form_for+, it yields # a FormBuilder object associated with a particular model object to a block, # and within the block allows methods to be called on the builder to diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 80f066b3be..0bbe08166b 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -465,17 +465,23 @@ module ActionView # # <strong>Ask me!</strong> # # </button> # - # button_tag "Checkout", data: { :disable_with => "Please wait..." } + # button_tag "Checkout", data: { disable_with: "Please wait..." } # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button> # def button_tag(content_or_options = nil, options = nil, &block) - options = content_or_options if block_given? && content_or_options.is_a?(Hash) - options ||= {} - options = options.stringify_keys + if content_or_options.is_a? Hash + options = content_or_options + else + options ||= {} + end - options.reverse_merge! 'name' => 'button', 'type' => 'submit' + options = { 'name' => 'button', 'type' => 'submit' }.merge!(options.stringify_keys) - content_tag :button, content_or_options || 'Button', options, &block + if block_given? + content_tag :button, options, &block + else + content_tag :button, content_or_options || 'Button', options + end end # Displays an image which when clicked will submit the form. diff --git a/actionview/lib/action_view/helpers/rendering_helper.rb b/actionview/lib/action_view/helpers/rendering_helper.rb index 15b88bcda6..ebfc35a4c7 100644 --- a/actionview/lib/action_view/helpers/rendering_helper.rb +++ b/actionview/lib/action_view/helpers/rendering_helper.rb @@ -17,8 +17,9 @@ module ActionView # * <tt>:html</tt> - Renders the html safe string passed in out, otherwise # performs html escape on the string first. Setting the content type as # <tt>text/html</tt>. - # * <tt>:body</tt> - Renders the text passed in, and does not set content - # type in the response. + # * <tt>:body</tt> - Renders the text passed in, and inherits the content + # type of <tt>text/html</tt> from <tt>ActionDispatch::Response</tt> + # object. # # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter # as the locals hash. diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb index 3528381781..a9f3b0ffbc 100644 --- a/actionview/lib/action_view/helpers/tag_helper.rb +++ b/actionview/lib/action_view/helpers/tag_helper.rb @@ -42,7 +42,8 @@ module ActionView # For example, a key +user_id+ would render as <tt>data-user-id</tt> and # thus accessed as <tt>dataset.userId</tt>. # - # Values are encoded to JSON, with the exception of strings and symbols. + # Values are encoded to JSON, with the exception of strings, symbols and + # BigDecimals. # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt> # from 1.4.3. # @@ -56,6 +57,9 @@ module ActionView # tag("input", type: 'text', disabled: true) # # => <input type="text" disabled="disabled" /> # + # tag("input", type: 'text', class: ["strong", "highlight"]) + # # => <input class="strong highlight" type="text" /> + # # tag("img", src: "open & shut.png") # # => <img src="open & shut.png" /> # @@ -75,7 +79,7 @@ module ActionView # Set escape to false to disable attribute value escaping. # # ==== Options - # The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and + # The +options+ hash can be used with attributes with no value like (<tt>disabled</tt> and # <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use # symbols or strings for the attribute names. # @@ -84,6 +88,8 @@ module ActionView # # => <p>Hello world!</p> # content_tag(:div, content_tag(:p, "Hello world!"), class: "strong") # # => <div class="strong"><p>Hello world!</p></div> + # content_tag(:div, "Hello world!", class: ["strong", "highlight"]) + # # => <div class="strong highlight">Hello world!</div> # content_tag("select", options, multiple: true) # # => <select multiple="multiple">...options...</select> # diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index f96587c816..017302d40f 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -102,11 +102,6 @@ module ActionView # Assign the rendered format to lookup context. def _process_format(format, options = {}) #:nodoc: super - - if options[:body] - self.no_content_type = true - end - lookup_context.formats = [format.to_sym] lookup_context.rendered_format = lookup_context.formats.first end diff --git a/actionview/lib/action_view/version.rb b/actionview/lib/action_view/version.rb index b3ff415775..f55d3fdaef 100644 --- a/actionview/lib/action_view/version.rb +++ b/actionview/lib/action_view/version.rb @@ -1,11 +1,8 @@ +require_relative 'gem_version' + module ActionView - # Returns the version of the currently loaded ActionView as a Gem::Version + # Returns the version of the currently loaded ActionView as a <tt>Gem::Version</tt> def self.version - Gem::Version.new "4.2.0.alpha" - end - - module VERSION #:nodoc: - MAJOR, MINOR, TINY, PRE = ActionView.version.segments - STRING = ActionView.version.to_s + gem_version end end diff --git a/actionview/test/fixtures/digestor/messages/new.html+iphone.erb b/actionview/test/fixtures/digestor/messages/new.html+iphone.erb new file mode 100644 index 0000000000..791e1d36b4 --- /dev/null +++ b/actionview/test/fixtures/digestor/messages/new.html+iphone.erb @@ -0,0 +1,15 @@ +<%# Template Dependency: messages/message %> + +<%= render "header" %> +<%= render "comments/comments" %> + +<%= render "messages/actions/move" %> + +<%= render @message.history.events %> + +<%# render "something_missing" %> +<%# render "something_missing_1" %> + +<% + # Template Dependency: messages/form +%>
\ No newline at end of file diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb index 779a7fb53c..2406a64310 100644 --- a/actionview/test/template/digestor_test.rb +++ b/actionview/test/template/digestor_test.rb @@ -26,7 +26,11 @@ class FixtureFinder end def find(logical_name, keys, partial, options) - FixtureTemplate.new("digestor/#{partial ? logical_name.gsub(%r|/([^/]+)$|, '/_\1') : logical_name}.#{options[:formats].first}.erb") + partial_name = partial ? logical_name.gsub(%r|/([^/]+)$|, '/_\1') : logical_name + format = options[:formats].first.to_s + format += "+#{options[:variants].first}" if options[:variants].any? + + FixtureTemplate.new("digestor/#{partial_name}.#{format}.erb") end end @@ -194,6 +198,13 @@ class TemplateDigestorTest < ActionView::TestCase end end + def test_variants + assert_digest_difference("messages/new", false, variant: :iphone) do + change_template("messages/new", :iphone) + change_template("messages/_header", :iphone) + end + end + def test_dependencies_via_options_results_in_different_digest digest_plain = digest("comments/_comment") digest_fridge = digest("comments/_comment", dependencies: ["fridge"]) @@ -242,6 +253,11 @@ class TemplateDigestorTest < ActionView::TestCase ActionView::Resolver.caching = resolver_before end + def test_arguments_deprecation + assert_deprecated(/should be provided as a hash/) { ActionView::Digestor.digest('messages/show', :html, finder) } + assert_deprecated(/should be provided as a hash/) { ActionView::Digestor.new('messages/show', :html, finder) } + end + private def assert_logged(message) old_logger = ActionView::Base.logger @@ -258,26 +274,29 @@ class TemplateDigestorTest < ActionView::TestCase end end - def assert_digest_difference(template_name, persistent = false) - previous_digest = digest(template_name) + def assert_digest_difference(template_name, persistent = false, options = {}) + previous_digest = digest(template_name, options) ActionView::Digestor.cache.clear unless persistent yield - assert previous_digest != digest(template_name), "digest didn't change" + assert previous_digest != digest(template_name, options), "digest didn't change" ActionView::Digestor.cache.clear end - def digest(template_name, options={}) - ActionView::Digestor.digest(template_name, :html, finder, options) + def digest(template_name, options = {}) + options = options.dup + ActionView::Digestor.digest({ name: template_name, format: :html, finder: finder }.merge(options)) end def finder @finder ||= FixtureFinder.new end - def change_template(template_name) - File.open("digestor/#{template_name}.html.erb", "w") do |f| + def change_template(template_name, variant = nil) + variant = "+#{variant}" if variant.present? + + File.open("digestor/#{template_name}.html#{variant}.erb", "w") do |f| f.write "\nTHIS WAS CHANGED!" end end diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb index 0d5831dc6f..cf824e2733 100644 --- a/actionview/test/template/form_tag_helper_test.rb +++ b/actionview/test/template/form_tag_helper_test.rb @@ -476,6 +476,11 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal('<button name="temptation" type="button"><strong>Do not press me</strong></button>', output) end + def test_button_tag_defaults_with_block_and_options + output = button_tag(:name => 'temptation', :value => 'within') { content_tag(:strong, 'Do not press me') } + assert_dom_equal('<button name="temptation" value="within" type="submit" ><strong>Do not press me</strong></button>', output) + end + def test_button_tag_with_confirmation assert_dom_equal( %(<button name="button" type="submit" data-confirm="Are you sure?">Save</button>), |