Returns a suitable form input for the given method
, using the
database column information and other factors (like the method name) to
figure out what you probably want.
Options:
:as - override the input type (eg force a :string to render as a :password field)
:label - use something other than the method name as the label text, when false no label is printed
:required - specify if the column is required (true) or not (false)
:hint - provide some text to hint or help the user provide the correct information for a field
:input_html - provide options that will be passed down to the generated input
:wrapper_html - provide options that will be passed down to the li wrapper
Input Types:
Most inputs map directly to one of ActiveRecord's column types by default (eg #string_input), but there are a few special cases and some simplification (:integer, :float and :decimal columns all map to a single #numeric_input, for example).
:select (a select menu for associations) - default to association names
:check_boxes (a set of check_box inputs for associations) - alternative to :select has_many and has_and_belongs_to_many associations
:radio (a set of radio inputs for associations) - alternative to :select belongs_to associations
:time_zone (a select menu with time zones)
:password (a password input) - default for :string column types with 'password' in the method name
:text (a textarea) - default for :text column types
:date (a date select) - default for :date column types
:datetime (a date and time select) - default for :datetime and :timestamp column types
:time (a time select) - default for :time column types
:boolean (a checkbox) - default for :boolean column types (you can also have booleans as :select and :radio)
:string (a text field) - default for :string column types
:numeric (a text field, like string) - default for :integer, :float and :decimal column types
:email (an email input) - default for :string column types with 'email' as the method name.
:url (a url input) - default for :string column types with 'url' as the method name.
:phone (a tel input) - default for :string column types with 'phone' or 'fax' in the method name.
:search (a search input) - default for :string column types with 'search' as the method name.
:country (a select menu of country names) - requires a country_select plugin to be installed
:email (an email input) - New in HTML5 - needs to be explicitly provided with :as => :email
:url (a url input) - New in HTML5 - needs to be explicitly provided with :as => :url
:phone (a tel input) - New in HTML5 - needs to be explicitly provided with :as => :phone
:search (a search input) - New in HTML5 - needs to be explicity provided with :as => :search
:country (a select menu of country names) - requires a country_select plugin to be installed
:hidden (a hidden field) - creates a hidden field (added for compatibility)
Example:
<% semantic_form_for @employee do |form| %> <% form.inputs do -%> <%= form.input :name, :label => "Full Name" %> <%= form.input :manager, :as => :radio %> <%= form.input :secret, :as => :password, :input_html => { :value => "xxxx" } %> <%= form.input :hired_at, :as => :date, :label => "Date Hired" %> <%= form.input :phone, :required => false, :hint => "Eg: +1 555 1234" %> <%= form.input :email %> <%= form.input :website, :as => :url, :hint => "You may wish to omit the http://" %> <% end %> <% end %>
# File lib/formtastic.rb, line 109 def input(method, options = {}) options = options.dup # Allow options to be shared without being tainted by Formtastic options[:required] = method_required?(method) unless options.key?(:required) options[:as] ||= default_input_type(method, options) html_class = [ options[:as], (options[:required] ? :required : :optional) ] html_class << 'error' if has_errors?(method, options) wrapper_html = options.delete(:wrapper_html) || {} wrapper_html[:id] ||= generate_html_id(method) wrapper_html[:class] = (html_class << wrapper_html[:class]).flatten.compact.join(' ') if options[:input_html] && options[:input_html][:id] options[:label_html] ||= {} options[:label_html][:for] ||= options[:input_html][:id] end input_parts = (custom_inline_order[options[:as]] || inline_order).dup input_parts = input_parts - [:errors, :hints] if options[:as] == :hidden list_item_content = input_parts.map do |type| send(:"inline_#{type}_for", method, options) end.compact.join("\n") return template.content_tag(:li, Formtastic::Util.html_safe(list_item_content), wrapper_html) end
Creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be called either with a block (in which you can do the usual Rails form stuff, HTML, ERB, etc), or with a list of fields. These two examples are functionally equivalent:
# With a block: <% semantic_form_for @post do |form| %> <% form.inputs do %> <%= form.input :title %> <%= form.input :body %> <% end %> <% end %> # With a list of fields: <% semantic_form_for @post do |form| %> <%= form.inputs :title, :body %> <% end %> # Output: <form ...> <fieldset class="inputs"> <ol> <li class="string">...</li> <li class="text">...</li> </ol> </fieldset> </form>
When called without a block or a field list, an input is rendered for each column in the model's database table, just like Rails' scaffolding. You'll obviously want more control than this in a production application, but it's a great way to get started, then come back later to customise the form with a field list or a block of inputs. Example:
<% semantic_form_for @post do |form| %> <%= form.inputs %> <% end %> With a few arguments: <% semantic_form_for @post do |form| %> <%= form.inputs "Post details", :title, :body %> <% end %>
All options (with the exception of :name/:title) are passed down to the fieldset as HTML attributes (id, class, style, etc). If provided, the :name/:title option is passed into a legend tag inside the fieldset.
# With a block: <% semantic_form_for @post do |form| %> <% form.inputs :name => "Create a new post", :style => "border:1px;" do %> ... <% end %> <% end %> # With a list (the options must come after the field list): <% semantic_form_for @post do |form| %> <%= form.inputs :title, :body, :name => "Create a new post", :style => "border:1px;" %> <% end %> # ...or the equivalent: <% semantic_form_for @post do |form| %> <%= form.inputs "Create a new post", :title, :body, :style => "border:1px;" %> <% end %>
Instead of hard-coding fieldsets & legends into your form to logically group related fields, use inputs:
<% semantic_form_for @post do |f| %> <% f.inputs do %> <%= f.input :title %> <%= f.input :body %> <% end %> <% f.inputs :name => "Advanced", :id => "advanced" do %> <%= f.input :created_at %> <%= f.input :user_id, :label => "Author" %> <% end %> <% f.inputs "Extra" do %> <%= f.input :update_at %> <% end %> <% end %> # Output: <form ...> <fieldset class="inputs"> <ol> <li class="string">...</li> <li class="text">...</li> </ol> </fieldset> <fieldset class="inputs" id="advanced"> <legend><span>Advanced</span></legend> <ol> <li class="datetime">...</li> <li class="select">...</li> </ol> </fieldset> <fieldset class="inputs"> <legend><span>Extra</span></legend> <ol> <li class="datetime">...</li> </ol> </fieldset> </form>
As in Rails, you can use #semantic_fields_for to nest attributes:
<% semantic_form_for @post do |form| %> <%= form.inputs :title, :body %> <% form.semantic_fields_for :author, @bob do |author_form| %> <% author_form.inputs do %> <%= author_form.input :first_name, :required => false %> <%= author_form.input :last_name %> <% end %> <% end %> <% end %>
But this does not look formtastic! This is equivalent:
<% semantic_form_for @post do |form| %> <%= form.inputs :title, :body %> <% form.inputs :for => [ :author, @bob ] do |author_form| %> <%= author_form.input :first_name, :required => false %> <%= author_form.input :last_name %> <% end %> <% end %>
And if you don't need to give options to your input call, you could do it in just one line:
<% semantic_form_for @post do |form| %> <%= form.inputs :title, :body %> <%= form.inputs :first_name, :last_name, :for => @bob %> <% end %>
Just remember that calling inputs generates a new fieldset to wrap your inputs. If you have two separate models, but, semantically, on the page they are part of the same fieldset, you should use #semantic_fields_for instead (just as you would do with Rails' form builder).
# File lib/formtastic.rb, line 283 def inputs(*args, &block) title = field_set_title_from_args(*args) html_options = args.extract_options! html_options[:class] ||= "inputs" html_options[:name] = title if html_options[:for] # Nested form inputs_for_nested_attributes(*(args << html_options), &block) elsif block_given? field_set_and_list_wrapping(*(args << html_options), &block) else if @object && args.empty? args = association_columns(:belongs_to) args += content_columns args -= RESERVED_COLUMNS args.compact! end legend = args.shift if args.first.is_a?(::String) contents = args.collect { |method| input(method.to_sym) } args.unshift(legend) if legend.present? field_set_and_list_wrapping(*((args << html_options) << contents)) end end
Generates the label for the input. It also accepts the same arguments as Rails label method. It has three options that are not supported by Rails label method:
:required - Appends an abbr tag if :required is true
:label - An alternative form to give the label content. Whenever label
is false, a blank string is returned.
:input_name - Gives the input to match for. This is needed when you want to
to call f.label :authors but it should match :author_ids.
f.label :title # like in rails, except that it searches the label on I18n API too f.label :title, "Your post title" f.label :title, :label => "Your post title" # Added for formtastic API f.label :title, :required => true # Returns <label>Title<abbr title="required">*</abbr></label>
# File lib/formtastic.rb, line 427 def label(method, options_or_text=nil, options=nil) if options_or_text.is_a?(Hash) return "" if options_or_text[:label] == false options = options_or_text text = options.delete(:label) else text = options_or_text options ||= {} end text = localized_string(method, text, :label) || humanized_attribute_name(method) text += required_or_optional_string(options.delete(:required)) text = Formtastic::Util.html_safe(text) # special case for boolean (checkbox) labels, which have a nested input if options.key?(:label_prefix_for_nested_input) text = options.delete(:label_prefix_for_nested_input) + text end input_name = options.delete(:input_name) || method super(input_name, text, options) end
Generates error messages for given method names and for base. You can pass a hash with html options that will be added to ul tag
f.semantic_errors # This will show only errors on base f.semantic_errors :state # This will show errors on base and state f.semantic_errors :state, :class => "awesome" # errors will be rendered in ul.awesome
# File lib/formtastic.rb, line 480 def semantic_errors(*args) html_options = args.extract_options! full_errors = args.inject([]) do |array, method| attribute = localized_string(method, method.to_sym, :label) || humanized_attribute_name(method) errors = Array(@object.errors[method.to_sym]).to_sentence errors.present? ? array << [attribute, errors].join(" ") : array ||= [] end full_errors << @object.errors[:base] full_errors.flatten! full_errors.compact! return nil if full_errors.blank? html_options[:class] ||= "errors" template.content_tag(:ul, html_options) do Formtastic::Util.html_safe(full_errors.map { |error| template.content_tag(:li, Formtastic::Util.html_safe(error)) }.join) end end
A thin wrapper around fields_for to set :builder => Formtastic::SemanticFormBuilder for nesting forms:
# Example: <% semantic_form_for @post do |post| %> <% post.semantic_fields_for :author do |author| %> <% author.inputs :name %> <% end %> <% end %> # Output: <form ...> <fieldset class="inputs"> <ol> <li class="string"><input type='text' name='post[author][name]' id='post_author_name' /></li> </ol> </fieldset> </form>
# File lib/formtastic.rb, line 401 def semantic_fields_for(record_or_name_or_array, *args, &block) opts = args.extract_options! opts[:builder] ||= self.class args.push(opts) fields_for(record_or_name_or_array, *args, &block) end
# File lib/formtastic.rb, line 546 def association_primary_key(method) reflection = reflection_for(method) reflection.options[:foreign_key] if reflection && !reflection.options[:foreign_key].blank? :"#{method}_id" end
Outputs a label containing a checkbox and the label text. The label defaults to the column name (method name) and can be altered with the :label option. :checked_value and :unchecked_value options are also available.
# File lib/formtastic.rb, line 1255 def boolean_input(method, options) html_options = options.delete(:input_html) || {} checked_value = options.delete(:checked_value) || '1' unchecked_value = options.delete(:unchecked_value) || '0' html_options[:id] = html_options[:id] || generate_html_id(method, "") input = template.check_box_tag( "#{@object_name}[#{method}]", checked_value, (@object && @object.send(:"#{method}")), html_options ) options = options_for_label(options) options[:for] ||= html_options[:id] # the label() method will insert this nested input into the label at the last minute options[:label_prefix_for_nested_input] = input template.hidden_field_tag("#{@object_name}[#{method}]", unchecked_value, :id => nil) << label(method, options) end
Outputs a fieldset containing a legend for the label text, and an ordered list (ol) of list items, one for each possible choice in the belongs_to association. Each li contains a label and a check_box input.
This is an alternative for has many and has and belongs to many associations.
Example:
f.input :author, :as => :check_boxes
Output:
<fieldset> <legend class="label"><label>Authors</label></legend> <ol> <li> <input type="hidden" name="book[author_id][1]" value=""> <label for="book_author_id_1"><input id="book_author_id_1" name="book[author_id][1]" type="checkbox" value="1" /> Justin French</label> </li> <li> <input type="hidden" name="book[author_id][2]" value=""> <label for="book_author_id_2"><input id="book_author_id_2" name="book[owner_id][2]" type="checkbox" value="2" /> Kate French</label> </li> </ol> </fieldset>
Notice that the value of the checkbox is the same as the id and the hidden field has empty value. You can override the hidden field value using the unchecked_value option.
You can customize the options available in the set by passing in a collection (Array) of ActiveRecord objects through the :collection option. If not provided, the choices are found by inferring the parent's class name from the method name and simply calling all on it (Author.all in the example above).
Examples:
f.input :author, :as => :check_boxes, :collection => @authors f.input :author, :as => :check_boxes, :collection => Author.all f.input :author, :as => :check_boxes, :collection => [@justin, @kate]
The :label_method option allows you to customize the label for each checkbox two ways:
by naming the correct method to call on each object in the collection as a symbol (:name, :login, etc)
by passing a Proc that will be called on each object in the collection, allowing you to use helpers or multiple model attributes together
Examples:
f.input :author, :as => :check_boxes, :label_method => :full_name f.input :author, :as => :check_boxes, :label_method => :login f.input :author, :as => :check_boxes, :label_method => :full_name_with_post_count f.input :author, :as => :check_boxes, :label_method => Proc.new { |a| "#{a.name} (#{pluralize("post", a.posts.count)})" }
The :value_method option provides the same customization of the value attribute of each checkbox input tag.
Examples:
f.input :author, :as => :check_boxes, :value_method => :full_name f.input :author, :as => :check_boxes, :value_method => :login f.input :author, :as => :check_boxes, :value_method => Proc.new { |a| "author_#{a.login}" }
Formtastic works around a bug in rails handling of check box collections by not generating the hidden fields for state checking of the checkboxes The :hidden_fields option provides a way to re-enable these hidden inputs by setting it to true.
f.input :authors, :as => :check_boxes, :hidden_fields => false f.input :authors, :as => :check_boxes, :hidden_fields => true
Finally, you can set :value_as_class => true if you want the li wrapper around each checkbox / label combination to contain a class with the value of the radio button (useful for applying specific CSS or Javascript to a particular checkbox).
# File lib/formtastic.rb, line 1152 def check_boxes_input(method, options) collection = find_collection_for_column(method, options) html_options = options.delete(:input_html) || {} input_name = generate_association_input_name(method) hidden_fields = options.delete(:hidden_fields) value_as_class = options.delete(:value_as_class) unchecked_value = options.delete(:unchecked_value) || '' html_options = { :name => "#{@object_name}[#{input_name}][]" }.merge(html_options) input_ids = [] selected_values = find_selected_values_for_column(method, options) disabled_option_is_present = options.key?(:disabled) disabled_values = [*options[:disabled]] if disabled_option_is_present li_options = value_as_class ? { :class => [method.to_s.singularize, 'default'].join('_') } : {} list_item_content = collection.map do |c| label = c.is_a?(Array) ? c.first : c value = c.is_a?(Array) ? c.last : c input_id = generate_html_id(input_name, value.to_s.gsub(/\s/, '_').gsub(/\W/, '').downcase) input_ids << input_id html_options[:checked] = selected_values.include?(value) html_options[:disabled] = disabled_values.include?(value) if disabled_option_is_present html_options[:id] = input_id li_content = template.content_tag(:label, Formtastic::Util.html_safe("#{create_check_boxes(input_name, html_options, value, unchecked_value, hidden_fields)} #{escape_html_entities(label)}"), :for => input_id ) li_options = value_as_class ? { :class => [method.to_s.singularize, value.to_s.downcase].join('_') } : {} template.content_tag(:li, Formtastic::Util.html_safe(li_content), li_options) end fieldset_content = legend_tag(method, options) fieldset_content << create_hidden_field_for_check_boxes(input_name, value_as_class) unless hidden_fields fieldset_content << template.content_tag(:ol, Formtastic::Util.html_safe(list_item_content.join)) template.content_tag(:fieldset, fieldset_content) end
Outputs a country select input, wrapping around a regular country_select helper. Rails doesn't come with a country_select helper by default any more, so you'll need to install the “official” plugin, or, if you wish, any other country_select plugin that behaves in the same way.
The Rails plugin iso-3166-country-select plugin can be found “here”:github.com/rails/iso-3166-country-select.
By default, Formtastic includes a handfull of english-speaking countries as “priority counties”, which you can change to suit your market and user base (see README for more info on config).
Examples:
f.input :location, :as => :country # use Formtastic::SemanticFormBuilder.priority_countries array for the priority countries f.input :location, :as => :country, :priority_countries => /Australia/ # set your own
# File lib/formtastic.rb, line 1237 def country_input(method, options) raise "To use the :country input, please install a country_select plugin, like this one: http://github.com/rails/iso-3166-country-select" unless respond_to?(:country_select) html_options = options.delete(:input_html) || {} priority_countries = options.delete(:priority_countries) || self.priority_countries field_id = generate_html_id(method, "") html_options[:id] ||= field_id label_options = options_for_label(options) label_options[:for] ||= html_options[:id] label(method, label_options) << country_select(method, priority_countries, strip_formtastic_options(options), html_options) end
Outputs a fieldset with a legend for the method label, and a ordered list (ol) of list items (li), one for each fragment for the date (year, month, day). Each li contains a label (eg “Year”) and a select box. Overwriting the label is possible by adding the :labels option. :labels should be a hash with the field (e.g. day) as key and the label text as value. See #date_or_datetime_input for a more detailed output example.
Some of Rails' options for select_date are supported, but not everything yet, see documentation of #date_or_datetime_input() for more information.
# File lib/formtastic.rb, line 962 def date_input(method, options) options = set_include_blank(options) date_or_datetime_input(method, options.merge(:discard_hour => true)) end
Helper method used by :as => (:date|:datetime|:time). Generates a fieldset containing a legend (for what would normally be considered the label), and an ordered list of list items for year, month, day, hour, etc, each containing a label and a select. Example:
<fieldset>
<legend>Created At</legend> <ol> <li> <label for="user_created_at_1i">Year</label> <select id="user_created_at_1i" name="user[created_at(1i)]"> <option value="2003">2003</option> ... <option value="2013">2013</option> </select> </li> <li> <label for="user_created_at_2i">Month</label> <select id="user_created_at_2i" name="user[created_at(2i)]"> <option value="1">January</option> ... <option value="12">December</option> </select> </li> <li> <label for="user_created_at_3i">Day</label> <select id="user_created_at_3i" name="user[created_at(3i)]"> <option value="1">1</option> ... <option value="31">31</option> </select> </li> </ol>
</fieldset>
This is an absolute abomination, but so is the official Rails select_date().
Options:
* @:order => [:month, :day, :year]@ * @:include_seconds@ => true@ * @:discard_(year|month|day|hour|minute) => true@ * @:include_blank => true@ * @:labels => {}@
# File lib/formtastic.rb, line 1036 def date_or_datetime_input(method, options) position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 } i18n_date_order = ::I18n.t(:order, :scope => [:date]) i18n_date_order = nil unless i18n_date_order.is_a?(Array) inputs = options.delete(:order) || i18n_date_order || [:year, :month, :day] inputs = [] if options[:ignore_date] labels = options.delete(:labels) || {} time_inputs = [:hour, :minute] time_inputs << :second if options[:include_seconds] list_items_capture = "" hidden_fields_capture = "" datetime = @object.send(method) if @object && @object.send(method) html_options = options.delete(:input_html) || {} input_ids = [] (inputs + time_inputs).each do |input| input_ids << input_id = generate_html_id(method, "#{position[input]}i") field_name = "#{method}(#{position[input]}i)" if options[:"discard_#{input}"] break if time_inputs.include?(input) hidden_value = datetime.respond_to?(input) ? datetime.send(input) : datetime hidden_fields_capture << template.hidden_field_tag("#{@object_name}[#{field_name}]", (hidden_value || 1), :id => input_id) else opts = strip_formtastic_options(options).merge(:prefix => @object_name, :field_name => field_name, :default => datetime) item_label_text = labels[input] || ::I18n.t(input.to_s, :default => input.to_s.humanize, :scope => [:datetime, :prompts]) list_items_capture << template.content_tag(:li, Formtastic::Util.html_safe([ !item_label_text.blank? ? template.content_tag(:label, Formtastic::Util.html_safe(item_label_text), :for => input_id) : "", template.send(:"select_#{input}", datetime, opts, html_options.merge(:id => input_id)) ].join("")) ) end end hidden_fields_capture << field_set_and_list_wrapping_for_method(method, options.merge(:label_for => input_ids.first), list_items_capture) end
Outputs a fieldset with a legend for the method label, and a ordered list (ol) of list items (li), one for each fragment for the date (year, month, day, hour, min, sec). Each li contains a label (eg “Year”) and a select box. Overwriting the label is possible by adding the :labels option. :labels should be a hash with the field (e.g. day) as key and the label text as value. See #date_or_datetime_input for a more detailed output example.
Some of Rails' options for select_date are supported, but not everything yet, see documentation of #date_or_datetime_input() for more information.
# File lib/formtastic.rb, line 975 def datetime_input(method, options) options = set_include_blank(options) date_or_datetime_input(method, options) end
Detects the method to call for fetching group members from the groups when grouping select options
# File lib/formtastic.rb, line 1560 def detect_group_association(method, group_by) object_to_method_reflection = reflection_for(method) method_class = object_to_method_reflection.klass method_to_group_association = method_class.reflect_on_association(group_by) group_class = method_to_group_association.klass # This will return in the normal case return method.to_s.pluralize.to_sym if group_class.reflect_on_association(method.to_s.pluralize) # This is for belongs_to associations named differently than their class # form.input :parent, :group_by => :customer # eg. # class Project # belongs_to :parent, :class_name => 'Project', :foreign_key => 'parent_id' # belongs_to :customer # end # class Customer # has_many :projects # end group_method = method_class.to_s.underscore.pluralize.to_sym return group_method if group_class.reflect_on_association(group_method) # :projects # This is for has_many associations named differently than their class # eg. # class Project # belongs_to :parent, :class_name => 'Project', :foreign_key => 'parent_id' # belongs_to :customer # end # class Customer # has_many :tasks, :class_name => 'Project', :foreign_key => 'customer_id' # end possible_associations = group_class.reflect_on_all_associations(:has_many).find_all{|assoc| assoc.klass == object_class} return possible_associations.first.name.to_sym if possible_associations.count == 1 raise "Cannot infer group association for #{method} grouped by #{group_by}, there were #{possible_associations.empty? ? 'no' : possible_associations.size} possible associations. Please specify using :group_association" end
Detects the label and value methods from a collection using methods set in collection_label_methods and collection_value_methods. For some ruby core classes sensible defaults have been defined. It will use and delete the options :label_method and :value_methods when present.
# File lib/formtastic.rb, line 1532 def detect_label_and_value_method!(collection, options = {}) sample = collection.first || collection.last case sample when Array label, value = :first, :last when Integer label, value = :to_s, :to_i when String, NilClass label, value = :to_s, :to_s end # Order of preference: user supplied method, class defaults, auto-detect label = options[:label_method] || label || collection_label_methods.find { |m| sample.respond_to?(m) } value = options[:value_method] || value || collection_value_methods.find { |m| sample.respond_to?(m) } [label, value] end
Outputs a label and a standard Rails email field inside the wrapper.
# File lib/formtastic.rb, line 683 def email_input(method, options) basic_input_helper(:email_field, :email, method, options) end
# File lib/formtastic.rb, line 499 def error_keys(method, options) @methods_for_error ||= {} @methods_for_error[method] ||= begin methods_for_error = [method.to_sym] methods_for_error << file_metadata_suffixes.map{|suffix| "#{method}_#{suffix}".to_sym} if is_file?(method, options) methods_for_error << [association_primary_key(method)] if association_macro_for_method(method) == :belongs_to methods_for_error.flatten.compact.uniq end end
Outputs a label and a standard Rails file field inside the wrapper.
# File lib/formtastic.rb, line 678 def file_input(method, options) basic_input_helper(:file_field, :file, method, options) end
Used by check_boxes input. The selected values will be set by retrieving the value through the association.
If the collection is not a hash or an array of strings, fixnums or symbols, we use value_method to retrieve an array with the values
# File lib/formtastic.rb, line 1199 def find_selected_values_for_column(method, options) if object.respond_to?(method) collection = [object.send(method)].compact.flatten label, value = detect_label_and_value_method!(collection, options) [*collection.map { |o| send_or_call(value, o) }].compact else [] end end
# File lib/formtastic.rb, line 1677 def get_maxlength_for(method) validation = validations_for(method).find do |validation| (validation.respond_to?(:macro) && validation.macro == :validates_length_of) || # Rails 2 validation (validation.respond_to?(:kind) && validation.kind == :length) # Rails 3 validator end if validation validation.options[:maximum] || (validation.options[:within].present? ? validation.options[:within].max : nil) else nil end end
# File lib/formtastic.rb, line 509 def has_errors?(method, options) methods_for_error = error_keys(method,options) @object && @object.respond_to?(:errors) && methods_for_error.any?{|error| !@object.errors[error].blank?} end
Generates an input for the given method using the type supplied with :as.
# File lib/formtastic.rb, line 1278 def inline_input_for(method, options) send(:"#{options.delete(:as)}_input", method, options) end
# File lib/formtastic.rb, line 1467 def is_file?(method, options = {}) @files ||= {} @files[method] ||= (options[:as].present? && options[:as] == :file) || begin file = @object.send(method) if @object && @object.respond_to?(method) file && file_methods.any?{|m| file.respond_to?(m)} end end
Generates the legend for radiobuttons and checkboxes
# File lib/formtastic.rb, line 1410 def legend_tag(method, options = {}) if options[:label] == false Formtastic::Util.html_safe("") else text = localized_string(method, options[:label], :label) || humanized_attribute_name(method) text += required_or_optional_string(options.delete(:required)) text = Formtastic::Util.html_safe(text) template.content_tag :legend, template.label_tag(nil, text, :for => nil), :class => :label end end
# File lib/formtastic.rb, line 1820 def model_name @object.present? ? @object.class.name : @object_name.to_s.classify end
# File lib/formtastic.rb, line 1824 def normalize_model_name(name) if name =~ /(.+)\[(.+)\]/ [$1, $2] else [name] end end
Outputs a label and standard Rails text field inside the wrapper.
# File lib/formtastic.rb, line 668 def numeric_input(method, options) basic_input_helper(:text_field, :numeric, method, options) end
Determines whether the given options evaluate to true
# File lib/formtastic.rb, line 629 def options_require_validation?(options) #nodoc allow_blank = options[:allow_blank] return !allow_blank unless allow_blank.nil? if_condition = !options[:if].nil? condition = if_condition ? options[:if] : options[:unless] condition = if condition.respond_to?(:call) condition.call(@object) elsif condition.is_a?(::Symbol) && @object.respond_to?(condition) @object.send(condition) else condition end if_condition ? !!condition : !condition end
Outputs a label and standard Rails password field inside the wrapper.
# File lib/formtastic.rb, line 663 def password_input(method, options) basic_input_helper(:password_field, :password, method, options) end
Outputs a label and a standard Rails phone field inside the wrapper.
# File lib/formtastic.rb, line 688 def phone_input(method, options) basic_input_helper(:phone_field, :phone, method, options) end
Outputs a fieldset containing a legend for the label text, and an ordered list (ol) of list items, one for each possible choice in the belongs_to association. Each li contains a label and a radio input.
Example:
f.input :author, :as => :radio
Output:
<fieldset> <legend><span>Author</span></legend> <ol> <li> <label for="book_author_id_1"><input id="book_author_id_1" name="book[author_id]" type="radio" value="1" /> Justin French</label> </li> <li> <label for="book_author_id_2"><input id="book_author_id_2" name="book[owner_id]" type="radio" value="2" /> Kate French</label> </li> </ol> </fieldset>
You can customize the choices available in the radio button set by passing in a collection (an Array or Hash) through the :collection option. If not provided, the choices are found by reflecting on the association (Author.all in the example above).
Examples:
f.input :author, :as => :radio, :collection => @authors f.input :author, :as => :radio, :collection => Author.all f.input :author, :as => :radio, :collection => [@justin, @kate] f.input :author, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
The :label_method option allows you to customize the label for each radio button two ways:
by naming the correct method to call on each object in the collection as a symbol (:name, :login, etc)
by passing a Proc that will be called on each object in the collection, allowing you to use helpers or multiple model attributes together
Examples:
f.input :author, :as => :radio, :label_method => :full_name f.input :author, :as => :radio, :label_method => :login f.input :author, :as => :radio, :label_method => :full_name_with_post_count f.input :author, :as => :radio, :label_method => Proc.new { |a| "#{a.name} (#{pluralize("post", a.posts.count)})" }
The :value_method option provides the same customization of the value attribute of each option tag.
Examples:
f.input :author, :as => :radio, :value_method => :full_name f.input :author, :as => :radio, :value_method => :login f.input :author, :as => :radio, :value_method => Proc.new { |a| "author_#{a.login}" }
Finally, you can set :value_as_class => true if you want the li wrapper around each radio button / label combination to contain a class with the value of the radio button (useful for applying specific CSS or Javascript to a particular radio button).
# File lib/formtastic.rb, line 924 def radio_input(method, options) collection = find_collection_for_column(method, options) html_options = strip_formtastic_options(options).merge(options.delete(:input_html) || {}) input_name = generate_association_input_name(method) value_as_class = options.delete(:value_as_class) input_ids = [] list_item_content = collection.map do |c| label = c.is_a?(Array) ? c.first : c value = c.is_a?(Array) ? c.last : c input_id = generate_html_id(input_name, value.to_s.gsub(/\s/, '_').gsub(/\W/, '').downcase) input_ids << input_id html_options[:id] = input_id li_content = template.content_tag(:label, Formtastic::Util.html_safe("#{radio_button(input_name, value, html_options)} #{escape_html_entities(label)}"), :for => input_id ) li_options = value_as_class ? { :class => [method.to_s.singularize, value.to_s.downcase].join('_') } : {} template.content_tag(:li, Formtastic::Util.html_safe(li_content), li_options) end template.content_tag(:fieldset, legend_tag(method, options) << template.content_tag(:ol, Formtastic::Util.html_safe(list_item_content.join)) ) end
# File lib/formtastic.rb, line 514 def render_inline_errors? @object && @object.respond_to?(:errors) && INLINE_ERROR_TYPES.include?(inline_errors) end
Outputs a label and a standard Rails search field inside the wrapper.
# File lib/formtastic.rb, line 698 def search_input(method, options) basic_input_helper(:search_field, :search, method, options) end
Outputs a label and a select box containing options from the parent (belongs_to, has_many, has_and_belongs_to_many) association. If an association is has_many or has_and_belongs_to_many the select box will be set as multi-select and size = 5
Example (belongs_to):
f.input :author <label for="book_author_id">Author</label> <select id="book_author_id" name="book[author_id]"> <option value=""></option> <option value="1">Justin French</option> <option value="2">Jane Doe</option> </select>
Example (has_many):
f.input :chapters <label for="book_chapter_ids">Chapters</label> <select id="book_chapter_ids" name="book[chapter_ids]"> <option value=""></option> <option value="1">Chapter 1</option> <option value="2">Chapter 2</option> </select>
Example (has_and_belongs_to_many):
f.input :authors <label for="book_author_ids">Authors</label> <select id="book_author_ids" name="book[author_ids]"> <option value=""></option> <option value="1">Justin French</option> <option value="2">Jane Doe</option> </select>
You can customize the options available in the select by passing in a collection. A collection can be given as an Array, a Hash or as a String (containing pre-rendered HTML options). If not provided, the choices are found by inferring the parent's class name from the method name and simply calling all on it (VehicleOwner.all in the example above).
Examples:
f.input :author, :collection => @authors f.input :author, :collection => Author.all f.input :author, :collection => [@justin, @kate] f.input :author, :collection => {@justin.name => @justin.id, @kate.name => @kate.id} f.input :author, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"] f.input :author, :collection => grouped_options_for_select(["North America",[["United States","US"],["Canada","CA"]]])
The :label_method option allows you to customize the text label inside each option tag two ways:
by naming the correct method to call on each object in the collection as a symbol (:name, :login, etc)
by passing a Proc that will be called on each object in the collection, allowing you to use helpers or multiple model attributes together
Examples:
f.input :author, :label_method => :full_name f.input :author, :label_method => :login f.input :author, :label_method => :full_name_with_post_count f.input :author, :label_method => Proc.new { |a| "#{a.name} (#{pluralize("post", a.posts.count)})" }
The :value_method option provides the same customization of the value attribute of each option tag.
Examples:
f.input :author, :value_method => :full_name f.input :author, :value_method => :login f.input :author, :value_method => Proc.new { |a| "author_#{a.login}" }
You can pass html_options to the select tag using :input_html => {}
Examples:
f.input :authors, :input_html => {:size => 20, :multiple => true}
By default, all select inputs will have a blank option at the top of the list. You can add a prompt with the :prompt option, or disable the blank option with :include_blank => false.
You can group the options in optgroup elements by passing the :group_by option (Note: only tested for belongs_to relations)
Examples:
f.input :author, :group_by => :continent
All the other options should work as expected. If you want to call a custom method on the group item. You can include the option:group_label_method Examples:
f.input :author, :group_by => :continents, :group_label_method => :something_different
# File lib/formtastic.rb, line 810 def select_input(method, options) html_options = options.delete(:input_html) || {} html_options[:multiple] = html_options[:multiple] || options.delete(:multiple) html_options.delete(:multiple) if html_options[:multiple].nil? reflection = reflection_for(method) if reflection && [ :has_many, :has_and_belongs_to_many ].include?(reflection.macro) html_options[:multiple] = true if html_options[:multiple].nil? html_options[:size] ||= 5 options[:include_blank] ||= false end options = set_include_blank(options) input_name = generate_association_input_name(method) html_options[:id] ||= generate_html_id(input_name, "") select_html = if options[:group_by] # The grouped_options_select is a bit counter intuitive and not optimised (mostly due to ActiveRecord). # The formtastic user however shouldn't notice this too much. raw_collection = find_raw_collection_for_column(method, options.reverse_merge(:find_options => { :include => options[:group_by] })) label, value = detect_label_and_value_method!(raw_collection, options) group_collection = raw_collection.map { |option| option.send(options[:group_by]) }.uniq group_label_method = options[:group_label_method] || detect_label_method(group_collection) group_collection = group_collection.sort_by { |group_item| group_item.send(group_label_method) } group_association = options[:group_association] || detect_group_association(method, options[:group_by]) # Here comes the monster with 8 arguments grouped_collection_select(input_name, group_collection, group_association, group_label_method, value, label, strip_formtastic_options(options), html_options) else collection = find_collection_for_column(method, options) select(input_name, collection, strip_formtastic_options(options), html_options) end label_options = options_for_label(options).merge(:input_name => input_name) label_options[:for] ||= html_options[:id] label(method, label_options) << select_html end
# File lib/formtastic.rb, line 1832 def send_or_call(duck, object) if duck.is_a?(Proc) duck.call(object) else object.send(duck) end end
# File lib/formtastic.rb, line 1840 def set_include_blank(options) unless options.key?(:include_blank) || options.key?(:prompt) options[:include_blank] = include_blank_for_select_by_default end options end
Outputs a label and standard Rails text field inside the wrapper.
# File lib/formtastic.rb, line 658 def string_input(method, options) basic_input_helper(:text_field, :string, method, options) end
Ouputs a label and standard Rails text area inside the wrapper.
# File lib/formtastic.rb, line 673 def text_input(method, options) basic_input_helper(:text_area, :text, method, options) end
Outputs a fieldset with a legend for the method label, and a ordered list (ol) of list items (li), one for each fragment for the time (hour, minute, second). Each li contains a label (eg “Hour”) and a select box. Overwriting the label is possible by adding the :labels option. :labels should be a hash with the field (e.g. day) as key and the label text as value. See #date_or_datetime_input for a more detailed output example.
Some of Rails' options for select_time are supported, but not everything yet, see documentation of #date_or_datetime_input() for more information.
# File lib/formtastic.rb, line 988 def time_input(method, options) options = set_include_blank(options) date_or_datetime_input(method, options.merge(:discard_year => true, :discard_month => true, :discard_day => true)) end
Outputs a timezone select input as Rails' time_zone_select helper. You can give priority zones as option.
Examples:
f.input :time_zone, :as => :time_zone, :priority_zones => /Australia/
# File lib/formtastic.rb, line 857 def time_zone_input(method, options) html_options = options.delete(:input_html) || {} field_id = generate_html_id(method, "") html_options[:id] ||= field_id label_options = options_for_label(options) label_options[:for] ||= html_options[:id] label(method, label_options) << time_zone_select(method, options.delete(:priority_zones), strip_formtastic_options(options), html_options) end
Outputs a label and a standard Rails url field inside the wrapper.
# File lib/formtastic.rb, line 693 def url_input(method, options) basic_input_helper(:url_field, :url, method, options) end
Returns the active validations for the given method or an empty Array if no validations are found for the method.
By default, the if/unless options of the validations are evaluated and only the validations that should be run for the current object state are returned. Pass :all to the last parameter to return :all validations regardless of if/unless options.
Requires the ValidationReflection plugin to be present or an ActiveModel. Returns an epmty Array if neither is the case.
# File lib/formtastic.rb, line 1653 def validations_for(method, mode = :active) # ActiveModel? validations = if @object && @object.class.respond_to?(:validators_on) @object.class.validators_on(method) else # ValidationReflection plugin? if @object && @object.class.respond_to?(:reflect_on_validations_for) @object.class.reflect_on_validations_for(method) else [] end end validations = validations.select do |validation| (validation.options.present? ? options_require_validation?(validation.options) : true) end unless mode == :all return validations end