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'
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
#
2006-01-12 23:43:33 -05: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 %>" />
#
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
2007-05-06 01:23:03 -04:00
# Creates a form and a scope around a specific model object that is used as a base for questioning about
2007-11-06 00:56:02 -05:00
# values for the fields.
2005-11-13 06:13:11 -05:00
#
2005-12-18 20:52:05 -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 : <%= f.text_area :biography %>
# Admin? : <%= f.check_box :admin %>
# <% end %>
#
2007-11-06 00:56:02 -05:00
# Worth noting is that the form_for tag is called in a ERb evaluation block, not an ERb output block. So that's <tt><% %></tt>,
2007-05-06 01:23:03 -04:00
# not <tt><%= %></tt>. Also worth noting is that form_for yields a <tt>form_builder</tt> object, in this example as <tt>f</tt>, which emulates
2005-11-13 06:13:11 -05:00
# the API for the stand-alone FormHelper methods, but without the object name. So instead of <tt>text_field :person, :name</tt>,
2007-11-06 00:56:02 -05:00
# you get away with <tt>f.text_field :name</tt>.
2005-11-13 06:13:11 -05:00
#
2007-11-06 00:56:02 -05:00
# Even further, the form_for method allows you to more easily escape the instance variable convention. So while the stand-alone
# approach would require <tt>text_field :person, :name, :object => person</tt>
2005-11-13 06:13:11 -05:00
# to work with local variables instead of instance ones, the form_for calls remain the same. You simply declare once with
2005-12-01 19:37:33 -05:00
# <tt>:person, person</tt> and all subsequent field calls save <tt>:person</tt> and <tt>:object => person</tt>.
2005-11-13 06:13:11 -05:00
#
# Also note that form_for doesn't create an exclusive scope. It's still possible to use both the stand-alone FormHelper methods
2007-05-06 01:23:03 -04:00
# 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 %>
#
2007-11-06 00:56:02 -05: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.
2006-02-04 14:58:45 -05:00
#
2007-05-06 01:23:03 -04:00
# HTML attributes for the form tag can be given as :html => {...}. For example:
2007-11-06 00:56:02 -05:00
#
2006-02-11 18:29:53 -05:00
# <% form_for :person, @person, :html => {:id => 'person_form'} do |f| %>
# ...
# <% end %>
#
2007-05-06 01:23:03 -04:00
# The above form will then have the <tt>id</tt> attribute with the value </tt>person_form</tt>, which you can then
# style with CSS or manipulate with JavaScript.
#
2007-05-14 13:30:35 -04:00
# === Relying on record identification
#
# In addition to manually configuring the form_for call, you can also rely on record identification, which will use
# the conventions and named routes of that approach. Examples:
#
# <% form_for(@post) do |f| %>
# ...
# <% end %>
#
# This will expand to be the same as:
#
# <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
# ...
# <% end %>
#
# And for new records:
#
# <% form_for(Post.new) do |f| %>
# ...
# <% end %>
#
# This will expand to be the same as:
#
# <% form_for :post, @post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
# ...
# <% end %>
#
# You can also overwrite the individual conventions, like this:
#
# <% form_for(@post, :url => super_post_path(@post)) do |f| %>
# ...
# <% end %>
#
2007-10-08 01:50:59 -04:00
# And for namespaced routes, like admin_post_url:
#
# <% 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!
# form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuiler)), &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
2006-02-26 02:23:42 -05:00
concat ( form_tag ( options . delete ( :url ) || { } , options . delete ( :html ) || { } ) , proc . binding )
2006-03-26 15:21:27 -05:00
fields_for ( object_name , * ( args << options ) , & proc )
2006-02-11 19:22:24 -05:00
concat ( '</form>' , proc . binding )
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
when Array
object = record_or_name_or_array . last
object_name = ActionController :: RecordIdentifier . singular_class_name ( object )
apply_form_for_options! ( record_or_name_or_array , options )
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)
# #=> <label for="post_title">Title</label>
#
# label(:post, :title, "A short title")
# #=> <label for="post_title">A short title</label>
#
# label(:post, :title, "A short title", :class => "title_label")
# #=> <label for="post_title" class="title_label">A short title</label>
#
def label ( object_name , method , text = nil , options = { } )
InstanceTag . new ( object_name , method , self , nil , options . delete ( :object ) ) . to_label_tag ( text , options )
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 = { } )
InstanceTag . new ( object_name , method , self , nil , 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 = { } )
InstanceTag . new ( object_name , method , self , nil , 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.
#
# ==== Examples
# 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 = { } )
2005-11-13 07:54:00 -05:00
InstanceTag . new ( object_name , method , self , nil , 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 = { } )
InstanceTag . new ( object_name , method , self , nil , 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 = { } )
InstanceTag . new ( object_name , method , self , nil , 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
# assigned to the template (identified by +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+
2007-05-06 01:23:03 -04:00
# is set to 0 which is convenient for boolean values. Since HTTP standards say that unchecked checkboxes don't post anything,
# we add a hidden value with the same name as the checkbox as a work around.
2004-11-23 20:04:44 -05:00
#
2007-05-06 01:23:03 -04:00
# ==== Examples
# # Let's say that @post.validated? is 1:
2004-11-23 20:04:44 -05:00
# check_box("post", "validated")
2007-05-06 01:23:03 -04:00
# # => <input type="checkbox" id="post_validate" name="post[validated]" value="1" checked="checked" />
# # <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" />
#
# check_box("eula", "accepted", {}, "yes", "no", :class => 'eula_check')
# # => <input type="checkbox" id="eula_accepted" name="eula[accepted]" value="no" />
# # <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 " )
InstanceTag . new ( object_name , method , self , nil , 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")
2007-05-06 01:23:03 -04:00
# # => <input type="radio" id="post_category" name="post[category]" value="rails" checked="checked" />
# # <input type="radio" id="post_category" 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")
# # => <input type="radio" id="user_receive_newsletter" name="user[receive_newsletter]" value="yes" />
# # <input type="radio" id="user_receive_newsletter" 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 = { } )
InstanceTag . new ( object_name , method , self , nil , 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:
include Helpers :: TagHelper
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 )
2005-03-06 06:50:41 -05:00
DEFAULT_DATE_OPTIONS = { :discard_type = > true } . freeze unless const_defined? ( :DEFAULT_DATE_OPTIONS )
2004-11-23 20:04:44 -05:00
2005-11-13 06:13:11 -05:00
def initialize ( object_name , method_name , template_object , local_binding = nil , object = nil )
2006-02-25 20:07:05 -05:00
@object_name , @method_name = object_name . to_s . dup , method_name . to_s . dup
2004-11-23 20:04:44 -05:00
@template_object , @local_binding = template_object , local_binding
2005-11-13 06:13:11 -05:00
@object = object
2005-01-24 10:35:30 -05:00
if @object_name . sub! ( / \ [ \ ]$ / , " " )
2007-10-26 00:48:19 -04:00
if object || = @template_object . instance_variable_get ( " @ #{ Regexp . last_match . pre_match } " ) and object . respond_to? ( :to_param )
@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 = { } )
name_and_id = options . dup
add_default_name_and_id ( name_and_id )
options [ " for " ] = name_and_id [ " id " ]
content = ( text . blank? ? nil : text . to_s ) || method_name . humanize
content_tag ( " label " , content , options )
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 "
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 ) ?
2005-04-30 04:46:22 -04:00
" #{ @object_name } _ #{ @auto_index } _ #{ @method_name } _ #{ pretty_tag_value } " :
" #{ @object_name } _ #{ @method_name } _ #{ 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_date_tag ( )
2005-03-06 06:50:41 -05:00
defaults = DEFAULT_DATE_OPTIONS . dup
2006-04-25 15:38:52 -04:00
date = value ( object ) || Date . today
2005-03-06 06:50:41 -05:00
options = Proc . new { | position | defaults . merge ( :prefix = > " #{ @object_name } [ #{ @method_name } ( #{ position } i)] " ) }
2004-11-23 20:04:44 -05:00
html_day_select ( date , options . call ( 3 ) ) +
2005-03-06 06:50:41 -05:00
html_month_select ( date , options . call ( 2 ) ) +
2004-11-23 20:04:44 -05:00
html_year_select ( date , options . call ( 1 ) )
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
2007-07-09 12:19:40 -04:00
@object || ( @template_object . instance_variable_get ( " @ #{ @object_name } " ) rescue 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
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
" #{ @object_name } [ #{ @method_name } ] "
end
2005-03-06 06:50:41 -05:00
2005-01-10 18:57:36 -05:00
def tag_name_with_index ( index )
" #{ @object_name } [ #{ index } ][ #{ @method_name } ] "
end
2004-11-23 20:04:44 -05:00
def tag_id
2007-01-16 19:04:02 -05:00
" #{ sanitized_object_name } _ #{ @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 )
2007-01-16 19:04:02 -05:00
" #{ sanitized_object_name } _ #{ index } _ #{ @method_name } "
end
def sanitized_object_name
@object_name . gsub ( / [^-a-zA-Z0-9:.] / , " _ " ) . 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
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 = {})
@template . send ( #{selector.inspect}, @object_name, method, options.merge(:object => @object))
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 )
case record_or_name_or_array
when String , Symbol
name = " #{ object_name } [ #{ record_or_name_or_array } ] "
when Array
object = record_or_name_or_array . last
name = " #{ object_name } [ #{ ActionController :: RecordIdentifier . singular_class_name ( object ) } ] "
args . unshift ( object )
else
object = record_or_name_or_array
name = " #{ object_name } [ #{ ActionController :: RecordIdentifier . singular_class_name ( object ) } ] "
args . unshift ( object )
end
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 = { } )
@template . label ( @object_name , method , text , options . merge ( :object = > @object ) )
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 " )
@template . check_box ( @object_name , method , options . merge ( :object = > @object ) , checked_value , unchecked_value )
end
2007-11-06 00:56:02 -05:00
2005-11-13 06:13:11 -05:00
def radio_button ( method , tag_value , options = { } )
2006-01-06 12:49:14 -05:00
@template . radio_button ( @object_name , method , tag_value , options . merge ( :object = > @object ) )
2005-11-13 06:13:11 -05:00
end
2007-11-06 00:56:02 -05:00
2007-01-25 22:38:23 -05:00
def error_message_on ( method , prepend_text = " " , append_text = " " , css_class = " formError " )
2007-10-07 15:50:30 -04:00
@template . error_message_on ( @object , method , prepend_text , append_text , css_class )
2007-11-06 00:56:02 -05:00
end
2007-01-25 22:38:23 -05:00
def error_messages ( options = { } )
2007-10-07 15:50:30 -04:00
@template . error_messages_for ( @object_name , options . merge ( :object = > @object ) )
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
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