aboutsummaryrefslogtreecommitdiffstats
path: root/actionview
diff options
context:
space:
mode:
Diffstat (limited to 'actionview')
-rw-r--r--actionview/CHANGELOG.md85
-rw-r--r--actionview/actionview.gemspec8
-rw-r--r--actionview/lib/action_view/digestor.rb153
-rw-r--r--actionview/lib/action_view/flows.rb9
-rw-r--r--actionview/lib/action_view/gem_version.rb2
-rw-r--r--actionview/lib/action_view/helpers/atom_feed_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb45
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb22
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb6
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb9
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb10
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb9
-rw-r--r--actionview/lib/action_view/helpers/output_safety_helper.rb30
-rw-r--r--actionview/lib/action_view/helpers/sanitize_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb65
-rw-r--r--actionview/lib/action_view/log_subscriber.rb36
-rw-r--r--actionview/lib/action_view/lookup_context.rb45
-rw-r--r--actionview/lib/action_view/railtie.rb2
-rw-r--r--actionview/lib/action_view/renderer/abstract_renderer.rb10
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb33
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb42
-rw-r--r--actionview/lib/action_view/tasks/cache_digests.rake (renamed from actionview/lib/action_view/tasks/dependencies.rake)4
-rw-r--r--actionview/lib/action_view/template.rb23
-rw-r--r--actionview/lib/action_view/template/error.rb4
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb25
-rw-r--r--actionview/lib/action_view/template/resolver.rb20
-rw-r--r--actionview/lib/action_view/template/types.rb1
-rw-r--r--actionview/lib/action_view/view_paths.rb4
-rw-r--r--actionview/test/abstract_unit.rb14
-rw-r--r--actionview/test/fixtures/digestor/messages/peek.html.erb2
-rw-r--r--actionview/test/fixtures/test/_customer.mobile.erb1
-rw-r--r--actionview/test/fixtures/test/_🍣.erb1
-rw-r--r--actionview/test/lib/controller/fake_models.rb21
-rw-r--r--actionview/test/template/date_helper_test.rb404
-rw-r--r--actionview/test/template/digestor_test.rb30
-rw-r--r--actionview/test/template/form_helper_test.rb56
-rw-r--r--actionview/test/template/form_tag_helper_test.rb4
-rw-r--r--actionview/test/template/log_subscriber_test.rb29
-rw-r--r--actionview/test/template/output_safety_helper_test.rb55
-rw-r--r--actionview/test/template/render_test.rb70
-rw-r--r--actionview/test/template/resolver_cache_test.rb7
-rw-r--r--actionview/test/template/resolver_patterns_test.rb19
-rw-r--r--actionview/test/template/template_test.rb32
-rw-r--r--actionview/test/template/url_helper_test.rb50
44 files changed, 1075 insertions, 426 deletions
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index bebe78c360..6fb41e9d4c 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,66 @@
+* `date_select` helper `:with_css_classes` option now accepts a hash of strings
+ for `:year`, `:month`, `:day`, `:hour`, `:minute`, `:second` that will extend
+ the select type with the given css class value.
+
+ ```erb
+ <%= f.date_select :birthday, with_css_classes: { month: "my-month", year: "my-year" } %>
+ ```
+
+ ```html
+ <select id="user_birthday_3i" name="user[birthday(3i)]">…</select>
+ <select id="user_birthday_2i" name="user[birthday(2i)]" class="my-month">…</select>
+ <select id="user_birthday_1i" name="user[birthday(1i)]" class="my-year">…</select>
+ ```
+
+ *Matthias Neumayr*
+
+* Add `to_sentence` helper that is a HTML-safe aware version of `Array#to_sentence`.
+
+ *Neil Matatall*
+
+* Deprecate `datetime_field` and `datetime_field_tag` helpers.
+ Datetime input type was removed from HTML specification.
+ One can use `datetime_local_field` and `datetime_local_field_tag` instead.
+
+ *Wojciech Wnętrzak*
+
+* Added log "Rendering ...", when starting to render a template to log that
+ we have started rendering something. This helps to easily identify the origin
+ of queries in the log whether they came from controller or views.
+
+ *Vipul A M and Prem Sichanugrist*
+
+## Rails 5.0.0.beta3 (February 24, 2016) ##
+
+* Collection rendering can cache and fetch multiple partials at once.
+
+ Collections rendered as:
+
+ ```ruby
+ <%= render partial: 'notifications/notification', collection: @notifications, as: :notification, cached: true %>
+ ```
+
+ will read several partials from cache at once. The templates in the collection
+ that haven't been cached already will automatically be written to cache. Works
+ great alongside individual template fragment caching. For instance if the
+ template the collection renders is cached like:
+
+ ```ruby
+ # notifications/_notification.html.erb
+ <% cache notification do %>
+ <%# ... %>
+ <% end %>
+ ```
+
+ Then any collection renders shares that cache when attempting to read multiple
+ ones at once.
+
+ *Kasper Timm Hansen*
+
+* Add support for nested hashes/arrays to `:params` option of `button_to` helper.
+
+ *James Coleman*
+
## Rails 5.0.0.beta2 (February 01, 2016) ##
* Fix stripping the digest from the automatically generated img tag alt
@@ -7,7 +70,7 @@
* Create a new `ActiveSupport::SafeBuffer` instance when `content_for` is flushed.
- Fixes #19890
+ Fixes #19890.
*Yoong Kang Lim*
@@ -193,26 +256,6 @@
*Ulisses Almeida*
-* Collection rendering automatically caches and fetches multiple partials.
-
- Collections rendered as:
-
- ```ruby
- <%= render @notifications %>
- <%= render partial: 'notifications/notification', collection: @notifications, as: :notification %>
- ```
-
- will now read several partials from cache at once, if the template starts with a cache call:
-
- ```ruby
- # notifications/_notification.html.erb
- <% cache notification do %>
- <%# ... %>
- <% end %>
- ```
-
- *Kasper Timm Hansen*
-
* Fixed a dependency tracker bug that caused template dependencies not
count layouts as dependencies for partials.
diff --git a/actionview/actionview.gemspec b/actionview/actionview.gemspec
index 612e94021d..8b0e031dee 100644
--- a/actionview/actionview.gemspec
+++ b/actionview/actionview.gemspec
@@ -9,11 +9,11 @@ Gem::Specification.new do |s|
s.required_ruby_version = '>= 2.2.2'
- s.license = 'MIT'
+ s.license = 'MIT'
- s.author = 'David Heinemeier Hansson'
- s.email = 'david@loudthinking.com'
- s.homepage = 'http://www.rubyonrails.org'
+ s.author = 'David Heinemeier Hansson'
+ s.email = 'david@loudthinking.com'
+ s.homepage = 'http://rubyonrails.org'
s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
s.require_path = 'lib'
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 657026fa14..b99d1af998 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -4,13 +4,11 @@ require 'monitor'
module ActionView
class Digestor
- cattr_reader(:cache)
- @@cache = Concurrent::Map.new
- @@digest_monitor = Monitor.new
+ @@digest_mutex = Mutex.new
class PerRequestDigestCacheExpiry < Struct.new(:app) # :nodoc:
def call(env)
- ActionView::Digestor.cache.clear
+ ActionView::LookupContext::DetailsKey.clear
app.call(env)
end
end
@@ -22,111 +20,104 @@ module ActionView
# * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
# * <tt>dependencies</tt> - An array of dependent views
# * <tt>partial</tt> - Specifies whether the template is a partial
- def digest(name:, finder:, **options)
- options.assert_valid_keys(:dependencies, :partial)
-
- cache_key = ([ name, finder.details_key.hash ].compact + Array.wrap(options[:dependencies])).join('.')
+ def digest(name:, finder:, dependencies: [])
+ dependencies ||= []
+ cache_key = ([ name ].compact + dependencies).join('.')
# this is a correctly done double-checked locking idiom
# (Concurrent::Map'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, finder, options)
+ finder.digest_cache[cache_key] || @@digest_mutex.synchronize do
+ finder.digest_cache.fetch(cache_key) do # re-check under lock
+ partial = name.include?("/_")
+ root = tree(name, finder, partial)
+ dependencies.each do |injected_dep|
+ root.children << Injected.new(injected_dep, nil, nil)
+ end
+ finder.digest_cache[cache_key] = root.digest(finder)
end
end
end
- private
- def compute_and_store_digest(cache_key, name, finder, options) # called under @@digest_monitor lock
- klass = if options[:partial] || 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.
- pre_stored = @@cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
- PartialDigestor
- else
- Digestor
- end
-
- @@cache[cache_key] = stored_digest = klass.new(name, finder, options).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
- end
- end
-
- attr_reader :name, :finder, :options
+ def logger
+ ActionView::Base.logger || NullLogger
+ end
- def initialize(name, finder, options = {})
- @name, @finder = name, finder
- @options = options
- end
+ # Create a dependency tree for template named +name+.
+ def tree(name, finder, partial = false, seen = {})
+ logical_name = name.gsub(%r|/_|, "/")
- def digest
- Digest::MD5.hexdigest("#{source}-#{dependency_digest}").tap do |digest|
- logger.debug " Cache digest for #{template.inspect}: #{digest}"
- end
- rescue ActionView::MissingTemplate
- logger.error " Couldn't find template for digesting: #{name}"
- ''
- end
+ if finder.disable_cache { finder.exists?(logical_name, [], partial) }
+ template = finder.disable_cache { finder.find(logical_name, [], partial) }
- def dependencies
- DependencyTracker.find_dependencies(name, template, finder.view_paths)
- rescue ActionView::MissingTemplate
- logger.error " '#{name}' file doesn't exist, so no dependencies"
- []
- end
+ if node = seen[template.identifier] # handle cycles in the tree
+ node
+ else
+ node = seen[template.identifier] = Node.create(name, logical_name, template, partial)
- def nested_dependencies
- dependencies.collect do |dependency|
- dependencies = PartialDigestor.new(dependency, finder).nested_dependencies
- dependencies.any? ? { dependency => dependencies } : dependency
+ deps = DependencyTracker.find_dependencies(name, template, finder.view_paths)
+ deps.uniq { |n| n.gsub(%r|/_|, "/") }.each do |dep_file|
+ node.children << tree(dep_file, finder, true, seen)
+ end
+ node
+ end
+ else
+ logger.error " '#{name}' file doesn't exist, so no dependencies"
+ logger.error " Couldn't find template for digesting: #{name}"
+ seen[name] ||= Missing.new(name, logical_name, nil)
+ end
end
end
- private
- class NullLogger
- def self.debug(_); end
- def self.error(_); end
- end
+ class Node
+ attr_reader :name, :logical_name, :template, :children
- def logger
- ActionView::Base.logger || NullLogger
+ def self.create(name, logical_name, template, partial)
+ klass = partial ? Partial : Node
+ klass.new(name, logical_name, template, [])
end
- def logical_name
- name.gsub(%r|/_|, "/")
+ def initialize(name, logical_name, template, children = [])
+ @name = name
+ @logical_name = logical_name
+ @template = template
+ @children = children
end
- def partial?
- false
+ def digest(finder, stack = [])
+ Digest::MD5.hexdigest("#{template.source}-#{dependency_digest(finder, stack)}")
end
- def template
- @template ||= finder.disable_cache { finder.find(logical_name, [], partial?) }
+ def dependency_digest(finder, stack)
+ children.map do |node|
+ if stack.include?(node)
+ false
+ else
+ finder.digest_cache[node.name] ||= begin
+ stack.push node
+ node.digest(finder, stack).tap { stack.pop }
+ end
+ end
+ end.join("-")
end
- def source
- template.source
+ def to_dep_map
+ children.any? ? { name => children.map(&:to_dep_map) } : name
end
+ end
- def dependency_digest
- template_digests = dependencies.collect do |template_name|
- Digestor.digest(name: template_name, finder: finder, partial: true)
- end
+ class Partial < Node; end
- (template_digests + injected_dependencies).join("-")
- end
+ class Missing < Node
+ def digest(finder, _ = []) '' end
+ end
- def injected_dependencies
- Array.wrap(options[:dependencies])
- end
- end
+ class Injected < Node
+ def digest(finder, _ = []) name end
+ end
- class PartialDigestor < Digestor # :nodoc:
- def partial?
- true
+ class NullLogger
+ def self.debug(_); end
+ def self.error(_); end
end
end
end
diff --git a/actionview/lib/action_view/flows.rb b/actionview/lib/action_view/flows.rb
index bc61920848..4b912f0b2b 100644
--- a/actionview/lib/action_view/flows.rb
+++ b/actionview/lib/action_view/flows.rb
@@ -37,9 +37,8 @@ module ActionView
end
# Try to get stored content. If the content
- # is not available and we are inside the layout
- # fiber, we set that we are waiting for the given
- # key and yield.
+ # is not available and we're inside the layout fiber,
+ # then it will begin waiting for the given key and yield.
def get(key)
return super if @content.key?(key)
@@ -60,8 +59,8 @@ module ActionView
end
# Appends the contents for the given key. This is called
- # by provides and resumes back to the fiber if it is
- # the key it is waiting for.
+ # by providing and resuming back to the fiber,
+ # if that's the key it's waiting for.
def append!(key, value)
super
@fiber.resume if @waiting_for == key
diff --git a/actionview/lib/action_view/gem_version.rb b/actionview/lib/action_view/gem_version.rb
index bb5c96cb39..efb565bf59 100644
--- a/actionview/lib/action_view/gem_version.rb
+++ b/actionview/lib/action_view/gem_version.rb
@@ -8,7 +8,7 @@ module ActionView
MAJOR = 5
MINOR = 0
TINY = 0
- PRE = "beta2"
+ PRE = "beta3"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
end
diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb
index dba70e284e..c875f5870f 100644
--- a/actionview/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb
@@ -132,7 +132,7 @@ module ActionView
end
private
- # Delegate to xml builder, first wrapping the element in a xhtml
+ # Delegate to xml builder, first wrapping the element in an xhtml
# namespaced div element if the method and arguments indicate
# that an xhtml_block? is desired.
def method_missing(method, *arguments, &block)
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 401f398721..4c7c4b91c6 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -126,44 +126,29 @@ module ActionView
#
# Now all you have to do is change that timestamp when the helper method changes.
#
- # === Automatic Collection Caching
+ # === Collection Caching
#
- # When rendering collections such as:
+ # When rendering a collection of objects that each use the same partial, a `cached`
+ # option can be passed.
+ # For collections rendered such:
#
- # <%= render @notifications %>
- # <%= render partial: 'notifications/notification', collection: @notifications %>
+ # <%= render partial: 'notifications/notification', collection: @notifications, cached: true %>
#
- # If the notifications/_notification partial starts with a cache call as:
+ # The `cached: true` will make Action View's rendering read several templates
+ # from cache at once instead of one call per template.
#
- # <% cache notification do %>
- # <%= notification.name %>
- # <% end %>
- #
- # The collection can then automatically use any cached renders for that
- # template by reading them at once instead of one by one.
- #
- # See ActionView::Template::Handlers::ERB.resource_cache_call_pattern for
- # more information on what cache calls make a template eligible for this
- # collection caching.
- #
- # The automatic cache multi read can be turned off like so:
+ # Templates in the collection not already cached are written to cache.
#
- # <%= render @notifications, cache: false %>
+ # Works great alongside individual template fragment caching.
+ # For instance if the template the collection renders is cached like:
#
- # === Explicit Collection Caching
- #
- # If the partial template doesn't start with a clean cache call as
- # mentioned above, you can still benefit from collection caching by
- # adding a special comment format anywhere in the template, like:
- #
- # <%# Template Collection: notification %>
- # <% my_helper_that_calls_cache(some_arg, notification) do %>
- # <%= notification.name %>
+ # # notifications/_notification.html.erb
+ # <% cache notification do %>
+ # <%# ... %>
# <% end %>
#
- # The pattern used to match these is <tt>/# Template Collection: (\S+)/</tt>,
- # so it's important that you type it out just so.
- # You can only declare one collection in a partial template file.
+ # Any collection renders will find those cached templates when attempting
+ # to read multiple templates at once.
def cache(name = {}, options = {}, &block)
if controller.respond_to?(:perform_caching) && controller.perform_caching
name_options = options.slice(:skip_digest, :virtual_path)
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index 233e613e97..9042b9cffd 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -226,8 +226,10 @@ module ActionView
# for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
# Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
# or the given prompt string.
- # * <tt>:with_css_classes</tt> - Set to true if you want assign different styles for 'select' tags. This option
- # automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second' for your 'select' tags.
+ # * <tt>:with_css_classes</tt> - Set to true or a hash of strings. Use true if you want to assign generic styles for
+ # select tags. This automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second'. A hash of
+ # strings for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt>, <tt>:second</tt>
+ # will extend the select type with the given value. Use +html_options+ to modify every select tag in the set.
# * <tt>:use_hidden</tt> - Set to true if you only want to generate hidden input tags.
#
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
@@ -994,7 +996,7 @@ module ActionView
:name => input_name_from_type(type)
}.merge!(@html_options)
select_options[:disabled] = 'disabled' if @options[:disabled]
- select_options[:class] = [select_options[:class], type].compact.join(' ') if @options[:with_css_classes]
+ select_options[:class] = css_class_attribute(type, select_options[:class], @options[:with_css_classes]) if @options[:with_css_classes]
select_html = "\n"
select_html << content_tag("option".freeze, '', :value => '') + "\n" if @options[:include_blank]
@@ -1004,6 +1006,20 @@ module ActionView
(content_tag("select".freeze, select_html.html_safe, select_options) + "\n").html_safe
end
+ # Builds the css class value for the select element
+ # css_class_attribute(:year, 'date optional', { year: 'my-year' })
+ # => "date optional my-year"
+ def css_class_attribute(type, html_options_class, options) # :nodoc:
+ css_class = case options
+ when Hash
+ options[type.to_sym]
+ else
+ type
+ end
+
+ [html_options_class, css_class].compact.join(' ')
+ end
+
# Builds a prompt option tag with supplied options or from default options.
# prompt_option_tag(:month, prompt: 'Select month')
# => "<option value="">Select month</option>"
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index c1015ffe89..7ced37572e 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -1118,6 +1118,10 @@ module ActionView
# # => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />
#
def datetime_field(object_name, method, options = {})
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
+ datetime_field is deprecated and will be removed in Rails 5.1.
+ Use datetime_local_field instead.
+ MESSAGE
Tags::DatetimeField.new(object_name, method, self, options).render
end
@@ -1922,8 +1926,6 @@ module ActionView
@object_name.to_s.humanize
end
- model = model.downcase
-
defaults = []
defaults << :"helpers.submit.#{object_name}.#{key}"
defaults << :"helpers.submit.#{key}"
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index 430051379d..b277efd7b6 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -268,10 +268,11 @@ module ActionView
# for more information.)
#
# You can also supply an array of ActiveSupport::TimeZone objects
- # as +priority_zones+, so that they will be listed above the rest of the
- # (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience
- # for obtaining a list of the US time zones, or a Regexp to select the zones
- # of your choice)
+ # as +priority_zones+ so that they will be listed above the rest of the
+ # (long) list. You can use ActiveSupport::TimeZone.us_zones for a list
+ # of US time zones, ActiveSupport::TimeZone.country_zones(country_code)
+ # for another country's time zones, or a Regexp to select the zones of
+ # your choice.
#
# Finally, this method supports a <tt>:default</tt> option, which selects
# a default ActiveSupport::TimeZone if the object's time zone is +nil+.
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 55dac74d00..cfff0bef5d 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -691,6 +691,10 @@ module ActionView
# * <tt>:step</tt> - The acceptable value granularity.
# * Otherwise accepts the same options as text_field_tag.
def datetime_field_tag(name, value = nil, options = {})
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
+ datetime_field_tag is deprecated and will be removed in Rails 5.1.
+ Use datetime_local_field_tag instead.
+ MESSAGE
text_field_tag(name, value, options.merge(type: :datetime))
end
@@ -862,13 +866,13 @@ module ActionView
def extra_tags_for_form(html_options)
authenticity_token = html_options.delete("authenticity_token")
- method = html_options.delete("method").to_s
+ method = html_options.delete("method").to_s.downcase
method_tag = case method
- when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
+ when 'get'
html_options["method"] = "get"
''
- when /^post$/i, "", nil
+ when 'post', ''
html_options["method"] = "post"
token_tag(authenticity_token, form_options: {
action: html_options["action"],
diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb
index 161aa031c6..f0222582c7 100644
--- a/actionview/lib/action_view/helpers/number_helper.rb
+++ b/actionview/lib/action_view/helpers/number_helper.rb
@@ -23,7 +23,7 @@ module ActionView
end
end
- # Formats a +number+ into a US phone number (e.g., (555)
+ # Formats a +number+ into a phone number (US by default e.g., (555)
# 123-9876). You can customize the format in the +options+ hash.
#
# ==== Options
@@ -35,6 +35,8 @@ module ActionView
# end of the generated number.
# * <tt>:country_code</tt> - Sets the country code for the phone
# number.
+ # * <tt>:pattern</tt> - Specifies how the number is divided into three
+ # groups with the custom regexp to override the default format.
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
# the argument is invalid.
#
@@ -52,6 +54,11 @@ module ActionView
#
# number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
# # => +1.123.555.1234 x 1343
+ #
+ # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
+ # # => "(755) 6123-4567"
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/))
+ # # => "133-1234-5678"
def number_to_phone(number, options = {})
return unless number
options = options.symbolize_keys
diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb
index c0fc3b820f..d4b55423a8 100644
--- a/actionview/lib/action_view/helpers/output_safety_helper.rb
+++ b/actionview/lib/action_view/helpers/output_safety_helper.rb
@@ -33,6 +33,36 @@ module ActionView #:nodoc:
array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
end
+
+ # Converts the array to a comma-separated sentence where the last element is
+ # joined by the connector word. This is the html_safe-aware version of
+ # ActiveSupport's {Array#to_sentence}[http://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
+ #
+ def to_sentence(array, options = {})
+ options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
+
+ default_connectors = {
+ :words_connector => ', ',
+ :two_words_connector => ' and ',
+ :last_word_connector => ', and '
+ }
+ if defined?(I18n)
+ i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
+ default_connectors.merge!(i18n_connectors)
+ end
+ options = default_connectors.merge!(options)
+
+ case array.length
+ when 0
+ ''.html_safe
+ when 1
+ ERB::Util.html_escape(array[0])
+ when 2
+ safe_join([array[0], array[1]], options[:two_words_connector])
+ else
+ safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]])
+ end
+ end
end
end
end
diff --git a/actionview/lib/action_view/helpers/sanitize_helper.rb b/actionview/lib/action_view/helpers/sanitize_helper.rb
index 191a881de0..f9784c3483 100644
--- a/actionview/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionview/lib/action_view/helpers/sanitize_helper.rb
@@ -120,7 +120,7 @@ module ActionView
attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
# Vendors the full, link and white list sanitizers.
- # Provided strictly for compatibility and can be removed in Rails 5.
+ # Provided strictly for compatibility and can be removed in Rails 5.1.
def sanitizer_vendor
Rails::Html::Sanitizer
end
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index 3a4561a083..11c7daf4da 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -302,7 +302,7 @@ module ActionView
params = html_options.delete('params')
method = html_options.delete('method').to_s
- method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : ''.html_safe
+ method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : ''.freeze.html_safe
form_method = method == 'get' ? 'get' : 'post'
form_options = html_options.delete('form') || {}
@@ -312,9 +312,10 @@ module ActionView
form_options[:'data-remote'] = true if remote
request_token_tag = if form_method == 'post'
- token_tag(nil, form_options: form_options)
+ request_method = method.empty? ? 'post' : method
+ token_tag(nil, form_options: { action: url, method: request_method })
else
- ''
+ ''.freeze
end
html_options = convert_options_to_data_attributes(options, html_options)
@@ -329,8 +330,8 @@ module ActionView
inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
if params
- params.each do |param_name, value|
- inner_tags.safe_concat tag(:input, type: "hidden", name: param_name, value: value.to_param)
+ to_form_params(params).each do |param|
+ inner_tags.safe_concat tag(:input, type: "hidden", name: param[:name], value: param[:value])
end
end
content_tag('form', inner_tags, form_options)
@@ -480,7 +481,7 @@ module ActionView
option = html_options.delete(item).presence || next
"#{item.dasherize}=#{ERB::Util.url_encode(option)}"
}.compact
- extras = extras.empty? ? '' : '?' + extras.join('&')
+ extras = extras.empty? ? ''.freeze : '?' + extras.join('&')
encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
@@ -558,29 +559,29 @@ module ActionView
def convert_options_to_data_attributes(options, html_options)
if html_options
html_options = html_options.stringify_keys
- html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
+ html_options['data-remote'] = 'true'.freeze if link_to_remote_options?(options) || link_to_remote_options?(html_options)
- method = html_options.delete('method')
+ method = html_options.delete('method'.freeze)
add_method_to_attributes!(html_options, method) if method
html_options
else
- link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
+ link_to_remote_options?(options) ? {'data-remote' => 'true'.freeze} : {}
end
end
def link_to_remote_options?(options)
if options.is_a?(Hash)
- options.delete('remote') || options.delete(:remote)
+ options.delete('remote'.freeze) || options.delete(:remote)
end
end
def add_method_to_attributes!(html_options, method)
- if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/
- html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip
+ if method && method.to_s.downcase != "get".freeze && html_options["rel".freeze] !~ /nofollow/
+ html_options["rel".freeze] = "#{html_options["rel".freeze]} nofollow".lstrip
end
- html_options["data-method"] = method
+ html_options["data-method".freeze] = method
end
def token_tag(token=nil, form_options: {})
@@ -588,13 +589,49 @@ module ActionView
token ||= form_authenticity_token(form_options: form_options)
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
else
- ''
+ ''.freeze
end
end
def method_tag(method)
tag('input', type: 'hidden', name: '_method', value: method.to_s)
end
+
+ # Returns an array of hashes each containing :name and :value keys
+ # suitable for use as the names and values of form input fields:
+ #
+ # to_form_params(name: 'David', nationality: 'Danish')
+ # # => [{name: :name, value: 'David'}, {name: 'nationality', value: 'Danish'}]
+ #
+ # to_form_params(country: {name: 'Denmark'})
+ # # => [{name: 'country[name]', value: 'Denmark'}]
+ #
+ # to_form_params(countries: ['Denmark', 'Sweden']})
+ # # => [{name: 'countries[]', value: 'Denmark'}, {name: 'countries[]', value: 'Sweden'}]
+ #
+ # An optional namespace can be passed to enclose key names:
+ #
+ # to_form_params({ name: 'Denmark' }, 'country')
+ # # => [{name: 'country[name]', value: 'Denmark'}]
+ def to_form_params(attribute, namespace = nil) # :nodoc:
+ params = []
+ case attribute
+ when Hash
+ attribute.each do |key, value|
+ prefix = namespace ? "#{namespace}[#{key}]" : key
+ params.push(*to_form_params(value, prefix))
+ end
+ when Array
+ array_prefix = "#{namespace}[]"
+ attribute.each do |value|
+ params.push(*to_form_params(value, array_prefix))
+ end
+ else
+ params << { name: namespace, value: attribute.to_param }
+ end
+
+ params.sort_by { |pair| pair[:name] }
+ end
end
end
end
diff --git a/actionview/lib/action_view/log_subscriber.rb b/actionview/lib/action_view/log_subscriber.rb
index 9047dbdd85..5a29c68214 100644
--- a/actionview/lib/action_view/log_subscriber.rb
+++ b/actionview/lib/action_view/log_subscriber.rb
@@ -20,7 +20,23 @@ module ActionView
end
end
alias :render_partial :render_template
- alias :render_collection :render_template
+
+ def render_collection(event)
+ identifier = event.payload[:identifier] || 'templates'
+
+ info do
+ " Rendered collection of #{from_rails_root(identifier)}" \
+ " #{render_count(event.payload)} (#{event.duration.round(1)}ms)"
+ end
+ end
+
+ def start(name, id, payload)
+ if name == "render_template.action_view"
+ log_rendering_start(payload)
+ end
+
+ super
+ end
def logger
ActionView::Base.logger
@@ -38,6 +54,24 @@ module ActionView
def rails_root
@root ||= "#{Rails.root}/"
end
+
+ def render_count(payload)
+ if payload[:cache_hits]
+ "[#{payload[:cache_hits]} / #{payload[:count]} cache hits]"
+ else
+ "[#{payload[:count]} times]"
+ end
+ end
+
+ private
+
+ def log_rendering_start(payload)
+ info do
+ message = " Rendering #{from_rails_root(payload[:identifier])}"
+ message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
+ message
+ end
+ end
end
end
diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb
index 126f289f55..626c4b8f5e 100644
--- a/actionview/lib/action_view/lookup_context.rb
+++ b/actionview/lib/action_view/lookup_context.rb
@@ -55,9 +55,7 @@ module ActionView
class DetailsKey #:nodoc:
alias :eql? :equal?
- alias :object_hash :hash
- attr_reader :hash
@details_keys = Concurrent::Map.new
def self.get(details)
@@ -72,8 +70,14 @@ module ActionView
@details_keys.clear
end
+ def self.digest_caches
+ @details_keys.values.map(&:digest_cache)
+ end
+
+ attr_reader :digest_cache
+
def initialize
- @hash = object_hash
+ @digest_cache = Concurrent::Map.new
end
end
@@ -132,6 +136,11 @@ module ActionView
end
alias :template_exists? :exists?
+ def any?(name, prefixes = [], partial = false)
+ @view_paths.exists?(*args_for_any(name, prefixes, partial))
+ end
+ alias :any_templates? :any?
+
# Adds fallbacks to the view paths. Useful in cases when you are rendering
# a :file.
def with_fallbacks
@@ -168,6 +177,32 @@ module ActionView
[user_details, details_key]
end
+ def args_for_any(name, prefixes, partial) # :nodoc:
+ name, prefixes = normalize_name(name, prefixes)
+ details, details_key = detail_args_for_any
+ [name, prefixes, partial || false, details, details_key]
+ end
+
+ def detail_args_for_any # :nodoc:
+ @detail_args_for_any ||= begin
+ details = {}
+
+ registered_details.each do |k|
+ if k == :variants
+ details[k] = :any
+ else
+ details[k] = Accessors::DEFAULT_PROCS[k].call
+ end
+ end
+
+ if @cache
+ [details, DetailsKey.get(details)]
+ else
+ [details, nil]
+ end
+ end
+ end
+
# Support legacy foo.erb names even though we now ignore .erb
# as well as incorrectly putting part of the path in the template
# name instead of the prefix.
@@ -200,6 +235,10 @@ module ActionView
self.view_paths = view_paths
end
+ def digest_cache
+ details_key.digest_cache
+ end
+
def initialize_details(target, details)
registered_details.each do |k|
target[k] = details[k] || Accessors::DEFAULT_PROCS[k].call
diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb
index 59d869d92d..df14ae09f4 100644
--- a/actionview/lib/action_view/railtie.rb
+++ b/actionview/lib/action_view/railtie.rb
@@ -59,7 +59,7 @@ module ActionView
rake_tasks do |app|
unless app.config.api_only
- load "action_view/tasks/dependencies.rake"
+ load "action_view/tasks/cache_digests.rake"
end
end
end
diff --git a/actionview/lib/action_view/renderer/abstract_renderer.rb b/actionview/lib/action_view/renderer/abstract_renderer.rb
index aa77a77acf..1dddf53df0 100644
--- a/actionview/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionview/lib/action_view/renderer/abstract_renderer.rb
@@ -15,7 +15,7 @@ module ActionView
# that new object is called in turn. This abstracts the setup and rendering
# into a separate classes for partials and templates.
class AbstractRenderer #:nodoc:
- delegate :find_template, :find_file, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
+ delegate :find_template, :find_file, :template_exists?, :any_templates?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
def initialize(lookup_context)
@lookup_context = lookup_context
@@ -35,8 +35,12 @@ module ActionView
end
end
- def instrument(name, options={})
- ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
+ def instrument(name, **options)
+ options[:identifier] ||= (@template && @template.identifier) || @path
+
+ ActiveSupport::Notifications.instrument("render_#{name}.action_view", options) do |payload|
+ yield payload
+ end
end
def prepend_formats(formats)
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index a9bd257e76..13b4ec6133 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -294,7 +294,7 @@ module ActionView
def render(context, options, block)
setup(context, options, block)
- identifier = (@template = find_partial) ? @template.identifier : @path
+ @template = find_partial
@lookup_context.rendered_format ||= begin
if @template && @template.formats.present?
@@ -305,11 +305,9 @@ module ActionView
end
if @collection
- instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
- render_collection
- end
+ render_collection
else
- instrument(:partial, :identifier => identifier) do
+ instrument(:partial) do
render_partial
end
end
@@ -318,15 +316,17 @@ module ActionView
private
def render_collection
- return nil if @collection.blank?
+ instrument(:collection, count: @collection.size) do |payload|
+ return nil if @collection.blank?
- if @options.key?(:spacer_template)
- spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
- end
+ if @options.key?(:spacer_template)
+ spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
+ end
- cache_collection_render do
- @template ? collection_with_template : collection_without_template
- end.join(spacer).html_safe
+ cache_collection_render(payload) do
+ @template ? collection_with_template : collection_without_template
+ end.join(spacer).html_safe
+ end
end
def render_partial
@@ -428,8 +428,6 @@ module ActionView
layout = find_template(layout, @template_keys)
end
- collection_cache_eligible = automatic_cache_eligible?
-
partial_iteration = PartialIteration.new(@collection.size)
locals[iteration] = partial_iteration
@@ -438,11 +436,6 @@ module ActionView
locals[counter] = partial_iteration.index
content = template.render(view, locals)
-
- if collection_cache_eligible
- collection_cache_rendered_partial(content, object)
- end
-
content = layout.render(view, locals) { content } if layout
partial_iteration.iterate!
content
@@ -528,7 +521,7 @@ module ActionView
def retrieve_variable(path, as)
variable = as || begin
base = path[-1] == "/".freeze ? "".freeze : File.basename(path)
- raise_invalid_identifier(path) unless base =~ /\A_?(.*)(?:\.\w+)*\z/
+ raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
$1.to_sym
end
if @collection
diff --git a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
index 4860f00243..f7deba94ce 100644
--- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/object/try'
-
module ActionView
module CollectionCaching # :nodoc:
extend ActiveSupport::Concern
@@ -11,40 +9,25 @@ module ActionView
end
private
- def cache_collection_render
- return yield unless cache_collection?
+ def cache_collection_render(instrumentation_payload)
+ return yield unless @options[:cached]
keyed_collection = collection_by_cache_keys
- cached_partials = collection_cache.read_multi(*keyed_collection.keys)
+ cached_partials = collection_cache.read_multi(*keyed_collection.keys)
+ instrumentation_payload[:cache_hits] = cached_partials.size
@collection = keyed_collection.reject { |key, _| cached_partials.key?(key) }.values
rendered_partials = @collection.empty? ? [] : yield
index = 0
- keyed_collection.map do |cache_key, _|
- cached_partials.fetch(cache_key) do
- rendered_partials[index].tap { index += 1 }
- end
+ fetch_or_cache_partial(cached_partials, order_by: keyed_collection.each_key) do
+ rendered_partials[index].tap { index += 1 }
end
end
- def cache_collection?
- @options.fetch(:cache, automatic_cache_eligible?)
- end
-
- def automatic_cache_eligible?
- @template && @template.eligible_for_collection_caching?(as: @options[:as])
- end
-
- def callable_cache_key?
- @options[:cache].respond_to?(:call)
- end
-
def collection_by_cache_keys
- seed = callable_cache_key? ? @options[:cache] : ->(i) { i }
-
@collection.each_with_object({}) do |item, hash|
- hash[expanded_cache_key(seed.call(item))] = item
+ hash[expanded_cache_key(item)] = item
end
end
@@ -53,10 +36,13 @@ module ActionView
key.frozen? ? key.dup : key # #read_multi & #write may require mutability, Dalli 2.6.0.
end
- def collection_cache_rendered_partial(rendered_partial, key_by)
- if callable_cache_key?
- key = expanded_cache_key(@options[:cache].call(key_by))
- collection_cache.write(key, rendered_partial, @options[:cache_options])
+ def fetch_or_cache_partial(cached_partials, order_by:)
+ order_by.map do |cache_key|
+ cached_partials.fetch(cache_key) do
+ yield.tap do |rendered_partial|
+ collection_cache.write(cache_key, rendered_partial)
+ end
+ end
end
end
end
diff --git a/actionview/lib/action_view/tasks/dependencies.rake b/actionview/lib/action_view/tasks/cache_digests.rake
index 9932ff8b6d..045bdf5691 100644
--- a/actionview/lib/action_view/tasks/dependencies.rake
+++ b/actionview/lib/action_view/tasks/cache_digests.rake
@@ -2,13 +2,13 @@ namespace :cache_digests do
desc 'Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
task :nested_dependencies => :environment do
abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
- puts JSON.pretty_generate ActionView::Digestor.new(CacheDigests.template_name, CacheDigests.finder).nested_dependencies
+ puts JSON.pretty_generate ActionView::Digestor.tree(CacheDigests.template_name, CacheDigests.finder).children.map(&:to_dep_map)
end
desc 'Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
task :dependencies => :environment do
abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
- puts JSON.pretty_generate ActionView::Digestor.new(CacheDigests.template_name, CacheDigests.finder).dependencies
+ puts JSON.pretty_generate ActionView::Digestor.tree(CacheDigests.template_name, CacheDigests.finder).children.map(&:name)
end
class CacheDigests
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index 15fc2b71a3..169ee55fdc 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -130,7 +130,6 @@ module ActionView
@source = source
@identifier = identifier
@handler = handler
- @cache_name = extract_resource_cache_name
@compiled = false
@original_encoding = nil
@locals = details[:locals] || []
@@ -166,10 +165,6 @@ module ActionView
@type ||= Types[@formats.first] if @formats.first
end
- def eligible_for_collection_caching?(as: nil)
- @cache_name == (as || inferred_cache_name).to_s
- end
-
# Receives a view object and return a template similar to self by using @virtual_path.
#
# This method is useful if you have a template object but it does not contain its source
@@ -355,23 +350,5 @@ module ActionView
ActiveSupport::Notifications.instrument("#{action}.action_view".freeze, payload, &block)
end
end
-
- EXPLICIT_COLLECTION = /# Template Collection: (?<resource_name>\w+)/
-
- def extract_resource_cache_name
- if match = @source.match(EXPLICIT_COLLECTION) || resource_cache_call_match
- match[:resource_name]
- end
- end
-
- def resource_cache_call_match
- if @handler.respond_to?(:resource_cache_call_pattern)
- @source.match(@handler.resource_cache_call_pattern)
- end
- end
-
- def inferred_cache_name
- @inferred_cache_name ||= @virtual_path.split('/'.freeze).last.sub('_'.freeze, ''.freeze)
- end
end
end
diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb
index ccee785d3e..3f38c3d2b9 100644
--- a/actionview/lib/action_view/template/error.rb
+++ b/actionview/lib/action_view/template/error.rb
@@ -135,13 +135,13 @@ module ActionView
end
def formatted_code_for(source_code, line_counter, indent, output)
- start_value = (output == :html) ? {} : ""
+ start_value = (output == :html) ? {} : []
source_code.inject(start_value) do |result, line|
line_counter += 1
if output == :html
result.update(line_counter.to_s => "%#{indent}s %s\n" % ["", line])
else
- result << "%#{indent}s: %s\n" % [line_counter, line]
+ result << "%#{indent}s: %s" % [line_counter, line]
end
end
end
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index 1f8459c24b..85a100ed4c 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -123,31 +123,6 @@ module ActionView
).src
end
- # Returns Regexp to extract a cached resource's name from a cache call at the
- # first line of a template.
- # The extracted cache name is captured as :resource_name.
- #
- # <% cache notification do %> # => notification
- #
- # The pattern should support templates with a beginning comment:
- #
- # <%# Still extractable even though there's a comment %>
- # <% cache notification do %> # => notification
- #
- # But fail to extract a name if a resource association is cached.
- #
- # <% cache notification.event do %> # => nil
- def resource_cache_call_pattern
- /\A
- (?:<%\#.*%>)* # optional initial comment
- \s* # followed by optional spaces or newlines
- <%\s*cache[\(\s] # followed by an ERB call to cache
- \s* # followed by optional spaces or newlines
- (?<resource_name>\w+) # capture the cache call argument as :resource_name
- [\s\)] # followed by a space or close paren
- /xm
- end
-
private
def valid_encoding(string, encoding)
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index 8a675cd521..c5e69b1833 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -55,6 +55,10 @@ module ActionView
@query_cache = SmallCache.new
end
+ def inspect
+ "#<#{self.class.name}:0x#{(object_id << 1).to_s(16)} keys=#{@data.size} queries=#{@query_cache.size}>"
+ end
+
# Cache the templates returned by the block
def cache(key, name, prefix, partial, locals)
if Resolver.caching?
@@ -222,7 +226,7 @@ module ActionView
end
def find_template_paths(query)
- Dir[query].reject do |filename|
+ Dir[query].uniq.reject do |filename|
File.directory?(filename) ||
# deals with case-insensitive file systems.
!File.fnmatch(query, filename, File::FNM_EXTGLOB)
@@ -245,8 +249,12 @@ module ActionView
partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
query.gsub!(/:action/, partial)
- details.each do |ext, variants|
- query.gsub!(/:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
+ details.each do |ext, candidates|
+ if ext == :variants && candidates == :any
+ query.gsub!(/:#{ext}/, "*")
+ else
+ query.gsub!(/:#{ext}/, "{#{candidates.compact.uniq.join(',')}}")
+ end
end
File.expand_path(query, @path)
@@ -340,7 +348,11 @@ module ActionView
query = escape_entry(File.join(@path, path))
exts = EXTENSIONS.map do |ext, prefix|
- "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
+ if ext == :variants && details[ext] == :any
+ "{#{prefix}*,}"
+ else
+ "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
+ end
end.join
query + exts
diff --git a/actionview/lib/action_view/template/types.rb b/actionview/lib/action_view/template/types.rb
index c233d06ccb..b32567cd66 100644
--- a/actionview/lib/action_view/template/types.rb
+++ b/actionview/lib/action_view/template/types.rb
@@ -1,4 +1,3 @@
-require 'set'
require 'active_support/core_ext/module/attribute_accessors'
module ActionView
diff --git a/actionview/lib/action_view/view_paths.rb b/actionview/lib/action_view/view_paths.rb
index 37722013ce..717d6866c5 100644
--- a/actionview/lib/action_view/view_paths.rb
+++ b/actionview/lib/action_view/view_paths.rb
@@ -1,5 +1,3 @@
-require 'action_view/base'
-
module ActionView
module ViewPaths
extend ActiveSupport::Concern
@@ -10,7 +8,7 @@ module ActionView
self._view_paths.freeze
end
- delegate :template_exists?, :view_paths, :formats, :formats=,
+ delegate :template_exists?, :any_templates?, :view_paths, :formats, :formats=,
:locale, :locale=, :to => :lookup_context
module ClassMethods
diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb
index 79173f730f..3256d8fc4d 100644
--- a/actionview/test/abstract_unit.rb
+++ b/actionview/test/abstract_unit.rb
@@ -1,5 +1,3 @@
-require File.expand_path('../../../load_paths', __FILE__)
-
$:.unshift(File.dirname(__FILE__) + '/lib')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers')
@@ -95,12 +93,14 @@ module ActionDispatch
super
return if DrawOnce.drew
- SharedTestRoutes.draw do
- get ':controller(/:action)'
- end
+ ActiveSupport::Deprecation.silence do
+ SharedTestRoutes.draw do
+ get ':controller(/:action)'
+ end
- ActionDispatch::IntegrationTest.app.routes.draw do
- get ':controller(/:action)'
+ ActionDispatch::IntegrationTest.app.routes.draw do
+ get ':controller(/:action)'
+ end
end
DrawOnce.drew = true
diff --git a/actionview/test/fixtures/digestor/messages/peek.html.erb b/actionview/test/fixtures/digestor/messages/peek.html.erb
new file mode 100644
index 0000000000..84885ab0bc
--- /dev/null
+++ b/actionview/test/fixtures/digestor/messages/peek.html.erb
@@ -0,0 +1,2 @@
+<%# Template Dependency: messages/message %>
+<%= render "comments/comments" %>
diff --git a/actionview/test/fixtures/test/_customer.mobile.erb b/actionview/test/fixtures/test/_customer.mobile.erb
new file mode 100644
index 0000000000..d8220afeda
--- /dev/null
+++ b/actionview/test/fixtures/test/_customer.mobile.erb
@@ -0,0 +1 @@
+Hello: <%= customer.name rescue "Anonymous" %> \ No newline at end of file
diff --git a/actionview/test/fixtures/test/_🍣.erb b/actionview/test/fixtures/test/_🍣.erb
new file mode 100644
index 0000000000..4bbe59410a
--- /dev/null
+++ b/actionview/test/fixtures/test/_🍣.erb
@@ -0,0 +1 @@
+🍣
diff --git a/actionview/test/lib/controller/fake_models.rb b/actionview/test/lib/controller/fake_models.rb
index a122fe17c9..65c68fc34a 100644
--- a/actionview/test/lib/controller/fake_models.rb
+++ b/actionview/test/lib/controller/fake_models.rb
@@ -31,27 +31,6 @@ end
class GoodCustomer < Customer
end
-class TicketType < Struct.new(:name)
- extend ActiveModel::Naming
- include ActiveModel::Conversion
- extend ActiveModel::Translation
-
- def initialize(*args)
- super
- @persisted = false
- end
-
- def persisted=(boolean)
- @persisted = boolean
- end
-
- def persisted?
- @persisted
- end
-
- attr_accessor :name
-end
-
class Post < Struct.new(:title, :author_name, :body, :secret, :persisted, :written_on, :cost)
extend ActiveModel::Naming
include ActiveModel::Conversion
diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb
index 4678998bdc..e67d5d0e8c 100644
--- a/actionview/test/template/date_helper_test.rb
+++ b/actionview/test/template/date_helper_test.rb
@@ -255,6 +255,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_day(16, :prompt => 'Choose day')
end
+ def test_select_day_with_generic_with_css_classes
+ expected = %(<select id="date_day" name="date[day]" class="day">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_day(16, with_css_classes: true)
+ end
+
+ def test_select_day_with_custom_with_css_classes
+ expected = %(<select id="date_day" name="date[day]" class="my-day">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_day(16, with_css_classes: { day: 'my-day' })
+ end
+
def test_select_month
expected = %(<select id="date_month" name="date[month]">\n)
expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
@@ -408,6 +424,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_month(8, :prompt => 'Choose month')
end
+ def test_select_month_with_generic_with_css_classes
+ expected = %(<select id="date_month" name="date[month]" class="month">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_month(8, with_css_classes: true)
+ end
+
+ def test_select_month_with_custom_with_css_classes
+ expected = %(<select id="date_month" name="date[month]" class="my-month">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_month(8, with_css_classes: { month: 'my-month' })
+ end
+
def test_select_year
expected = %(<select id="date_year" name="date[year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -487,6 +519,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_year(nil, :start_year => 2003, :end_year => 2005, :prompt => 'Choose year')
end
+ def test_select_year_with_generic_with_css_classes
+ expected = %(<select id="date_year" name="date[year]" class="year">\n)
+ expected << %(<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_year(nil, start_year: 2003, end_year: 2005, with_css_classes: true)
+ end
+
+ def test_select_year_with_custom_with_css_classes
+ expected = %(<select id="date_year" name="date[year]" class="my-year">\n)
+ expected << %(<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_year(nil, start_year: 2003, end_year: 2005, with_css_classes: { year: 'my-year' })
+ end
+
def test_select_hour
expected = %(<select id="date_hour" name="date[hour]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
@@ -559,6 +607,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => 'Choose hour')
end
+ def test_select_hour_with_generic_with_css_classes
+ expected = %(<select id="date_hour" name="date[hour]" class="hour">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), with_css_classes: true)
+ end
+
+ def test_select_hour_with_custom_with_css_classes
+ expected = %(<select id="date_hour" name="date[hour]" class="my-hour">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), with_css_classes: { hour: 'my-hour' })
+ end
+
def test_select_minute
expected = %(<select id="date_minute" name="date[minute]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
@@ -647,6 +711,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => 'Choose minute')
end
+ def test_select_minute_with_generic_with_css_classes
+ expected = %(<select id="date_minute" name="date[minute]" class="minute">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), with_css_classes: true)
+ end
+
+ def test_select_minute_with_custom_with_css_classes
+ expected = %(<select id="date_minute" name="date[minute]" class="my-minute">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), with_css_classes: { minute: 'my-minute' })
+ end
+
def test_select_second
expected = %(<select id="date_second" name="date[second]">\n)
expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
@@ -711,6 +791,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => 'Choose seconds')
end
+ def test_select_second_with_generic_with_css_classes
+ expected = %(<select id="date_second" name="date[second]" class="second">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), with_css_classes: true)
+ end
+
+ def test_select_second_with_custom_with_css_classes
+ expected = %(<select id="date_second" name="date[second]" class="my-second">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), with_css_classes: { second: 'my-second' })
+ end
+
def test_select_date
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -1018,6 +1114,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]", :with_css_classes => true})
end
+ def test_select_date_with_custom_with_css_classes
+ expected = %(<select id="date_year" name="date[year]" class="my-year">\n)
+ expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_month" name="date[month]" class="my-month">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_day" name="date[day]" class="my-day">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), start_year: 2003, end_year: 2005, with_css_classes: { year: 'my-year', month: 'my-month', day: 'my-day' })
+ end
+
def test_select_date_with_css_classes_option_and_html_class_option
expected = %(<select id="date_first_year" name="date[first][year]" class="datetime optional year">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -1034,6 +1146,54 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]", :with_css_classes => true}, { class: 'datetime optional' })
end
+ def test_select_date_with_custom_with_css_classes_and_html_class_option
+ expected = %(<select id="date_year" name="date[year]" class="date optional my-year">\n)
+ expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_month" name="date[month]" class="date optional my-month">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_day" name="date[day]" class="date optional my-day">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {start_year: 2003, end_year: 2005, with_css_classes: { year: 'my-year', month: 'my-month', day: 'my-day' }}, { class: 'date optional' })
+ end
+
+ def test_select_date_with_partial_with_css_classes_and_html_class_option
+ expected = %(<select id="date_year" name="date[year]" class="date optional">\n)
+ expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_month" name="date[month]" class="date optional my-month custom-grid">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_day" name="date[day]" class="date optional">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {start_year: 2003, end_year: 2005, with_css_classes: { month: 'my-month custom-grid' }}, { class: 'date optional' })
+ end
+
+ def test_select_date_with_html_class_option
+ expected = %(<select id="date_year" name="date[year]" class="date optional custom-grid">\n)
+ expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_month" name="date[month]" class="date optional custom-grid">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_day" name="date[day]" class="date optional custom-grid">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005 }, { class: 'date optional custom-grid' })
+ end
+
def test_select_datetime
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -1270,6 +1430,62 @@ class DateHelperTest < ActionView::TestCase
:prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year', :hour => 'Choose hour', :minute => 'Choose minute'})
end
+ def test_select_datetime_with_generic_with_css_classes
+ expected = %(<select id="date_year" name="date[year]" class="year">\n)
+ expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_month" name="date[month]" class="month">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_day" name="date[day]" class="day">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ expected << " &mdash; "
+
+ expected << %(<select id="date_hour" name="date[hour]" class="hour">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+ expected << "</select>\n"
+
+ expected << " : "
+
+ expected << %(<select id="date_minute" name="date[minute]" class="minute">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), start_year: 2003, end_year: 2005, with_css_classes: true)
+ end
+
+ def test_select_datetime_with_custom_with_css_classes
+ expected = %(<select id="date_year" name="date[year]" class="my-year">\n)
+ expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_month" name="date[month]" class="my-month">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_day" name="date[day]" class="my-day">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ expected << " &mdash; "
+
+ expected << %(<select id="date_hour" name="date[hour]" class="my-hour">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+ expected << "</select>\n"
+
+ expected << " : "
+
+ expected << %(<select id="date_minute" name="date[minute]" class="my-minute">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), start_year: 2003, end_year: 2005, with_css_classes: { day: 'my-day', month: 'my-month', year: 'my-year', hour: 'my-hour', minute: 'my-minute' })
+ end
+
def test_select_datetime_with_custom_hours
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="">Choose year</option>\n<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -1486,6 +1702,54 @@ class DateHelperTest < ActionView::TestCase
:prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'})
end
+ def test_select_time_with_generic_with_css_classes
+ expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
+ expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
+ expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
+
+ expected << %(<select id="date_hour" name="date[hour]" class="hour">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+ expected << "</select>\n"
+
+ expected << " : "
+
+ expected << %(<select id="date_minute" name="date[minute]" class="minute">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ expected << " : "
+
+ expected << %(<select id="date_second" name="date[second]" class="second">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), include_seconds: true, with_css_classes: true)
+ end
+
+ def test_select_time_with_custom_with_css_classes
+ expected = %(<input name="date[year]" id="date_year" value="2003" type="hidden" />\n)
+ expected << %(<input name="date[month]" id="date_month" value="8" type="hidden" />\n)
+ expected << %(<input name="date[day]" id="date_day" value="16" type="hidden" />\n)
+
+ expected << %(<select id="date_hour" name="date[hour]" class="my-hour">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n)
+ expected << "</select>\n"
+
+ expected << " : "
+
+ expected << %(<select id="date_minute" name="date[minute]" class="my-minute">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ expected << " : "
+
+ expected << %(<select id="date_second" name="date[second]" class="my-second">\n)
+ expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), include_seconds: true, with_css_classes: { hour: 'my-hour', minute: 'my-minute', second: 'my-second' })
+ end
+
def test_select_time_with_hidden
expected = %(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n)
expected << %(<input id="date_first_month" name="date[first][month]" type="hidden" value="8" />\n)
@@ -2014,6 +2278,46 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, date_select("post", "written_on", :prompt => {:year => 'Choose year', :month => 'Choose month', :day => 'Choose day'})
end
+ def test_date_select_with_generic_with_css_classes
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="year">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]" class="month">\n}
+ expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]" class="day">\n}
+ expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+
+ expected << "</select>\n"
+
+ assert_dom_equal expected, date_select("post", "written_on", with_css_classes: true)
+ end
+
+ def test_date_select_with_custom_with_css_classes
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="my-year">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]" class="my-month">\n}
+ expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]" class="my-day">\n}
+ expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+
+ expected << "</select>\n"
+
+ assert_dom_equal expected, date_select("post", "written_on", with_css_classes: { year: 'my-year', month: 'my-month', day: 'my-day' })
+ end
+
def test_time_select
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
@@ -2220,6 +2524,48 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, time_select("post", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute'})
end
+ def test_time_select_with_generic_with_css_classes
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+ expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+ expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+ expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]" class="hour">\n)
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ expected << " : "
+
+ expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]" class="minute">\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, time_select("post", "written_on", with_css_classes: true)
+ end
+
+ def test_time_select_with_custom_with_css_classes
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+ expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+ expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+ expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]" class="my-hour">\n)
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ expected << " : "
+
+ expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]" class="my-minute">\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, time_select("post", "written_on", with_css_classes: { hour: 'my-hour', minute: 'my-minute' })
+ end
+
def test_time_select_with_disabled_html_option
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
@@ -2493,6 +2839,64 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at", :start_year=>1999, :end_year=>2009, :prompt => {:year => 'Choose year', :month => 'Choose month', :day => 'Choose day', :hour => 'Choose hour', :minute => 'Choose minute'})
end
+ def test_datetime_select_with_generic_with_css_classes
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="year">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]" class="month">\n}
+ expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]" class="day">\n}
+ expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+ expected << "</select>\n"
+
+ expected << " &mdash; "
+
+ expected << %{<select id="post_written_on_4i" name="post[written_on(4i)]" class="hour">\n}
+ expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n}
+ expected << "</select>\n"
+ expected << " : "
+ expected << %{<select id="post_written_on_5i" name="post[written_on(5i)]" class="minute">\n}
+ expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n}
+ expected << "</select>\n"
+
+ assert_dom_equal expected, datetime_select("post", "written_on", start_year: 1999, end_year: 2009, with_css_classes: true)
+ end
+
+ def test_datetime_select_with_custom_with_css_classes
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="my-year">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]" class="my-month">\n}
+ expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_3i" name="post[written_on(3i)]" class="my-day">\n}
+ expected << %{<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n}
+ expected << "</select>\n"
+
+ expected << " &mdash; "
+
+ expected << %{<select id="post_written_on_4i" name="post[written_on(4i)]" class="my-hour">\n}
+ expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n}
+ expected << "</select>\n"
+ expected << " : "
+ expected << %{<select id="post_written_on_5i" name="post[written_on(5i)]" class="my-minute">\n}
+ expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n}
+ expected << "</select>\n"
+
+ assert_dom_equal expected, datetime_select("post", "written_on", start_year: 1999, end_year: 2009, with_css_classes: { year: 'my-year', month: 'my-month', day: 'my-day', hour: 'my-hour', minute: 'my-minute' })
+ end
+
def test_date_select_with_zero_value_and_no_start_year
expected = %(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index bfab97cf1e..d4c5048bde 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -26,6 +26,7 @@ class TemplateDigestorTest < ActionView::TestCase
@cwd = Dir.pwd
@tmp_dir = Dir.mktmpdir
+ ActionView::LookupContext::DetailsKey.clear
FileUtils.cp_r FixtureFinder::FIXTURES_DIR, @tmp_dir
Dir.chdir @tmp_dir
end
@@ -33,7 +34,6 @@ class TemplateDigestorTest < ActionView::TestCase
def teardown
Dir.chdir @cwd
FileUtils.rm_r @tmp_dir
- ActionView::Digestor.cache.clear
end
def test_top_level_change_reflected
@@ -130,12 +130,27 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
+ def test_getting_of_singly_nested_dependencies
+ singly_nested_dependencies = ["messages/header", "messages/form", "messages/message", "events/event", "comments/comment"]
+ assert_equal singly_nested_dependencies, nested_dependencies('messages/edit')
+ end
+
+ def test_getting_of_doubly_nested_dependencies
+ doubly_nested = [{"comments/comments"=>["comments/comment"]}, "messages/message"]
+ assert_equal doubly_nested, nested_dependencies('messages/peek')
+ end
+
def test_nested_template_directory
assert_digest_difference("messages/show") do
change_template("messages/actions/_move")
end
end
+ def test_nested_template_deps
+ nested_deps = ["messages/header", {"comments/comments"=>["comments/comment"]}, "messages/actions/move", "events/event", "messages/something_missing", "messages/something_missing_1", "messages/message", "messages/form"]
+ assert_equal nested_deps, nested_dependencies("messages/show")
+ end
+
def test_recursion_in_renders
assert digest("level/recursion") # assert recursion is possible
assert_not_nil digest("level/recursion") # assert digest is stored
@@ -291,28 +306,29 @@ class TemplateDigestorTest < ActionView::TestCase
def assert_digest_difference(template_name, options = {})
previous_digest = digest(template_name, options)
- ActionView::Digestor.cache.clear
+ finder.digest_cache.clear
yield
assert_not_equal previous_digest, digest(template_name, options), "digest didn't change"
- ActionView::Digestor.cache.clear
+ finder.digest_cache.clear
end
def digest(template_name, options = {})
options = options.dup
finder.variants = options.delete(:variants) || []
-
- ActionView::Digestor.digest({ name: template_name, finder: finder }.merge(options))
+ ActionView::Digestor.digest(name: template_name, finder: finder, dependencies: (options[:dependencies] || []))
end
def dependencies(template_name)
- ActionView::Digestor.new(template_name, finder).dependencies
+ tree = ActionView::Digestor.tree(template_name, finder)
+ tree.children.map(&:name)
end
def nested_dependencies(template_name)
- ActionView::Digestor.new(template_name, finder).nested_dependencies
+ tree = ActionView::Digestor.tree(template_name, finder)
+ tree.children.map(&:to_dep_map)
end
def finder
diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb
index 034b8a4bf6..310d0ce514 100644
--- a/actionview/test/template/form_helper_test.rb
+++ b/actionview/test/template/form_helper_test.rb
@@ -128,8 +128,6 @@ class FormHelperTest < ActionView::TestCase
@post_delegator.title = 'Hello World'
@car = Car.new("#000FFF")
-
- @ticket_type = TicketType.new
end
Routes = ActionDispatch::Routing::RouteSet.new
@@ -138,8 +136,6 @@ class FormHelperTest < ActionView::TestCase
resources :comments
end
- resources :ticket_types
-
namespace :admin do
resources :posts do
resources :comments
@@ -1143,13 +1139,17 @@ class FormHelperTest < ActionView::TestCase
def test_datetime_field
expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T00:00:00.000+0000" />}
- assert_dom_equal(expected, datetime_field("post", "written_on"))
+ assert_deprecated do
+ assert_dom_equal(expected, datetime_field("post", "written_on"))
+ end
end
def test_datetime_field_with_datetime_value
expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T01:02:03.000+0000" />}
@post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
- assert_dom_equal(expected, datetime_field("post", "written_on"))
+ assert_deprecated do
+ assert_dom_equal(expected, datetime_field("post", "written_on"))
+ end
end
def test_datetime_field_with_extra_attrs
@@ -1158,20 +1158,26 @@ class FormHelperTest < ActionView::TestCase
min_value = DateTime.new(2000, 6, 15, 20, 45, 30)
max_value = DateTime.new(2010, 8, 15, 10, 25, 00)
step = 60
- assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value, step: step))
+ assert_deprecated do
+ assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value, step: step))
+ end
end
def test_datetime_field_with_value_attr
expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2013-06-29T13:37:00+00:00" />}
value = DateTime.new(2013,6,29,13,37)
- assert_dom_equal(expected, datetime_field("post", "written_on", value: value))
+ assert_deprecated do
+ assert_dom_equal(expected, datetime_field("post", "written_on", value: value))
+ end
end
def test_datetime_field_with_timewithzone_value
previous_time_zone, Time.zone = Time.zone, 'UTC'
expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" value="2004-06-15T15:30:45.000+0000" />}
@post.written_on = Time.zone.parse('2004-06-15 15:30:45')
- assert_dom_equal(expected, datetime_field("post", "written_on"))
+ assert_deprecated do
+ assert_dom_equal(expected, datetime_field("post", "written_on"))
+ end
ensure
Time.zone = previous_time_zone
end
@@ -1179,7 +1185,9 @@ class FormHelperTest < ActionView::TestCase
def test_datetime_field_with_nil_value
expected = %{<input id="post_written_on" name="post[written_on]" type="datetime" />}
@post.written_on = nil
- assert_dom_equal(expected, datetime_field("post", "written_on"))
+ assert_deprecated do
+ assert_dom_equal(expected, datetime_field("post", "written_on"))
+ end
end
def test_datetime_field_with_string_values_for_min_and_max
@@ -1187,7 +1195,9 @@ class FormHelperTest < ActionView::TestCase
@post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
min_value = "2000-06-15T20:45:30.000+0000"
max_value = "2010-08-15T10:25:00.000+0000"
- assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value))
+ assert_deprecated do
+ assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value))
+ end
end
def test_datetime_field_with_invalid_string_values_for_min_and_max
@@ -1195,7 +1205,9 @@ class FormHelperTest < ActionView::TestCase
@post.written_on = DateTime.new(2004, 6, 15, 1, 2, 3)
min_value = "foo"
max_value = "bar"
- assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value))
+ assert_deprecated do
+ assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value))
+ end
end
def test_datetime_local_field
@@ -1876,20 +1888,6 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
- def test_lowercase_model_name_default_submit_button_value
- form_for(@ticket_type) do |f|
- concat f.submit
- end
-
- expected =
- '<form class="new_ticket_type" id="new_ticket_type" action="/ticket_types" accept-charset="UTF-8" method="post">' +
- hidden_fields +
- '<input type="submit" name="commit" value="Create ticket type" data-disable-with="Create ticket type" />' +
- '</form>'
-
- assert_dom_equal expected, output_buffer
- end
-
def test_form_for_with_symbol_object_name
form_for(@post, as: "other_name", html: { id: "create-post" }) do |f|
concat f.label(:title, class: 'post_title')
@@ -2257,7 +2255,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form('/posts', 'new_post', 'new_post') do
- "<input name='commit' data-disable-with='Create post' type='submit' value='Create post' />"
+ "<input name='commit' data-disable-with='Create Post' type='submit' value='Create Post' />"
end
assert_dom_equal expected, output_buffer
@@ -2272,7 +2270,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
- "<input name='commit' data-disable-with='Confirm post changes' type='submit' value='Confirm post changes' />"
+ "<input name='commit' data-disable-with='Confirm Post changes' type='submit' value='Confirm Post changes' />"
end
assert_dom_equal expected, output_buffer
@@ -2300,7 +2298,7 @@ class FormHelperTest < ActionView::TestCase
end
expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', method: 'patch') do
- "<input name='commit' data-disable-with='Update your post' type='submit' value='Update your post' />"
+ "<input name='commit' data-disable-with='Update your Post' type='submit' value='Update your Post' />"
end
assert_dom_equal expected, output_buffer
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index 07b3fba754..7b93c8dc29 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -622,7 +622,9 @@ class FormTagHelperTest < ActionView::TestCase
def test_datetime_field_tag
expected = %{<input id="appointment" name="appointment" type="datetime" />}
- assert_dom_equal(expected, datetime_field_tag("appointment"))
+ assert_deprecated do
+ assert_dom_equal(expected, datetime_field_tag("appointment"))
+ end
end
def test_datetime_local_field_tag
diff --git a/actionview/test/template/log_subscriber_test.rb b/actionview/test/template/log_subscriber_test.rb
index 4776c18b0b..7683444bf0 100644
--- a/actionview/test/template/log_subscriber_test.rb
+++ b/actionview/test/template/log_subscriber_test.rb
@@ -35,7 +35,8 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
@view.render(:file => "test/hello_world")
wait
- assert_equal 1, @logger.logged(:info).size
+ assert_equal 2, @logger.logged(:info).size
+ assert_match(/Rendering test\/hello_world\.erb/, @logger.logged(:info).first)
assert_match(/Rendered test\/hello_world\.erb/, @logger.logged(:info).last)
end
end
@@ -45,7 +46,8 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
@view.render(:text => "TEXT")
wait
- assert_equal 1, @logger.logged(:info).size
+ assert_equal 2, @logger.logged(:info).size
+ assert_match(/Rendering text template/, @logger.logged(:info).first)
assert_match(/Rendered text template/, @logger.logged(:info).last)
end
end
@@ -55,7 +57,8 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
@view.render(:inline => "<%= 'TEXT' %>")
wait
- assert_equal 1, @logger.logged(:info).size
+ assert_equal 2, @logger.logged(:info).size
+ assert_match(/Rendering inline template/, @logger.logged(:info).first)
assert_match(/Rendered inline template/, @logger.logged(:info).last)
end
end
@@ -86,7 +89,7 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
wait
assert_equal 1, @logger.logged(:info).size
- assert_match(/Rendered test\/_customer.erb/, @logger.logged(:info).last)
+ assert_match(/Rendered collection of test\/_customer.erb \[2 times\]/, @logger.logged(:info).last)
end
end
@@ -96,7 +99,7 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
wait
assert_equal 1, @logger.logged(:info).size
- assert_match(/Rendered customers\/_customer\.html\.erb/, @logger.logged(:info).last)
+ assert_match(/Rendered collection of customers\/_customer\.html\.erb \[2 times\]/, @logger.logged(:info).last)
end
end
@@ -106,7 +109,21 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
wait
assert_equal 1, @logger.logged(:info).size
- assert_match(/Rendered collection/, @logger.logged(:info).last)
+ assert_match(/Rendered collection of templates/, @logger.logged(:info).last)
+ end
+ end
+
+ def test_render_collection_with_cached_set
+ Rails.stub(:root, File.expand_path(FIXTURE_LOAD_PATH)) do
+ def @view.view_cache_dependencies; []; end
+ def @view.fragment_cache_key(*); 'ahoy `controller` dependency'; end
+
+ @view.render(partial: 'customers/customer', collection: [ Customer.new('david'), Customer.new('mary') ], cached: true,
+ locals: { greeting: 'hi' })
+ wait
+
+ assert_equal 1, @logger.logged(:info).size
+ assert_match(/Rendered collection of customers\/_customer\.html\.erb \[0 \/ 2 cache hits\]/, @logger.logged(:info).last)
end
end
end
diff --git a/actionview/test/template/output_safety_helper_test.rb b/actionview/test/template/output_safety_helper_test.rb
index 8de0ae2f6f..b940c9dd36 100644
--- a/actionview/test/template/output_safety_helper_test.rb
+++ b/actionview/test/template/output_safety_helper_test.rb
@@ -32,4 +32,59 @@ class OutputSafetyHelperTest < ActionView::TestCase
joined = safe_join(['"a"',['<b>','<c>']], ' <br/> ')
assert_equal '&quot;a&quot; &lt;br/&gt; &lt;b&gt; &lt;br/&gt; &lt;c&gt;', joined
end
+
+ test "to_sentence should escape non-html_safe values" do
+ actual = to_sentence(%w(< > & ' "))
+ assert actual.html_safe?
+ assert_equal("&lt;, &gt;, &amp;, &#39;, and &quot;", actual)
+
+ actual = to_sentence(%w(<script>))
+ assert actual.html_safe?
+ assert_equal("&lt;script&gt;", actual)
+ end
+
+ test "to_sentence does not double escape if single value is html_safe" do
+ assert_equal("&lt;script&gt;", to_sentence([ERB::Util.html_escape("<script>")]))
+ assert_equal("&lt;script&gt;", to_sentence(["&lt;script&gt;".html_safe]))
+ assert_equal("&amp;lt;script&amp;gt;", to_sentence(["&lt;script&gt;"]))
+ end
+
+ test "to_sentence connector words are checked for html safety" do
+ assert_equal "one & two, and three", to_sentence(['one', 'two', 'three'], words_connector: ' & '.html_safe)
+ assert_equal "one & two", to_sentence(['one', 'two'], two_words_connector: ' & '.html_safe)
+ assert_equal "one, two &lt;script&gt;alert(1)&lt;/script&gt; three", to_sentence(['one', 'two', 'three'], last_word_connector: ' <script>alert(1)</script> ')
+ end
+
+ test "to_sentence should not escape html_safe values" do
+ ptag = content_tag("p") do
+ safe_join(["<marquee>shady stuff</marquee>", tag("br")])
+ end
+ url = "https://example.com"
+ expected = %(<a href="#{url}">#{url}</a> and <p>&lt;marquee&gt;shady stuff&lt;/marquee&gt;<br /></p>)
+ actual = to_sentence([link_to(url, url), ptag])
+ assert actual.html_safe?
+ assert_equal(expected, actual)
+ end
+
+ test "to_sentence handles blank strings" do
+ actual = to_sentence(['', 'two', 'three'])
+ assert actual.html_safe?
+ assert_equal ", two, and three", actual
+ end
+
+ test "to_sentence handles nil values" do
+ actual = to_sentence([nil, 'two', 'three'])
+ assert actual.html_safe?
+ assert_equal ", two, and three", actual
+ end
+
+ test "to_sentence still supports ActiveSupports Array#to_sentence arguments" do
+ assert_equal "one two, and three", to_sentence(['one', 'two', 'three'], words_connector: ' ')
+ assert_equal "one & two, and three", to_sentence(['one', 'two', 'three'], words_connector: ' & '.html_safe)
+ assert_equal "onetwo, and three", to_sentence(['one', 'two', 'three'], words_connector: nil)
+ assert_equal "one, two, and also three", to_sentence(['one', 'two', 'three'], last_word_connector: ', and also ')
+ assert_equal "one, twothree", to_sentence(['one', 'two', 'three'], last_word_connector: nil)
+ assert_equal "one, two three", to_sentence(['one', 'two', 'three'], last_word_connector: ' ')
+ assert_equal "one, two and three", to_sentence(['one', 'two', 'three'], last_word_connector: ' and ')
+ end
end
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 333e0cca11..ad93236d32 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -207,6 +207,10 @@ module RenderTestCases
assert_nothing_raised { @view.render(:partial => "test/a-in") }
end
+ def test_render_partial_with_unicode_text
+ assert_nothing_raised { @view.render(:partial => "test/🍣") }
+ end
+
def test_render_partial_with_invalid_option_as
e = assert_raises(ArgumentError) { @view.render(:partial => "test/partial_only", :as => 'a-in') }
assert_equal "The value (a-in) of the option `as` is not a valid Ruby identifier; " +
@@ -226,13 +230,13 @@ module RenderTestCases
assert_match %r!method.*doesnt_exist!, e.message
assert_equal "", e.sub_template_message
assert_equal "1", e.line_number
- assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code.strip
+ assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code[0].strip
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
end
def test_render_error_indentation
e = assert_raises(ActionView::Template::Error) { @view.render(:partial => "test/raise_indentation") }
- error_lines = e.annoted_source_code.split("\n")
+ error_lines = e.annoted_source_code
assert_match %r!error\shere!, e.message
assert_equal "11", e.line_number
assert_equal " 9: <p>Ninth paragraph</p>", error_lines.second
@@ -252,7 +256,7 @@ module RenderTestCases
assert_match %r!method.*doesnt_exist!, e.message
assert_equal "", e.sub_template_message
assert_equal "1", e.line_number
- assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code.strip
+ assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code[0].strip
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
end
@@ -270,6 +274,11 @@ module RenderTestCases
assert_equal "Hello: davidHello: mary", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), Customer.new("mary") ])
end
+ def test_render_partial_collection_with_partial_name_containing_dot
+ assert_equal "Hello: davidHello: mary",
+ @view.render(:partial => "test/customer.mobile", :collection => [ Customer.new("david"), Customer.new("mary") ])
+ end
+
def test_render_partial_collection_as_by_string
assert_equal "david david davidmary mary mary",
@view.render(:partial => "test/customer_with_var", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => 'customer')
@@ -628,56 +637,59 @@ class LazyViewRenderTest < ActiveSupport::TestCase
end
end
-class CachedCollectionViewRenderTest < CachedViewRenderTest
+class CachedCollectionViewRenderTest < ActiveSupport::TestCase
class CachedCustomer < Customer; end
- teardown do
- ActionView::PartialRenderer.collection_cache.clear
- end
+ include RenderTestCases
- test "with custom key" do
- customer = Customer.new("david")
- key = cache_key([customer, 'key'], "test/_customer")
+ # Ensure view path cache is primed
+ setup do
+ view_paths = ActionController::Base.view_paths
+ assert_equal ActionView::OptimizedFileSystemResolver, view_paths.first.class
- ActionView::PartialRenderer.collection_cache.write(key, 'Hello')
+ ActionView::PartialRenderer.collection_cache = ActiveSupport::Cache::MemoryStore.new
- assert_equal "Hello",
- @view.render(partial: "test/customer", collection: [customer], cache: ->(item) { [item, 'key'] })
+ setup_view(view_paths)
end
- test "with caching with custom key and rendering with different key" do
- customer = Customer.new("david")
- key = cache_key([customer, 'key'], "test/_customer")
+ teardown do
+ GC.start
+ I18n.reload!
+ end
- ActionView::PartialRenderer.collection_cache.write(key, 'Hello')
+ test "collection caching does not cache by default" do
+ customer = Customer.new("david", 1)
+ key = cache_key(customer, "test/_customer")
- assert_equal "Hello: david",
- @view.render(partial: "test/customer", collection: [customer], cache: ->(item) { [item, 'another_key'] })
+ ActionView::PartialRenderer.collection_cache.write(key, 'Cached')
+
+ assert_not_equal "Cached",
+ @view.render(partial: "test/customer", collection: [customer])
end
- test "automatic caching with inferred cache name" do
- customer = CachedCustomer.new("david")
- key = cache_key(customer, "test/_cached_customer")
+ test "collection caching with partial that doesn't use fragment caching" do
+ customer = Customer.new("david", 1)
+ key = cache_key(customer, "test/_customer")
ActionView::PartialRenderer.collection_cache.write(key, 'Cached')
assert_equal "Cached",
- @view.render(partial: "test/cached_customer", collection: [customer])
+ @view.render(partial: "test/customer", collection: [customer], cached: true)
end
- test "automatic caching with as name" do
- customer = CachedCustomer.new("david")
- key = cache_key(customer, "test/_cached_customer_as")
+ test "collection caching with cached true" do
+ customer = CachedCustomer.new("david", 1)
+ key = cache_key(customer, "test/_cached_customer")
ActionView::PartialRenderer.collection_cache.write(key, 'Cached')
assert_equal "Cached",
- @view.render(partial: "test/cached_customer_as", collection: [customer], as: :buyer)
+ @view.render(partial: "test/cached_customer", collection: [customer], cached: true)
end
private
- def cache_key(names, virtual_path)
+ def cache_key(*names, virtual_path)
digest = ActionView::Digestor.digest name: virtual_path, finder: @view.lookup_context, dependencies: []
- @view.fragment_cache_key([ *Array(names), digest ])
+ @view.fragment_cache_key([ *names, digest ])
end
end
diff --git a/actionview/test/template/resolver_cache_test.rb b/actionview/test/template/resolver_cache_test.rb
new file mode 100644
index 0000000000..1081c13db0
--- /dev/null
+++ b/actionview/test/template/resolver_cache_test.rb
@@ -0,0 +1,7 @@
+require 'abstract_unit'
+
+class ResolverCacheTest < ActiveSupport::TestCase
+ def test_inspect_shields_cache_internals
+ assert_match %r(#<ActionView::Resolver:0x[0-9a-f]+ @cache=#<ActionView::Resolver::Cache:0x[0-9a-f]+ keys=0 queries=0>>), ActionView::Resolver.new.inspect
+ end
+end
diff --git a/actionview/test/template/resolver_patterns_test.rb b/actionview/test/template/resolver_patterns_test.rb
index 575eb9bd28..1a091bd692 100644
--- a/actionview/test/template/resolver_patterns_test.rb
+++ b/actionview/test/template/resolver_patterns_test.rb
@@ -3,17 +3,17 @@ require 'abstract_unit'
class ResolverPatternsTest < ActiveSupport::TestCase
def setup
path = File.expand_path("../../fixtures/", __FILE__)
- pattern = ":prefix/{:formats/,}:action{.:formats,}{.:handlers,}"
+ pattern = ":prefix/{:formats/,}:action{.:formats,}{+:variants,}{.:handlers,}"
@resolver = ActionView::FileSystemResolver.new(path, pattern)
end
def test_should_return_empty_list_for_unknown_path
- templates = @resolver.find_all("unknown", "custom_pattern", false, {:locale => [], :formats => [:html], :handlers => [:erb]})
+ templates = @resolver.find_all("unknown", "custom_pattern", false, {locale: [], formats: [:html], variants: [], handlers: [:erb]})
assert_equal [], templates, "expected an empty list of templates"
end
def test_should_return_template_for_declared_path
- templates = @resolver.find_all("path", "custom_pattern", false, {:locale => [], :formats => [:html], :handlers => [:erb]})
+ templates = @resolver.find_all("path", "custom_pattern", false, {locale: [], formats: [:html], variants: [], handlers: [:erb]})
assert_equal 1, templates.size, "expected one template"
assert_equal "Hello custom patterns!", templates.first.source
assert_equal "custom_pattern/path", templates.first.virtual_path
@@ -21,11 +21,22 @@ class ResolverPatternsTest < ActiveSupport::TestCase
end
def test_should_return_all_templates_when_ambiguous_pattern
- templates = @resolver.find_all("another", "custom_pattern", false, {:locale => [], :formats => [:html], :handlers => [:erb]})
+ templates = @resolver.find_all("another", "custom_pattern", false, {locale: [], formats: [:html], variants: [], handlers: [:erb]})
assert_equal 2, templates.size, "expected two templates"
assert_equal "Another template!", templates[0].source
assert_equal "custom_pattern/another", templates[0].virtual_path
assert_equal "Hello custom patterns!", templates[1].source
assert_equal "custom_pattern/another", templates[1].virtual_path
end
+
+ def test_should_return_all_variants_for_any
+ templates = @resolver.find_all("hello_world", "test", false, {locale: [], formats: [:html, :text], variants: :any, handlers: [:erb]})
+ assert_equal 3, templates.size, "expected three templates"
+ assert_equal "Hello phone!", templates[0].source
+ assert_equal "test/hello_world", templates[0].virtual_path
+ assert_equal "Hello texty phone!", templates[1].source
+ assert_equal "test/hello_world", templates[1].virtual_path
+ assert_equal "Hello world!", templates[2].source
+ assert_equal "test/hello_world", templates[2].virtual_path
+ end
end
diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb
index 921011b073..533c1c3219 100644
--- a/actionview/test/template/template_test.rb
+++ b/actionview/test/template/template_test.rb
@@ -192,38 +192,6 @@ class TestERBTemplate < ActiveSupport::TestCase
assert_match(/\xFC/, e.message)
end
- def test_not_eligible_for_collection_caching_without_cache_call
- [
- "<%= 'Hello' %>",
- "<% cache_customer = 42 %>",
- "<% cache customer.name do %><% end %>",
- "<% my_cache customer do %><% end %>"
- ].each do |body|
- template = new_template(body, virtual_path: "test/foo/_customer")
- assert_not template.eligible_for_collection_caching?, "Template #{body.inspect} should not be eligible for collection caching"
- end
- end
-
- def test_eligible_for_collection_caching_with_cache_call_or_explicit
- [
- "<% cache customer do %><% end %>",
- "<% cache(customer) do %><% end %>",
- "<% cache( customer) do %><% end %>",
- "<% cache( customer ) do %><% end %>",
- "<%cache customer do %><% end %>",
- "<% cache customer do %><% end %>",
- " <% cache customer do %>\n<% end %>\n",
- "<%# comment %><% cache customer do %><% end %>",
- "<%# comment %>\n<% cache customer do %><% end %>",
- "<%# comment\n line 2\n line 3 %>\n<% cache customer do %><% end %>",
- "<%# comment 1 %>\n<%# comment 2 %>\n<% cache customer do %><% end %>",
- "<%# comment 1 %>\n<%# Template Collection: customer %>\n<% my_cache customer do %><% end %>"
- ].each do |body|
- template = new_template(body, virtual_path: "test/foo/_customer")
- assert template.eligible_for_collection_caching?, "Template #{body.inspect} should be eligible for collection caching"
- end
- end
-
def with_external_encoding(encoding)
old = Encoding.default_external
Encoding::Converter.new old, encoding if old != encoding
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 3010656166..ab56d80de3 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -71,6 +71,34 @@ class UrlHelperTest < ActiveSupport::TestCase
assert_equal 'javascript:history.back()', url_for(:back)
end
+ def test_to_form_params_with_hash
+ assert_equal(
+ [{ name: :name, value: 'David' }, { name: :nationality, value: 'Danish' }],
+ to_form_params(name: 'David', nationality: 'Danish')
+ )
+ end
+
+ def test_to_form_params_with_nested_hash
+ assert_equal(
+ [{ name: 'country[name]', value: 'Denmark' }],
+ to_form_params(country: { name: 'Denmark' })
+ )
+ end
+
+ def test_to_form_params_with_array_nested_in_hash
+ assert_equal(
+ [{ name: 'countries[]', value: 'Denmark' }, { name: 'countries[]', value: 'Sweden' }],
+ to_form_params(countries: ['Denmark', 'Sweden'])
+ )
+ end
+
+ def test_to_form_params_with_namespace
+ assert_equal(
+ [{ name: 'country[name]', value: 'Denmark' }],
+ to_form_params({name: 'Denmark'}, 'country')
+ )
+ end
+
def test_button_to_with_straight_url
assert_dom_equal %{<form method="post" action="http://www.example.com" class="button_to"><input type="submit" value="Hello" /></form>}, button_to("Hello", "http://www.example.com")
end
@@ -189,8 +217,22 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_button_to_with_params
assert_dom_equal(
- %{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="foo" value="bar" /><input type="hidden" name="baz" value="quux" /></form>},
- button_to("Hello", "http://www.example.com", params: {foo: :bar, baz: "quux"})
+ %{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="baz" value="quux" /><input type="hidden" name="foo" value="bar" /></form>},
+ button_to("Hello", "http://www.example.com", params: { foo: :bar, baz: "quux" })
+ )
+ end
+
+ def test_button_to_with_nested_hash_params
+ assert_dom_equal(
+ %{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="foo[bar]" value="baz" /></form>},
+ button_to("Hello", "http://www.example.com", params: { foo: { bar: 'baz' } })
+ )
+ end
+
+ def test_button_to_with_nested_array_params
+ assert_dom_equal(
+ %{<form action="http://www.example.com" class="button_to" method="post"><input type="submit" value="Hello" /><input type="hidden" name="foo[]" value="bar" /></form>},
+ button_to("Hello", "http://www.example.com", params: { foo: ['bar'] })
)
end
@@ -613,7 +655,9 @@ class UrlHelperControllerTest < ActionController::TestCase
to: 'url_helper_controller_test/url_helper#show_named_route',
as: :show_named_route
- get "/:controller(/:action(/:id))"
+ ActiveSupport::Deprecation.silence do
+ get "/:controller(/:action(/:id))"
+ end
get 'url_helper_controller_test/url_helper/normalize_recall_params',
to: UrlHelperController.action(:normalize_recall),