diff options
Diffstat (limited to 'actionview/lib/action_view')
23 files changed, 260 insertions, 135 deletions
diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index 455ce531ae..900f96255e 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -131,7 +131,8 @@ module ActionView #:nodoc: # end # end # - # More builder documentation can be found at http://builder.rubyforge.org. + # For more information on Builder please consult the [source + # code](https://github.com/jimweirich/builder). class Base include Helpers, ::ERB::Util, Context diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index aa49f1edc1..7333ea999a 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -142,22 +142,29 @@ module ActionView ) end - # Returns a link loading a favicon file. You may specify a different file - # in the first argument. The helper accepts an additional options hash where - # you can override "rel" and "type". + # Returns a link tag for a favicon managed by the asset pipeline. # - # ==== Options + # If a page has no link like the one generated by this helper, browsers + # ask for <tt>/favicon.ico</tt> automatically, and cache the file if the + # request succeeds. If the favicon changes it is hard to get it updated. # - # * <tt>:rel</tt> - Specify the relation of this link, defaults to 'shortcut icon' - # * <tt>:type</tt> - Override the auto-generated mime type, defaults to 'image/vnd.microsoft.icon' + # To have better control applications may let the asset pipeline manage + # their favicon storing the file under <tt>app/assets/images</tt>, and + # using this helper to generate its corresponding link tag. # - # ==== Examples + # The helper gets the name of the favicon file as first argument, which + # defaults to "favicon.ico", and also supports +:rel+ and +:type+ options + # to override their defaults, "shortcut icon" and "image/x-icon" + # respectively: + # + # favicon_link_tag + # # => <link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon" /> # # favicon_link_tag 'myicon.ico' - # # => <link href="/assets/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" /> + # # => <link href="/assets/myicon.ico" rel="shortcut icon" type="image/x-icon" /> # - # Mobile Safari looks for a different <link> tag, pointing to an image that - # will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad. + # Mobile Safari looks for a different link tag, pointing to an image that + # will be used if you add the page to the home screen of an iOS device. # The following call would generate such a tag: # # favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png' @@ -165,7 +172,7 @@ module ActionView def favicon_link_tag(source='favicon.ico', options={}) tag('link', { :rel => 'shortcut icon', - :type => 'image/vnd.microsoft.icon', + :type => 'image/x-icon', :href => path_to_image(source) }.merge!(options.symbolize_keys)) end @@ -251,19 +258,19 @@ module ActionView # ==== Examples # # video_tag("trailer") - # # => <video src="/videos/trailer" /> + # # => <video src="/videos/trailer"></video> # video_tag("trailer.ogg") - # # => <video src="/videos/trailer.ogg" /> + # # => <video src="/videos/trailer.ogg"></video> # video_tag("trailer.ogg", controls: true, autobuffer: true) - # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" /> + # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" ></video> # video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png") - # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" /> + # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png"></video> # video_tag("/trailers/hd.avi", size: "16x16") - # # => <video src="/trailers/hd.avi" width="16" height="16" /> + # # => <video src="/trailers/hd.avi" width="16" height="16"></video> # video_tag("/trailers/hd.avi", size: "16") - # # => <video height="16" src="/trailers/hd.avi" width="16" /> + # # => <video height="16" src="/trailers/hd.avi" width="16"></video> # video_tag("/trailers/hd.avi", height: '32', width: '32') - # # => <video height="32" src="/trailers/hd.avi" width="32" /> + # # => <video height="32" src="/trailers/hd.avi" width="32"></video> # video_tag("trailer.ogg", "trailer.flv") # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video> # video_tag(["trailer.ogg", "trailer.flv"]) @@ -282,11 +289,11 @@ module ActionView # your public audios directory. # # audio_tag("sound") - # # => <audio src="/audios/sound" /> + # # => <audio src="/audios/sound"></audio> # audio_tag("sound.wav") - # # => <audio src="/audios/sound.wav" /> + # # => <audio src="/audios/sound.wav"></audio> # audio_tag("sound.wav", autoplay: true, controls: true) - # # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" /> + # # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav"></audio> # audio_tag("sound.wav", "sound.mid") # # => <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio> def audio_tag(*sources) diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb index c830ab23e3..006b15be91 100644 --- a/actionview/lib/action_view/helpers/asset_url_helper.rb +++ b/actionview/lib/action_view/helpers/asset_url_helper.rb @@ -7,10 +7,10 @@ module ActionView # urls. # # image_path("rails.png") - # # => "/assets/rails.png" + # # => "/images/rails.png" # # image_url("rails.png") - # # => "http://www.example.com/assets/rails.png" + # # => "http://www.example.com/images/rails.png" # # === Using asset hosts # @@ -88,9 +88,12 @@ module ActionView # still sending assets for plain HTTP requests from asset hosts. If you don't # have SSL certificates for each of the asset hosts this technique allows you # to avoid warnings in the client about mixed media. + # Note that the request parameter might not be supplied, e.g. when the assets + # are precompiled via a Rake task. Make sure to use a Proc instead of a lambda, + # since a Proc allows missing parameters and sets them to nil. # # config.action_controller.asset_host = Proc.new { |source, request| - # if request.ssl? + # if request && request.ssl? # "#{request.protocol}#{request.host_with_port}" # else # "#{request.protocol}assets.example.com" @@ -113,13 +116,13 @@ module ActionView # # All other asset *_path helpers delegate through this method. # - # asset_path "application.js" # => /application.js - # asset_path "application", type: :javascript # => /javascripts/application.js - # asset_path "application", type: :stylesheet # => /stylesheets/application.css + # asset_path "application.js" # => /assets/application.js + # asset_path "application", type: :javascript # => /assets/application.js + # asset_path "application", type: :stylesheet # => /assets/application.css # asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js def asset_path(source, options = {}) - source = source.to_s return "" unless source.present? + source = source.to_s return source if source =~ URI_REGEXP tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '') @@ -134,11 +137,11 @@ module ActionView relative_url_root = defined?(config.relative_url_root) && config.relative_url_root if relative_url_root - source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/") + source = File.join(relative_url_root, source) unless source.starts_with?("#{relative_url_root}/") end if host = compute_asset_host(source, options) - source = "#{host}#{source}" + source = File.join(host, source) end "#{source}#{tail}" @@ -147,7 +150,14 @@ module ActionView # Computes the full URL to an asset in the public directory. This # will use +asset_path+ internally, so most of their behaviors - # will be the same. + # will be the same. If :host options is set, it overwrites global + # +config.action_controller.asset_host+ setting. + # + # All other options provided are forwarded to +asset_path+ call. + # + # asset_url "application.js" # => http://example.com/application.js + # asset_url "application.js", host: "http://cdn.example.com" # => http://cdn.example.com/assets/application.js + # def asset_url(source, options = {}) path_to_asset(source, options.merge(:protocol => :request)) end @@ -191,7 +201,8 @@ module ActionView # (proc or otherwise). def compute_asset_host(source = "", options = {}) request = self.request if respond_to?(:request) - host = config.asset_host if defined? config.asset_host + 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) @@ -223,7 +234,7 @@ module ActionView # 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. + # Used internally by +javascript_include_tag+ to build the script path. # # javascript_path "xmlhr" # => /javascripts/xmlhr.js # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js @@ -243,7 +254,7 @@ module ActionView alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route # Computes the path to a stylesheet asset in the public stylesheets directory. - # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs). + # If the +source+ filename has no extension, .css will be appended (except for explicit URIs). # Full paths from the document root will be passed through. # Used internally by +stylesheet_link_tag+ to build the stylesheet path. # @@ -268,9 +279,9 @@ module ActionView # Full paths from the document root will be passed through. # Used internally by +image_tag+ to build the image path: # - # image_path("edit") # => "/assets/edit" - # image_path("edit.png") # => "/assets/edit.png" - # image_path("icons/edit.png") # => "/assets/icons/edit.png" + # image_path("edit") # => "/images/edit" + # image_path("edit.png") # => "/images/edit.png" + # image_path("icons/edit.png") # => "/images/icons/edit.png" # image_path("/icons/edit.png") # => "/icons/edit.png" # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png" # @@ -334,9 +345,9 @@ module ActionView # Computes the path to a font asset. # Full paths from the document root will be passed through. # - # font_path("font") # => /assets/font - # font_path("font.ttf") # => /assets/font.ttf - # font_path("dir/font.ttf") # => /assets/dir/font.ttf + # font_path("font") # => /fonts/font + # font_path("font.ttf") # => /fonts/font.ttf + # font_path("dir/font.ttf") # => /fonts/dir/font.ttf # font_path("/dir/font.ttf") # => /dir/font.ttf # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf def font_path(source, options = {}) diff --git a/actionview/lib/action_view/helpers/capture_helper.rb b/actionview/lib/action_view/helpers/capture_helper.rb index 5afe435459..75d1634b2e 100644 --- a/actionview/lib/action_view/helpers/capture_helper.rb +++ b/actionview/lib/action_view/helpers/capture_helper.rb @@ -202,15 +202,6 @@ module ActionView ensure self.output_buffer = old_buffer end - - # Add the output buffer to the response body and start a new one. - def flush_output_buffer #:nodoc: - if output_buffer && !output_buffer.empty? - response.stream.write output_buffer - self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0] - nil - end - end end end end diff --git a/actionview/lib/action_view/helpers/debug_helper.rb b/actionview/lib/action_view/helpers/debug_helper.rb index c29c1b1eea..16cddec339 100644 --- a/actionview/lib/action_view/helpers/debug_helper.rb +++ b/actionview/lib/action_view/helpers/debug_helper.rb @@ -11,20 +11,16 @@ module ActionView # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead. # Useful for inspecting an object at the time of rendering. # - # @user = User.new({ username: 'testing', password: 'xyz', age: 42}) %> + # @user = User.new({ username: 'testing', password: 'xyz', age: 42}) # debug(@user) # # => # <pre class='debug_dump'>--- !ruby/object:User # attributes: # updated_at: # username: testing - # # age: 42 # password: xyz # created_at: - # attributes_cache: {} - # - # new_record: true # </pre> def debug(object) Marshal::dump(object) diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index 1ff090f244..789a413c8d 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -434,7 +434,8 @@ module ActionView output = capture(builder, &block) html_options[:multipart] ||= builder.multipart? - form_tag(options[:url] || {}, html_options) { output } + html_options = html_options_for_form(options[:url] || {}, html_options) + form_tag_with_body(html_options, output) end def apply_form_for_options!(record, object, options) #:nodoc: @@ -449,7 +450,11 @@ module ActionView method: method ) - options[:url] ||= polymorphic_path(record, format: options.delete(:format)) + options[:url] ||= if options.key?(:format) + polymorphic_path(record, format: options.delete(:format)) + else + polymorphic_path(record, {}) + end end private :apply_form_for_options! @@ -746,6 +751,7 @@ module ActionView # label(:post, :terms) do # 'Accept <a href="/terms">Terms</a>.'.html_safe # end + # # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label> def label(object_name, method, content_or_options = nil, options = nil, &block) Tags::Label.new(object_name, method, self, content_or_options, options).render(&block) end diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 66c9e20682..9c0c43d096 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -67,7 +67,7 @@ module ActionView def form_tag(url_for_options = {}, options = {}, &block) html_options = html_options_for_form(url_for_options, options) if block_given? - form_tag_in_block(html_options, &block) + form_tag_with_body(html_options, capture(&block)) else form_tag_html(html_options) end @@ -82,14 +82,18 @@ module ActionView # ==== Options # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input. - # * <tt>:include_blank</tt> - If set to true, an empty option will be created. - # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something + # * <tt>:include_blank</tt> - If set to true, an empty option will be created. If set to a string, the string will be used as the option's content and the value will be empty. + # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something. + # * <tt>:selected</tt> - Provide a default selected value. It should be of the exact type as the provided options. # * Any other key creates standard HTML attributes for the tag. # # ==== Examples # select_tag "people", options_from_collection_for_select(@people, "id", "name") # # <select id="people" name="people"><option value="1">David</option></select> # + # select_tag "people", options_from_collection_for_select(@people, "id", "name"), selected: ["1", "David"] + # # <select id="people" name="people"><option value="1" selected="selected">David</option></select> + # # select_tag "people", "<option>David</option>".html_safe # # => <select id="people" name="people"><option>David</option></select> # @@ -105,13 +109,16 @@ module ActionView # # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option> # # <option>Out</option></select> # - # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input' - # # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option> + # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input', id: 'unique_id' + # # => <select class="form_input" id="unique_id" multiple="multiple" name="access[]"><option>Read</option> # # <option>Write</option></select> # # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true # # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select> # + # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: "All" + # # => <select id="people" name="people"><option value="">All</option><option value="1">David</option></select> + # # select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something" # # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select> # @@ -614,6 +621,19 @@ module ActionView # # ==== Options # * Accepts the same options as text_field_tag. + # + # ==== Examples + # date_field_tag 'name' + # # => <input id="name" name="name" type="date" /> + # + # date_field_tag 'date', '01/01/2014' + # # => <input id="date" name="date" type="date" value="01/01/2014" /> + # + # date_field_tag 'date', nil, class: 'special_input' + # # => <input class="special_input" id="date" name="date" type="date" /> + # + # date_field_tag 'date', '01/01/2014', class: 'special_input', disabled: true + # # => <input disabled="disabled" class="special_input" id="date" name="date" type="date" value="01/01/2014" /> def date_field_tag(name, value = nil, options = {}) text_field_tag(name, value, options.stringify_keys.update("type" => "date")) end @@ -698,6 +718,19 @@ module ActionView # # ==== Options # * Accepts the same options as text_field_tag. + # + # ==== Examples + # email_field_tag 'name' + # # => <input id="name" name="name" type="email" /> + # + # email_field_tag 'email', 'email@example.com' + # # => <input id="email" name="email" type="email" value="email@example.com" /> + # + # email_field_tag 'email', nil, class: 'special_input' + # # => <input class="special_input" id="email" name="email" type="email" /> + # + # email_field_tag 'email', 'email@example.com', class: 'special_input', disabled: true + # # => <input disabled="disabled" class="special_input" id="email" name="email" type="email" value="email@example.com" /> def email_field_tag(name, value = nil, options = {}) text_field_tag(name, value, options.stringify_keys.update("type" => "email")) end @@ -709,12 +742,40 @@ module ActionView # * <tt>:max</tt> - The maximum acceptable value. # * <tt>:in</tt> - A range specifying the <tt>:min</tt> and # <tt>:max</tt> values. + # * <tt>:within</tt> - Same as <tt>:in</tt>. # * <tt>:step</tt> - The acceptable value granularity. # * Otherwise accepts the same options as text_field_tag. # # ==== Examples + # number_field_tag 'quantity' + # # => <input id="quantity" name="quantity" type="number" /> + # + # number_field_tag 'quantity', '1' + # # => <input id="quantity" name="quantity" type="number" value="1" /> + # + # number_field_tag 'quantity', nil, class: 'special_input' + # # => <input class="special_input" id="quantity" name="quantity" type="number" /> + # + # number_field_tag 'quantity', nil, min: 1 + # # => <input id="quantity" name="quantity" min="1" type="number" /> + # + # number_field_tag 'quantity', nil, max: 9 + # # => <input id="quantity" name="quantity" max="9" type="number" /> + # # number_field_tag 'quantity', nil, in: 1...10 # # => <input id="quantity" name="quantity" min="1" max="9" type="number" /> + # + # number_field_tag 'quantity', nil, within: 1...10 + # # => <input id="quantity" name="quantity" min="1" max="9" type="number" /> + # + # number_field_tag 'quantity', nil, min: 1, max: 10 + # # => <input id="quantity" name="quantity" min="1" max="9" type="number" /> + # + # number_field_tag 'quantity', nil, min: 1, max: 10, step: 2 + # # => <input id="quantity" name="quantity" min="1" max="9" step="2" type="number" /> + # + # number_field_tag 'quantity', '1', class: 'special_input', disabled: true + # # => <input disabled="disabled" class="special_input" id="quantity" name="quantity" type="number" value="1" /> def number_field_tag(name, value = nil, options = {}) options = options.stringify_keys options["type"] ||= "number" @@ -735,7 +796,10 @@ module ActionView # Creates the hidden UTF8 enforcer tag. Override this method in a helper # to customize the tag. def utf8_enforcer_tag - tag(:input, :type => "hidden", :name => "utf8", :value => "✓".html_safe) + # Use raw HTML to ensure the value is written as an HTML entity; it + # needs to be the right character regardless of which encoding the + # browser infers. + '<input name="utf8" type="hidden" value="✓" />'.html_safe end private @@ -790,8 +854,7 @@ module ActionView tag(:form, html_options, true) + extra_tags end - def form_tag_in_block(html_options, &block) - content = capture(&block) + def form_tag_with_body(html_options, content) output = form_tag_html(html_options) output << content output.safe_concat("</form>") diff --git a/actionview/lib/action_view/helpers/javascript_helper.rb b/actionview/lib/action_view/helpers/javascript_helper.rb index e475d5b018..629c447f3f 100644 --- a/actionview/lib/action_view/helpers/javascript_helper.rb +++ b/actionview/lib/action_view/helpers/javascript_helper.rb @@ -47,7 +47,13 @@ module ActionView # tag. # # javascript_tag "alert('All is good')", defer: 'defer' - # # => <script defer="defer">alert('All is good')</script> + # + # Returns: + # <script defer="defer"> + # //<![CDATA[ + # alert('All is good') + # //]]> + # </script> # # Instead of passing the content as an argument, you can also use a block # in which case, you pass your +html_options+ as the first parameter. diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb index 7157a95146..7220bded3c 100644 --- a/actionview/lib/action_view/helpers/number_helper.rb +++ b/actionview/lib/action_view/helpers/number_helper.rb @@ -266,14 +266,8 @@ module ActionView # number_to_human_size(1234567, precision: 2) # => 1.2 MB # number_to_human_size(483989, precision: 2) # => 470 KB # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB - # - # Non-significant zeros after the fractional separator are - # stripped out by default (set - # <tt>:strip_insignificant_zeros</tt> to +false+ to change - # that): - # - # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB" - # number_to_human_size(524288000, precision: 5) # => "500 MB" + # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB" + # number_to_human_size(524288000, precision: 5) # => "500 MB" def number_to_human_size(number, options = {}) delegate_number_helper_method(:number_to_human_size, number, options) end @@ -343,11 +337,15 @@ module ActionView # separator: ',', # significant: false) # => "1,2 Million" # + # number_to_human(500000000, precision: 5) # => "500 Million" + # number_to_human(12345012345, significant: false) # => "12.345 Billion" + # # Non-significant zeros after the decimal separator are stripped # out by default (set <tt>:strip_insignificant_zeros</tt> to # +false+ to change that): - # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion" - # number_to_human(500000000, precision: 5) # => "500 Million" + # + # number_to_human(12.00001) # => "12" + # number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0" # # ==== Custom Unit Quantifiers # diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb index 60a4478c26..b0d9c7c7f9 100644 --- a/actionview/lib/action_view/helpers/output_safety_helper.rb +++ b/actionview/lib/action_view/helpers/output_safety_helper.rb @@ -18,9 +18,9 @@ module ActionView #:nodoc: end # This method returns a html safe string similar to what <tt>Array#join</tt> - # would return. All items in the array, including the supplied separator, are - # html escaped unless they are html safe, and the returned string is marked - # as html safe. + # 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. # # safe_join(["<p>foo</p>".html_safe, "<p>bar</p>"], "<br />") # # => "<p>foo</p><br /><p>bar</p>" @@ -29,9 +29,9 @@ module ActionView #:nodoc: # # => "<p>foo</p><br /><p>bar</p>" # def safe_join(array, sep=$,) - sep = ERB::Util.html_escape(sep) + sep = ERB::Util.unwrapped_html_escape(sep) - array.map { |i| ERB::Util.html_escape(i) }.join(sep).html_safe + array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe end end end diff --git a/actionview/lib/action_view/helpers/sanitize_helper.rb b/actionview/lib/action_view/helpers/sanitize_helper.rb index e5cb843670..049af275b6 100644 --- a/actionview/lib/action_view/helpers/sanitize_helper.rb +++ b/actionview/lib/action_view/helpers/sanitize_helper.rb @@ -48,7 +48,7 @@ module ActionView # Change allowed default attributes # # class Application < Rails::Application - # config.action_view.sanitized_allowed_attributes = 'id', 'class', 'style' + # config.action_view.sanitized_allowed_attributes = ['id', 'class', 'style'] # end # # Please note that sanitizing user-provided text does not guarantee that the @@ -204,7 +204,7 @@ module ActionView # Adds to the Set of allowed HTML attributes for the +sanitize+ helper. # # class Application < Rails::Application - # config.action_view.sanitized_allowed_attributes = 'onclick', 'longdesc' + # config.action_view.sanitized_allowed_attributes = ['onclick', 'longdesc'] # end # def sanitized_allowed_attributes=(attributes) diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb index a9f3b0ffbc..35444bcfb4 100644 --- a/actionview/lib/action_view/helpers/tag_helper.rb +++ b/actionview/lib/action_view/helpers/tag_helper.rb @@ -139,7 +139,7 @@ module ActionView def content_tag_string(name, content, options, escape = true) tag_options = tag_options(options, escape) if options - content = ERB::Util.h(content) if escape + content = ERB::Util.unwrapped_html_escape(content) if escape "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe end @@ -173,8 +173,11 @@ module ActionView end def tag_option(key, value, escape) - value = value.join(" ") if value.is_a?(Array) - value = ERB::Util.h(value) if escape + if value.is_a?(Array) + value = escape ? safe_join(value, " ") : value.join(" ") + else + value = escape ? ERB::Util.unwrapped_html_escape(value) : value + end %(#{key}="#{value}") end end diff --git a/actionview/lib/action_view/helpers/tags/label.rb b/actionview/lib/action_view/helpers/tags/label.rb index 6335e3dd4d..a5bcaf8153 100644 --- a/actionview/lib/action_view/helpers/tags/label.rb +++ b/actionview/lib/action_view/helpers/tags/label.rb @@ -35,9 +35,9 @@ module ActionView if block_given? content = @template_object.capture(&block) else + method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name content = if @content.blank? @object_name.gsub!(/\[(.*)_attributes\]\[\d+\]/, '.\1') - method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name if object.respond_to?(:to_model) key = object.class.model_name.i18n_key @@ -51,7 +51,7 @@ module ActionView end content ||= if object && object.class.respond_to?(:human_attribute_name) - object.class.human_attribute_name(@method_name) + object.class.human_attribute_name(method_and_value) end content ||= @method_name.humanize diff --git a/actionview/lib/action_view/helpers/tags/text_field.rb b/actionview/lib/action_view/helpers/tags/text_field.rb index e910879ebf..e0b80d81c2 100644 --- a/actionview/lib/action_view/helpers/tags/text_field.rb +++ b/actionview/lib/action_view/helpers/tags/text_field.rb @@ -7,7 +7,6 @@ module ActionView options["size"] = options["maxlength"] unless options.key?("size") options["type"] ||= field_type options["value"] = options.fetch("value") { value_before_type_cast(object) } unless field_type == "file" - options["value"] &&= ERB::Util.html_escape(options["value"]) add_default_name_and_id(options) tag("input", options) end diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb index 0bc40874d9..17ec6a40bf 100644 --- a/actionview/lib/action_view/helpers/translation_helper.rb +++ b/actionview/lib/action_view/helpers/translation_helper.rb @@ -7,7 +7,7 @@ module ActionView module TranslationHelper # Delegates to <tt>I18n#translate</tt> but also performs three additional functions. # - # First, it will ensure that any thrown +MissingTranslation+ messages will be turned + # First, it will ensure that any thrown +MissingTranslation+ messages will be turned # into inline spans that: # # * have a "translation-missing" class set, @@ -34,6 +34,7 @@ module ActionView # naming convention helps to identify translations that include HTML tags so that # you know what kind of output to expect when you call translate in a template. def translate(key, options = {}) + options = options.dup options[:default] = wrap_translate_defaults(options[:default]) if options[:default] # If the user has specified rescue_format then pass it all through, otherwise use diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb index 894616a449..c3be47133c 100644 --- a/actionview/lib/action_view/helpers/url_helper.rb +++ b/actionview/lib/action_view/helpers/url_helper.rb @@ -462,8 +462,6 @@ module ActionView # <strong>Email me:</strong> <span>me@domain.com</span> # </a> def mail_to(email_address, name = nil, html_options = {}, &block) - email_address = ERB::Util.html_escape(email_address) - html_options, name = name, nil if block_given? html_options = (html_options || {}).stringify_keys @@ -471,11 +469,11 @@ module ActionView option = html_options.delete(item) || next "#{item}=#{Rack::Utils.escape_path(option)}" }.compact - extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&')) + extras = extras.empty? ? '' : '?' + extras.join('&') - html_options["href"] = "mailto:#{email_address}#{extras}".html_safe + html_options["href"] = "mailto:#{email_address}#{extras}" - content_tag(:a, name || email_address.html_safe, html_options, &block) + content_tag(:a, name || email_address, html_options, &block) end # True if the current request URI was generated by the given +options+. diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb index 855fed0190..5fff6b0771 100644 --- a/actionview/lib/action_view/lookup_context.rb +++ b/actionview/lib/action_view/lookup_context.rb @@ -114,7 +114,7 @@ module ActionView module ViewPaths attr_reader :view_paths, :html_fallback_for_js - # Whenever setting view paths, makes a copy so we can manipulate then in + # Whenever setting view paths, makes a copy so that we can manipulate them in # instance objects as we wish. def view_paths=(paths) @view_paths = ActionView::PathSet.new(Array(paths)) @@ -134,7 +134,8 @@ module ActionView end alias :template_exists? :exists? - # Add fallbacks to the view paths. Useful in cases you are rendering a :file. + # Adds fallbacks to the view paths. Useful in cases when you are rendering + # a :file. def with_fallbacks added_resolvers = 0 self.class.fallbacks.each do |resolver| @@ -227,7 +228,7 @@ module ActionView end # Overload locale= to also set the I18n.locale. If the current I18n.config object responds - # to original_config, it means that it's has a copy of the original I18n configuration and it's + # to original_config, it means that it has a copy of the original I18n configuration and it's # acting as proxy, which we need to skip. def locale=(value) if value @@ -238,7 +239,7 @@ module ActionView super(@skip_default_locale ? I18n.locale : default_locale) end - # A method which only uses the first format in the formats array for layout lookup. + # Uses the first format in the formats array for layout lookup. def with_layout_format if formats.size == 1 yield diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index 017302d40f..c92d090cce 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -62,8 +62,8 @@ module ActionView # # The view class must have the following methods: # View.new[lookup_context, assigns, controller] - # Create a new ActionView instance for a controller - # View#render[options] + # Create a new ActionView instance for a controller and we can also pass the arguments. + # View#render(option) # Returns String with the rendered template # # Override this method in a module to change the default behavior. diff --git a/actionview/lib/action_view/routing_url_for.rb b/actionview/lib/action_view/routing_url_for.rb index 33be06cbf7..881a123572 100644 --- a/actionview/lib/action_view/routing_url_for.rb +++ b/actionview/lib/action_view/routing_url_for.rb @@ -1,3 +1,5 @@ +require 'action_dispatch/routing/polymorphic_routes' + module ActionView module RoutingUrlFor @@ -77,16 +79,20 @@ module ActionView case options when String options - when nil, Hash - options ||= {} - options = { :only_path => options[:host].nil? }.merge!(options.symbolize_keys) - super + when nil + super({:only_path => true}) + when Hash + super({ :only_path => options[:host].nil? }.merge!(options.symbolize_keys)) when :back _back_url + when Symbol + ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_string_call self, options when Array polymorphic_path(options, options.extract_options!) + when Class + ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_class_call self, options else - polymorphic_path(options) + ActionDispatch::Routing::PolymorphicRoutes::HelperMethodBuilder.path.handle_model_call self, options end end diff --git a/actionview/lib/action_view/tasks/dependencies.rake b/actionview/lib/action_view/tasks/dependencies.rake index 1b9426c0e5..b39f7d583b 100644 --- a/actionview/lib/action_view/tasks/dependencies.rake +++ b/actionview/lib/action_view/tasks/dependencies.rake @@ -2,16 +2,20 @@ 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? - template, format = ENV['TEMPLATE'].split(".") - format ||= :html - puts JSON.pretty_generate ActionView::Digestor.new(template, format, ApplicationController.new.lookup_context).nested_dependencies + puts JSON.pretty_generate ActionView::Digestor.new(name: template_name, finder: finder).nested_dependencies 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? - template, format = ENV['TEMPLATE'].split(".") - format ||= :html - puts JSON.pretty_generate ActionView::Digestor.new(template, format, ApplicationController.new.lookup_context).dependencies + puts JSON.pretty_generate ActionView::Digestor.new(name: template_name, finder: finder).dependencies + end + + def template_name + ENV['TEMPLATE'].split('.', 2).first + end + + def finder + ApplicationController.new.lookup_context end end diff --git a/actionview/lib/action_view/template/handlers.rb b/actionview/lib/action_view/template/handlers.rb index d9cddc0040..33bfcb458c 100644 --- a/actionview/lib/action_view/template/handlers.rb +++ b/actionview/lib/action_view/template/handlers.rb @@ -32,6 +32,15 @@ module ActionView #:nodoc: @@template_extensions = nil end + # Opposite to register_template_handler. + def unregister_template_handler(*extensions) + extensions.each do |extension| + handler = @@template_handlers.delete extension.to_sym + @@default_template_handlers = nil if @@default_template_handlers == handler + end + @@template_extensions = nil + end + def template_handler_extensions @@template_handlers.keys.map {|key| key.to_s }.sort end diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 403824bd8e..d29d020c17 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -181,13 +181,7 @@ module ActionView def query(path, details, formats) query = build_query(path, details) - # deals with case-insensitive file systems. - sanitizer = Hash.new { |h,dir| h[dir] = Dir["#{dir}/*"] } - - template_paths = Dir[query].reject { |filename| - File.directory?(filename) || - !sanitizer[File.dirname(filename)].include?(filename) - } + template_paths = find_template_paths query template_paths.map { |template| handler, format, variant = extract_handler_and_format_and_variant(template, formats) @@ -202,6 +196,26 @@ module ActionView } end + if RUBY_VERSION >= '2.2.0' + def find_template_paths(query) + Dir[query].reject { |filename| + File.directory?(filename) || + # deals with case-insensitive file systems. + !File.fnmatch(query, filename, File::FNM_EXTGLOB) + } + end + else + def find_template_paths(query) + # deals with case-insensitive file systems. + sanitizer = Hash.new { |h,dir| h[dir] = Dir["#{dir}/*"] } + + Dir[query].reject { |filename| + File.directory?(filename) || + !sanitizer[File.dirname(filename)].include?(filename) + } + end + end + # Helper for building query glob string based on resolver's pattern. def build_query(path, details) query = @pattern.dup @@ -228,7 +242,7 @@ module ActionView File.mtime(p) end - # Extract handler and formats from path. If a format cannot be a found neither + # Extract handler, formats and variant from path. If a format cannot be found neither # from the path, or the handler, we should return the array of formats given # to the resolver. def extract_handler_and_format_and_variant(path, default_formats) diff --git a/actionview/lib/action_view/view_paths.rb b/actionview/lib/action_view/view_paths.rb index 6c349feb1d..80a41f2418 100644 --- a/actionview/lib/action_view/view_paths.rb +++ b/actionview/lib/action_view/view_paths.rb @@ -14,27 +14,38 @@ module ActionView :locale, :locale=, :to => :lookup_context module ClassMethods - def parent_prefixes - @parent_prefixes ||= begin - parent_controller = superclass - prefixes = [] - - until parent_controller.abstract? - prefixes << parent_controller.controller_path - parent_controller = parent_controller.superclass - end + def _prefixes # :nodoc: + @_prefixes ||= begin + deprecated_prefixes = handle_deprecated_parent_prefixes + if deprecated_prefixes + deprecated_prefixes + else + return local_prefixes if superclass.abstract? - prefixes + local_prefixes + superclass._prefixes + end end end + + private + + # Override this method in your controller if you want to change paths prefixes for finding views. + # Prefixes defined here will still be added to parents' <tt>._prefixes</tt>. + def local_prefixes + [controller_path] + end + + def handle_deprecated_parent_prefixes # TODO: remove in 4.3/5.0. + return unless respond_to?(:parent_prefixes) + + ActiveSupport::Deprecation.warn "Overriding ActionController::Base::parent_prefixes is deprecated, override .local_prefixes instead." + local_prefixes + parent_prefixes + end end # The prefixes used in render "foo" shortcuts. - def _prefixes - @_prefixes ||= begin - parent_prefixes = self.class.parent_prefixes - parent_prefixes.dup.unshift(controller_path) - end + def _prefixes # :nodoc: + self.class._prefixes end # LookupContext is the object responsible to hold all information required to lookup |