diff options
Diffstat (limited to 'actionpack/lib/action_view/helpers')
16 files changed, 144 insertions, 56 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb index 3c05173a1b..05d5f1870a 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb @@ -60,8 +60,8 @@ module ActionView private - def path_to_asset(source, include_host = true, protocol = nil) - asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host, protocol) + def path_to_asset(source, options = {}) + asset_paths.compute_public_path(source, asset_name.to_s.pluralize, options.merge(:ext => extension)) end def path_to_asset_source(source) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb index 8b35aa8896..dd4e9ae4cc 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb @@ -41,7 +41,7 @@ module ActionView # Break out the asset path rewrite in case plugins wish to put the asset id # someplace other than the query string. - def rewrite_asset_path(source, dir) + def rewrite_asset_path(source, dir, options = nil) source = "/#{dir}/#{source}" unless source[0] == ?/ path = config.asset_path diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb index 25cc561608..d9f1f88ade 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb @@ -83,7 +83,7 @@ module ActionView # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js def javascript_path(source) - asset_paths.compute_public_path(source, 'javascripts', 'js') + asset_paths.compute_public_path(source, 'javascripts', :ext => 'js') end alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route @@ -119,10 +119,10 @@ module ActionView # # <script type="text/javascript" src="/elsewhere/cools.js?1423139606"></script> # # javascript_include_tag "http://www.example.com/xmlhr" - # # => <script type="text/javascript" src="http://www.example.com/xmlhr.js?1284139606"></script> + # # => <script type="text/javascript" src="http://www.example.com/xmlhr"></script> # # javascript_include_tag "http://www.example.com/xmlhr.js" - # # => <script type="text/javascript" src="http://www.example.com/xmlhr.js?1284139606"></script> + # # => <script type="text/javascript" src="http://www.example.com/xmlhr.js"></script> # # javascript_include_tag :defaults # # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script> diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb index 8c25d38bbd..343153c8c5 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb @@ -17,7 +17,7 @@ module ActionView def asset_tag(source, options) # We force the :request protocol here to avoid a double-download bug in IE7 and IE8 - tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source, true, :request)) }.merge(options), false, false) + tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => path_to_asset(source, :protocol => :request) }.merge(options)) end def custom_dir @@ -61,7 +61,7 @@ module ActionView # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css def stylesheet_path(source) - asset_paths.compute_public_path(source, 'stylesheets', 'css', true, :request) + asset_paths.compute_public_path(source, 'stylesheets', :ext => 'css', :protocol => :request) end alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index f81ce3e31c..850dd5f448 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -54,7 +54,7 @@ module ActionView output_safe = output_buffer.html_safe? fragment = output_buffer.slice!(pos..-1) if output_safe - self.output_buffer = output_buffer.html_safe + self.output_buffer = output_buffer.class.new(output_buffer) end controller.write_fragment(name, fragment, options) end diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 62f95379cd..8abd85c3a3 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -134,9 +134,9 @@ module ActionView # WARNING: content_for is ignored in caches. So you shouldn't use it # for elements that will be fragment cached. def content_for(name, content = nil, &block) - content = capture(&block) if block_given? - if content - @view_flow.append(name, content) + if content || block_given? + content = capture(&block) if block_given? + @view_flow.append(name, content) if content nil else @view_flow.get(name) diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index e850c258ce..4deb87180c 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -765,11 +765,16 @@ module ActionView if @options[:use_hidden] || @options[:discard_year] build_hidden(:year, val) else - options = {} - options[:start] = @options[:start_year] || middle_year - 5 - options[:end] = @options[:end_year] || middle_year + 5 - options[:step] = options[:start] < options[:end] ? 1 : -1 - options[:leading_zeros] = false + options = {} + options[:start] = @options[:start_year] || middle_year - 5 + options[:end] = @options[:end_year] || middle_year + 5 + options[:step] = options[:start] < options[:end] ? 1 : -1 + options[:leading_zeros] = false + options[:max_years_allowed] = @options[:max_years_allowed] || 1000 + + if (options[:end] - options[:start]).abs > options[:max_years_allowed] + raise ArgumentError, "There're too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter" + end build_options_and_select(:year, val, options) end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 85dea96bbb..5c10b67586 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1027,6 +1027,8 @@ module ActionView def to_number_field_tag(field_type, options = {}) options = options.stringify_keys + options['size'] ||= nil + if range = options.delete("in") || options.delete("within") options.update("min" => range.min, "max" => range.max) end @@ -1227,12 +1229,12 @@ module ActionView parent_builder.multipart = multipart if parent_builder end - def self._to_path - @_to_path ||= name.demodulize.underscore.sub!(/_builder$/, '') + def self._to_partial_path + @_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, '') end - def to_path - self.class._to_path + def to_partial_path + self.class._to_partial_path end def to_model diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index c677257d60..f895cad058 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -579,7 +579,12 @@ module ActionView def to_select_tag(choices, options, html_options) selected_value = options.has_key?(:selected) ? options[:selected] : value(object) - if !choices.empty? && choices.try(:first).try(:second).respond_to?(:each) + # Grouped choices look like this: + # + # [nil, []] + # { nil => [] } + # + if !choices.empty? && Array === choices.first.last option_tags = grouped_options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]) else option_tags = options_for_select(choices, :selected => selected_value, :disabled => options[:disabled]) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 79f07400b2..1424a3584d 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -56,8 +56,8 @@ module ActionView # form_tag('http://far.away.com/form', :authenticity_token => "cf50faa3fe97702ca1ae") # # form with custom authenticity token # - def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block) - html_options = html_options_for_form(url_for_options, options, *parameters_for_url) + 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) else @@ -177,9 +177,12 @@ module ActionView # label_tag 'name', nil, :class => 'small_label' # # => <label for="name" class="small_label">Name</label> def label_tag(name = nil, content_or_options = nil, options = nil, &block) - options = content_or_options if block_given? && content_or_options.is_a?(Hash) - options ||= {} - options.stringify_keys! + if block_given? && content_or_options.is_a?(Hash) + options = content_or_options = content_or_options.stringify_keys + else + options ||= {} + options = options.stringify_keys + end options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for") content_tag :label, content_or_options || name.to_s.humanize, options, &block end @@ -304,7 +307,7 @@ module ActionView # text_area_tag 'comment', nil, :class => 'comment_input' # # => <textarea class="comment_input" id="comment" name="comment"></textarea> def text_area_tag(name, content = nil, options = {}) - options.stringify_keys! + options = options.stringify_keys if size = options.delete("size") options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split) @@ -407,7 +410,7 @@ module ActionView # data-confirm="Are you sure?" /> # def submit_tag(value = "Save changes", options = {}) - options.stringify_keys! + options = options.stringify_keys if disable_with = options.delete("disable_with") options["data-disable-with"] = disable_with @@ -458,7 +461,7 @@ module ActionView def button_tag(content_or_options = nil, options = nil, &block) options = content_or_options if block_given? && content_or_options.is_a?(Hash) options ||= {} - options.stringify_keys! + options = options.stringify_keys if disable_with = options.delete("disable_with") options["data-disable-with"] = disable_with @@ -497,7 +500,7 @@ module ActionView # image_submit_tag("agree.png", :disabled => true, :class => "agree_disagree_button") # # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" /> def image_submit_tag(source, options = {}) - options.stringify_keys! + options = options.stringify_keys if confirm = options.delete("confirm") options["data-confirm"] = confirm @@ -579,7 +582,7 @@ module ActionView # # ==== Examples # number_field_tag 'quantity', nil, :in => 1...10 - # => <input id="quantity" name="quantity" min="1" max="9" /> + # => <input id="quantity" name="quantity" min="1" max="9" type="number" /> def number_field_tag(name, value = nil, options = {}) options = options.stringify_keys options["type"] ||= "number" @@ -604,12 +607,12 @@ module ActionView end private - def html_options_for_form(url_for_options, options, *parameters_for_url) + def html_options_for_form(url_for_options, options) options.stringify_keys.tap do |html_options| html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart") # The following URL is unescaped, this is just a hash of options, and it is the # responsibility of the caller to escape all the values. - html_options["action"] = url_for(url_for_options, *parameters_for_url) + html_options["action"] = url_for(url_for_options) html_options["accept-charset"] = "UTF-8" html_options["data-remote"] = true if html_options.delete("remote") html_options["authenticity_token"] = html_options.delete("authenticity_token") if html_options.has_key?("authenticity_token") @@ -653,7 +656,7 @@ module ActionView if token == false || !protect_against_forgery? '' else - token = form_authenticity_token if token.nil? + token ||= form_authenticity_token tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token) end end diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 4484390fde..1adcd716f8 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -1,4 +1,5 @@ require 'action_view/helpers/tag_helper' +require 'active_support/core_ext/string/encoding' module ActionView module Helpers @@ -10,15 +11,23 @@ module ActionView "\n" => '\n', "\r" => '\n', '"' => '\\"', - "'" => "\\'" } + "'" => "\\'" + } - # Escape carrier returns and single and double quotes for JavaScript segments. + if "ruby".encoding_aware? + JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '
' + else + JS_ESCAPE_MAP["\342\200\250"] = '
' + end + + # Escapes carriage returns and single and double quotes for JavaScript segments. + # # Also available through the alias j(). This is particularly helpful in JavaScript responses, like: # # $('some_element').replaceWith('<%=j render 'some/element_template' %>'); def escape_javascript(javascript) if javascript - result = javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) {|match| JS_ESCAPE_MAP[match] } + result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] } javascript.html_safe? ? result.html_safe : result else '' diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index ec6c2c8db3..7031694af4 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -69,7 +69,7 @@ module ActionView number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3") else number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3") - number.slice!(0, 1) if number.starts_with?('-') + number.slice!(0, 1) if number.starts_with?(delimiter) && !delimiter.blank? end str = [] diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index 142a25f118..b351302d01 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -17,6 +17,19 @@ module ActionView # # <div id="person_123" class="person foo"> Joe Bloggs </div> # + # You can also pass an array of Active Record objects, which will then + # get iterated over and yield each record as an argument for the block. + # For example: + # + # <%= div_for(@people, :class => "foo") do |person| %> + # <%= person.name %> + # <% end %> + # + # produces: + # + # <div id="person_123" class="person foo"> Joe Bloggs </div> + # <div id="person_124" class="person foo"> Jane Bloggs </div> + # def div_for(record, *args, &block) content_tag_for(:div, record, *args, &block) end @@ -42,6 +55,21 @@ module ActionView # # <tr id="foo_person_123" class="person">... # + # You can also pass an array of objects which this method will loop through + # and yield the current object to the supplied block, reducing the need for + # having to iterate through the object (using <tt>each</tt>) beforehand. + # For example (assuming @people is an array of Person objects): + # + # <%= content_tag_for(:tr, @people) do |person| %> + # <td><%= person.first_name %></td> + # <td><%= person.last_name %></td> + # <% end %> + # + # produces: + # + # <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 # with the default class name for your object. For example: @@ -52,12 +80,30 @@ module ActionView # # <li id="person_123" class="person bar">... # - def content_tag_for(tag_name, record, prefix = nil, options = nil, &block) - options, prefix = prefix, nil if prefix.is_a?(Hash) - options ||= {} - options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) }) - content_tag(tag_name, options, &block) + def content_tag_for(tag_name, single_or_multiple_records, prefix = nil, options = nil, &block) + if single_or_multiple_records.respond_to?(:to_ary) + single_or_multiple_records.to_ary.map do |single_record| + capture { content_tag_for_single_record(tag_name, single_record, prefix, options, &block) } + end.join("\n").html_safe + else + content_tag_for_single_record(tag_name, single_or_multiple_records, prefix, options, &block) + end end + + private + + # Called by <tt>content_tag_for</tt> internally to render a content tag + # for each record. + def content_tag_for_single_record(tag_name, record, prefix, options, &block) + options, prefix = prefix, nil if prefix.is_a?(Hash) + options ||= {} + options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) }) + if block.arity == 0 + content_tag(tag_name, capture(&block), options) + else + content_tag(tag_name, capture(record, &block), options) + end + end end end end diff --git a/actionpack/lib/action_view/helpers/rendering_helper.rb b/actionpack/lib/action_view/helpers/rendering_helper.rb index 47efdded42..626e1a1ab7 100644 --- a/actionpack/lib/action_view/helpers/rendering_helper.rb +++ b/actionpack/lib/action_view/helpers/rendering_helper.rb @@ -8,7 +8,7 @@ module ActionView module RenderingHelper # Returns the result of a render that's dictated by the options hash. The primary options are: # - # * <tt>:partial</tt> - See ActionView::Partials. + # * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt>. # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those. # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller. # * <tt>:text</tt> - Renders the text passed in out. @@ -87,4 +87,4 @@ module ActionView end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index ae71ade588..21074efe86 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -256,7 +256,7 @@ module ActionView # # => "<p><span>I'm allowed!</span> It's true.</p>" def simple_format(text, html_options={}, options={}) text = '' if text.nil? - text = text.dup if text.frozen? + text = text.dup start_tag = tag('p', html_options, true) text = sanitize(text) unless options[:sanitize] == false text = text.to_str diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 51baca8e03..0c2e1aa3a9 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -268,7 +268,7 @@ module ActionView # to change the HTTP verb used to submit the form. # # ==== Options - # The +options+ hash accepts the same options as url_for. + # The +options+ hash accepts the same options as +url_for+. # # There are a few special +html_options+: # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>, @@ -279,6 +279,7 @@ module ActionView # processed normally, otherwise no action is taken. # * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the # submit behavior. By default this behavior is an ajax submit. + # * <tt>:form</tt> - This hash will be form attributes # * <tt>:form_class</tt> - This controls the class of the form within which the submit button will # be placed # @@ -295,6 +296,12 @@ module ActionView # # </form>" # # + # <%= button_to "Create", :action => "create", :remote => true, :form => { "data-type" => "json" } %> + # # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json"> + # # <div><input value="Create" type="submit" /></div> + # # </form>" + # + # # <%= button_to "Delete Image", { :action => "delete", :id => @image.id }, # :confirm => "Are you sure?", :method => :delete %> # # => "<form method="post" action="/images/delete/1" class="button_to"> @@ -324,10 +331,11 @@ module ActionView end form_method = method.to_s == 'get' ? 'get' : 'post' - form_class = html_options.delete('form_class') || 'button_to' - + form_options = html_options.delete('form') || {} + form_options[:class] ||= html_options.delete('form_class') || 'button_to' + remote = html_options.delete('remote') - + request_token_tag = '' if form_method == 'post' && protect_against_forgery? request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) @@ -340,8 +348,10 @@ module ActionView html_options.merge!("type" => "submit", "value" => name) - ("<form method=\"#{form_method}\" action=\"#{ERB::Util.html_escape(url)}\" #{"data-remote=\"true\"" if remote} class=\"#{ERB::Util.html_escape(form_class)}\"><div>" + - method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe + form_options.merge!(:method => form_method, :action => url) + form_options.merge!("data-remote" => "true") if remote + + "#{tag(:form, form_options, true)}<div>#{method_tag}#{tag("input", html_options)}#{request_token_tag}</div></form>".html_safe end @@ -569,6 +579,12 @@ module ActionView # # current_page?(:controller => 'library', :action => 'checkout') # # => false + # + # Let's say we're in the <tt>/products</tt> action with method POST in case of invalid product. + # + # current_page?(:controller => 'product', :action => 'index') + # # => false + # def current_page?(options) unless request raise "You cannot use helpers that need to determine the current " \ @@ -576,6 +592,8 @@ module ActionView "in a #request method" end + return false unless request.get? + url_string = url_for(options) # We ignore any extra parameters in the request_uri if the @@ -596,9 +614,7 @@ module ActionView private def convert_options_to_data_attributes(options, html_options) - if html_options.nil? - link_to_remote_options?(options) ? {'data-remote' => 'true'} : {} - else + 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) @@ -611,6 +627,8 @@ module ActionView add_method_to_attributes!(html_options, method) if method html_options + else + link_to_remote_options?(options) ? {'data-remote' => 'true'} : {} end end |
