diff options
Diffstat (limited to 'actionpack/lib/action_view')
20 files changed, 112 insertions, 85 deletions
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 668515df59..08253de3f4 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -6,7 +6,7 @@ require 'action_view/log_subscriber' module ActionView #:nodoc: # = Action View Base # - # Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> extension then it uses a mixture of ERb + # Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> extension then it uses a mixture of ERB # (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> extension then Jim Weirich's Builder::XmlMarkup library is used. # # == ERB diff --git a/actionpack/lib/action_view/digestor.rb b/actionpack/lib/action_view/digestor.rb index 8bc69b9246..4507861dcc 100644 --- a/actionpack/lib/action_view/digestor.rb +++ b/actionpack/lib/action_view/digestor.rb @@ -2,7 +2,7 @@ require 'thread_safe' module ActionView class Digestor - EXPLICIT_DEPENDENCY = /# Template Dependency: ([^ ]+)/ + EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/ # Matches: # render partial: "comments/comment", collection: commentable.comments @@ -24,16 +24,17 @@ module ActionView @@cache = ThreadSafe::Cache.new def self.digest(name, format, finder, options = {}) - @@cache["#{name}.#{format}"] ||= begin + cache_key = [name, format] + Array.wrap(options[:dependencies]) + @@cache[cache_key.join('.')] ||= begin klass = options[:partial] || name.include?("/_") ? PartialDigestor : Digestor - klass.new(name, format, finder).digest + klass.new(name, format, finder, options).digest end end - attr_reader :name, :format, :finder + attr_reader :name, :format, :finder, :options - def initialize(name, format, finder) - @name, @format, @finder = name, format, finder + def initialize(name, format, finder, options={}) + @name, @format, @finder, @options = name, format, finder, options end def digest @@ -81,9 +82,11 @@ module ActionView end def dependency_digest - dependencies.collect do |template_name| + template_digests = dependencies.collect do |template_name| Digestor.digest(template_name, format, finder, partial: true) - end.join("-") + end + + (template_digests + injected_dependencies).join("-") end def render_dependencies @@ -105,6 +108,10 @@ module ActionView def explicit_dependencies source.scan(EXPLICIT_DEPENDENCY).flatten.uniq end + + def injected_dependencies + Array.wrap(options[:dependencies]) + end end class PartialDigestor < Digestor # :nodoc: diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index 995aa10afb..8fc78ea7fb 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -167,7 +167,7 @@ module ActionView if @virtual_path [ *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name), - Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context) + Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context, dependencies: view_cache_dependencies) ] else name diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb index d361a69a92..34fc23ac1a 100644 --- a/actionpack/lib/action_view/helpers/debug_helper.rb +++ b/actionpack/lib/action_view/helpers/debug_helper.rb @@ -27,14 +27,12 @@ module ActionView # new_record: true # </pre> def debug(object) - begin - Marshal::dump(object) - object = ERB::Util.html_escape(object.to_yaml).gsub(" ", " ").html_safe - content_tag(:pre, object, :class => "debug_dump") - rescue Exception # errors from Marshal or YAML - # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback - content_tag(:code, object.to_yaml, :class => "debug_dump") - end + Marshal::dump(object) + object = ERB::Util.html_escape(object.to_yaml).gsub(" ", " ").html_safe + content_tag(:pre, object, :class => "debug_dump") + rescue Exception # errors from Marshal or YAML + # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback + content_tag(:code, object.to_yaml, :class => "debug_dump") end end end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 516492ca30..50f1eff4ec 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -84,7 +84,7 @@ module ActionView # # <form action="/people/256" class="edit_person" id="edit_person_256" method="post"> # <div style="margin:0;padding:0;display:inline"> - # <input name="_method" type="hidden" value="put" /> + # <input name="_method" type="hidden" value="patch" /> # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> # </div> # <label for="person_first_name">First name</label>: @@ -101,9 +101,9 @@ module ActionView # and generate HTML accordingly. # # The controller would receive the form data again in <tt>params[:person]</tt>, ready to be - # passed to <tt>Person#update_attributes</tt>: + # passed to <tt>Person#update</tt>: # - # if @person.update_attributes(params[:person]) + # if @person.update(params[:person]) # # success # else # # error handling @@ -242,7 +242,7 @@ module ActionView # # is then equivalent to something like: # - # <%= form_for @post, as: :post, url: post_path(@post), method: :put, html: { class: "edit_post", id: "edit_post_45" } do |f| %> + # <%= form_for @post, as: :post, url: post_path(@post), method: :patch, html: { class: "edit_post", id: "edit_post_45" } do |f| %> # ... # <% end %> # @@ -318,7 +318,7 @@ module ActionView # # <form action='http://www.example.com' method='post' data-remote='true'> # <div style='margin:0;padding:0;display:inline'> - # <input name='_method' type='hidden' value='put' /> + # <input name='_method' type='hidden' value='patch' /> # </div> # ... # </form> @@ -336,7 +336,7 @@ module ActionView # # <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'> # <div style='margin:0;padding:0;display:inline'> - # <input name='_method' type='hidden' value='put' /> + # <input name='_method' type='hidden' value='patch' /> # </div> # ... # </form> @@ -388,9 +388,9 @@ module ActionView # In many cases you will want to wrap the above in another helper, so you # could do something like the following: # - # def labelled_form_for(record_or_name_or_array, *args, &proc) + # def labelled_form_for(record_or_name_or_array, *args, &block) # options = args.extract_options! - # form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &proc) + # form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &block) # end # # If you don't need to attach a form to a model instance, then check out @@ -412,10 +412,9 @@ module ActionView # <%= form_for @invoice, url: external_url, authenticity_token: false do |f| # ... # <% end %> - def form_for(record, options = {}, &proc) + def form_for(record, options = {}, &block) raise ArgumentError, "Missing block" unless block_given? - - options[:html] ||= {} + html_options = options[:html] ||= {} case record when String, Symbol @@ -428,17 +427,16 @@ module ActionView apply_form_for_options!(record, object, options) end - options[:html][:data] = options.delete(:data) if options.has_key?(:data) - options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote) - options[:html][:method] = options.delete(:method) if options.has_key?(:method) - options[:html][:authenticity_token] = options.delete(:authenticity_token) + html_options[:data] = options.delete(:data) if options.has_key?(:data) + html_options[:remote] = options.delete(:remote) if options.has_key?(:remote) + html_options[:method] = options.delete(:method) if options.has_key?(:method) + html_options[:authenticity_token] = options.delete(:authenticity_token) - builder = options[:parent_builder] = instantiate_builder(object_name, object, options) - fields_for = fields_for(object_name, object, options, &proc) - default_options = builder.multipart? ? { multipart: true } : {} - default_options.merge!(options.delete(:html)) + builder = instantiate_builder(object_name, object, options) + output = capture(builder, &block) + html_options[:multipart] = builder.multipart? - form_tag(options.delete(:url) || {}, default_options) { fields_for } + form_tag(options[:url] || {}, html_options) { output } end def apply_form_for_options!(record, object, options) #:nodoc: @@ -707,9 +705,7 @@ module ActionView # to prevent fields_for from rendering it automatically. def fields_for(record_name, record_object = nil, options = {}, &block) builder = instantiate_builder(record_name, record_object, options) - output = capture(builder, &block) - output.concat builder.hidden_field(:id) if output && options[:hidden_field_id] && !builder.emitted_hidden_id? - output + capture(builder, &block) end # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object @@ -897,7 +893,7 @@ module ActionView # invoice the user unchecks its check box, no +paid+ parameter is sent. So, # any mass-assignment idiom like # - # @invoice.update_attributes(params[:invoice]) + # @invoice.update(params[:invoice]) # # wouldn't update the flag. # @@ -1174,12 +1170,15 @@ module ActionView attr_accessor :object_name, :object, :options - attr_reader :multipart, :parent_builder, :index + attr_reader :multipart, :index alias :multipart? :multipart def multipart=(multipart) @multipart = multipart - parent_builder.multipart = multipart if parent_builder + + if parent_builder = @options[:parent_builder] + parent_builder.multipart = multipart + end end def self._to_partial_path @@ -1201,7 +1200,6 @@ module ActionView @nested_child_index = {} @object_name, @object, @template, @options = object_name, object, template, options - @parent_builder = options[:parent_builder] @default_options = @options ? @options.slice(:index, :namespace) : {} if @object_name.to_s.match(/\[\]$/) if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param) @@ -1478,8 +1476,8 @@ module ActionView def fields_for(record_name, record_object = nil, fields_options = {}, &block) fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options? fields_options[:builder] ||= options[:builder] - fields_options[:parent_builder] = self fields_options[:namespace] = options[:namespace] + fields_options[:parent_builder] = self case record_name when String, Symbol @@ -1569,7 +1567,7 @@ module ActionView # invoice the user unchecks its check box, no +paid+ parameter is sent. So, # any mass-assignment idiom like # - # @invoice.update_attributes(params[:invoice]) + # @invoice.update(params[:invoice]) # # wouldn't update the flag. # @@ -1820,13 +1818,17 @@ module ActionView end end - def fields_for_nested_model(name, object, options, block) + def fields_for_nested_model(name, object, fields_options, block) object = convert_to_model(object) + emit_hidden_id = object.persisted? && fields_options.fetch(:include_id) { + options.fetch(:include_id, true) + } - parent_include_id = self.options.fetch(:include_id, true) - include_id = options.fetch(:include_id, parent_include_id) - options[:hidden_field_id] = object.persisted? && include_id - @template.fields_for(name, object, options, &block) + @template.fields_for(name, object, fields_options) do |f| + output = @template.capture(f, &block) + output.concat f.hidden_field(:id) if output && emit_hidden_id && !f.emitted_hidden_id? + output + end end def nested_child_index(name) diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 1b5b788a35..bcad05e033 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -130,7 +130,7 @@ module ActionView # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So, # any mass-assignment idiom like # - # @user.update_attributes(params[:user]) + # @user.update(params[:user]) # # wouldn't update roles. # diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index ff83ef3ca1..86d9f94067 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -26,7 +26,7 @@ module ActionView # ==== Options # * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data". # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post". - # If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt> + # If "patch", "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt> # is added to simulate the verb over post. # * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to # pass custom authenticity token string, or to not add authenticity_token field at all @@ -184,7 +184,7 @@ module ActionView # # => <label for="name">Name</label> # # label_tag 'name', 'Your name' - # # => <label for="name">Your Name</label> + # # => <label for="name">Your name</label> # # label_tag 'name', nil, class: 'small_label' # # => <label for="name" class="small_label">Name</label> @@ -526,19 +526,19 @@ module ActionView # # ==== Examples # image_submit_tag("login.png") - # # => <input src="/images/login.png" type="image" /> + # # => <input alt="Login" src="/images/login.png" type="image" /> # # image_submit_tag("purchase.png", disabled: true) - # # => <input disabled="disabled" src="/images/purchase.png" type="image" /> + # # => <input alt="Purchase" disabled="disabled" src="/images/purchase.png" type="image" /> # - # image_submit_tag("search.png", class: 'search_button') - # # => <input class="search_button" src="/images/search.png" type="image" /> + # image_submit_tag("search.png", class: 'search_button', alt: 'Find') + # # => <input alt="Find" class="search_button" src="/images/search.png" type="image" /> # # image_submit_tag("agree.png", disabled: true, class: "agree_disagree_button") - # # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" /> + # # => <input alt="Agree" class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" /> # # image_submit_tag("save.png", data: { confirm: "Are you sure?" }) - # # => <input src="/images/save.png" data-confirm="Are you sure?" type="image" /> + # # => <input alt="Save" src="/images/save.png" data-confirm="Are you sure?" type="image" /> def image_submit_tag(source, options = {}) options = options.stringify_keys @@ -550,7 +550,7 @@ module ActionView options["data-confirm"] = confirm end - tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options) + tag :input, { "alt" => image_alt(source), "type" => "image", "src" => path_to_image(source) }.update(options) end # Creates a field set for grouping HTML form elements. diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index 33194250b7..271a194913 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -65,8 +65,8 @@ module ActionView # # produces: # - # <tr id="person_123" class="person">...</tr> - # <tr id="person_124" class="person">...</tr> + # <tr id="person_123" class="person">...</tr> + # <tr id="person_124" class="person">...</tr> # # content_tag_for also accepts a hash of options, which will be converted to # additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined @@ -95,7 +95,11 @@ module ActionView options[:class] = "#{dom_class(record, prefix)} #{options[:class]}".rstrip options[:id] = dom_id(record, prefix) - content_tag(tag_name, capture(record, &block), options) + if block_given? + content_tag(tag_name, capture(record, &block), options) + else + content_tag(tag_name, "", options) + end end end end diff --git a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb index 45f0bc3d7b..d27df45b5a 100644 --- a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb +++ b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb @@ -21,7 +21,7 @@ module ActionView if block_given? yield builder else - builder.check_box + builder.label + render_component(builder) end end @@ -31,6 +31,12 @@ module ActionView rendered_collection + hidden end + + private + + def render_component(builder) + builder.check_box + builder.label + end end end end diff --git a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb index 4e33e79a36..e92a318c73 100644 --- a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb +++ b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb @@ -44,7 +44,8 @@ module ActionView html_options = @html_options.dup [:checked, :selected, :disabled].each do |option| - next unless current_value = @options[option] + current_value = @options[option] + next if current_value.nil? accept = if current_value.respond_to?(:call) current_value.call(item) diff --git a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb index ba2035f074..81f2ecb2b3 100644 --- a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb +++ b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb @@ -20,10 +20,16 @@ module ActionView if block_given? yield builder else - builder.radio_button + builder.label + render_component(builder) end end end + + private + + def render_component(builder) + builder.radio_button + builder.label + end end end end diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 26d2142df9..2e124cf085 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -112,12 +112,12 @@ module ActionView # highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>') # # => You searched for: <a href="search?q=rails">rails</a> def highlight(text, phrases, options = {}) - highlighter = options.fetch(:highlighter, '<mark>\1</mark>') - text = sanitize(text) if options.fetch(:sanitize, true) + if text.blank? || phrases.blank? text else + highlighter = options.fetch(:highlighter, '<mark>\1</mark>') match = Array(phrases).map { |p| Regexp.escape(p) }.join('|') text.gsub(/(#{match})(?![^<]*?>)/i, highlighter) end.html_safe diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index aeee662071..bade121d44 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -170,7 +170,7 @@ module ActionView # You can also use custom data attributes using the <tt>:data</tt> option: # # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" } - # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a> + # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a> def link_to(name = nil, options = nil, html_options = nil, &block) html_options, options = options, name if block_given? options ||= {} diff --git a/actionpack/lib/action_view/record_identifier.rb b/actionpack/lib/action_view/record_identifier.rb index 2953654972..63f645431a 100644 --- a/actionpack/lib/action_view/record_identifier.rb +++ b/actionpack/lib/action_view/record_identifier.rb @@ -17,7 +17,7 @@ module ActionView # # controller # def update # post = Post.find(params[:id]) - # post.update_attributes(params[:post]) + # post.update(params[:post]) # # redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post) # end diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 37f93a13fc..43a88b0623 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -452,7 +452,7 @@ module ActionView def retrieve_template_keys keys = @locals.keys - keys << @variable + keys << @variable if @object || @collection keys << @variable_counter if @collection keys end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index aefc42be48..b927c69260 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -1,6 +1,5 @@ require 'active_support/core_ext/object/try' require 'active_support/core_ext/kernel/singleton_class' -require 'active_support/deprecation' require 'thread' module ActionView diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index e00056781d..b479f991bc 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -78,7 +78,7 @@ module ActionView end end - def source_extract(indentation = 0) + def source_extract(indentation = 0, output = :console) return unless num = line_number num = num.to_i @@ -88,13 +88,9 @@ module ActionView end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min indent = end_on_line.to_s.size + indentation - line_counter = start_on_line return unless source_code = source_code[start_on_line..end_on_line] - source_code.sum do |line| - line_counter += 1 - "%#{indent}s: %s\n" % [line_counter, line] - end + formatted_code_for(source_code, start_on_line, indent, output) end def sub_template_of(template_path) @@ -123,6 +119,18 @@ module ActionView 'in ' end + file_name end + + def formatted_code_for(source_code, line_counter, indent, output) + 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] + end + end + end end end diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 8b23029bbc..1a1083bf00 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -118,7 +118,7 @@ module ActionView private - delegate :caching?, :to => "self.class" + delegate :caching?, to: :class # This is what child classes implement. No defaults are needed # because Resolver guarantees that the arguments are present and diff --git a/actionpack/lib/action_view/template/types.rb b/actionpack/lib/action_view/template/types.rb index 7611c9e708..db77cb5d19 100644 --- a/actionpack/lib/action_view/template/types.rb +++ b/actionpack/lib/action_view/template/types.rb @@ -1,6 +1,5 @@ require 'set' require 'active_support/core_ext/class/attribute_accessors' -require 'active_support/core_ext/object/blank' module ActionView class Template diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index a548b44780..4479da5bc4 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -30,9 +30,6 @@ module ActionView end end - # Use AV::TestCase for the base class for helpers and views - register_spec_type(/(Helper|View)( ?Test)?\z/i, self) - module Behavior extend ActiveSupport::Concern |
