2009-11-18 18:50:43 -05:00
|
|
|
module SimpleForm
|
|
|
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
2009-12-13 16:16:00 -05:00
|
|
|
attr_reader :template, :object_name, :object, :attribute_name, :column,
|
2009-12-10 22:18:56 -05:00
|
|
|
:reflection, :input_type, :options
|
2009-12-09 13:55:39 -05:00
|
|
|
|
2009-12-10 22:18:56 -05:00
|
|
|
# Basic input helper, combines all components in the stack to generate
|
|
|
|
# input html based on options the user define and some guesses through
|
2009-12-10 12:57:24 -05:00
|
|
|
# database column information. By default a call to input will generate
|
2009-12-10 22:18:56 -05:00
|
|
|
# label + input + hint (when defined) + errors (when exists), and all can
|
|
|
|
# be configured inside a wrapper html.
|
2009-12-10 16:52:23 -05:00
|
|
|
#
|
2009-12-11 06:19:46 -05:00
|
|
|
# == Examples
|
2009-12-10 16:52:23 -05:00
|
|
|
#
|
2009-12-10 12:57:24 -05:00
|
|
|
# # Imagine @user has error "can't be blank" on name
|
|
|
|
# simple_form_for @user do |f|
|
|
|
|
# f.input :name, :hint => 'My hint'
|
|
|
|
# end
|
|
|
|
#
|
2009-12-10 21:34:20 -05:00
|
|
|
# This is the output html (only the input portion, not the form):
|
|
|
|
#
|
2009-12-10 17:23:29 -05:00
|
|
|
# <label class="string required" for="user_name">
|
|
|
|
# <abbr title="required">*</abbr> Super User Name!
|
|
|
|
# </label>
|
|
|
|
# <input class="string required" id="user_name" maxlength="100"
|
|
|
|
# name="user[name]" size="100" type="text" value="Carlos" />
|
2009-12-10 12:57:24 -05:00
|
|
|
# <span class="hint">My hint</span>
|
|
|
|
# <span class="error">can't be blank</span>
|
|
|
|
#
|
|
|
|
# Each database type will render a default input, based on some mappings and
|
|
|
|
# heuristic to determine which is the best option.
|
2009-12-10 16:52:23 -05:00
|
|
|
#
|
2009-12-10 12:57:24 -05:00
|
|
|
# You have some options for the input to enable/disable some functions:
|
|
|
|
#
|
2009-12-10 16:52:23 -05:00
|
|
|
# :as => allows you to define the input type you want, for instance you
|
|
|
|
# can use it to generate a text field for a date column.
|
|
|
|
#
|
|
|
|
# :required => defines whether this attribute is required or not. True
|
|
|
|
# by default.
|
|
|
|
#
|
|
|
|
# The fact SimpleForm is built in components allow the interface to be unified.
|
|
|
|
# So, for instance, if you need to disable :hint for a given input, you can pass
|
|
|
|
# :hint => false. The same works for :error, :label and :wrapper.
|
|
|
|
#
|
|
|
|
# Besides the html for any component can be changed. So, if you want to change
|
|
|
|
# the label html you just need to give a hash to :label_html. To configure the
|
|
|
|
# input html, supply :input_html instead and so on.
|
|
|
|
#
|
|
|
|
# == Options
|
|
|
|
#
|
|
|
|
# Some inputs, as datetime, time and select allow you to give extra options, like
|
2009-12-10 21:34:20 -05:00
|
|
|
# prompt and/or include blank. Such options are given in plainly:
|
2009-12-10 16:52:23 -05:00
|
|
|
#
|
2009-12-10 21:34:20 -05:00
|
|
|
# f.input :created_at, :include_blank => true
|
2009-12-10 16:52:23 -05:00
|
|
|
#
|
|
|
|
# == Collection
|
|
|
|
#
|
|
|
|
# When playing with collections (:radio and :select inputs), you have three extra
|
|
|
|
# options:
|
|
|
|
#
|
|
|
|
# :collection => use to determine the collection to generate the radio or select
|
|
|
|
#
|
|
|
|
# :label_method => the method to apply on the array collection to get the label
|
|
|
|
#
|
|
|
|
# :value_method => the method to apply on the array collection to get the value
|
|
|
|
#
|
2009-12-11 08:53:18 -05:00
|
|
|
# == Priority
|
|
|
|
#
|
|
|
|
# Some inputs, as :time_zone and :country accepts a :priority option. If none is
|
|
|
|
# given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectivelly.
|
|
|
|
#
|
2010-01-09 08:34:52 -05:00
|
|
|
module Labels
|
|
|
|
def self.included(base)
|
|
|
|
base.extend ClassMethods
|
|
|
|
end
|
|
|
|
|
|
|
|
module ClassMethods #:nodoc:
|
|
|
|
def translate_required_html
|
|
|
|
i18n_cache :translate_required_html do
|
|
|
|
I18n.t(:"simple_form.required.html", :default =>
|
|
|
|
%[<abbr title="#{translate_required_text}">#{translate_required_mark}</abbr>]
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def translate_required_text
|
|
|
|
I18n.t(:"simple_form.required.text", :default => 'required')
|
|
|
|
end
|
|
|
|
|
|
|
|
def translate_required_mark
|
|
|
|
I18n.t(:"simple_form.required.mark", :default => '*')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def label
|
|
|
|
@builder.label(label_target, label_text, label_html_options)
|
|
|
|
end
|
|
|
|
|
|
|
|
def label_text
|
|
|
|
SimpleForm.label_text.call(raw_label_text, required_label_text)
|
|
|
|
end
|
2010-01-09 09:39:14 -05:00
|
|
|
|
2010-01-09 08:34:52 -05:00
|
|
|
def label_target
|
2010-01-09 09:39:14 -05:00
|
|
|
attribute_name
|
2010-01-09 08:34:52 -05:00
|
|
|
end
|
2010-01-09 09:39:14 -05:00
|
|
|
|
2010-01-09 08:34:52 -05:00
|
|
|
def label_html_options
|
|
|
|
label_options = html_options_for(:label, input_type, required_class)
|
|
|
|
label_options[:for] = options[:input_html][:id] if options.key?(:input_html)
|
|
|
|
label_options
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
def raw_label_text #:nodoc:
|
|
|
|
options[:label] || label_translation
|
|
|
|
end
|
|
|
|
|
|
|
|
# Default required text when attribute is required.
|
|
|
|
def required_label_text #:nodoc:
|
|
|
|
attribute_required? ? self.class.translate_required_html.dup : ''
|
|
|
|
end
|
|
|
|
|
|
|
|
# First check human attribute name and then labels.
|
|
|
|
# TODO Remove me in Rails > 2.3.5
|
|
|
|
def label_translation #:nodoc:
|
|
|
|
default = if object.class.respond_to?(:human_attribute_name)
|
|
|
|
object.class.human_attribute_name(reflection_or_attribute_name.to_s)
|
|
|
|
else
|
|
|
|
attribute_name.to_s.humanize
|
|
|
|
end
|
2009-12-08 08:48:31 -05:00
|
|
|
|
2010-01-09 08:34:52 -05:00
|
|
|
translate(:labels, default)
|
2009-12-09 14:41:20 -05:00
|
|
|
end
|
2010-01-09 08:34:52 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
module Hints
|
|
|
|
def hint
|
|
|
|
template.content_tag(hint_tag, hint_text, hint_html_options) unless hint_text.blank?
|
|
|
|
end
|
|
|
|
|
|
|
|
def hint_tag
|
|
|
|
options[:hint_tag] || SimpleForm.hint_tag
|
|
|
|
end
|
|
|
|
|
|
|
|
def hint_text
|
|
|
|
@hint_text ||= options[:hint] || translate(:hints)
|
|
|
|
end
|
|
|
|
|
|
|
|
def hint_html_options
|
|
|
|
html_options_for(:hint, :hint)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
module Errors
|
|
|
|
def error
|
|
|
|
template.content_tag(error_tag, error_text, error_html_options) if object && errors.present?
|
|
|
|
end
|
|
|
|
|
|
|
|
def error_tag
|
|
|
|
options[:error_tag] || SimpleForm.error_tag
|
|
|
|
end
|
|
|
|
|
|
|
|
def error_text
|
|
|
|
errors.to_sentence
|
|
|
|
end
|
|
|
|
|
|
|
|
def error_html_options
|
|
|
|
html_options_for(:error, :error)
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
def errors
|
|
|
|
@errors ||= (errors_on_attribute + errors_on_association).compact
|
|
|
|
end
|
|
|
|
|
|
|
|
def errors_on_attribute
|
|
|
|
Array(object.errors[attribute_name])
|
|
|
|
end
|
|
|
|
|
|
|
|
def errors_on_association
|
|
|
|
reflection ? Array(object.errors[reflection.name]) : []
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-01-09 09:39:14 -05:00
|
|
|
class Base
|
|
|
|
extend I18nCache
|
|
|
|
|
2010-01-09 08:34:52 -05:00
|
|
|
include Errors
|
|
|
|
include Hints
|
|
|
|
include Labels
|
|
|
|
|
|
|
|
delegate :template, :object, :object_name, :attribute_name, :column,
|
|
|
|
:reflection, :input_type, :options, :to => :@builder
|
|
|
|
|
|
|
|
def initialize(builder)
|
|
|
|
@builder = builder
|
|
|
|
end
|
|
|
|
|
|
|
|
def input
|
2010-01-09 09:39:14 -05:00
|
|
|
raise NotImplemented
|
|
|
|
end
|
|
|
|
|
|
|
|
def input_options
|
|
|
|
options[:include_blank] = true unless skip_include_blank?
|
|
|
|
options
|
|
|
|
end
|
|
|
|
|
|
|
|
def input_html_options
|
|
|
|
html_options_for(:input, input_type, required_class)
|
2010-01-09 08:34:52 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def render
|
|
|
|
pieces = SimpleForm.components.select { |n| n unless @builder.options[n] == false }
|
2010-01-09 09:39:14 -05:00
|
|
|
content = pieces.map!{ |p| send(p).to_s }.join
|
|
|
|
wrap(content)
|
|
|
|
end
|
|
|
|
|
|
|
|
def wrap(content)
|
|
|
|
if wrapper_tag && options[:wrapper] != false
|
|
|
|
template.content_tag(wrapper_tag, content, wrapper_html_options)
|
|
|
|
else
|
|
|
|
content
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def wrapper_tag
|
|
|
|
options[:wrapper_tag] || SimpleForm.wrapper_tag
|
|
|
|
end
|
|
|
|
|
|
|
|
def wrapper_html_options
|
|
|
|
html_options_for(:wrapper, input_type, required_class)
|
2010-01-09 08:34:52 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
# When action is create or update, we still should use new and edit
|
|
|
|
ACTIONS = {
|
|
|
|
:create => :new,
|
|
|
|
:update => :edit
|
|
|
|
}
|
|
|
|
|
|
|
|
def attribute_required?
|
|
|
|
options[:required] != false
|
|
|
|
end
|
|
|
|
|
|
|
|
def required_class
|
|
|
|
attribute_required? ? :required : :optional
|
|
|
|
end
|
|
|
|
|
|
|
|
# Find reflection name when available, otherwise use attribute
|
|
|
|
def reflection_or_attribute_name
|
|
|
|
reflection ? reflection.name : attribute_name
|
|
|
|
end
|
|
|
|
|
2010-01-09 09:39:14 -05:00
|
|
|
# Check if :include_blank must be included by default.
|
|
|
|
def skip_include_blank?
|
|
|
|
options.key?(:prompt) || options.key?(:include_blank)
|
|
|
|
end
|
|
|
|
|
2010-01-09 08:34:52 -05:00
|
|
|
# Retrieve options for the given namespace from the options hash
|
|
|
|
def html_options_for(namespace, *extra)
|
|
|
|
html_options = options[:"#{namespace}_html"] || {}
|
|
|
|
html_options[:class] = (extra << html_options[:class]).join(' ').strip if extra.present?
|
|
|
|
html_options
|
|
|
|
end
|
|
|
|
|
|
|
|
# Lookup translations for the given namespace using I18n, based on object name,
|
|
|
|
# actual action and attribute name. Lookup priority as follows:
|
|
|
|
#
|
|
|
|
# simple_form.{namespace}.{model}.{action}.{attribute}
|
|
|
|
# simple_form.{namespace}.{model}.{attribute}
|
|
|
|
# simple_form.{namespace}.{attribute}
|
|
|
|
#
|
|
|
|
# Namespace is used for :labels and :hints.
|
|
|
|
#
|
|
|
|
# Model is the actual object name, for a @user object you'll have :user.
|
|
|
|
# Action is the action being rendered, usually :new or :edit.
|
|
|
|
# And attribute is the attribute itself, :name for example.
|
|
|
|
#
|
|
|
|
# Example:
|
|
|
|
#
|
|
|
|
# simple_form:
|
|
|
|
# labels:
|
|
|
|
# user:
|
|
|
|
# new:
|
|
|
|
# email: 'E-mail para efetuar o sign in.'
|
|
|
|
# edit:
|
|
|
|
# email: 'E-mail.'
|
|
|
|
#
|
|
|
|
# Take a look at our locale example file.
|
|
|
|
def translate(namespace, default='')
|
|
|
|
lookups = []
|
|
|
|
lookups << :"#{object_name}.#{lookup_action}.#{reflection_or_attribute_name}"
|
|
|
|
lookups << :"#{object_name}.#{reflection_or_attribute_name}"
|
|
|
|
lookups << :"#{reflection_or_attribute_name}"
|
|
|
|
lookups << default
|
|
|
|
I18n.t(lookups.shift, :scope => :"simple_form.#{namespace}", :default => lookups)
|
|
|
|
end
|
|
|
|
|
|
|
|
# The action to be used in lookup.
|
|
|
|
def lookup_action
|
|
|
|
action = template.controller.action_name.to_sym
|
|
|
|
ACTIONS[action] || action
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-01-09 09:39:14 -05:00
|
|
|
# Uses MapType to handle basic input types.
|
|
|
|
class MappingInput < Base
|
|
|
|
extend MapType
|
|
|
|
|
|
|
|
map_type :boolean, :to => :check_box
|
|
|
|
map_type :password, :to => :password_field
|
|
|
|
map_type :text, :to => :text_area
|
|
|
|
map_type :file, :to => :file_field
|
|
|
|
|
|
|
|
def input
|
|
|
|
@builder.send(input_method, attribute_name, input_html_options)
|
|
|
|
end
|
|
|
|
|
|
|
|
def input_method
|
|
|
|
method = self.class.mappings[input_type]
|
|
|
|
raise "Could not find method for #{input_type.inspect}" unless method
|
|
|
|
method
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Handles common text field inputs, as String, Numeric, Float and Decimal.
|
|
|
|
class TextFieldInput < Base
|
|
|
|
def input
|
|
|
|
@builder.text_field(attribute_name, input_html_options)
|
|
|
|
end
|
|
|
|
|
|
|
|
def input_html_options
|
|
|
|
input_options = super
|
|
|
|
input_options[:max_length] ||= column.limit if column
|
|
|
|
input_options
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class DateTimeInput < Base
|
|
|
|
def input
|
|
|
|
@builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options)
|
|
|
|
end
|
|
|
|
|
|
|
|
def label_target
|
|
|
|
case input_type
|
|
|
|
when :date, :datetime
|
|
|
|
"#{attribute_name}_1i"
|
|
|
|
when :time
|
|
|
|
"#{attribute_name}_4i"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class CollectionInput < Base
|
|
|
|
# Default boolean collection for use with selects/radios when no
|
|
|
|
# collection is given. Always fallback to this boolean collection.
|
|
|
|
# Texts can be translated using i18n in "simple_form.true" and
|
|
|
|
# "simple_form.false" keys. See the example locale file.
|
|
|
|
def self.boolean_collection
|
|
|
|
i18n_cache :boolean_collection do
|
|
|
|
[ [I18n.t(:"simple_form.yes", :default => 'Yes'), true],
|
|
|
|
[I18n.t(:"simple_form.no", :default => 'No'), false] ]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def input
|
|
|
|
collection = (options[:collection] || self.class.boolean_collection).to_a
|
|
|
|
detect_collection_methods(collection, options)
|
|
|
|
@builder.send(:"collection_#{input_type}", attribute_name, collection, options[:value_method],
|
|
|
|
options[:label_method], input_options, input_html_options)
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
def skip_include_blank?
|
|
|
|
super || options[:input_html].try(:[], :multiple)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Detect the right method to find the label and value for a collection.
|
|
|
|
# If no label or value method are defined, will attempt to find them based
|
|
|
|
# on default label and value methods that can be configured through
|
|
|
|
# SimpleForm.collection_label_methods and
|
|
|
|
# SimpleForm.collection_value_methods.
|
|
|
|
def detect_collection_methods(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
|
|
|
|
|
|
|
|
options[:label_method] ||= label || SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) }
|
|
|
|
options[:value_method] ||= value || SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Handles hidden input.
|
|
|
|
class HiddenInput < Base
|
|
|
|
def render
|
|
|
|
@builder.hidden_field(attribute_name, input_html_options)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class PriorityInput < Base
|
|
|
|
def input
|
|
|
|
@builder.send(:"#{input_type}_select", attribute_name, input_priority,
|
|
|
|
input_options, input_html_options)
|
|
|
|
end
|
|
|
|
|
|
|
|
def input_priority
|
|
|
|
options[:priority] || SimpleForm.send(:"#{input_type}_priority")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
extend MapType
|
|
|
|
|
|
|
|
map_type :boolean, :password, :text, :file, :to => MappingInput
|
|
|
|
map_type :hidden, :to => HiddenInput # TODO This should be automatic
|
|
|
|
map_type :string, :integer, :decimal, :float, :to => TextFieldInput
|
|
|
|
map_type :select, :radio, :check_boxes, :to => CollectionInput
|
|
|
|
map_type :date, :time, :datetime, :to => DateTimeInput
|
|
|
|
map_type :country, :time_zone, :to => PriorityInput
|
|
|
|
|
2010-01-09 08:34:52 -05:00
|
|
|
def input(attribute_name, options={})
|
|
|
|
define_simple_form_attributes(attribute_name, options)
|
2010-01-09 09:39:14 -05:00
|
|
|
|
|
|
|
if klass = self.class.mappings[input_type]
|
|
|
|
klass.new(self).render
|
|
|
|
else
|
|
|
|
const_get(:"#{input_type.to_s.camelize}Input").new(self).render
|
|
|
|
end
|
2009-11-19 16:26:16 -05:00
|
|
|
end
|
2009-12-13 16:16:00 -05:00
|
|
|
alias :attribute :input
|
2009-11-19 16:26:16 -05:00
|
|
|
|
2009-12-10 21:18:14 -05:00
|
|
|
# Helper for dealing with association selects/radios, generating the
|
2009-12-11 06:19:46 -05:00
|
|
|
# collection automatically. It's just a wrapper to input, so all options
|
|
|
|
# supported in input are also supported by association. Some extra options
|
|
|
|
# can also be given:
|
|
|
|
#
|
|
|
|
# == Options
|
2009-12-10 21:18:14 -05:00
|
|
|
#
|
2009-12-11 06:19:46 -05:00
|
|
|
# * :conditions - Given as conditions when retrieving the collection
|
|
|
|
#
|
2009-12-14 14:56:42 -05:00
|
|
|
# * :include - Given as include when retrieving the collection
|
|
|
|
#
|
|
|
|
# * :joins - Given as joins when retrieving the collection
|
|
|
|
#
|
2009-12-11 06:19:46 -05:00
|
|
|
# * :order - Given as order when retrieving the collection
|
|
|
|
#
|
|
|
|
# * :scope - Given as scopes when retrieving the collection
|
|
|
|
#
|
|
|
|
# == Examples
|
2009-12-10 21:18:14 -05:00
|
|
|
#
|
|
|
|
# simple_form_for @user do |f|
|
|
|
|
# f.association :company # Company.all
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# f.association :company, :order => 'name'
|
|
|
|
# # Company.all(:order => 'name')
|
|
|
|
#
|
|
|
|
# f.association :company, :conditions => { :active => true }
|
|
|
|
# # Company.all(:conditions => { :active => true })
|
|
|
|
#
|
|
|
|
# f.association :company, :collection => Company.all(:order => 'name')
|
|
|
|
# # Same as using :order option, but overriding collection
|
|
|
|
#
|
2009-12-11 06:19:46 -05:00
|
|
|
# f.association :company, :scope => [ :public, :not_broken ]
|
|
|
|
# # Same as doing Company.public.not_broken.all
|
|
|
|
#
|
2009-12-13 16:16:00 -05:00
|
|
|
def association(association, options={})
|
2009-12-10 21:18:14 -05:00
|
|
|
raise ArgumentError, "Association cannot be used in forms not associated with an object" unless @object
|
|
|
|
|
2009-12-11 07:43:43 -05:00
|
|
|
options[:as] ||= :select
|
2009-12-13 16:16:00 -05:00
|
|
|
@reflection = find_association_reflection(association)
|
2009-12-14 05:37:03 -05:00
|
|
|
raise "Association #{association.inspect} not found" unless @reflection
|
2009-12-10 21:34:26 -05:00
|
|
|
|
2009-12-11 07:43:43 -05:00
|
|
|
case @reflection.macro
|
|
|
|
when :belongs_to
|
|
|
|
attribute = @reflection.options[:foreign_key] || :"#{@reflection.name}_id"
|
|
|
|
when :has_one
|
|
|
|
raise ":has_one association are not supported by f.association"
|
|
|
|
else
|
2009-12-11 10:09:00 -05:00
|
|
|
attribute = :"#{@reflection.name.to_s.singularize}_ids"
|
2009-12-11 07:43:43 -05:00
|
|
|
|
|
|
|
if options[:as] == :select
|
|
|
|
html_options = options[:input_html] ||= {}
|
|
|
|
html_options[:size] ||= 5
|
|
|
|
html_options[:multiple] = true unless html_options.key?(:multiple)
|
|
|
|
end
|
|
|
|
end
|
2009-12-10 21:18:14 -05:00
|
|
|
|
2009-12-10 21:34:26 -05:00
|
|
|
options[:collection] ||= begin
|
2009-12-14 14:56:42 -05:00
|
|
|
find_options = options.slice(:conditions, :order, :include, :joins)
|
2009-12-11 06:19:46 -05:00
|
|
|
klass = Array(options[:scope]).inject(@reflection.klass) do |klass, scope|
|
|
|
|
klass.send(scope)
|
|
|
|
end
|
|
|
|
klass.all(find_options)
|
2009-12-10 21:18:14 -05:00
|
|
|
end
|
|
|
|
|
2009-12-10 22:18:56 -05:00
|
|
|
returning(input(attribute, options)) { @reflection = nil }
|
2009-12-10 21:18:14 -05:00
|
|
|
end
|
|
|
|
|
2009-12-10 15:03:27 -05:00
|
|
|
# Creates a button:
|
|
|
|
#
|
|
|
|
# form_for @user do |f|
|
|
|
|
# f.button :submit
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# If the record is a new_record?, it will create a button with label "Create User",
|
|
|
|
# otherwise it will create with label "Update User". You can overwrite the label
|
|
|
|
# giving a second parameter or giving :label.
|
|
|
|
#
|
|
|
|
# f.button :submit, "Create a new user"
|
|
|
|
# f.button :submit, :label => "Create a new user"
|
|
|
|
#
|
|
|
|
# button is actually just a wrapper that adds a default text, that said, f.button
|
|
|
|
# above is just calling:
|
|
|
|
#
|
|
|
|
# submit_tag "Create a new user"
|
|
|
|
#
|
|
|
|
# All options given to button are given straight to submit_tag. That said, you can
|
|
|
|
# use :confirm normally:
|
|
|
|
#
|
|
|
|
# f.button :submit, :confirm => "Are you sure?"
|
|
|
|
#
|
|
|
|
# And if you want to use image_submit_tag, just give it as an option:
|
|
|
|
#
|
|
|
|
# f.button :image_submit, "/images/foo/bar.png"
|
|
|
|
#
|
|
|
|
# This comes with a bonus that any method added to your ApplicationController can
|
|
|
|
# be used by SimpleForm, as long as it ends with _tag. So is quite easy to customize
|
|
|
|
# your buttons.
|
|
|
|
#
|
|
|
|
def button(type, *args)
|
|
|
|
options = args.extract_options!
|
|
|
|
value = args.first || options.delete(:label)
|
2009-12-11 06:28:09 -05:00
|
|
|
key = @object ? (@object.new_record? ? :create : :update) : :submit
|
2009-12-10 15:03:27 -05:00
|
|
|
|
|
|
|
value ||= begin
|
2009-12-11 06:28:09 -05:00
|
|
|
model = if @object.class.respond_to?(:human_name)
|
|
|
|
@object.class.human_name
|
|
|
|
else
|
|
|
|
@object_name.to_s.humanize
|
2009-12-10 15:03:27 -05:00
|
|
|
end
|
|
|
|
|
2009-12-11 15:01:15 -05:00
|
|
|
I18n.t(:"simple_form.buttons.#{key}", :model => model, :default => "#{key.to_s.humanize} #{model}")
|
2009-12-10 15:03:27 -05:00
|
|
|
end
|
|
|
|
|
2009-12-11 06:28:09 -05:00
|
|
|
options[:class] = "#{key} #{options[:class]}".strip
|
2009-12-10 15:03:27 -05:00
|
|
|
@template.send(:"#{type}_tag", value, options)
|
|
|
|
end
|
|
|
|
|
2009-12-10 12:57:24 -05:00
|
|
|
# Creates an error tag based on the given attribute, only when the attribute
|
2009-12-10 16:52:23 -05:00
|
|
|
# contains errors. All the given options are sent as :error_html.
|
|
|
|
#
|
|
|
|
# == Examples
|
|
|
|
#
|
|
|
|
# f.error :name
|
|
|
|
# f.error :name, :id => "cool_error"
|
2009-12-10 17:11:15 -05:00
|
|
|
#
|
2009-12-13 16:16:00 -05:00
|
|
|
def error(attribute_name, options={})
|
|
|
|
define_simple_form_attributes(attribute_name, :error_html => options)
|
2010-01-09 09:39:14 -05:00
|
|
|
Base.new(self).error
|
2009-12-10 10:49:17 -05:00
|
|
|
end
|
|
|
|
|
2009-12-10 16:52:23 -05:00
|
|
|
# Creates a hint tag for the given attribute. Accepts a symbol indicating
|
|
|
|
# an attribute for I18n lookup or a string. All the given options are sent
|
|
|
|
# as :hint_html.
|
|
|
|
#
|
|
|
|
# == Examples
|
|
|
|
#
|
|
|
|
# f.hint :name # Do I18n lookup
|
|
|
|
# f.hint :name, :id => "cool_hint"
|
|
|
|
# f.hint "Don't forget to accept this"
|
|
|
|
#
|
2009-12-13 16:16:00 -05:00
|
|
|
def hint(attribute_name, options={})
|
|
|
|
attribute_name, options[:hint] = nil, attribute_name if attribute_name.is_a?(String)
|
|
|
|
define_simple_form_attributes(attribute_name, :hint => options.delete(:hint), :hint_html => options)
|
2010-01-09 09:39:14 -05:00
|
|
|
Base.new(self).hint
|
2009-12-10 10:49:17 -05:00
|
|
|
end
|
|
|
|
|
2009-12-10 12:57:24 -05:00
|
|
|
# Creates a default label tag for the given attribute. You can give a label
|
2009-12-10 16:52:23 -05:00
|
|
|
# through the :label option or using i18n. All the given options are sent
|
|
|
|
# as :label_html.
|
|
|
|
#
|
|
|
|
# == Examples
|
|
|
|
#
|
|
|
|
# f.label :name # Do I18n lookup
|
|
|
|
# f.label :name, "Name" # Same behavior as Rails, do not add required tag
|
|
|
|
# f.label :name, :label => "Name" # Same as above, but adds required tag
|
|
|
|
#
|
|
|
|
# f.label :name, :required => false
|
|
|
|
# f.label :name, :id => "cool_label"
|
|
|
|
#
|
2009-12-13 16:16:00 -05:00
|
|
|
def label(attribute_name, *args)
|
2009-12-10 16:52:23 -05:00
|
|
|
return super if args.first.is_a?(String)
|
|
|
|
options = args.extract_options!
|
2009-12-13 16:16:00 -05:00
|
|
|
define_simple_form_attributes(attribute_name, :label => options.delete(:label),
|
2009-12-10 16:52:23 -05:00
|
|
|
:label_html => options, :required => options.delete(:required))
|
2010-01-09 09:39:14 -05:00
|
|
|
Base.new(self).label
|
2009-12-10 10:49:17 -05:00
|
|
|
end
|
|
|
|
|
2009-12-09 20:57:05 -05:00
|
|
|
private
|
2009-12-08 11:49:14 -05:00
|
|
|
|
2009-12-10 12:57:24 -05:00
|
|
|
# Setup default simple form attributes.
|
2009-12-13 16:16:00 -05:00
|
|
|
def define_simple_form_attributes(attribute_name, options) #:nodoc:
|
2009-12-10 16:52:23 -05:00
|
|
|
@options = options
|
2009-12-10 10:49:17 -05:00
|
|
|
|
2009-12-13 16:16:00 -05:00
|
|
|
if @attribute_name = attribute_name
|
2009-12-10 16:52:23 -05:00
|
|
|
@column = find_attribute_column
|
|
|
|
@input_type = default_input_type
|
|
|
|
end
|
2009-12-10 10:49:17 -05:00
|
|
|
end
|
|
|
|
|
2009-12-10 12:57:24 -05:00
|
|
|
# Attempt to guess the better input type given the defined options. By
|
|
|
|
# default alwayls fallback to the user :as option, or to a :select when a
|
|
|
|
# collection is given.
|
2009-12-11 06:19:46 -05:00
|
|
|
def default_input_type #:nodoc:
|
2009-12-10 08:01:44 -05:00
|
|
|
return @options[:as].to_sym if @options[:as]
|
|
|
|
return :select if @options[:collection]
|
2009-12-09 20:11:57 -05:00
|
|
|
|
2009-12-10 16:52:23 -05:00
|
|
|
input_type = @column.try(:type)
|
2009-12-09 19:55:42 -05:00
|
|
|
|
2009-12-09 20:57:05 -05:00
|
|
|
case input_type
|
|
|
|
when :timestamp
|
|
|
|
:datetime
|
|
|
|
when :string, nil
|
2009-12-13 16:16:00 -05:00
|
|
|
match = case @attribute_name.to_s
|
2009-12-11 08:12:57 -05:00
|
|
|
when /password/ then :password
|
|
|
|
when /time_zone/ then :time_zone
|
|
|
|
when /country/ then :country
|
|
|
|
end
|
|
|
|
|
|
|
|
match || input_type || file_method? || :string
|
2009-12-09 20:57:05 -05:00
|
|
|
else
|
|
|
|
input_type
|
2009-12-08 11:49:14 -05:00
|
|
|
end
|
2009-12-09 20:57:05 -05:00
|
|
|
end
|
2009-12-08 14:08:36 -05:00
|
|
|
|
2009-12-11 08:12:57 -05:00
|
|
|
# Checks if attribute is a file_method.
|
|
|
|
def file_method? #:nodoc:
|
2009-12-13 16:16:00 -05:00
|
|
|
file = @object.send(@attribute_name) if @object.respond_to?(@attribute_name)
|
2009-12-11 08:12:57 -05:00
|
|
|
:file if file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
|
|
|
|
end
|
|
|
|
|
2009-12-10 12:57:24 -05:00
|
|
|
# Finds the database column for the given attribute
|
2009-12-11 06:19:46 -05:00
|
|
|
def find_attribute_column #:nodoc:
|
2009-12-13 16:16:00 -05:00
|
|
|
@object.column_for_attribute(@attribute_name) if @object.respond_to?(:column_for_attribute)
|
2009-12-10 08:57:05 -05:00
|
|
|
end
|
|
|
|
|
2009-12-14 05:37:03 -05:00
|
|
|
# Find reflection related to association
|
2009-12-13 16:16:00 -05:00
|
|
|
def find_association_reflection(association) #:nodoc:
|
|
|
|
@object.class.reflect_on_association(association) if @object.class.respond_to?(:reflect_on_association)
|
2009-12-10 21:18:14 -05:00
|
|
|
end
|
|
|
|
|
2009-11-18 18:50:43 -05:00
|
|
|
end
|
|
|
|
end
|