2004-11-23 20:04:44 -05:00
require 'cgi'
2007-01-28 02:16:55 -05:00
require 'action_view/helpers/date_helper'
require 'action_view/helpers/tag_helper'
2008-04-30 18:14:28 -04:00
require 'action_view/helpers/form_tag_helper'
2004-11-23 20:04:44 -05:00
module ActionView
module Helpers
2007-11-06 00:56:02 -05:00
# Form helpers are designed to make working with models much easier compared to using just standard HTML
# elements by providing a set of methods for creating forms based on your models. This helper generates the HTML
# for forms, providing a method for each sort of input (e.g., text, password, select, and so on). When the form
# is submitted (i.e., when the user hits the submit button or <tt>form.submit</tt> is called via JavaScript), the form inputs will be bundled into the <tt>params</tt> object and passed back to the controller.
2007-02-25 15:19:24 -05:00
#
2007-11-06 00:56:02 -05:00
# There are two types of form helpers: those that specifically work with model attributes and those that don't.
2007-05-06 01:23:03 -04:00
# This helper deals with those that work with model attributes; to see an example of form helpers that don't work
# with model attributes, check the ActionView::Helpers::FormTagHelper documentation.
2007-02-25 15:19:24 -05:00
#
2007-11-06 00:56:02 -05:00
# The core method of this helper, form_for, gives you the ability to create a form for a model instance;
2007-05-06 01:23:03 -04:00
# for example, let's say that you have a model <tt>Person</tt> and want to create a new instance of it:
2004-11-23 20:04:44 -05:00
#
2007-11-06 00:56:02 -05:00
# # Note: a @person variable will have been created in the controller.
2007-05-06 01:23:03 -04:00
# # For example: @person = Person.new
# <% form_for :person, @person, :url => { :action => "create" } do |f| %>
2007-02-25 15:19:24 -05:00
# <%= f.text_field :first_name %>
# <%= f.text_field :last_name %>
2007-05-06 01:23:03 -04:00
# <%= submit_tag 'Create' %>
2007-02-25 15:19:24 -05:00
# <% end %>
2004-11-23 20:04:44 -05:00
#
2007-05-06 01:23:03 -04:00
# The HTML generated for this would be:
2004-11-23 20:04:44 -05:00
#
2007-05-06 01:23:03 -04:00
# <form action="/persons/create" method="post">
2007-02-25 15:19:24 -05:00
# <input id="person_first_name" name="person[first_name]" size="30" type="text" />
# <input id="person_last_name" name="person[last_name]" size="30" type="text" />
2007-05-06 01:23:03 -04:00
# <input name="commit" type="submit" value="Create" />
2007-02-25 15:19:24 -05:00
# </form>
2004-11-23 20:04:44 -05:00
#
2008-01-15 21:01:57 -05:00
# If you are using a partial for your form fields, you can use this shortcut:
#
# <% form_for :person, @person, :url => { :action => "create" } do |f| %>
# <%= render :partial => f %>
# <%= submit_tag 'Create' %>
# <% end %>
#
# This example will render the <tt>people/_form</tt> partial, setting a local variable called <tt>form</tt> which references the yielded FormBuilder.
#
2007-05-06 01:23:03 -04:00
# The <tt>params</tt> object created when this form is submitted would look like:
2004-11-23 20:04:44 -05:00
#
2007-05-06 01:23:03 -04:00
# {"action"=>"create", "controller"=>"persons", "person"=>{"first_name"=>"William", "last_name"=>"Smith"}}
2007-11-06 00:56:02 -05:00
#
2007-05-06 01:23:03 -04:00
# The params hash has a nested <tt>person</tt> value, which can therefore be accessed with <tt>params[:person]</tt> in the controller.
# If were editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than <tt>Person.new</tt> in the controller), the objects
# attribute values are filled into the form (e.g., the <tt>person_first_name</tt> field would have that person's first name in it).
2007-11-06 00:56:02 -05:00
#
2007-05-06 01:23:03 -04:00
# If the object name contains square brackets the id for the object will be inserted. For example:
2005-03-06 08:57:16 -05:00
#
2008-04-30 18:14:28 -04:00
# <%= text_field "person[]", "name" %>
2007-11-06 00:56:02 -05:00
#
2007-05-06 01:23:03 -04:00
# ...will generate the following ERb.
2005-03-06 08:57:16 -05:00
#
# <input type="text" id="person_<%= @person.id %>_name" name="person[<%= @person.id %>][name]" value="<%= @person.name %>" />
#
2005-01-10 18:57:36 -05:00
# If the helper is being used to generate a repetitive sequence of similar form elements, for example in a partial
2007-05-06 01:23:03 -04:00
# used by <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may come in handy. Example:
2005-01-10 18:57:36 -05:00
#
# <%= text_field "person", "name", "index" => 1 %>
#
2007-05-06 01:23:03 -04:00
# ...becomes...
2005-03-06 06:50:41 -05:00
#
2005-01-10 18:57:36 -05:00
# <input type="text" id="person_1_name" name="person[1][name]" value="<%= @person.name %>" />
#
2008-02-02 01:16:04 -05:00
# An <tt>index</tt> option may also be passed to <tt>form_for</tt> and <tt>fields_for</tt>. This automatically applies
# the <tt>index</tt> to all the nested fields.
#
2007-02-25 15:19:24 -05:00
# There are also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
2004-11-23 20:04:44 -05:00
# link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html
module FormHelper
2008-05-16 18:01:32 -04:00
# Creates a form and a scope around a specific model object that is used as
# a base for questioning about values for the fields.
2005-11-13 06:13:11 -05:00
#
2008-07-16 08:00:36 -04:00
# Rails provides succinct resource-oriented form generation with +form_for+
2008-05-16 18:01:32 -04:00
# 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 />
2005-11-13 06:13:11 -05:00
# <% end %>
#
2008-05-25 07:29:00 -04:00
# There, +form_for+ is able to generate the rest of RESTful form parameters
2008-05-16 18:01:32 -04:00
# 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.
2005-11-13 06:13:11 -05:00
#
2008-05-16 18:01:32 -04:00
# === Generic form_for
#
2008-05-25 07:29:00 -04:00
# The generic way to call +form_for+ yields a form builder around a model:
2008-05-16 18:01:32 -04:00
#
2008-05-25 07:29:00 -04:00
# <% form_for :person, :url => { :action => "update" } do |f| %>
2008-05-16 18:01:32 -04:00
# <%= f.error_messages %>
# First name: <%= f.text_field :first_name %><br />
# Last name : <%= f.text_field :last_name %><br />
# Biography : <%= f.text_area :biography %><br />
# Admin? : <%= f.check_box :admin %><br />
# <% end %>
2005-11-13 06:13:11 -05:00
#
2008-05-25 07:29:00 -04:00
# There, the first argument is a symbol or string with the name of the
# object the form is about, and also the name of the instance variable the
# object is stored in.
#
# The form builder acts as a regular form helper that somehow carries the
# model. Thus, the idea is that
#
# <%= f.text_field :first_name %>
#
# gets expanded to
#
# <%= text_field :person, :first_name %>
#
# If the instance variable is not <tt>@person</tt> you can pass the actual
# record as the second argument:
#
# <% form_for :person, person, :url => { :action => "update" } do |f| %>
# ...
# <% end %>
#
# In that case you can think
#
# <%= f.text_field :first_name %>
#
# gets expanded to
#
# <%= text_field :person, :first_name, :object => person %>
#
# You can even display error messages of the wrapped model this way:
#
# <%= f.error_messages %>
#
# In any of its variants, 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.
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
#
2008-05-16 18:01:32 -04:00
# Worth noting is that the +form_for+ tag is called in a ERb evaluation block,
2008-05-25 07:29:00 -04:00
# not an ERb output block. So that's <tt><% %></tt>, not <tt><%= %></tt>.
2008-05-16 18:01:32 -04:00
#
# Also note that +form_for+ doesn't create an exclusive scope. It's still
# possible to use both the stand-alone FormHelper methods and methods from
# FormTagHelper. For example:
2005-11-13 06:13:11 -05:00
#
2005-12-01 19:37:33 -05:00
# <% form_for :person, @person, :url => { :action => "update" } do |f| %>
2005-11-13 06:13:11 -05:00
# First name: <%= f.text_field :first_name %>
# Last name : <%= f.text_field :last_name %>
# Biography : <%= text_area :person, :biography %>
# Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %>
# <% end %>
#
2008-05-16 18:01:32 -04:00
# 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.
2006-02-04 14:58:45 -05:00
#
2008-05-25 07:29:00 -04:00
# === Resource-oriented style
2007-05-14 13:30:35 -04:00
#
2008-05-16 18:01:32 -04:00
# As we said above, in addition to manually configuring the +form_for+ call,
2008-05-25 07:29:00 -04:00
# 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.
#
# For example, if <tt>@post</tt> is an existing record you want to edit
2007-05-14 13:30:35 -04:00
#
2008-05-25 07:29:00 -04:00
# <% form_for @post do |f| %>
2007-05-14 13:30:35 -04:00
# ...
# <% end %>
#
2008-05-25 07:29:00 -04:00
# is equivalent to something like:
2007-05-14 13:30:35 -04:00
#
# <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
# ...
# <% end %>
#
2008-05-25 07:29:00 -04:00
# And for new records
2007-05-14 13:30:35 -04:00
#
# <% form_for(Post.new) do |f| %>
# ...
# <% end %>
#
2008-05-25 07:29:00 -04:00
# expands to
2007-05-14 13:30:35 -04:00
#
2008-05-16 18:01:32 -04:00
# <% form_for :post, Post.new, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
2007-05-14 13:30:35 -04:00
# ...
# <% end %>
#
# You can also overwrite the individual conventions, like this:
#
# <% form_for(@post, :url => super_post_path(@post)) do |f| %>
# ...
# <% end %>
#
2008-05-16 18:01:32 -04:00
# And for namespaced routes, like +admin_post_url+:
2007-10-08 01:50:59 -04:00
#
# <% form_for([:admin, @post]) do |f| %>
# ...
# <% end %>
#
2007-05-14 13:30:35 -04:00
# === Customized form builders
#
2006-02-04 14:58:45 -05:00
# You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers,
2007-11-06 00:56:02 -05:00
# then use your custom builder. For example, let's say you made a helper to automatically add labels to form inputs.
#
2006-02-04 14:58:45 -05:00
# <% form_for :person, @person, :url => { :action => "update" }, :builder => LabellingFormBuilder do |f| %>
# <%= f.text_field :first_name %>
# <%= f.text_field :last_name %>
# <%= text_area :person, :biography %>
# <%= check_box_tag "person[admin]", @person.company.admin? %>
# <% end %>
2007-11-06 00:56:02 -05:00
#
2008-01-15 21:01:57 -05:00
# In this case, if you use this:
#
# <%= render :partial => f %>
#
# The rendered template is <tt>people/_labelling_form</tt> and the local variable referencing the form builder is called <tt>labelling_form</tt>.
#
2007-05-06 01:23:03 -04:00
# In many cases you will want to wrap the above in another helper, so you could do something like the following:
2006-02-04 14:58:45 -05:00
#
2008-01-08 16:16:22 -05:00
# def labelled_form_for(record_or_name_or_array, *args, &proc)
# options = args.extract_options!
2008-02-07 11:50:55 -05:00
# form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
2006-02-04 14:58:45 -05:00
# end
#
2007-05-06 01:23:03 -04:00
# If you don't need to attach a form to a model instance, then check out FormTagHelper#form_tag.
2007-06-06 12:52:37 -04:00
def form_for ( record_or_name_or_array , * args , & proc )
2006-02-11 19:22:24 -05:00
raise ArgumentError , " Missing block " unless block_given?
2007-05-14 13:30:35 -04:00
2007-07-24 12:48:57 -04:00
options = args . extract_options!
2007-05-14 13:30:35 -04:00
2007-06-06 12:52:37 -04:00
case record_or_name_or_array
2007-05-14 13:30:35 -04:00
when String , Symbol
2007-06-06 12:52:37 -04:00
object_name = record_or_name_or_array
2007-06-05 15:10:59 -04:00
when Array
2007-06-06 12:52:37 -04:00
object = record_or_name_or_array . last
2007-06-05 15:10:59 -04:00
object_name = ActionController :: RecordIdentifier . singular_class_name ( object )
2007-06-06 12:52:37 -04:00
apply_form_for_options! ( record_or_name_or_array , options )
2007-06-05 15:10:59 -04:00
args . unshift object
2007-05-14 13:30:35 -04:00
else
2007-06-06 12:52:37 -04:00
object = record_or_name_or_array
2007-06-05 15:10:59 -04:00
object_name = ActionController :: RecordIdentifier . singular_class_name ( object )
2007-06-07 17:35:01 -04:00
apply_form_for_options! ( [ object ] , options )
2007-05-17 20:36:14 -04:00
args . unshift object
2007-05-14 13:30:35 -04:00
end
2008-06-02 16:49:14 -04:00
concat ( form_tag ( options . delete ( :url ) || { } , options . delete ( :html ) || { } ) )
2006-03-26 15:21:27 -05:00
fields_for ( object_name , * ( args << options ) , & proc )
2008-06-02 16:49:14 -04:00
concat ( '</form>' )
2005-11-13 06:13:11 -05:00
end
2007-06-05 15:10:59 -04:00
2007-06-06 12:52:37 -04:00
def apply_form_for_options! ( object_or_array , options ) #:nodoc:
object = object_or_array . is_a? ( Array ) ? object_or_array . last : object_or_array
2007-11-06 00:56:02 -05:00
2007-06-05 15:10:59 -04:00
html_options =
if object . respond_to? ( :new_record? ) && object . new_record?
{ :class = > dom_class ( object , :new ) , :id = > dom_id ( object ) , :method = > :post }
else
{ :class = > dom_class ( object , :edit ) , :id = > dom_id ( object , :edit ) , :method = > :put }
end
2007-05-14 13:30:35 -04:00
options [ :html ] || = { }
options [ :html ] . reverse_merge! ( html_options )
2007-06-06 12:52:37 -04:00
options [ :url ] || = polymorphic_path ( object_or_array )
2007-05-14 13:30:35 -04:00
end
2005-11-13 06:13:11 -05:00
# Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes
2007-05-06 01:23:03 -04:00
# fields_for suitable for specifying additional model objects in the same form:
2005-11-13 06:13:11 -05:00
#
2007-05-06 01:23:03 -04:00
# ==== Examples
2007-12-01 19:46:43 -05:00
# <% form_for @person, :url => { :action => "update" } do |person_form| %>
2005-11-13 06:13:11 -05:00
# First name: <%= person_form.text_field :first_name %>
# Last name : <%= person_form.text_field :last_name %>
2007-11-06 00:56:02 -05:00
#
2007-12-01 19:46:43 -05:00
# <% fields_for @person.permission do |permission_fields| %>
2005-11-13 06:13:11 -05:00
# Admin? : <%= permission_fields.check_box :admin %>
# <% end %>
# <% end %>
#
2007-12-01 19:46:43 -05:00
# ...or if you have an object that needs to be represented as a different parameter, like a Client that acts as a Person:
#
# <% fields_for :person, @client do |permission_fields| %>
# Admin?: <%= permission_fields.check_box :admin %>
# <% end %>
#
# ...or if you don't have an object, just a name of the parameter
#
# <% fields_for :person do |permission_fields| %>
# Admin?: <%= permission_fields.check_box :admin %>
# <% end %>
#
2007-05-06 01:23:03 -04:00
# 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.
2007-12-01 19:46:43 -05:00
def fields_for ( record_or_name_or_array , * args , & block )
2006-02-11 19:22:24 -05:00
raise ArgumentError , " Missing block " unless block_given?
2007-07-24 12:48:57 -04:00
options = args . extract_options!
2007-12-01 19:46:43 -05:00
case record_or_name_or_array
when String , Symbol
object_name = record_or_name_or_array
object = args . first
else
object = record_or_name_or_array
object_name = ActionController :: RecordIdentifier . singular_class_name ( object )
end
2006-11-02 23:16:58 -05:00
builder = options [ :builder ] || ActionView :: Base . default_form_builder
yield builder . new ( object_name , object , self , options , block )
2005-11-13 06:13:11 -05:00
end
2007-09-22 13:17:22 -04:00
# 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 you specify
# it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
2007-11-06 00:56:02 -05:00
# onto the HTML as an HTML element attribute as in the example shown.
2007-09-22 13:17:22 -04:00
#
# ==== Examples
# label(:post, :title)
2008-05-25 07:29:00 -04:00
# # => <label for="post_title">Title</label>
2007-09-22 13:17:22 -04:00
#
# label(:post, :title, "A short title")
2008-05-25 07:29:00 -04:00
# # => <label for="post_title">A short title</label>
2007-09-22 13:17:22 -04:00
#
# label(:post, :title, "A short title", :class => "title_label")
2008-05-25 07:29:00 -04:00
# # => <label for="post_title" class="title_label">A short title</label>
2007-09-22 13:17:22 -04:00
#
def label ( object_name , method , text = nil , options = { } )
2008-07-02 12:53:04 -04:00
InstanceTag . new ( object_name , method , self , options . delete ( :object ) ) . to_label_tag ( text , options )
2007-09-22 13:17:22 -04:00
end
2004-11-23 20:04:44 -05:00
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
2007-11-06 00:56:02 -05:00
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
2007-02-25 15:19:24 -05:00
# shown.
2004-11-23 20:04:44 -05:00
#
2007-05-06 01:23:03 -04:00
# ==== Examples
2007-02-25 15:19:24 -05:00
# text_field(:post, :title, :size => 20)
2007-05-06 01:23:03 -04:00
# # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
#
# text_field(:post, :title, :class => "create_input")
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
#
# text_field(:session, :user, :onchange => "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }"/>
#
# text_field(:snippet, :code, :size => 20, :class => 'code_input')
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
#
2005-11-13 06:13:11 -05:00
def text_field ( object_name , method , options = { } )
2008-07-02 12:53:04 -04:00
InstanceTag . new ( object_name , method , self , options . delete ( :object ) ) . to_input_field_tag ( " text " , options )
2004-11-23 20:04:44 -05:00
end
2007-05-06 01:23:03 -04:00
# Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
2007-11-06 00:56:02 -05:00
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
2007-05-06 01:23:03 -04:00
# shown.
#
# ==== Examples
# password_field(:login, :pass, :size => 20)
# # => <input type="text" id="login_pass" name="login[pass]" size="20" value="#{@login.pass}" />
#
# password_field(:account, :secret, :class => "form_input")
# # => <input type="text" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
#
# password_field(:user, :password, :onchange => "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
# # => <input type="text" id="user_password" name="user[password]" value="#{@user.password}" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
#
# password_field(:account, :pin, :size => 20, :class => 'form_input')
# # => <input type="text" id="account_pin" name="account[pin]" size="20" value="#{@account.pin}" class="form_input" />
#
2005-11-13 06:13:11 -05:00
def password_field ( object_name , method , options = { } )
2008-07-02 12:53:04 -04:00
InstanceTag . new ( object_name , method , self , options . delete ( :object ) ) . to_input_field_tag ( " password " , options )
2004-11-23 20:04:44 -05:00
end
2007-05-06 01:23:03 -04:00
# Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
2007-11-06 00:56:02 -05:00
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
2007-05-06 01:23:03 -04:00
# shown.
#
2008-04-30 18:14:28 -04:00
# ==== Examples
2007-05-06 01:23:03 -04:00
# hidden_field(:signup, :pass_confirm)
# # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
#
# hidden_field(:post, :tag_list)
# # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
#
# hidden_field(:user, :token)
2007-11-06 00:56:02 -05:00
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
2005-11-13 06:13:11 -05:00
def hidden_field ( object_name , method , options = { } )
2008-07-02 12:53:04 -04:00
InstanceTag . new ( object_name , method , self , options . delete ( :object ) ) . to_input_field_tag ( " hidden " , options )
2004-11-23 20:04:44 -05:00
end
2007-05-06 01:23:03 -04:00
# Returns an file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
2007-11-06 00:56:02 -05:00
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
2007-05-06 01:23:03 -04:00
# shown.
#
# ==== Examples
# file_field(:user, :avatar)
# # => <input type="file" id="user_avatar" name="user[avatar]" />
#
# file_field(:post, :attached, :accept => 'text/html')
# # => <input type="file" id="post_attached" name="post[attached]" />
#
# file_field(:attachment, :file, :class => 'file_input')
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
#
2005-11-13 06:13:11 -05:00
def file_field ( object_name , method , options = { } )
2008-07-02 12:53:04 -04:00
InstanceTag . new ( object_name , method , self , options . delete ( :object ) ) . to_input_field_tag ( " file " , options )
2005-02-22 19:50:34 -05:00
end
2004-11-23 20:04:44 -05:00
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
# on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
# hash with +options+.
#
2007-05-06 01:23:03 -04:00
# ==== Examples
# text_area(:post, :body, :cols => 20, :rows => 40)
# # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
# # #{@post.body}
# # </textarea>
#
# text_area(:comment, :text, :size => "20x30")
# # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
# # #{@comment.text}
# # </textarea>
#
# text_area(:application, :notes, :cols => 40, :rows => 15, :class => 'app_input')
# # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
# # #{@application.notes}
# # </textarea>
#
# text_area(:entry, :body, :size => "20x20", :disabled => 'disabled')
# # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
# # #{@entry.body}
# # </textarea>
2005-11-13 06:13:11 -05:00
def text_area ( object_name , method , options = { } )
2008-07-02 12:53:04 -04:00
InstanceTag . new ( object_name , method , self , options . delete ( :object ) ) . to_text_area_tag ( options )
2004-11-23 20:04:44 -05:00
end
2005-03-06 06:50:41 -05:00
2004-11-23 20:04:44 -05:00
# Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
2008-10-05 17:16:26 -04:00
# assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
# It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
2008-07-16 08:00:36 -04:00
#
# ==== Gotcha
#
# The HTML specification says unchecked check boxes are not successful, and
# thus web browsers do not send them. Unfortunately this introduces a gotcha:
# if an Invoice model has a +paid+ flag, and in the form that edits a paid
# invoice the user unchecks its check box, no +paid+ parameter is sent. So,
# any mass-assignment idiom like
#
# @invoice.update_attributes(params[:invoice])
#
# wouldn't update the flag.
#
# To prevent this the helper generates a hidden field with the same name as
# the checkbox after the very check box. So, the client either sends only the
# hidden field (representing the check box is unchecked), or both fields.
# Since the HTML specification says key/value pairs have to be sent in the
# same order they appear in the form and Rails parameters extraction always
# gets the first occurrence of any given key, that works in ordinary forms.
#
# Unfortunately that workaround does not work when the check box goes
# within an array-like parameter, as in
#
# <% fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
# <%= form.check_box :paid %>
# ...
# <% end %>
#
# because parameter name repetition is precisely what Rails seeks to distinguish
# the elements of the array.
2004-11-23 20:04:44 -05:00
#
2008-04-30 18:14:28 -04:00
# ==== Examples
2007-05-06 01:23:03 -04:00
# # Let's say that @post.validated? is 1:
2004-11-23 20:04:44 -05:00
# check_box("post", "validated")
2008-03-26 08:27:52 -04:00
# # => <input type="checkbox" id="post_validated" name="post[validated]" value="1" />
2007-05-06 01:23:03 -04:00
# # <input name="post[validated]" type="hidden" value="0" />
2004-11-23 20:04:44 -05:00
#
2007-05-06 01:23:03 -04:00
# # Let's say that @puppy.gooddog is "no":
2004-11-23 20:04:44 -05:00
# check_box("puppy", "gooddog", {}, "yes", "no")
2007-05-06 01:23:03 -04:00
# # => <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
# # <input name="puppy[gooddog]" type="hidden" value="no" />
#
2008-03-26 08:27:52 -04:00
# check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
# # => <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
2007-05-06 01:23:03 -04:00
# # <input name="eula[accepted]" type="hidden" value="no" />
#
2005-11-13 06:13:11 -05:00
def check_box ( object_name , method , options = { } , checked_value = " 1 " , unchecked_value = " 0 " )
2008-07-02 12:53:04 -04:00
InstanceTag . new ( object_name , method , self , options . delete ( :object ) ) . to_check_box_tag ( options , checked_value , unchecked_value )
2004-11-23 20:04:44 -05:00
end
2004-12-14 08:48:27 -05:00
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
# radio button will be checked. Additional options on the input tag can be passed as a
2005-03-06 06:50:41 -05:00
# hash with +options+.
2007-05-06 01:23:03 -04:00
#
# ==== Examples
# # Let's say that @post.category returns "rails":
2004-12-14 08:48:27 -05:00
# radio_button("post", "category", "rails")
# radio_button("post", "category", "java")
2008-03-26 08:27:52 -04:00
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
2005-03-06 06:50:41 -05:00
#
2007-05-06 01:23:03 -04:00
# radio_button("user", "receive_newsletter", "yes")
# radio_button("user", "receive_newsletter", "no")
2008-03-26 08:27:52 -04:00
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
2005-11-13 06:13:11 -05:00
def radio_button ( object_name , method , tag_value , options = { } )
2008-07-02 12:53:04 -04:00
InstanceTag . new ( object_name , method , self , options . delete ( :object ) ) . to_radio_button_tag ( tag_value , options )
2004-12-14 08:48:27 -05:00
end
2004-11-23 20:04:44 -05:00
end
class InstanceTag #:nodoc:
2008-04-30 18:14:28 -04:00
include Helpers :: TagHelper , Helpers :: FormTagHelper
2004-11-23 20:04:44 -05:00
attr_reader :method_name , :object_name
2005-03-06 06:50:41 -05:00
DEFAULT_FIELD_OPTIONS = { " size " = > 30 } . freeze unless const_defined? ( :DEFAULT_FIELD_OPTIONS )
2005-04-10 11:11:15 -04:00
DEFAULT_RADIO_OPTIONS = { } . freeze unless const_defined? ( :DEFAULT_RADIO_OPTIONS )
2005-07-02 02:39:43 -04:00
DEFAULT_TEXT_AREA_OPTIONS = { " cols " = > 40 , " rows " = > 20 } . freeze unless const_defined? ( :DEFAULT_TEXT_AREA_OPTIONS )
2004-11-23 20:04:44 -05:00
2008-07-02 12:53:04 -04:00
def initialize ( object_name , method_name , template_object , object = nil )
2006-02-25 20:07:05 -05:00
@object_name , @method_name = object_name . to_s . dup , method_name . to_s . dup
2008-07-19 16:08:53 -04:00
@template_object = template_object
2005-11-13 06:13:11 -05:00
@object = object
2008-07-19 16:08:53 -04:00
if @object_name . sub! ( / \ [ \ ]$ / , " " ) || @object_name . sub! ( / \ [ \ ] \ ]$ / , " ] " )
if ( object || = @template_object . instance_variable_get ( " @ #{ Regexp . last_match . pre_match } " ) ) && object . respond_to? ( :to_param )
2007-10-26 00:48:19 -04:00
@auto_index = object . to_param
2006-07-18 11:42:22 -04:00
else
2007-10-26 00:48:19 -04:00
raise ArgumentError , " object[] naming but object param and @object var don't exist or don't respond to to_param: #{ object . inspect } "
2006-07-18 11:42:22 -04:00
end
2005-01-24 10:35:30 -05:00
end
2004-11-23 20:04:44 -05:00
end
2005-03-06 06:50:41 -05:00
2007-09-22 13:17:22 -04:00
def to_label_tag ( text = nil , options = { } )
2008-04-30 18:14:28 -04:00
options = options . stringify_keys
2007-09-22 13:17:22 -04:00
name_and_id = options . dup
add_default_name_and_id ( name_and_id )
2008-04-30 18:21:18 -04:00
options . delete ( " index " )
2008-04-30 18:14:28 -04:00
options [ " for " ] || = name_and_id [ " id " ]
2007-09-22 13:17:22 -04:00
content = ( text . blank? ? nil : text . to_s ) || method_name . humanize
2008-04-30 18:14:28 -04:00
label_tag ( name_and_id [ " id " ] , content , options )
2007-09-22 13:17:22 -04:00
end
2004-11-23 20:04:44 -05:00
def to_input_field_tag ( field_type , options = { } )
2005-03-06 06:50:41 -05:00
options = options . stringify_keys
2007-05-24 16:47:03 -04:00
options [ " size " ] = options [ " maxlength " ] || DEFAULT_FIELD_OPTIONS [ " size " ] unless options . key? ( " size " )
2005-04-10 11:11:15 -04:00
options = DEFAULT_FIELD_OPTIONS . merge ( options )
2005-03-06 06:50:41 -05:00
if field_type == " hidden "
options . delete ( " size " )
end
options [ " type " ] = field_type
2006-04-25 15:38:52 -04:00
options [ " value " ] || = value_before_type_cast ( object ) unless field_type == " file "
2008-05-01 18:56:32 -04:00
options [ " value " ] && = html_escape ( options [ " value " ] )
2005-03-06 06:50:41 -05:00
add_default_name_and_id ( options )
tag ( " input " , options )
end
def to_radio_button_tag ( tag_value , options = { } )
2005-04-10 11:11:15 -04:00
options = DEFAULT_RADIO_OPTIONS . merge ( options . stringify_keys )
2005-03-06 06:50:41 -05:00
options [ " type " ] = " radio "
options [ " value " ] = tag_value
2006-04-25 15:38:52 -04:00
if options . has_key? ( " checked " )
cv = options . delete " checked "
checked = cv == true || cv == " checked "
else
checked = self . class . radio_button_checked? ( value ( object ) , tag_value )
end
2006-10-08 20:53:59 -04:00
options [ " checked " ] = " checked " if checked
2005-06-16 02:34:08 -04:00
pretty_tag_value = tag_value . to_s . gsub ( / \ s / , " _ " ) . gsub ( / \ W / , " " ) . downcase
2007-11-06 00:56:02 -05:00
options [ " id " ] || = defined? ( @auto_index ) ?
2008-03-24 17:32:50 -04:00
" #{ tag_id_with_index ( @auto_index ) } _ #{ pretty_tag_value } " :
" #{ tag_id } _ #{ pretty_tag_value } "
2005-03-06 06:50:41 -05:00
add_default_name_and_id ( options )
tag ( " input " , options )
end
2004-11-23 20:04:44 -05:00
def to_text_area_tag ( options = { } )
2005-03-06 06:50:41 -05:00
options = DEFAULT_TEXT_AREA_OPTIONS . merge ( options . stringify_keys )
2004-11-23 20:04:44 -05:00
add_default_name_and_id ( options )
2006-05-11 12:36:59 -04:00
if size = options . delete ( " size " )
2007-03-03 13:04:44 -05:00
options [ " cols " ] , options [ " rows " ] = size . split ( " x " ) if size . respond_to? ( :split )
2006-05-11 12:36:59 -04:00
end
2006-04-25 15:38:52 -04:00
content_tag ( " textarea " , html_escape ( options . delete ( 'value' ) || value_before_type_cast ( object ) ) , options )
2004-11-23 20:04:44 -05:00
end
def to_check_box_tag ( options = { } , checked_value = " 1 " , unchecked_value = " 0 " )
2005-03-06 06:50:41 -05:00
options = options . stringify_keys
options [ " type " ] = " checkbox "
options [ " value " ] = checked_value
2006-04-25 15:38:52 -04:00
if options . has_key? ( " checked " )
cv = options . delete " checked "
checked = cv == true || cv == " checked "
2005-03-06 06:50:41 -05:00
else
2006-04-25 15:38:52 -04:00
checked = self . class . check_box_checked? ( value ( object ) , checked_value )
2005-03-06 06:50:41 -05:00
end
2006-04-25 15:38:52 -04:00
options [ " checked " ] = " checked " if checked
2004-11-23 20:04:44 -05:00
add_default_name_and_id ( options )
2007-10-23 15:25:28 -04:00
tag ( " input " , options ) << tag ( " input " , " name " = > options [ " name " ] , " type " = > " hidden " , " value " = > options [ 'disabled' ] && checked ? checked_value : unchecked_value )
2004-11-23 20:04:44 -05:00
end
def to_boolean_select_tag ( options = { } )
2005-03-06 06:50:41 -05:00
options = options . stringify_keys
2004-11-23 20:04:44 -05:00
add_default_name_and_id ( options )
2006-04-25 15:38:52 -04:00
value = value ( object )
2004-11-23 20:04:44 -05:00
tag_text = " <select "
tag_text << tag_options ( options )
tag_text << " ><option value= \" false \" "
tag_text << " selected " if value == false
tag_text << " >False</option><option value= \" true \" "
tag_text << " selected " if value
tag_text << " >True</option></select> "
end
2007-11-06 00:56:02 -05:00
2005-09-11 03:52:53 -04:00
def to_content_tag ( tag_name , options = { } )
2006-04-25 15:38:52 -04:00
content_tag ( tag_name , value ( object ) , options )
2005-09-11 03:52:53 -04:00
end
2007-11-06 00:56:02 -05:00
2004-11-23 20:04:44 -05:00
def object
2008-07-01 13:43:57 -04:00
@object || @template_object . instance_variable_get ( " @ #{ @object_name } " )
rescue NameError
# As @object_name may contain the nested syntax (item[subobject]) we
# need to fallback to nil.
nil
2004-11-23 20:04:44 -05:00
end
2006-04-25 15:38:52 -04:00
def value ( object )
self . class . value ( object , @method_name )
2004-11-23 20:04:44 -05:00
end
2006-04-25 15:38:52 -04:00
def value_before_type_cast ( object )
self . class . value_before_type_cast ( object , @method_name )
end
2007-11-06 00:56:02 -05:00
2006-04-25 15:38:52 -04:00
class << self
def value ( object , method_name )
object . send method_name unless object . nil?
end
2007-11-06 00:56:02 -05:00
2006-04-25 15:38:52 -04:00
def value_before_type_cast ( object , method_name )
unless object . nil?
object . respond_to? ( method_name + " _before_type_cast " ) ?
object . send ( method_name + " _before_type_cast " ) :
object . send ( method_name )
end
end
2007-11-06 00:56:02 -05:00
2006-04-25 15:38:52 -04:00
def check_box_checked? ( value , checked_value )
case value
when TrueClass , FalseClass
value
when NilClass
false
when Integer
value != 0
when String
value == checked_value
2008-05-14 10:07:21 -04:00
when Array
value . include? ( checked_value )
2006-04-25 15:38:52 -04:00
else
value . to_i != 0
end
end
2007-11-06 00:56:02 -05:00
2006-04-25 15:38:52 -04:00
def radio_button_checked? ( value , checked_value )
value . to_s == checked_value . to_s
2004-12-19 11:39:56 -05:00
end
2004-12-19 06:25:55 -05:00
end
2004-11-23 20:04:44 -05:00
private
def add_default_name_and_id ( options )
2005-03-06 06:50:41 -05:00
if options . has_key? ( " index " )
options [ " name " ] || = tag_name_with_index ( options [ " index " ] )
options [ " id " ] || = tag_id_with_index ( options [ " index " ] )
2005-01-10 18:57:36 -05:00
options . delete ( " index " )
2006-07-08 14:14:49 -04:00
elsif defined? ( @auto_index )
2005-03-06 06:50:41 -05:00
options [ " name " ] || = tag_name_with_index ( @auto_index )
options [ " id " ] || = tag_id_with_index ( @auto_index )
2005-01-10 18:57:36 -05:00
else
2007-01-28 11:17:55 -05:00
options [ " name " ] || = tag_name + ( options . has_key? ( 'multiple' ) ? '[]' : '' )
2005-03-06 06:50:41 -05:00
options [ " id " ] || = tag_id
2005-01-10 18:57:36 -05:00
end
2004-11-23 20:04:44 -05:00
end
2005-03-06 06:50:41 -05:00
2004-11-23 20:04:44 -05:00
def tag_name
2008-05-05 15:45:09 -04:00
" #{ @object_name } [ #{ sanitized_method_name } ] "
2004-11-23 20:04:44 -05:00
end
2005-03-06 06:50:41 -05:00
2005-01-10 18:57:36 -05:00
def tag_name_with_index ( index )
2008-05-05 15:45:09 -04:00
" #{ @object_name } [ #{ index } ][ #{ sanitized_method_name } ] "
2005-01-10 18:57:36 -05:00
end
2004-11-23 20:04:44 -05:00
def tag_id
2008-05-05 15:45:09 -04:00
" #{ sanitized_object_name } _ #{ sanitized_method_name } "
2004-11-23 20:04:44 -05:00
end
2005-01-10 18:57:36 -05:00
def tag_id_with_index ( index )
2008-05-05 15:45:09 -04:00
" #{ sanitized_object_name } _ #{ index } _ #{ sanitized_method_name } "
2007-01-16 19:04:02 -05:00
end
def sanitized_object_name
2008-07-19 16:08:53 -04:00
@sanitized_object_name || = @object_name . gsub ( / \ ] \ [|[^-a-zA-Z0-9:.] / , " _ " ) . sub ( / _$ / , " " )
2008-05-05 15:45:09 -04:00
end
def sanitized_method_name
@sanitized_method_name || = @method_name . sub ( / \ ?$ / , " " )
2005-01-10 18:57:36 -05:00
end
2004-11-23 20:04:44 -05:00
end
2005-11-13 06:13:11 -05:00
2006-02-11 19:28:45 -05:00
class FormBuilder #:nodoc:
2006-02-04 14:58:45 -05:00
# The methods which wrap a form helper call.
class_inheritable_accessor :field_helpers
self . field_helpers = ( FormHelper . instance_methods - [ 'form_for' ] )
2006-02-11 19:28:45 -05:00
2006-09-08 18:01:00 -04:00
attr_accessor :object_name , :object , :options
2006-02-11 19:28:45 -05:00
2006-02-04 14:58:45 -05:00
def initialize ( object_name , object , template , options , proc )
2007-11-06 00:56:02 -05:00
@object_name , @object , @template , @options , @proc = object_name , object , template , options , proc
2008-02-02 01:16:04 -05:00
@default_options = @options ? @options . slice ( :index ) : { }
2008-07-19 16:08:53 -04:00
if @object_name . to_s . match ( / \ [ \ ]$ / )
if object || = @template . instance_variable_get ( " @ #{ Regexp . last_match . pre_match } " ) and object . respond_to? ( :to_param )
@auto_index = object . to_param
else
raise ArgumentError , " object[] naming but object param and @object var don't exist or don't respond to to_param: #{ object . inspect } "
end
end
2005-11-13 06:13:11 -05:00
end
2007-11-06 00:56:02 -05:00
2007-09-22 13:17:22 -04:00
( field_helpers - %w( label check_box radio_button fields_for ) ) . each do | selector |
2005-11-13 06:13:11 -05:00
src = <<-end_src
def #{selector}(method, options = {})
2008-02-02 01:16:04 -05:00
@template . send ( #{selector.inspect}, @object_name, method, objectify_options(options))
2005-11-13 06:13:11 -05:00
end
end_src
class_eval src , __FILE__ , __LINE__
end
2007-01-16 19:04:02 -05:00
2007-12-01 20:10:50 -05:00
def fields_for ( record_or_name_or_array , * args , & block )
2008-07-19 16:08:53 -04:00
if options . has_key? ( :index )
index = " [ #{ options [ :index ] } ] "
elsif defined? ( @auto_index )
self . object_name = @object_name . to_s . sub ( / \ [ \ ]$ / , " " )
index = " [ #{ @auto_index } ] "
else
index = " "
end
2007-12-01 20:10:50 -05:00
case record_or_name_or_array
when String , Symbol
2008-07-19 16:08:53 -04:00
name = " #{ object_name } #{ index } [ #{ record_or_name_or_array } ] "
2007-12-01 20:10:50 -05:00
when Array
object = record_or_name_or_array . last
2008-07-19 16:08:53 -04:00
name = " #{ object_name } #{ index } [ #{ ActionController :: RecordIdentifier . singular_class_name ( object ) } ] "
2007-12-01 20:10:50 -05:00
args . unshift ( object )
else
object = record_or_name_or_array
2008-07-19 16:08:53 -04:00
name = " #{ object_name } #{ index } [ #{ ActionController :: RecordIdentifier . singular_class_name ( object ) } ] "
2007-12-01 20:10:50 -05:00
args . unshift ( object )
end
2008-02-02 01:16:04 -05:00
2007-01-16 19:04:02 -05:00
@template . fields_for ( name , * args , & block )
end
2007-09-22 13:17:22 -04:00
def label ( method , text = nil , options = { } )
2008-02-02 01:16:04 -05:00
@template . label ( @object_name , method , text , objectify_options ( options ) )
2007-09-22 13:17:22 -04:00
end
2007-01-16 19:04:02 -05:00
2005-11-13 06:13:11 -05:00
def check_box ( method , options = { } , checked_value = " 1 " , unchecked_value = " 0 " )
2008-02-02 01:16:04 -05:00
@template . check_box ( @object_name , method , objectify_options ( options ) , checked_value , unchecked_value )
2005-11-13 06:13:11 -05:00
end
2007-11-06 00:56:02 -05:00
2005-11-13 06:13:11 -05:00
def radio_button ( method , tag_value , options = { } )
2008-02-02 01:16:04 -05:00
@template . radio_button ( @object_name , method , tag_value , objectify_options ( options ) )
2005-11-13 06:13:11 -05:00
end
2007-11-06 00:56:02 -05:00
2008-07-27 17:34:20 -04:00
def error_message_on ( method , * args )
@template . error_message_on ( @object , method , * args )
2007-11-06 00:56:02 -05:00
end
2007-01-25 22:38:23 -05:00
def error_messages ( options = { } )
2008-02-02 01:16:04 -05:00
@template . error_messages_for ( @object_name , objectify_options ( options ) )
2007-01-25 22:38:23 -05:00
end
2007-11-06 00:56:02 -05:00
2007-01-29 15:42:46 -05:00
def submit ( value = " Save changes " , options = { } )
@template . submit_tag ( value , options . reverse_merge ( :id = > " #{ object_name } _submit " ) )
end
2008-02-02 01:16:04 -05:00
private
def objectify_options ( options )
@default_options . merge ( options . merge ( :object = > @object ) )
end
2005-11-13 06:13:11 -05:00
end
2004-11-23 20:04:44 -05:00
end
2006-11-02 23:16:58 -05:00
class Base
cattr_accessor :default_form_builder
self . default_form_builder = :: ActionView :: Helpers :: FormBuilder
end
2004-12-08 05:50:59 -05:00
end