aboutsummaryrefslogtreecommitdiffstats
path: root/actionview/lib/action_view/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'actionview/lib/action_view/helpers')
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb5
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb9
-rw-r--r--actionview/lib/action_view/helpers/atom_feed_helper.rb8
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb27
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb349
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb33
-rw-r--r--actionview/lib/action_view/helpers/output_safety_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/tag_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb6
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_check_boxes.rb2
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_helpers.rb6
-rw-r--r--actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb2
-rw-r--r--actionview/lib/action_view/helpers/tags/select.rb8
-rw-r--r--actionview/lib/action_view/helpers/text_helper.rb11
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb1
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb23
17 files changed, 419 insertions, 79 deletions
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index b1563ac490..4e4f4823e6 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -1,6 +1,5 @@
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/hash/keys"
-require "active_support/core_ext/regexp"
require "action_view/helpers/asset_url_helper"
require "action_view/helpers/tag_helper"
@@ -169,7 +168,7 @@ module ActionView
#
# favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
# # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
- def favicon_link_tag(source="favicon.ico", options={})
+ def favicon_link_tag(source = "favicon.ico", options = {})
tag("link", {
rel: "shortcut icon",
type: "image/x-icon",
@@ -207,7 +206,7 @@ module ActionView
# # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
# image_tag("/icons/icon.gif", data: { title: 'Rails Application' })
# # => <img data-title="Rails Application" src="/icons/icon.gif" />
- def image_tag(source, options={})
+ def image_tag(source, options = {})
options = options.symbolize_keys
check_for_image_tag_errors(options)
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index 0967245855..c6a5e04aba 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -1,5 +1,4 @@
require "zlib"
-require "active_support/core_ext/regexp"
module ActionView
# = Action View Asset URL Helpers
@@ -37,7 +36,7 @@ module ActionView
# some asset downloads to wait for previous assets to finish before they can
# begin. You can use the <tt>%d</tt> wildcard in the +asset_host+ to
# distribute the requests over four hosts. For example,
- # <tt>assets%d.example.com<tt> will spread the asset requests over
+ # <tt>assets%d.example.com</tt> will spread the asset requests over
# "assets0.example.com", ..., "assets3.example.com".
#
# image_tag("rails.png")
@@ -97,8 +96,8 @@ module ActionView
# 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.
+ # 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 && request.ssl?
@@ -233,7 +232,7 @@ module ActionView
stylesheet: ".css"
}
- # Compute extname to append to asset path. Returns nil if
+ # Compute extname to append to asset path. Returns +nil+ if
# nothing should be added.
def compute_asset_extname(source, options = {})
return if options[:extname] == false
diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb
index 09d243c46d..3538515aee 100644
--- a/actionview/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb
@@ -103,7 +103,7 @@ module ActionView
xml = options.delete(:xml) || eval("xml", block.binding)
xml.instruct!
if options[:instruct]
- options[:instruct].each do |target,attrs|
+ options[:instruct].each do |target, attrs|
if attrs.respond_to?(:keys)
xml.instruct!(target, attrs)
elsif attrs.respond_to?(:each)
@@ -113,7 +113,7 @@ module ActionView
end
feed_opts = { "xml:lang" => options[:language] || "en-US", "xmlns" => "http://www.w3.org/2005/Atom" }
- feed_opts.merge!(options).reject! { |k,v| !k.to_s.match(/^xml/) }
+ feed_opts.merge!(options).reject! { |k, v| !k.to_s.match(/^xml/) }
xml.feed(feed_opts) do
xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
@@ -163,7 +163,7 @@ module ActionView
@xml, @view, @feed_options = xml, view, feed_options
end
- # Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
+ # Accepts a Date or Time object and inserts it in the proper format. If +nil+ is passed, current time in UTC is used.
def updated(date_or_time = nil)
@xml.updated((date_or_time || Time.now.utc).xmlschema)
end
@@ -174,7 +174,7 @@ module ActionView
#
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
- # * <tt>:url</tt>: The URL for this entry or false or nil for not having a link tag. Defaults to the polymorphic_url for the record.
+ # * <tt>:url</tt>: The URL for this entry or +false+ or +nil+ for not having a link tag. Defaults to the +polymorphic_url+ for the record.
# * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
# * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html".
def entry(record, options = {})
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 5258a01144..bf1c8ceaed 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -88,7 +88,7 @@ module ActionView
#
# === Explicit dependencies
#
- # Some times you'll have template dependencies that can't be derived at all. This is typically
+ # Sometimes you'll have template dependencies that can't be derived at all. This is typically
# the case when you have template rendering that happens in helpers. Here's an example:
#
# <%= render_sortable_todolists @project.todolists %>
@@ -218,7 +218,7 @@ module ActionView
def fragment_name_with_digest(name, virtual_path) #:nodoc:
virtual_path ||= @virtual_path
if virtual_path
- name = controller.url_for(name).split("://").last if name.is_a?(Hash)
+ name = controller.url_for(name).split("://").last if name.is_a?(Hash)
digest = Digestor.digest name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
[ name, digest ]
else
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index c277738920..09dc6ef6bd 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -98,7 +98,7 @@ module ActionView
from_time = from_time.to_time if from_time.respond_to?(:to_time)
to_time = to_time.to_time if to_time.respond_to?(:to_time)
from_time, to_time = to_time, from_time if from_time > to_time
- distance_in_minutes = ((to_time - from_time)/60.0).round
+ distance_in_minutes = ((to_time - from_time) / 60.0).round
distance_in_seconds = (to_time - from_time).round
I18n.with_options locale: options[:locale], scope: options[:scope] do |locale|
@@ -220,7 +220,7 @@ module ActionView
# the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
# * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
# dates.
- # * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
+ # * <tt>:default</tt> - Set a default date if the affected date isn't set or is +nil+.
# * <tt>:selected</tt> - Set a date that overrides the actual value.
# * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
# * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
@@ -267,7 +267,7 @@ module ActionView
# date_select("article", "written_on", default: 3.days.from_now)
#
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
- # # which is set in the form with todays date, regardless of the value in the Active Record object.
+ # # which is set in the form with today's date, regardless of the value in the Active Record object.
# date_select("article", "written_on", selected: Date.today)
#
# # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
@@ -303,7 +303,7 @@ module ActionView
# # the sunrise attribute.
# time_select("article", "start_time", include_seconds: true)
#
- # # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45.
+ # # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30, and 45.
# time_select 'game', 'game_time', {minute_step: 15}
#
# # Creates a time select tag with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
@@ -866,7 +866,7 @@ module ActionView
end
# Returns translated month names, but also ensures that a custom month
- # name array has a leading nil element.
+ # name array has a leading +nil+ element.
def month_names
@month_names ||= begin
month_names = @options[:use_month_names] || translated_month_names
@@ -1026,15 +1026,16 @@ module ActionView
# prompt_option_tag(:month, prompt: 'Select month')
# => "<option value="">Select month</option>"
def prompt_option_tag(type, options)
- prompt = case options
- when Hash
- default_options = { year: false, month: false, day: false, hour: false, minute: false, second: false }
- default_options.merge!(options)[type.to_sym]
- when String
- options
+ prompt = \
+ case options
+ when Hash
+ default_options = { year: false, month: false, day: false, hour: false, minute: false, second: false }
+ default_options.merge!(options)[type.to_sym]
+ when String
+ options
else
- I18n.translate(:"datetime.prompts.#{type}", locale: @options[:locale])
- end
+ I18n.translate(:"datetime.prompts.#{type}", locale: @options[:locale])
+ end
prompt ? content_tag("option".freeze, prompt, value: "") : ""
end
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 124a14f1d9..e7ea267211 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -474,6 +474,242 @@ module ActionView
end
private :apply_form_for_options!
+ # Creates a form tag based on mixing URLs, scopes, or models.
+ #
+ # # Using just a URL:
+ # <%= form_with url: posts_path do |form| %>
+ # <%= form.text_field :title %>
+ # <% end %>
+ # # =>
+ # <form action="/posts" method="post" data-remote="true">
+ # <input type="text" name="title">
+ # </form>
+ #
+ # # Adding a scope prefixes the input field names:
+ # <%= form_with scope: :post, url: posts_path do |form| %>
+ # <%= form.text_field :title %>
+ # <% end %>
+ # # =>
+ # <form action="/posts" method="post" data-remote="true">
+ # <input type="text" name="post[title]">
+ # </form>
+ #
+ # # Using a model infers both the URL and scope:
+ # <%= form_with model: Post.new do |form| %>
+ # <%= form.text_field :title %>
+ # <% end %>
+ # # =>
+ # <form action="/posts" method="post" data-remote="true">
+ # <input type="text" name="post[title]">
+ # </form>
+ #
+ # # An existing model makes an update form and fills out field values:
+ # <%= form_with model: Post.first do |form| %>
+ # <%= form.text_field :title %>
+ # <% end %>
+ # # =>
+ # <form action="/posts/1" method="post" data-remote="true">
+ # <input type="hidden" name="_method" value="patch">
+ # <input type="text" name="post[title]" value="<the title of the post>">
+ # </form>
+ #
+ # The parameters in the forms are accessible in controllers according to
+ # their name nesting. So inputs named +title+ and <tt>post[title]</tt> are
+ # accessible as <tt>params[:title]</tt> and <tt>params[:post][:title]</tt>
+ # respectively.
+ #
+ # By default +form_with+ attaches the <tt>data-remote</tt> attribute
+ # submitting the form via an XMLHTTPRequest in the background if an
+ # Unobtrusive JavaScript driver, like rails-ujs, is used. See the
+ # <tt>:remote</tt> option for more.
+ #
+ # For ease of comparison the examples above left out the submit button,
+ # as well as the auto generated hidden fields that enable UTF-8 support
+ # and adds an authenticity token needed for cross site request forgery
+ # protection.
+ #
+ # ==== +form_with+ options
+ #
+ # * <tt>:url</tt> - The URL the form submits to. Akin to values passed to
+ # +url_for+ or +link_to+. For example, you may use a named route
+ # directly. When a <tt>:scope</tt> is passed without a <tt>:url</tt> the
+ # form just submits to the current URL.
+ # * <tt>:method</tt> - The method to use when submitting the form, usually
+ # either "get" or "post". If "patch", "put", "delete", or another verb
+ # is used, a hidden input named <tt>_method</tt> is added to
+ # simulate the verb over post.
+ # * <tt>:format</tt> - The format of the route the form submits to.
+ # Useful when submitting to another resource type, like <tt>:json</tt>.
+ # Skipped if a <tt>:url</tt> is passed.
+ # * <tt>:scope</tt> - The scope to prefix input field names with and
+ # thereby how the submitted parameters are grouped in controllers.
+ # * <tt>:model</tt> - A model object to infer the <tt>:url</tt> and
+ # <tt>:scope</tt> by, plus fill out input field values.
+ # So if a +title+ attribute is set to "Ahoy!" then a +title+ input
+ # field's value would be "Ahoy!".
+ # If the model is a new record a create form is generated, if an
+ # existing record, however, an update form is generated.
+ # Pass <tt>:scope</tt> or <tt>:url</tt> to override the defaults.
+ # E.g. turn <tt>params[:post]</tt> into <tt>params[:article]</tt>.
+ # * <tt>:authenticity_token</tt> - Authenticity token to use in the form.
+ # Override with a custom authenticity token or pass <tt>false</tt> to
+ # skip the authenticity token field altogether.
+ # Useful when submitting to an external resource like a payment gateway
+ # that might limit the valid fields.
+ # Remote forms may omit the embedded authenticity token by setting
+ # <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
+ # This is helpful when fragment-caching the form. Remote forms
+ # get the authenticity token from the <tt>meta</tt> tag, so embedding is
+ # unnecessary unless you support browsers without JavaScript.
+ # * <tt>:local</tt> - By default form submits are remote and unobstrusive XHRs.
+ # Disable remote submits with <tt>local: true</tt>.
+ # * <tt>:skip_enforcing_utf8</tt> - By default a hidden field named +utf8+
+ # is output to enforce UTF-8 submits. Set to true to skip the field.
+ # * <tt>:builder</tt> - Override the object used to build the form.
+ # * <tt>:id</tt> - Optional HTML id attribute.
+ # * <tt>:class</tt> - Optional HTML class attribute.
+ # * <tt>:data</tt> - Optional HTML data attributes.
+ # * <tt>:html</tt> - Other optional HTML attributes for the form tag.
+ #
+ # === Examples
+ #
+ # When not passing a block, +form_with+ just generates an opening form tag.
+ #
+ # <%= form_with(model: @post, url: super_posts_path) %>
+ # <%= form_with(model: @post, scope: :article) %>
+ # <%= form_with(model: @post, format: :json) %>
+ # <%= form_with(model: @post, authenticity_token: false) %> # Disables the token.
+ #
+ # For namespaced routes, like +admin_post_url+:
+ #
+ # <%= form_with(model: [ :admin, @post ]) do |form| %>
+ # ...
+ # <% end %>
+ #
+ # If your resource has associations defined, for example, you want to add comments
+ # to the document given that the routes are set correctly:
+ #
+ # <%= form_with(model: [ @document, Comment.new ]) do |form| %>
+ # ...
+ # <% end %>
+ #
+ # Where <tt>@document = Document.find(params[:id])</tt>.
+ #
+ # === Mixing with other form helpers
+ #
+ # While +form_with+ uses a FormBuilder object it's possible to mix and
+ # match the stand-alone FormHelper methods and methods
+ # from FormTagHelper:
+ #
+ # <%= form_with scope: :person do |form| %>
+ # <%= form.text_field :first_name %>
+ # <%= form.text_field :last_name %>
+ #
+ # <%= text_area :person, :biography %>
+ # <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
+ #
+ # <%= form.submit %>
+ # <% end %>
+ #
+ # Same goes for the methods in FormOptionHelper and DateHelper designed
+ # to work with an object as a base, like
+ # FormOptionHelper#collection_select and DateHelper#datetime_select.
+ #
+ # === Setting the method
+ #
+ # You can force the form to use the full array of HTTP verbs by setting
+ #
+ # method: (:get|:post|:patch|:put|:delete)
+ #
+ # in the options hash. If the verb is not GET or POST, which are natively
+ # supported by HTML forms, the form will be set to POST and a hidden input
+ # called _method will carry the intended verb for the server to interpret.
+ #
+ # === Setting HTML options
+ #
+ # You can set data attributes directly in a data hash, but HTML options
+ # besides id and class must be wrapped in an HTML key:
+ #
+ # <%= form_with(model: @post, data: { behavior: "autosave" }, html: { name: "go" }) do |form| %>
+ # ...
+ # <% end %>
+ #
+ # generates
+ #
+ # <form action="/posts/123" method="post" data-behavior="autosave" name="go">
+ # <input name="_method" type="hidden" value="patch" />
+ # ...
+ # </form>
+ #
+ # === Removing hidden model id's
+ #
+ # The +form_with+ method automatically includes the model id as a hidden field in the form.
+ # This is used to maintain the correlation between the form data and its associated model.
+ # Some ORM systems do not use IDs on nested models so in this case you want to be able
+ # to disable the hidden id.
+ #
+ # In the following example the Post model has many Comments stored within it in a NoSQL database,
+ # thus there is no primary key for comments.
+ #
+ # <%= form_with(model: @post) do |form| %>
+ # <%= form.fields(:comments, skip_id: true) do |fields| %>
+ # ...
+ # <% end %>
+ # <% end %>
+ #
+ # === Customized form builders
+ #
+ # You can also build forms using a customized FormBuilder class. Subclass
+ # FormBuilder and override or define some more helpers, then use your
+ # custom builder. For example, let's say you made a helper to
+ # automatically add labels to form inputs.
+ #
+ # <%= form_with model: @person, url: { action: "create" }, builder: LabellingFormBuilder do |form| %>
+ # <%= form.text_field :first_name %>
+ # <%= form.text_field :last_name %>
+ # <%= form.text_area :biography %>
+ # <%= form.check_box :admin %>
+ # <%= form.submit %>
+ # <% end %>
+ #
+ # In this case, if you use:
+ #
+ # <%= render form %>
+ #
+ # The rendered template is <tt>people/_labelling_form</tt> and the local
+ # variable referencing the form builder is called
+ # <tt>labelling_form</tt>.
+ #
+ # The custom FormBuilder class is automatically merged with the options
+ # of a nested +fields+ call, unless it's explicitly set.
+ #
+ # In many cases you will want to wrap the above in another helper, so you
+ # could do something like the following:
+ #
+ # def labelled_form_with(**options, &block)
+ # form_with(**options.merge(builder: LabellingFormBuilder), &block)
+ # end
+ def form_with(model: nil, scope: nil, url: nil, format: nil, **options)
+ if model
+ url ||= polymorphic_path(model, format: format)
+
+ model = model.last if model.is_a?(Array)
+ scope ||= model_name_from_record_or_class(model).param_key
+ end
+
+ if block_given?
+ builder = instantiate_builder(scope, model, options)
+ output = capture(builder, &Proc.new)
+ options[:multipart] ||= builder.multipart?
+
+ html_options = html_options_for_form_with(url, model, options)
+ form_tag_with_body(html_options, output)
+ else
+ html_options = html_options_for_form_with(url, model, options)
+ form_tag_html(html_options)
+ end
+ end
+
# Creates a scope around a specific model object like form_for, but
# doesn't create the form tags themselves. This makes fields_for suitable
# for specifying additional model objects in the same form.
@@ -720,6 +956,62 @@ module ActionView
capture(builder, &block)
end
+ # Scopes input fields with either an explicit scope or model.
+ # Like +form_with+ does with <tt>:scope</tt> or <tt>:model</tt>,
+ # except it doesn't output the form tags.
+ #
+ # # Using a scope prefixes the input field names:
+ # <%= fields :comment do |fields| %>
+ # <%= fields.text_field :body %>
+ # <% end %>
+ # # => <input type="text" name="comment[body] id="comment_body">
+ #
+ # # Using a model infers the scope and assigns field values:
+ # <%= fields model: Comment.new(body: "full bodied") do |fields| %<
+ # <%= fields.text_field :body %>
+ # <% end %>
+ # # =>
+ # <input type="text" name="comment[body] id="comment_body" value="full bodied">
+ #
+ # # Using +fields+ with +form_with+:
+ # <%= form_with model: @post do |form| %>
+ # <%= form.text_field :title %>
+ #
+ # <%= form.fields :comment do |fields| %>
+ # <%= fields.text_field :body %>
+ # <% end %>
+ # <% end %>
+ #
+ # Much like +form_with+ a FormBuilder instance associated with the scope
+ # or model is yielded, so any generated field names are prefixed with
+ # either the passed scope or the scope inferred from the <tt>:model</tt>.
+ #
+ # === Mixing with other form helpers
+ #
+ # While +form_with+ uses a FormBuilder object it's possible to mix and
+ # match the stand-alone FormHelper methods and methods
+ # from FormTagHelper:
+ #
+ # <%= fields model: @comment do |fields| %>
+ # <%= fields.text_field :body %>
+ #
+ # <%= text_area :commenter, :biography %>
+ # <%= check_box_tag "comment[all_caps]", "1", @comment.commenter.hulk_mode? %>
+ # <% end %>
+ #
+ # Same goes for the methods in FormOptionHelper and DateHelper designed
+ # to work with an object as a base, like
+ # FormOptionHelper#collection_select and DateHelper#datetime_select.
+ def fields(scope = nil, model: nil, **options, &block)
+ # TODO: Remove when ids and classes are no longer output by default.
+ if model
+ scope ||= model_name_from_record_or_class(model).param_key
+ end
+
+ builder = instantiate_builder(scope, model, options)
+ capture(builder, &block)
+ end
+
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
@@ -1175,6 +1467,32 @@ module ActionView
end
private
+ def html_options_for_form_with(url_for_options = nil, model = nil, html: {}, local: false,
+ skip_enforcing_utf8: false, **options)
+ html_options = options.except(:index, :include_id, :builder).merge(html)
+ html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
+ html_options[:enforce_utf8] = !skip_enforcing_utf8
+
+ 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 || {})
+ html_options[:"accept-charset"] = "UTF-8"
+ html_options[:"data-remote"] = true unless local
+
+ if !local && !embed_authenticity_token_in_remote_forms &&
+ html_options[:authenticity_token].blank?
+ # The authenticity token is taken from the meta tag in this case
+ html_options[:authenticity_token] = false
+ elsif html_options[:authenticity_token] == true
+ # Include the default authenticity_token, which is only generated when its set to nil,
+ # but we needed the true value to override the default of no authenticity_token on data-remote.
+ html_options[:authenticity_token] = nil
+ end
+
+ html_options.stringify_keys!
+ end
def instantiate_builder(record_name, record_object, options)
case record_name
@@ -1183,7 +1501,7 @@ module ActionView
object_name = record_name
else
object = record_name
- object_name = model_name_from_record_or_class(object).param_key
+ object_name = model_name_from_record_or_class(object).param_key if object
end
builder = options[:builder] || default_form_builder_class
@@ -1249,7 +1567,7 @@ module ActionView
# The methods which wrap a form helper call.
class_attribute :field_helpers
- self.field_helpers = [:fields_for, :label, :text_field, :password_field,
+ self.field_helpers = [:fields_for, :fields, :label, :text_field, :password_field,
:hidden_field, :file_field, :text_area, :check_box,
:radio_button, :color_field, :search_field,
:telephone_field, :phone_field, :date_field,
@@ -1286,6 +1604,9 @@ module ActionView
@nested_child_index = {}
@object_name, @object, @template, @options = object_name, object, template, options
@default_options = @options ? @options.slice(:index, :namespace) : {}
+
+ convert_to_legacy_options(@options)
+
if @object_name.to_s.match(/\[\]$/)
if (object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}")) && object.respond_to?(:to_param)
@auto_index = object.to_param
@@ -1293,11 +1614,12 @@ module ActionView
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
end
end
+
@multipart = nil
@index = options[:index] || options[:child_index]
end
- (field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
+ (field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]).each do |selector|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{selector}(method, options = {}) # def text_field(method, options = {})
@template.send( # @template.send(
@@ -1569,7 +1891,7 @@ module ActionView
index = if options.has_key?(:index)
options[:index]
elsif defined?(@auto_index)
- self.object_name = @object_name.to_s.sub(/\[\]$/,"")
+ self.object_name = @object_name.to_s.sub(/\[\]$/, "")
@auto_index
end
@@ -1586,6 +1908,13 @@ module ActionView
@template.fields_for(record_name, record_object, fields_options, &block)
end
+ # See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method.
+ def fields(scope = nil, model: nil, **options, &block)
+ convert_to_legacy_options(options)
+
+ fields_for(scope || model, model, **options, &block)
+ end
+
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
@@ -1809,7 +2138,7 @@ module ActionView
# post:
# create: "Add %{model}"
#
- def submit(value=nil, options={})
+ def submit(value = nil, options = {})
value, options = nil, value if value.is_a?(Hash)
value ||= submit_default_value
@template.submit_tag(value, options)
@@ -1934,6 +2263,16 @@ module ActionView
@nested_child_index[name] ||= -1
@nested_child_index[name] += 1
end
+
+ def convert_to_legacy_options(options)
+ if options.key?(:skip_id)
+ options[:include_id] = !options.delete(:skip_id)
+ end
+
+ if options.key?(:local)
+ options[:remote] = !options.delete(:local)
+ end
+ end
end
end
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index a8abe6b7c4..7bd473507b 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -857,23 +857,24 @@ module ActionView
authenticity_token = html_options.delete("authenticity_token")
method = html_options.delete("method").to_s.downcase
- method_tag = case method
- when "get"
- html_options["method"] = "get"
- ""
- when "post", ""
- html_options["method"] = "post"
- token_tag(authenticity_token, form_options: {
- action: html_options["action"],
- method: "post"
- })
+ method_tag = \
+ case method
+ when "get"
+ html_options["method"] = "get"
+ ""
+ when "post", ""
+ html_options["method"] = "post"
+ token_tag(authenticity_token, form_options: {
+ action: html_options["action"],
+ method: "post"
+ })
else
- html_options["method"] = "post"
- method_tag(method) + token_tag(authenticity_token, form_options: {
- action: html_options["action"],
- method: method
- })
- end
+ html_options["method"] = "post"
+ method_tag(method) + token_tag(authenticity_token, form_options: {
+ action: html_options["action"],
+ method: method
+ })
+ end
if html_options.delete("enforce_utf8") { true }
utf8_enforcer_tag + method_tag
diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb
index 8e63e59fac..9f1a890f6a 100644
--- a/actionview/lib/action_view/helpers/output_safety_helper.rb
+++ b/actionview/lib/action_view/helpers/output_safety_helper.rb
@@ -28,7 +28,7 @@ module ActionView #:nodoc:
# safe_join([raw("<p>foo</p>"), raw("<p>bar</p>")], raw("<br />")
# # => "<p>foo</p><br /><p>bar</p>"
#
- def safe_join(array, sep=$,)
+ def safe_join(array, sep = $,)
sep = ERB::Util.unwrapped_html_escape(sep)
array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb
index 24c6d03cd1..306b71c85e 100644
--- a/actionview/lib/action_view/helpers/tag_helper.rb
+++ b/actionview/lib/action_view/helpers/tag_helper.rb
@@ -138,7 +138,7 @@ module ActionView
#
# ==== Options
#
- # Any passed options become attributes on the generated tag.
+ # Use symbol keyed options to add attributes to the generated tag.
#
# tag.section class: %w( kitties puppies )
# # => <section class="kitties puppies"></section>
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index e3e3c8b109..cf8a6d6028 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -11,7 +11,7 @@ module ActionView
@object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
@template_object = template_object
- @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
+ @object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]")
@object = retrieve_object(options.delete(:object))
@options = options
@auto_index = Regexp.last_match ? retrieve_autoindex(Regexp.last_match.pre_match) : nil
@@ -110,7 +110,7 @@ module ActionView
end
def sanitized_method_name
- @sanitized_method_name ||= @method_name.sub(/\?$/,"")
+ @sanitized_method_name ||= @method_name.sub(/\?$/, "")
end
def sanitized_value(value)
@@ -152,7 +152,7 @@ module ActionView
end
def name_and_id_index(options)
- options.key?("index") ? options.delete("index") || "" : @auto_index
+ options.key?("index") ? options.delete("index") || "" : @auto_index
end
end
end
diff --git a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
index 2a6bf49567..0359d4e65d 100644
--- a/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_check_boxes.rb
@@ -7,7 +7,7 @@ module ActionView
include CollectionHelpers
class CheckBoxBuilder < Builder # :nodoc:
- def check_box(extra_html_options={})
+ def check_box(extra_html_options = {})
html_options = extra_html_options.merge(@input_html_options)
html_options[:multiple] = true
@template_object.check_box(@object_name, @method_name, html_options, @value, nil)
diff --git a/actionview/lib/action_view/helpers/tags/collection_helpers.rb b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
index 70d7c484eb..c8be392865 100644
--- a/actionview/lib/action_view/helpers/tags/collection_helpers.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_helpers.rb
@@ -17,7 +17,7 @@ module ActionView
@input_html_options = input_html_options
end
- def label(label_html_options={}, &block)
+ def label(label_html_options = {}, &block)
html_options = @input_html_options.slice(:index, :namespace).merge(label_html_options)
html_options[:for] ||= @input_html_options[:id] if @input_html_options[:id]
@@ -41,8 +41,8 @@ module ActionView
sanitize_attribute_name(value), text, value, html_options)
end
- # Generate default options for collection helpers, such as :checked and
- # :disabled.
+ # Generate default options for collection helpers, such as :checked and
+ # :disabled.
def default_html_options_for_collection(item, value) #:nodoc:
html_options = @html_options.dup
diff --git a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
index eed7941cd6..a5f72af9ff 100644
--- a/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
+++ b/actionview/lib/action_view/helpers/tags/collection_radio_buttons.rb
@@ -7,7 +7,7 @@ module ActionView
include CollectionHelpers
class RadioButtonBuilder < Builder # :nodoc:
- def radio_button(extra_html_options={})
+ def radio_button(extra_html_options = {})
html_options = extra_html_options.merge(@input_html_options)
@template_object.radio_button(@object_name, @method_name, @value, html_options)
end
diff --git a/actionview/lib/action_view/helpers/tags/select.rb b/actionview/lib/action_view/helpers/tags/select.rb
index 8cc34e3180..667c7e945a 100644
--- a/actionview/lib/action_view/helpers/tags/select.rb
+++ b/actionview/lib/action_view/helpers/tags/select.rb
@@ -28,10 +28,10 @@ module ActionView
private
- # Grouped choices look like this:
- #
- # [nil, []]
- # { nil => [] }
+ # Grouped choices look like this:
+ #
+ # [nil, []]
+ # { nil => [] }
def grouped_choices?
!@choices.empty? && @choices.first.respond_to?(:last) && Array === @choices.first.last
end
diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb
index 0fea4df09c..07dccf5b41 100644
--- a/actionview/lib/action_view/helpers/text_helper.rb
+++ b/actionview/lib/action_view/helpers/text_helper.rb
@@ -151,7 +151,7 @@ module ActionView
# defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
# then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. Use the
# <tt>:separator</tt> option to choose the delimitation. The resulting string will be stripped in any case. If the +phrase+
- # isn't found, nil is returned.
+ # isn't found, +nil+ is returned.
#
# excerpt('This is an example', 'an', radius: 5)
# # => ...s is an exam...
@@ -225,14 +225,7 @@ module ActionView
#
# pluralize(2, 'Person', locale: :de)
# # => 2 Personen
- def pluralize(count, singular, deprecated_plural = nil, plural: nil, locale: I18n.locale)
- if deprecated_plural
- ActiveSupport::Deprecation.warn("Passing plural as a positional argument " \
- "is deprecated and will be removed in Rails 5.1. Use e.g. " \
- "pluralize(1, 'person', plural: 'people') instead.")
- plural ||= deprecated_plural
- end
-
+ def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
word = if (count == 1 || count =~ /^1(\.0+)?$/)
singular
else
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index cbabaf5757..47ed41a129 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -1,6 +1,5 @@
require "action_view/helpers/tag_helper"
require "active_support/core_ext/string/access"
-require "active_support/core_ext/regexp"
require "i18n/exceptions"
module ActionView
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index dad0e9dac3..22cc4b2920 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -2,7 +2,6 @@ require "action_view/helpers/javascript_helper"
require "active_support/core_ext/array/access"
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/string/output_safety"
-require "active_support/core_ext/regexp"
module ActionView
# = Action View URL Helpers
@@ -106,10 +105,9 @@ module ActionView
# driver to prompt with the question specified (in this case, the
# resulting text would be <tt>question?</tt>. If the user accepts, the
# link is processed normally, otherwise no action is taken.
- # * <tt>:disable_with</tt> - Value of this parameter will be
- # used as the value for a disabled version of the submit
- # button when the form is submitted. This feature is provided
- # by the unobtrusive JavaScript driver.
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the
+ # name for a disabled version of the link. This feature is provided by
+ # the unobtrusive JavaScript driver.
#
# ==== Examples
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
@@ -564,7 +562,7 @@ module ActionView
html_options = html_options.stringify_keys
html_options["data-remote"] = "true".freeze if link_to_remote_options?(options) || link_to_remote_options?(html_options)
- method = html_options.delete("method".freeze)
+ method = html_options.delete("method".freeze)
add_method_to_attributes!(html_options, method) if method
@@ -587,7 +585,7 @@ module ActionView
html_options["data-method".freeze] = method
end
- def token_tag(token=nil, form_options: {})
+ def token_tag(token = nil, form_options: {})
if token != false && protect_against_forgery?
token ||= form_authenticity_token(form_options: form_options)
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
@@ -617,6 +615,17 @@ module ActionView
# to_form_params({ name: 'Denmark' }, 'country')
# # => [{name: 'country[name]', value: 'Denmark'}]
def to_form_params(attribute, namespace = nil) # :nodoc:
+ attribute = if attribute.respond_to?(:permitted?)
+ unless attribute.permitted?
+ raise ArgumentError, "Attempting to generate a buttom from non-sanitized request parameters!" \
+ " Whitelist and sanitize passed parameters to be secure."
+ end
+
+ attribute.to_h
+ else
+ attribute
+ end
+
params = []
case attribute
when Hash