diff options
Diffstat (limited to 'actionview/lib')
8 files changed, 128 insertions, 27 deletions
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb index 72d79735ae..1f103786cb 100644 --- a/actionview/lib/action_view/digestor.rb +++ b/actionview/lib/action_view/digestor.rb @@ -60,7 +60,7 @@ module ActionView def digest Digest::MD5.hexdigest("#{source}-#{dependency_digest}").tap do |digest| - logger.try :info, " Cache digest for #{template.inspect}: #{digest}" + logger.try :debug, " Cache digest for #{template.inspect}: #{digest}" end rescue ActionView::MissingTemplate logger.try :error, " Couldn't find template for digesting: #{name}" diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb index 4a682ce4e2..9e8d005ec7 100644 --- a/actionview/lib/action_view/helpers/asset_url_helper.rb +++ b/actionview/lib/action_view/helpers/asset_url_helper.rb @@ -203,7 +203,6 @@ module ActionView request = self.request if respond_to?(:request) host = options[:host] host ||= config.asset_host if defined? config.asset_host - host ||= request.base_url if request && options[:protocol] == :request if host.respond_to?(:call) arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity @@ -214,6 +213,7 @@ module ActionView host = host % (Zlib.crc32(source) % 4) end + host ||= request.base_url if request && options[:protocol] == :request return unless host if host =~ URI_REGEXP @@ -231,7 +231,7 @@ module ActionView end end - # Computes the path to a javascript asset in the public javascripts directory. + # Computes the path to a JavaScript asset in the public javascripts directory. # If the +source+ filename has no extension, .js will be appended (except for explicit URIs) # Full paths from the document root will be passed through. # Used internally by +javascript_include_tag+ to build the script path. @@ -246,7 +246,7 @@ module ActionView end alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route - # Computes the full URL to a javascript asset in the public javascripts directory. + # Computes the full URL to a JavaScript asset in the public javascripts directory. # This will use +javascript_path+ internally, so most of their behaviors will be the same. def javascript_url(source, options = {}) url_to_asset(source, {type: :javascript}.merge!(options)) diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb index 2efb9612ac..27c7a26098 100644 --- a/actionview/lib/action_view/helpers/date_helper.rb +++ b/actionview/lib/action_view/helpers/date_helper.rb @@ -153,8 +153,8 @@ module ActionView # # Note that you cannot pass a <tt>Numeric</tt> value to <tt>time_ago_in_words</tt>. # - def time_ago_in_words(from_time, include_seconds_or_options = {}) - distance_of_time_in_words(from_time, Time.now, include_seconds_or_options) + def time_ago_in_words(from_time, options = {}) + distance_of_time_in_words(from_time, Time.now, options) end alias_method :distance_of_time_in_words_to_now, :time_ago_in_words diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index 789a413c8d..c6bc0c9e38 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -482,7 +482,7 @@ module ActionView # Admin? : <%= permission_fields.check_box :admin %> # <% end %> # - # <%= f.submit %> + # <%= person_form.submit %> # <% end %> # # In this case, the checkbox field will be represented by an HTML +input+ @@ -1013,6 +1013,18 @@ module ActionView # date_field("user", "born_on", value: "1984-05-12") # # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-05-12" /> # + # You can create values for the "min" and "max" attributes by passing + # instances of Date or Time to the options hash. + # + # date_field("user", "born_on", min: Date.today) + # # => <input id="user_born_on" name="user[born_on]" type="date" min="2014-05-20" /> + # + # Alternatively, you can pass a String formatted as an ISO8601 date as the + # values for "min" and "max." + # + # date_field("user", "born_on", min: "2014-05-20") + # # => <input id="user_born_on" name="user[born_on]" type="date" min="2014-05-20" /> + # def date_field(object_name, method, options = {}) Tags::DateField.new(object_name, method, self, options).render end @@ -1030,6 +1042,18 @@ module ActionView # time_field("task", "started_at") # # => <input id="task_started_at" name="task[started_at]" type="time" /> # + # You can create values for the "min" and "max" attributes by passing + # instances of Date or Time to the options hash. + # + # time_field("task", "started_at", min: Time.now) + # # => <input id="task_started_at" name="task[started_at]" type="time" min="01:00:00.000" /> + # + # Alternatively, you can pass a String formatted as an ISO8601 time as the + # values for "min" and "max." + # + # time_field("task", "started_at", min: "01:00:00") + # # => <input id="task_started_at" name="task[started_at]" type="time" min="01:00:00.000" /> + # def time_field(object_name, method, options = {}) Tags::TimeField.new(object_name, method, self, options).render end @@ -1047,6 +1071,18 @@ module ActionView # datetime_field("user", "born_on") # # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" /> # + # You can create values for the "min" and "max" attributes by passing + # instances of Date or Time to the options hash. + # + # datetime_field("user", "born_on", min: Date.today) + # # => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" /> + # + # Alternatively, you can pass a String formatted as an ISO8601 datetime + # with UTC offset as the values for "min" and "max." + # + # datetime_field("user", "born_on", min: "2014-05-20T00:00:00+0000") + # # => <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 = {}) Tags::DatetimeField.new(object_name, method, self, options).render end @@ -1064,6 +1100,18 @@ module ActionView # datetime_local_field("user", "born_on") # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" /> # + # You can create values for the "min" and "max" attributes by passing + # instances of Date or Time to the options hash. + # + # datetime_local_field("user", "born_on", min: Date.today) + # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" /> + # + # Alternatively, you can pass a String formatted as an ISO8601 datetime as + # the values for "min" and "max." + # + # datetime_local_field("user", "born_on", min: "2014-05-20T00:00:00") + # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" /> + # def datetime_local_field(object_name, method, options = {}) Tags::DatetimeLocalField.new(object_name, method, self, options).render end diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb index b0d9c7c7f9..f03362d0f5 100644 --- a/actionview/lib/action_view/helpers/output_safety_helper.rb +++ b/actionview/lib/action_view/helpers/output_safety_helper.rb @@ -17,7 +17,7 @@ module ActionView #:nodoc: stringish.to_s.html_safe end - # This method returns a html safe string similar to what <tt>Array#join</tt> + # This method returns an html safe string similar to what <tt>Array#join</tt> # would return. The array is flattened, and all items, including # the supplied separator, are html escaped unless they are html # safe, and the returned string is marked as html safe. diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb index 35444bcfb4..268558669e 100644 --- a/actionview/lib/action_view/helpers/tag_helper.rb +++ b/actionview/lib/action_view/helpers/tag_helper.rb @@ -9,6 +9,7 @@ module ActionView module TagHelper extend ActiveSupport::Concern include CaptureHelper + include OutputSafetyHelper BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer autoplay controls loop selected hidden scoped async diff --git a/actionview/lib/action_view/helpers/tags/datetime_field.rb b/actionview/lib/action_view/helpers/tags/datetime_field.rb index 25e7e05ec6..b2cee9d198 100644 --- a/actionview/lib/action_view/helpers/tags/datetime_field.rb +++ b/actionview/lib/action_view/helpers/tags/datetime_field.rb @@ -5,8 +5,8 @@ module ActionView def render options = @options.stringify_keys options["value"] ||= format_date(value(object)) - options["min"] = format_date(options["min"]) - options["max"] = format_date(options["max"]) + options["min"] = format_date(datetime_value(options["min"])) + options["max"] = format_date(datetime_value(options["max"])) @options = options super end @@ -16,6 +16,14 @@ module ActionView def format_date(value) value.try(:strftime, "%Y-%m-%dT%T.%L%z") end + + def datetime_value(value) + if value.is_a? String + DateTime.parse(value) rescue nil + else + value + end + end end end end diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index 36f17f01fd..a4f6573601 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -1,6 +1,33 @@ require 'thread_safe' module ActionView + class PartialIteration + # The number of iterations that will be done by the partial. + attr_reader :size + + # The current iteration of the partial. + attr_reader :index + + def initialize(size) + @size = size + @index = 0 + end + + # Check if this is the first iteration of the partial. + def first? + index == 0 + end + + # Check if this is the last iteration of the partial. + def last? + index == size - 1 + end + + def iterate! # :nodoc: + @index += 1 + end + end + # = Action View Partials # # There's also a convenience method for rendering sub templates within the current controller that depends on a @@ -56,8 +83,12 @@ module ActionView # <%= render partial: "ad", collection: @advertisements %> # # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An - # iteration counter will automatically be made available to the template with a name of the form - # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+. + # iteration object will automatically be made available to the template with a name of the form + # +partial_name_iteration+. The iteration object has knowledge about which index the current object has in + # the collection and the total size of the collection. The iteration object also has two convenience methods, + # +first?+ and +last?+. In the case of the example above, the template would be fed +ad_iteration+. + # For backwards compatibility the +partial_name_counter+ is still present and is mapped to the iteration's + # +index+ method. # # The <tt>:as</tt> option may be used when rendering partials. # @@ -352,7 +383,7 @@ module ActionView end if @path - @variable, @variable_counter = retrieve_variable(@path, as) + @variable, @variable_counter, @variable_iteration = retrieve_variable(@path, as) @template_keys = retrieve_template_keys else paths.map! { |path| retrieve_variable(path, as).unshift(path) } @@ -385,19 +416,22 @@ module ActionView def collection_with_template view, locals, template = @view, @locals, @template - as, counter = @variable, @variable_counter + as, counter, iteration = @variable, @variable_counter, @variable_iteration if layout = @options[:layout] layout = find_template(layout, @template_keys) end - index = -1 + partial_iteration = PartialIteration.new(@collection.size) + locals[iteration] = partial_iteration + @collection.map do |object| - locals[as] = object - locals[counter] = (index += 1) + locals[as] = object + locals[counter] = partial_iteration.index content = template.render(view, locals) content = layout.render(view, locals) { content } if layout + partial_iteration.iterate! content end end @@ -407,16 +441,20 @@ module ActionView cache = {} keys = @locals.keys - index = -1 + partial_iteration = PartialIteration.new(@collection.size) + @collection.map do |object| - index += 1 - path, as, counter = collection_data[index] + index = partial_iteration.index + path, as, counter, iteration = collection_data[index] - locals[as] = object - locals[counter] = index + locals[as] = object + locals[counter] = index + locals[iteration] = partial_iteration template = (cache[path] ||= find_template(path, keys + [as, counter])) - template.render(view, locals) + content = template.render(view, locals) + partial_iteration.iterate! + content end end @@ -466,8 +504,11 @@ module ActionView def retrieve_template_keys keys = @locals.keys - keys << @variable if @object || @collection - keys << @variable_counter if @collection + keys << @variable if @object || @collection + if @collection + keys << @variable_counter + keys << @variable_iteration + end keys end @@ -477,8 +518,11 @@ module ActionView raise_invalid_identifier(path) unless base =~ /\A_?([a-z]\w*)(\.\w+)*\z/ $1.to_sym end - variable_counter = :"#{variable}_counter" if @collection - [variable, variable_counter] + if @collection + variable_counter = :"#{variable}_counter" + variable_iteration = :"#{variable}_iteration" + end + [variable, variable_counter, variable_iteration] end IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " + |