aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_view/helpers')
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb5
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb36
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb242
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb14
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb28
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb5
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/tags/base.rb10
-rw-r--r--actionpack/lib/action_view/helpers/tags/check_box.rb10
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_helpers.rb1
-rw-r--r--actionpack/lib/action_view/helpers/tags/label.rb12
-rw-r--r--actionpack/lib/action_view/helpers/tags/text_area.rb6
-rw-r--r--actionpack/lib/action_view/helpers/tags/text_field.rb3
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb30
17 files changed, 274 insertions, 149 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 662adbe183..2f8b28c82b 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -24,9 +24,10 @@ module ActionView
# server by setting ActionController::Base.asset_host in the application
# configuration, typically in <tt>config/environments/production.rb</tt>.
# For example, you'd define <tt>assets.example.com</tt> to be your asset
- # host this way:
+ # host this way, inside the <tt>configure</tt> block of your environment-specific
+ # configuration files or <tt>config/application.rb</tt>:
#
- # ActionController::Base.asset_host = "assets.example.com"
+ # config.action_controller.asset_host = "assets.example.com"
#
# Helpers take that into account:
#
@@ -384,7 +385,7 @@ module ActionView
# image_tag("mouse.png", :mouseover => image_path("mouse_over.png")) # =>
# <img src="/images/mouse.png" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" alt="Mouse" />
def image_tag(source, options={})
- options = options.dup.symbolize_keys!
+ options = options.symbolize_keys
src = options[:src] = path_to_image(source)
@@ -477,7 +478,7 @@ module ActionView
end
def multiple_sources_tag(type, sources)
- options = sources.extract_options!.dup.symbolize_keys!
+ options = sources.extract_options!.symbolize_keys
sources.flatten!
yield options if block_given?
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index 73824dc1f8..f9aa8d7cee 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -176,6 +176,7 @@ module ActionView
# * <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. 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 = {})
@xml.entry do
@xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
@@ -188,7 +189,9 @@ module ActionView
@xml.updated((options[:updated] || record.updated_at).xmlschema)
end
- @xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:url] || @view.polymorphic_url(record))
+ type = options.fetch(:type, 'text/html')
+
+ @xml.link(:rel => 'alternate', :type => type, :href => options[:url] || @view.polymorphic_url(record))
yield AtomBuilder.new(@xml)
end
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 17bbfe2efd..278139cadb 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -215,7 +215,7 @@ module ActionView
def flush_output_buffer #:nodoc:
if output_buffer && !output_buffer.empty?
response.body_parts << output_buffer
- self.output_buffer = output_buffer[0,0]
+ self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0]
nil
end
end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 2d37923825..cb46f26d99 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -625,13 +625,18 @@ module ActionView
# time_tag Date.today, :pubdate => true # =>
# <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
#
- def time_tag(date_or_time, *args)
+ # <%= time_tag Time.now do %>
+ # <span>Right now</span>
+ # <% end %>
+ # # => <time datetime="2010-11-04T17:55:45+01:00"><span>Right now</span></time>
+ #
+ def time_tag(date_or_time, *args, &block)
options = args.extract_options!
format = options.delete(:format) || :long
content = args.first || I18n.l(date_or_time, :format => format)
datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.rfc3339
- content_tag(:time, content, options.reverse_merge(:datetime => datetime))
+ content_tag(:time, content, options.reverse_merge(:datetime => datetime), &block)
end
end
@@ -669,11 +674,7 @@ module ActionView
@options[:discard_minute] ||= true if @options[:discard_hour]
@options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
- # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
- # valid (otherwise it could be 31 and February wouldn't be a valid date)
- if @datetime && @options[:discard_day] && !@options[:discard_month]
- @datetime = @datetime.change(:day => 1)
- end
+ set_day_if_discarded
if @options[:tag] && @options[:ignore_date]
select_time
@@ -696,11 +697,7 @@ module ActionView
@options[:discard_month] ||= true unless order.include?(:month)
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
- # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
- # valid (otherwise it could be 31 and February wouldn't be a valid date)
- if @datetime && @options[:discard_day] && !@options[:discard_month]
- @datetime = @datetime.change(:day => 1)
- end
+ set_day_if_discarded
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
@@ -798,7 +795,15 @@ module ActionView
private
%w( sec min hour day month year ).each do |method|
define_method(method) do
- @datetime.kind_of?(Fixnum) ? @datetime : @datetime.send(method) if @datetime
+ @datetime.kind_of?(Numeric) ? @datetime : @datetime.send(method) if @datetime
+ end
+ end
+
+ # If the day is hidden, the day should be set to the 1st so all month and year choices are
+ # valid. Otherwise, February 31st or February 29th, 2011 can be selected, which are invalid.
+ def set_day_if_discarded
+ if @datetime && @options[:discard_day]
+ @datetime = @datetime.change(:day => 1)
end
end
@@ -972,7 +977,10 @@ module ActionView
# Returns the id attribute for the input tag.
# => "post_written_on_1i"
def input_id_from_type(type)
- input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
+ id = input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
+ id = @options[:namespace] + '_' + id if @options[:namespace]
+
+ id
end
# Given an ordering of datetime components, create the selection HTML
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 53ae8b66da..6219a7a924 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -9,6 +9,7 @@ require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/deprecation'
module ActionView
# = Action View Form Helpers
@@ -16,17 +17,28 @@ module ActionView
# Form helpers are designed to make working with resources much easier
# compared to using vanilla HTML.
#
- # Forms for models are created with +form_for+. That method yields a form
- # builder that knows the model the form is about. The form builder is thus
- # able to generate default values for input fields that correspond to model
- # attributes, and also convenient names, IDs, endpoints, etc.
+ # Typically, a form designed to create or update a resource reflects the
+ # identity of the resource in several ways: (i) the url that the form is
+ # sent to (the form element's +action+ attribute) should result in a request
+ # being routed to the appropriate controller action (with the appropriate <tt>:id</tt>
+ # parameter in the case of an existing resource), (ii) input fields should
+ # be named in such a way that in the controller their values appear in the
+ # appropriate places within the +params+ hash, and (iii) for an existing record,
+ # when the form is initially displayed, input fields corresponding to attributes
+ # of the resource should show the current values of those attributes.
#
- # Conventions in the generated field names allow controllers to receive form
- # data nicely structured in +params+ with no effort on your side.
+ # In Rails, this is usually achieved by creating the form using +form_for+ and
+ # a number of related helper methods. +form_for+ generates an appropriate <tt>form</tt>
+ # tag and yields a form builder object that knows the model the form is about.
+ # Input fields are created by calling methods defined on the form builder, which
+ # means they are able to generate the appropriate names and default values
+ # corresponding to the model attributes, as well as convenient IDs, etc.
+ # Conventions in the generated field names allow controllers to receive form data
+ # nicely structured in +params+ with no effort on your side.
#
# For example, to create a new person you typically set up a new instance of
# +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
- # pass it to +form_for+:
+ # in the view template pass that object to +form_for+:
#
# <%= form_for @person do |f| %>
# <%= f.label :first_name %>:
@@ -45,10 +57,10 @@ module ActionView
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
# </div>
# <label for="person_first_name">First name</label>:
- # <input id="person_first_name" name="person[first_name]" size="30" type="text" /><br />
+ # <input id="person_first_name" name="person[first_name]" type="text" /><br />
#
# <label for="person_last_name">Last name</label>:
- # <input id="person_last_name" name="person[last_name]" size="30" type="text" /><br />
+ # <input id="person_last_name" name="person[last_name]" type="text" /><br />
#
# <input name="commit" type="submit" value="Create Person" />
# </form>
@@ -76,10 +88,10 @@ module ActionView
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
# </div>
# <label for="person_first_name">First name</label>:
- # <input id="person_first_name" name="person[first_name]" size="30" type="text" value="John" /><br />
+ # <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br />
#
# <label for="person_last_name">Last name</label>:
- # <input id="person_last_name" name="person[last_name]" size="30" type="text" value="Smith" /><br />
+ # <input id="person_last_name" name="person[last_name]" type="text" value="Smith" /><br />
#
# <input name="commit" type="submit" value="Update Person" />
# </form>
@@ -109,29 +121,14 @@ module ActionView
object.respond_to?(:to_model) ? object.to_model : object
end
- # Creates a form and a scope around a specific model object that is used
- # as a base for questioning about values for the fields.
+ # Creates a form that allows the user to create or update the attributes
+ # of a specific model object.
#
- # Rails provides succinct resource-oriented form generation with +form_for+
- # like this:
- #
- # <%= form_for @offer do |f| %>
- # <%= f.label :version, 'Version' %>:
- # <%= f.text_field :version %><br />
- # <%= f.label :author, 'Author' %>:
- # <%= f.text_field :author %><br />
- # <%= f.submit %>
- # <% end %>
- #
- # There, +form_for+ is able to generate the rest of RESTful form
- # parameters based on introspection on the record, but to understand what
- # it does we need to dig first into the alternative generic usage it is
- # based upon.
- #
- # === Generic form_for
- #
- # The generic way to call +form_for+ yields a form builder around a
- # model:
+ # The method can be used in several slightly different ways, depending on
+ # how much you wish to rely on Rails to infer automatically from the model
+ # how the form should be constructed. For a generic model object, a form
+ # can be created by passing +form_for+ a string or symbol representing
+ # the object we are concerned with:
#
# <%= form_for :person do |f| %>
# First name: <%= f.text_field :first_name %><br />
@@ -141,24 +138,39 @@ module ActionView
# <%= f.submit %>
# <% end %>
#
- # There, the argument is a symbol or string with the name of the
- # object the form is about.
- #
- # The form builder acts as a regular form helper that somehow carries the
- # model. Thus, the idea is that
+ # The variable +f+ yielded to the block is a FormBuilder object that
+ # incorporates the knowledge about the model object represented by
+ # <tt>:person</tt> passed to +form_for+. Methods defined on the FormBuilder
+ # are used to generate fields bound to this model. Thus, for example,
#
# <%= f.text_field :first_name %>
#
- # gets expanded to
+ # will get expanded to
#
# <%= text_field :person, :first_name %>
+ # which results in an html <tt><input></tt> tag whose +name+ attribute is
+ # <tt>person[first_name]</tt>. This means that when the form is submitted,
+ # the value entered by the user will be available in the controller as
+ # <tt>params[:person][:first_name]</tt>.
+ #
+ # For fields generated in this way using the FormBuilder,
+ # if <tt>:person</tt> also happens to be the name of an instance variable
+ # <tt>@person</tt>, the default value of the field shown when the form is
+ # initially displayed (e.g. in the situation where you are editing an
+ # existing record) will be the value of the corresponding attribute of
+ # <tt>@person</tt>.
#
# The rightmost argument to +form_for+ is an
- # optional hash of options:
- #
- # * <tt>:url</tt> - The URL the form is submitted to. It takes the same
- # fields you pass to +url_for+ or +link_to+. In particular you may pass
- # here a named route directly as well. Defaults to the current action.
+ # optional hash of options -
+ #
+ # * <tt>:url</tt> - The URL the form is to be submitted to. This may be
+ # represented in the same way as values passed to +url_for+ or +link_to+.
+ # So for example you may use a named route directly. When the model is
+ # represented by a string or symbol, as in the example above, if the
+ # <tt>:url</tt> option is not specified, by default the form will be
+ # sent back to the current url (We will describe below an alternative
+ # resource-oriented usage of +form_for+ in which the URL does not need
+ # to be specified explicitly).
# * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
# id attributes on form elements. The namespace attribute will be prefixed
# with underscore on the generated HTML id.
@@ -168,7 +180,7 @@ module ActionView
# possible to use both the stand-alone FormHelper methods and methods
# from FormTagHelper. For example:
#
- # <%= form_for @person do |f| %>
+ # <%= form_for :person do |f| %>
# First name: <%= f.text_field :first_name %>
# Last name : <%= f.text_field :last_name %>
# Biography : <%= text_area :person, :biography %>
@@ -180,26 +192,65 @@ module ActionView
# are designed to work with an object as base, like
# FormOptionHelper#collection_select and DateHelper#datetime_select.
#
- # === Resource-oriented style
+ # === #form_for with a model object
+ #
+ # In the examples above, the object to be created or edited was
+ # represented by a symbol passed to +form_for+, and we noted that
+ # a string can also be used equivalently. It is also possible, however,
+ # to pass a model object itself to +form_for+. For example, if <tt>@post</tt>
+ # is an existing record you wish to edit, you can create the form using
+ #
+ # <%= form_for @post do |f| %>
+ # ...
+ # <% end %>
+ #
+ # This behaves in almost the same way as outlined previously, with a
+ # couple of small exceptions. First, the prefix used to name the input
+ # elements within the form (hence the key that denotes them in the +params+
+ # hash) is actually derived from the object's _class_, e.g. <tt>params[:post]</tt>
+ # if the object's class is +Post+. However, this can be overwritten using
+ # the <tt>:as</tt> option, e.g. -
+ #
+ # <%= form_for(@person, :as => :client) do |f| %>
+ # ...
+ # <% end %>
#
- # As we said above, in addition to manually configuring the +form_for+
- # call, you can rely on automated resource identification, which will use
- # the conventions and named routes of that approach. This is the
- # preferred way to use +form_for+ nowadays.
+ # would result in <tt>params[:client]</tt>.
#
- # For example, if <tt>@post</tt> is an existing record you want to edit
+ # Secondly, the field values shown when the form is initially displayed
+ # are taken from the attributes of the object passed to +form_for+,
+ # regardless of whether the object is an instance
+ # variable. So, for example, if we had a _local_ variable +post+
+ # representing an existing record,
+ #
+ # <%= form_for post do |f| %>
+ # ...
+ # <% end %>
+ #
+ # would produce a form with fields whose initial state reflect the current
+ # values of the attributes of +post+.
+ #
+ # === Resource-oriented style
+ #
+ # In the examples just shown, although not indicated explicitly, we still
+ # need to use the <tt>:url</tt> option in order to specify where the
+ # form is going to be sent. However, further simplification is possible
+ # if the record passed to +form_for+ is a _resource_, i.e. it corresponds
+ # to a set of RESTful routes, e.g. defined using the +resources+ method
+ # in <tt>config/routes.rb</tt>. In this case Rails will simply infer the
+ # appropriate URL from the record itself. For example,
#
# <%= form_for @post do |f| %>
# ...
# <% end %>
#
- # is equivalent to something like:
+ # is then equivalent to something like:
#
# <%= form_for @post, :as => :post, :url => post_path(@post), :method => :put, :html => { :class => "edit_post", :id => "edit_post_45" } do |f| %>
# ...
# <% end %>
#
- # And for new records
+ # And for a new record
#
# <%= form_for(Post.new) do |f| %>
# ...
@@ -211,7 +262,7 @@ module ActionView
# ...
# <% end %>
#
- # You can also overwrite the individual conventions, like this:
+ # However you can still overwrite individual conventions, such as:
#
# <%= form_for(@post, :url => super_posts_path) do |f| %>
# ...
@@ -223,13 +274,6 @@ module ActionView
# ...
# <% end %>
#
- # If you have an object that needs to be represented as a different
- # parameter, like a Person that acts as a Client:
- #
- # <%= form_for(@person, :as => :client) do |f| %>
- # ...
- # <% end %>
- #
# For namespaced routes, like +admin_post_url+:
#
# <%= form_for([:admin, @post]) do |f| %>
@@ -252,9 +296,9 @@ module ActionView
#
# :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.
+ # 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.
#
# === Unobtrusive JavaScript
#
@@ -402,30 +446,59 @@ module ActionView
#
# === Generic Examples
#
+ # Although the usage and purpose of +field_for+ is similar to +form_for+'s,
+ # its method signature is slightly different. Like +form_for+, it yields
+ # a FormBuilder object associated with a particular model object to a block,
+ # and within the block allows methods to be called on the builder to
+ # generate fields associated with the model object. Fields may reflect
+ # a model object in two ways - how they are named (hence how submitted
+ # values appear within the +params+ hash in the controller) and what
+ # default values are shown when the form the fields appear in is first
+ # displayed. In order for both of these features to be specified independently,
+ # both an object name (represented by either a symbol or string) and the
+ # object itself can be passed to the method separately -
+ #
# <%= form_for @person do |person_form| %>
# First name: <%= person_form.text_field :first_name %>
# Last name : <%= person_form.text_field :last_name %>
#
- # <%= fields_for @person.permission do |permission_fields| %>
+ # <%= fields_for :permission, @person.permission do |permission_fields| %>
# Admin? : <%= permission_fields.check_box :admin %>
# <% end %>
#
# <%= f.submit %>
# <% end %>
#
- # ...or if you have an object that needs to be represented as a different
- # parameter, like a Client that acts as a Person:
+ # In this case, the checkbox field will be represented by an HTML +input+
+ # tag with the +name+ attribute <tt>permission[admin]</tt>, and the submitted
+ # value will appear in the controller as <tt>params[:permission][:admin]</tt>.
+ # If <tt>@person.permission</tt> is an existing record with an attribute
+ # +admin+, the initial state of the checkbox when first displayed will
+ # reflect the value of <tt>@person.permission.admin</tt>.
+ #
+ # Often this can be simplified by passing just the name of the model
+ # object to +fields_for+ -
#
- # <%= fields_for :person, @client do |permission_fields| %>
+ # <%= fields_for :permission do |permission_fields| %>
# Admin?: <%= permission_fields.check_box :admin %>
# <% end %>
#
- # ...or if you don't have an object, just a name of the parameter:
+ # ...in which case, if <tt>:permission</tt> also happens to be the name of an
+ # instance variable <tt>@permission</tt>, the initial state of the input
+ # field will reflect the value of that variable's attribute <tt>@permission.admin</tt>.
#
- # <%= fields_for :person do |permission_fields| %>
+ # Alternatively, you can pass just the model object itself (if the first
+ # argument isn't a string or symbol +fields_for+ will realize that the
+ # name has been omitted) -
+ #
+ # <%= fields_for @person.permission do |permission_fields| %>
# Admin?: <%= permission_fields.check_box :admin %>
# <% end %>
#
+ # and +fields_for+ will derive the required name of the field from the
+ # _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
+ # of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
+ #
# Note: This also works for the methods in FormOptionHelper and
# DateHelper that are designed to work with an object as base, like
# FormOptionHelper#collection_select and DateHelper#datetime_select.
@@ -860,20 +933,20 @@ module ActionView
# ==== Examples
#
# search_field(:user, :name)
- # # => <input id="user_name" name="user[name]" size="30" type="search" />
+ # # => <input id="user_name" name="user[name]" type="search" />
# search_field(:user, :name, :autosave => false)
- # # => <input autosave="false" id="user_name" name="user[name]" size="30" type="search" />
+ # # => <input autosave="false" id="user_name" name="user[name]" type="search" />
# search_field(:user, :name, :results => 3)
- # # => <input id="user_name" name="user[name]" results="3" size="30" type="search" />
+ # # => <input id="user_name" name="user[name]" results="3" type="search" />
# # Assume request.host returns "www.example.com"
# search_field(:user, :name, :autosave => true)
- # # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" size="30" type="search" />
+ # # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" type="search" />
# search_field(:user, :name, :onsearch => true)
- # # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" size="30" type="search" />
+ # # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
# search_field(:user, :name, :autosave => false, :onsearch => true)
- # # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" size="30" type="search" />
+ # # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
# search_field(:user, :name, :autosave => true, :onsearch => true)
- # # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" size="30" type="search" />
+ # # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" type="search" />
#
def search_field(object_name, method, options = {})
Tags::SearchField.new(object_name, method, self, options).render
@@ -882,7 +955,7 @@ module ActionView
# Returns a text_field of type "tel".
#
# telephone_field("user", "phone")
- # # => <input id="user_phone" name="user[phone]" size="30" type="tel" />
+ # # => <input id="user_phone" name="user[phone]" type="tel" />
#
def telephone_field(object_name, method, options = {})
Tags::TelField.new(object_name, method, self, options).render
@@ -910,7 +983,7 @@ module ActionView
# Returns a text_field of type "url".
#
# url_field("user", "homepage")
- # # => <input id="user_homepage" size="30" name="user[homepage]" type="url" />
+ # # => <input id="user_homepage" name="user[homepage]" type="url" />
#
def url_field(object_name, method, options = {})
Tags::UrlField.new(object_name, method, self, options).render
@@ -919,7 +992,7 @@ module ActionView
# Returns a text_field of type "email".
#
# email_field("user", "address")
- # # => <input id="user_address" size="30" name="user[address]" type="email" />
+ # # => <input id="user_address" name="user[address]" type="email" />
#
def email_field(object_name, method, options = {})
Tags::EmailField.new(object_name, method, self, options).render
@@ -985,7 +1058,12 @@ module ActionView
self
end
- def initialize(object_name, object, template, options)
+ def initialize(object_name, object, template, options, block=nil)
+ if block
+ ActiveSupport::Deprecation.warn(
+ "Giving a block to FormBuilder is deprecated and has no effect anymore.")
+ end
+
@nested_child_index = {}
@object_name, @object, @template, @options = object_name, object, template, options
@parent_builder = options[:parent_builder]
@@ -1016,7 +1094,7 @@ module ActionView
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
fields_options[:builder] ||= options[:builder]
fields_options[:parent_builder] = self
- fields_options[:namespace] = fields_options[:parent_builder].options[:namespace]
+ fields_options[:namespace] = options[:namespace]
case record_name
when String, Symbol
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index f73ca220fb..d61c2bbee2 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -153,6 +153,8 @@ module ActionView
# form, and parameters extraction gets the last occurrence of any repeated
# key in the query string, that works for ordinary forms.
#
+ # In case if you don't want the helper to generate this hidden field you can specify <tt>:include_blank => false</tt> option.
+ #
def select(object, method, choices, options = {}, html_options = {})
Tags::Select.new(object, method, self, choices, options, html_options).render
end
@@ -578,9 +580,9 @@ module ActionView
# b.label(:class => "radio_button") { b.radio_button(:class => "radio_button") }
# end
#
- # There are also two special methods available: <tt>text</tt> and
- # <tt>value</tt>, which are the current text and value methods for the
- # item being rendered, respectively. You can use them like this:
+ # There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
+ # <tt>value</tt>, which are the current item being rendered, its text and value methods,
+ # respectively. You can use them like this:
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
# b.label(:"data-value" => b.value) { b.radio_button + b.text }
# end
@@ -641,9 +643,9 @@ module ActionView
# b.label(:class => "check_box") { b.check_box(:class => "check_box") }
# end
#
- # There are also two special methods available: <tt>text</tt> and
- # <tt>value</tt>, which are the current text and value methods for the
- # item being rendered, respectively. You can use them like this:
+ # There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
+ # <tt>value</tt>, which are the current item being rendered, its text and value methods,
+ # respectively. You can use them like this:
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
# b.label(:"data-value" => b.value) { b.check_box + b.text }
# end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 9c86120865..b5e0970612 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -2,6 +2,7 @@ require 'cgi'
require 'action_view/helpers/tag_helper'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'
+require 'active_support/core_ext/module/attribute_accessors'
module ActionView
# = Action View Form Tag Helpers
@@ -17,6 +18,9 @@ module ActionView
include UrlHelper
include TextHelper
+ mattr_accessor :embed_authenticity_token_in_remote_forms
+ self.embed_authenticity_token_in_remote_forms = false
+
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
# ActionController::Base#url_for. The method for the form defaults to POST.
#
@@ -27,7 +31,11 @@ module ActionView
# is added to simulate the verb over post.
# * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
# pass custom authenticity token string, or to not add authenticity_token field at all
- # (by passing <tt>false</tt>).
+ # (by passing <tt>false</tt>). 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 you're fragment-caching the form. Remote forms get the
+ # authenticity from the <tt>meta</tt> tag, so embedding is unnecessary unless you
+ # support browsers without JavaScript.
# * A list of parameters to feed to the URL the form will be posted to.
# * <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.
@@ -93,7 +101,7 @@ module ActionView
# # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
# # <option>Green</option><option>Blue</option></select>
#
- # select_tag "locations", "<option>Home</option><option selected="selected">Work</option><option>Out</option>".html_safe
+ # select_tag "locations", "<option>Home</option><option selected='selected'>Work</option><option>Out</option>".html_safe
# # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
# # <option>Out</option></select>
#
@@ -525,10 +533,9 @@ module ActionView
# <% end %>
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
def field_set_tag(legend = nil, options = nil, &block)
- content = capture(&block)
output = tag(:fieldset, options, true)
output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
- output.concat(content)
+ output.concat(capture(&block)) if block_given?
output.safe_concat("</fieldset>")
end
@@ -617,8 +624,19 @@ module ActionView
# 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 if html_options.delete("remote")
- html_options["authenticity_token"] = html_options.delete("authenticity_token") if html_options.has_key?("authenticity_token")
+
+ if html_options["data-remote"] &&
+ !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
end
end
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index ac9e530f01..d88f5babb9 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -14,6 +14,8 @@ module ActionView
}
JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '&#x2028;'
+ JS_ESCAPE_MAP["\342\200\251".force_encoding('UTF-8').encode!] = '&#x2029;'
+
# Escapes carriage returns and single and double quotes for JavaScript segments.
#
@@ -22,7 +24,7 @@ module ActionView
# $('some_element').replaceWith('<%=j render 'some/element_template' %>');
def escape_javascript(javascript)
if javascript
- result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
+ result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\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 b0860f87c4..fc895fb7bc 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -137,12 +137,12 @@ module ActionView
begin
value = number_with_precision(number, options.merge(:raise => true))
- format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
+ format.gsub('%n', value).gsub('%u', unit).html_safe
rescue InvalidNumberError => e
if options[:raise]
raise
else
- formatted_number = format.gsub(/%n/, e.number).gsub(/%u/, unit)
+ formatted_number = format.gsub('%n', e.number).gsub('%u', unit)
e.number.to_s.html_safe? ? formatted_number.html_safe : formatted_number
end
end
@@ -292,6 +292,7 @@ module ActionView
precision = precision > 0 ? precision : 0 #don't let it be negative
else
rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
+ rounded_number = rounded_number.zero? ? rounded_number.abs : rounded_number #prevent showing negative zeros
end
formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options)
if strip_insignificant_zeros
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index ecd26891d6..71be2955a8 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -17,6 +17,10 @@ module ActionView
autofocus novalidate formnovalidate open pubdate).to_set
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
+ PRE_CONTENT_STRINGS = {
+ :textarea => "\n"
+ }
+
# Returns an empty HTML tag of type +name+ which by default is XHTML
# compliant. Set +open+ to true to create an open tag compatible
# with HTML 4.0 and below. Add HTML attributes by passing an attributes
@@ -126,7 +130,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
- "<#{name}#{tag_options}>#{content}</#{name}>".html_safe
+ "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
end
def tag_options(options, escape = true)
diff --git a/actionpack/lib/action_view/helpers/tags/base.rb b/actionpack/lib/action_view/helpers/tags/base.rb
index c9c891daa1..7c2f12d8e7 100644
--- a/actionpack/lib/action_view/helpers/tags/base.rb
+++ b/actionpack/lib/action_view/helpers/tags/base.rb
@@ -5,8 +5,6 @@ module ActionView
include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
include FormOptionsHelper
- DEFAULT_FIELD_OPTIONS = { "size" => 30 }
-
attr_reader :object
def initialize(object_name, method_name, template_object, options = {})
@@ -124,7 +122,8 @@ module ActionView
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
- if html_options["multiple"]
+
+ if html_options["multiple"] && options.fetch(:include_hidden) { true }
tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
else
select
@@ -133,12 +132,11 @@ module ActionView
def add_options(option_tags, options, value = nil)
if options[:include_blank]
- include_blank = options[:include_blank] if options[:include_blank].kind_of?(String)
- option_tags = content_tag(:option, include_blank, :value => '').safe_concat("\n").safe_concat(option_tags)
+ option_tags = content_tag('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
end
if value.blank? && options[:prompt]
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
- option_tags = content_tag(:option, prompt, :value => '').safe_concat("\n").safe_concat(option_tags)
+ option_tags = content_tag('option', prompt, :value => '') + "\n" + option_tags
end
option_tags
end
diff --git a/actionpack/lib/action_view/helpers/tags/check_box.rb b/actionpack/lib/action_view/helpers/tags/check_box.rb
index 579cdb9fc9..1a4aebb936 100644
--- a/actionpack/lib/action_view/helpers/tags/check_box.rb
+++ b/actionpack/lib/action_view/helpers/tags/check_box.rb
@@ -25,9 +25,15 @@ module ActionView
add_default_name_and_id(options)
end
- hidden = hidden_field_for_checkbox(options)
+ include_hidden = options.delete("include_hidden") { true }
checkbox = tag("input", options)
- hidden + checkbox
+
+ if include_hidden
+ hidden = hidden_field_for_checkbox(options)
+ hidden + checkbox
+ else
+ checkbox
+ end
end
private
diff --git a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
index 6f950e552a..6a1479069f 100644
--- a/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
+++ b/actionpack/lib/action_view/helpers/tags/collection_helpers.rb
@@ -59,6 +59,7 @@ module ActionView
end
end
+ html_options[:object] = @object
html_options
end
diff --git a/actionpack/lib/action_view/helpers/tags/label.rb b/actionpack/lib/action_view/helpers/tags/label.rb
index 1bd71c2778..16135fcd5a 100644
--- a/actionpack/lib/action_view/helpers/tags/label.rb
+++ b/actionpack/lib/action_view/helpers/tags/label.rb
@@ -3,16 +3,16 @@ module ActionView
module Tags
class Label < Base #:nodoc:
def initialize(object_name, method_name, template_object, content_or_options = nil, options = nil)
+ options ||= {}
+
content_is_options = content_or_options.is_a?(Hash)
if content_is_options
- options = content_or_options
+ options.merge! content_or_options
@content = nil
else
@content = content_or_options
end
- options ||= {}
-
super(object_name, method_name, template_object, options)
end
@@ -33,7 +33,7 @@ module ActionView
options["for"] = name_and_id["id"] unless options.key?("for")
if block_given?
- @template_object.label_tag(name_and_id["id"], options, &block)
+ content = @template_object.capture(&block)
else
content = if @content.blank?
@object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1')
@@ -55,9 +55,9 @@ module ActionView
end
content ||= @method_name.humanize
-
- label_tag(name_and_id["id"], content, options)
end
+
+ label_tag(name_and_id["id"], content, options)
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/text_area.rb b/actionpack/lib/action_view/helpers/tags/text_area.rb
index a7db8eb437..f74652c5e7 100644
--- a/actionpack/lib/action_view/helpers/tags/text_area.rb
+++ b/actionpack/lib/action_view/helpers/tags/text_area.rb
@@ -2,17 +2,15 @@ module ActionView
module Helpers
module Tags
class TextArea < Base #:nodoc:
- DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }
-
def render
- options = DEFAULT_TEXT_AREA_OPTIONS.merge(@options.stringify_keys)
+ options = @options.stringify_keys
add_default_name_and_id(options)
if size = options.delete("size")
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
end
- content_tag("textarea", ERB::Util.html_escape(options.delete('value') || value_before_type_cast(object)), options)
+ content_tag("textarea", options.delete('value') || value_before_type_cast(object), options)
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tags/text_field.rb b/actionpack/lib/action_view/helpers/tags/text_field.rb
index ce5182d20f..024a1a8af2 100644
--- a/actionpack/lib/action_view/helpers/tags/text_field.rb
+++ b/actionpack/lib/action_view/helpers/tags/text_field.rb
@@ -4,8 +4,7 @@ module ActionView
class TextField < Base #:nodoc:
def render
options = @options.stringify_keys
- options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size")
- options = DEFAULT_FIELD_OPTIONS.merge(options)
+ 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"])
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 8d7417809b..4a641fada3 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -23,20 +23,25 @@ module ActionView
include ActionDispatch::Routing::UrlFor
include TagHelper
- def _routes_context
- controller
- end
+ # We need to override url_optoins, _routes_context
+ # and optimize_routes_generation? to consider the controller.
- # Need to map default url options to controller one.
- # def default_url_options(*args) #:nodoc:
- # controller.send(:default_url_options, *args)
- # end
- #
- def url_options
+ def url_options #:nodoc:
return super unless controller.respond_to?(:url_options)
controller.url_options
end
+ def _routes_context #:nodoc:
+ controller
+ end
+ protected :_routes_context
+
+ def optimize_routes_generation? #:nodoc:
+ controller.respond_to?(:optimize_routes_generation?) ?
+ controller.optimize_routes_generation? : super
+ end
+ protected :optimize_routes_generation?
+
# Returns the URL for the set of +options+ provided. This takes the
# same options as +url_for+ in Action Controller (see the
# documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
@@ -55,7 +60,7 @@ module ActionView
#
# ==== Relying on named routes
#
- # Passing a record (like an Active Record or Active Resource) instead of a Hash as the options parameter will
+ # Passing a record (like an Active Record) instead of a Hash as the options parameter will
# trigger the named route for that record. The lookup will happen on the name of the class. So passing a
# Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
# +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
@@ -329,7 +334,7 @@ module ActionView
remote = html_options.delete('remote')
method = html_options.delete('method').to_s
- method_tag = %w{patch put delete}.include?(method) ? method_tag(method) : ""
+ method_tag = %w{patch put delete}.include?(method) ? method_tag(method) : ''.html_safe
form_method = method == 'get' ? 'get' : 'post'
form_options = html_options.delete('form') || {}
@@ -342,7 +347,8 @@ module ActionView
html_options = convert_options_to_data_attributes(options, html_options)
html_options.merge!("type" => "submit", "value" => name || url)
- "#{tag(:form, form_options, true)}<div>#{method_tag}#{tag("input", html_options)}#{request_token_tag}</div></form>".html_safe
+ inner_tags = method_tag.safe_concat tag('input', html_options).safe_concat request_token_tag
+ content_tag('form', content_tag('div', inner_tags), form_options)
end