Fix merge conflicts.
This commit is contained in:
commit
2883707b92
171
README.rdoc
171
README.rdoc
|
@ -1,8 +1,175 @@
|
|||
SimpleForm
|
||||
==========
|
||||
== SimpleForm
|
||||
|
||||
Forms made easy!
|
||||
|
||||
SimpleForm aims to be as flexible as possible while helping you with powerful components to create your forms. The basic goal of simple form is to not touch your way of defining the layout, this way letting you find how you find the better design for your eyes.
|
||||
|
||||
== Instalation
|
||||
|
||||
sudo gem install simple_form
|
||||
|
||||
== Usage
|
||||
|
||||
SimpleForm was designed to be customized as you need to. Basically it's a stack of components that are generated to create a complete html input for you, with label + hints + errors. The best of this is that you can add any element on this stack in any place, or even remove any of them.
|
||||
|
||||
To start using SimpleForm you just have to use the helper it provides:
|
||||
|
||||
<% simple_form_for @user do |f| -%>
|
||||
<p><%= f.input :username %></p>
|
||||
<p><%= f.input :password %></p>
|
||||
<p><%= f.submit 'Save' %></p>
|
||||
<% end -%>
|
||||
|
||||
This will generate an entire form with labels for user name and password as well, and render errors by default when you render the form with invalid data (after submiting for example).
|
||||
|
||||
You can overwrite the default label by passing it to the input method, or even add a hint:
|
||||
|
||||
<% simple_form_for @user do |f| -%>
|
||||
<p><%= f.input :username, :label => 'Your username please' %></p>
|
||||
<p><%= f.input :password, :hint => 'No special characters.' %></p>
|
||||
<p><%= f.submit 'Save' %></p>
|
||||
<% end -%>
|
||||
|
||||
Or you can disable labels, hints and errors inside a specific input:
|
||||
|
||||
<% simple_form_for @user do |f| -%>
|
||||
<p><%= f.input :username, :label => false %></p>
|
||||
<p><%= f.input :password, :hint => false, :error => false %></p>
|
||||
<p><%= f.input :password_confirmation, :error => false %></p>
|
||||
<p><%= f.submit 'Save' %></p>
|
||||
<% end -%>
|
||||
|
||||
You can also pass html options for the label, input, hint or error tag:
|
||||
|
||||
<% simple_form_for @user do |f| -%>
|
||||
<p><%= f.input :name, :label_html => { :class => 'my_class' } %></p>
|
||||
<p><%= f.input :username, :input_html => { :disabled => true } %></p>
|
||||
<p><%= f.input :password, :hint => 'Confirm!', :hint_html => { :id => 'password_hint' } %></p>
|
||||
<p><%= f.input :password_confirmation, :error_html => { :id => 'password_error' } %></p>
|
||||
<p><%= f.submit 'Save' %></p>
|
||||
<% end -%>
|
||||
|
||||
By default all inputs are required, but you can disable it in any input you want:
|
||||
|
||||
<% simple_form_for @user do |f| -%>
|
||||
<p><%= f.input :name, :required => false %></p>
|
||||
<p><%= f.input :username %></p>
|
||||
<p><%= f.input :password %></p>
|
||||
<p><%= f.submit 'Save' %></p>
|
||||
<% end -%>
|
||||
|
||||
This way the name input will not have the required text and css classes. SimpleForm also lets you overwrite the default input type it creates:
|
||||
|
||||
<% simple_form_for @user do |f| -%>
|
||||
<p><%= f.input :username %></p>
|
||||
<p><%= f.input :password %></p>
|
||||
<p><%= f.input :active, :as => :radio %></p>
|
||||
<p><%= f.submit 'Save' %></p>
|
||||
<% end -%>
|
||||
|
||||
So instead of a checkbox for the active attribute, you'll have a set of boolean radio buttons with yes/no options. You can do the same with :as => :select option for boolean attributes.
|
||||
|
||||
What if you want to create a select containing the age from 18 to 60 in your form? You can do it overriding the :collection option:
|
||||
|
||||
<% simple_form_for @user do |f| -%>
|
||||
<p><%= f.input :user %></p>
|
||||
<p><%= f.input :age, :colletion => 18..60 %></p>
|
||||
<p><%= f.submit 'Save' %></p>
|
||||
<% end -%>
|
||||
|
||||
Collections can be arrays or ranges, and when a :collection is given the :select input will be rendered by default, so we don't need to pass the :as => :select option.
|
||||
|
||||
== Inputs available
|
||||
|
||||
== I18n
|
||||
|
||||
SimpleForm uses all power of I18n API to lookup labels and hints for you. To customize your forms you can create a locale file like this:
|
||||
|
||||
en:
|
||||
simple_form:
|
||||
labels:
|
||||
user:
|
||||
username: 'User name'
|
||||
password: 'Password'
|
||||
hints:
|
||||
user:
|
||||
username: 'User name to sign in.'
|
||||
password: 'No special characters, please.'
|
||||
|
||||
And your forms will use this information to render labels and hints for you.
|
||||
|
||||
SimpleForm also lets you be more specific, separating lookups through actions. Let's say you want a different label and hint for new and edit actions, the locale file would be something like:
|
||||
|
||||
en:
|
||||
simple_form:
|
||||
labels:
|
||||
user:
|
||||
new:
|
||||
username: 'User name'
|
||||
password: 'Password'
|
||||
edit:
|
||||
username: 'Change user name'
|
||||
password: 'Change password'
|
||||
hints:
|
||||
user:
|
||||
new:
|
||||
username: 'User name to sign in.'
|
||||
password: 'No special characters, please.'
|
||||
edit:
|
||||
username: 'Update your user name to sign in.'
|
||||
password: 'Let it blank to not change your password.'
|
||||
|
||||
This way SimpleForm will figure out the right translation for you, based on the action being rendered. And to be a little bit DRYer with your locale file, you can skip the model information inside it:
|
||||
|
||||
en:
|
||||
simple_form:
|
||||
labels:
|
||||
username: 'User name'
|
||||
password: 'Password'
|
||||
hints:
|
||||
username: 'User name to sign in.'
|
||||
password: 'No special characters, please.'
|
||||
|
||||
SimpleForm will always look for a default attribute translation if no specific is found inside the model key. In addition, SimpleForm will fallback to default human_attribute_name from Rails when no other translation is found.
|
||||
|
||||
Finally, you can also overwrite labels and hints inside your view, just by passing the label/hint manually. This way the I18n lookup will be skipped.
|
||||
|
||||
There are other options that can be configured through I18n API, such as required for labels and boolean texts, you just need to overwrite the following keys:
|
||||
|
||||
en:
|
||||
simple_form:
|
||||
true: 'Yes'
|
||||
false: 'No'
|
||||
required:
|
||||
text: 'required'
|
||||
mark: '*'
|
||||
|
||||
Instead of using the text and mark options from required, you can also overwrite the entire required html string as follows:
|
||||
|
||||
en:
|
||||
simple_form:
|
||||
required:
|
||||
string: '<abbr title="required">*</abbr> '
|
||||
|
||||
== Configuration
|
||||
|
||||
You have a set of options available to configure SimpleForm:
|
||||
|
||||
* component_tag => default tag used in components. by default is :span.
|
||||
* components => stack of components used in form builder to create the input.
|
||||
You can add or remove any of this components as you need.
|
||||
* terminator => the last component will call this terminator. By default it's
|
||||
a lambda returning an empty string.
|
||||
* collection_label_methods => all methods available to detect the label for a
|
||||
collection.
|
||||
* collection_value_methods => all methods available to detect the value for a
|
||||
collection.
|
||||
* wrapper_tag => wrapper tag to wrap the inputs. By default no wrapper exists.
|
||||
|
||||
To do it so you just need to create a file inside your initializer folder and use the configurations as follows:
|
||||
|
||||
SimpleForm.collection_label_methods = [:to_label, :title, :description, :name, :to_s]
|
||||
|
||||
== TODO
|
||||
|
||||
Please refer to TODO file.
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
== General
|
||||
|
||||
* Allow I18n lookup for labels and hints based on the current action
|
||||
* Sample CSS
|
||||
* Test forms with non objects
|
||||
* Get default string options from column definition
|
||||
* Add support to default :prompt methods on datetime inputs
|
||||
* Add support to default label method
|
||||
|
|
|
@ -8,7 +8,7 @@ module SimpleForm
|
|||
autoload :MapType, 'simple_form/map_type'
|
||||
autoload :RequiredHelpers, 'simple_form/required_helpers'
|
||||
|
||||
# Default tag used in componenents.
|
||||
# Default tag used in components.
|
||||
mattr_accessor :component_tag
|
||||
@@component_tag = :span
|
||||
|
||||
|
@ -35,4 +35,4 @@ module SimpleForm
|
|||
# You can wrap all inputs in a pre-defined tag. By default is nil.
|
||||
mattr_accessor :wrapper_tag
|
||||
@@wrapper_tag = nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,22 @@ module SimpleForm
|
|||
# A collection of methods required by simple_form but added to rails default form.
|
||||
# This means that you can use such methods outside simple_form context.
|
||||
module Builder
|
||||
|
||||
# Create a collection of radio inputs for the attribute. Basically this
|
||||
# helper will create a radio input associated with a label for each
|
||||
# text/value option in the collection, using value_method and text_method
|
||||
# to convert these text/value. Based on collection_select.
|
||||
# Example:
|
||||
#
|
||||
# form_for @user do |f|
|
||||
# f.collection_radio :active, [['Yes', true] ,['No', false]], :first, :last
|
||||
# end
|
||||
#
|
||||
# <input id="user_active_true" name="user[active]" type="radio" value="true" />
|
||||
# <label class="radio" for="user_active_true">Yes</label>
|
||||
# <input id="user_active_false" name="user[active]" type="radio" value="false" />
|
||||
# <label class="radio" for="user_active_false">No</label>
|
||||
#
|
||||
def collection_radio(attribute, collection, value_method, text_method, html_options={})
|
||||
collection.inject('') do |result, item|
|
||||
value = item.send value_method
|
||||
|
@ -13,6 +29,15 @@ module SimpleForm
|
|||
end
|
||||
end
|
||||
|
||||
# Wrapper for using simple form inside a default rails form.
|
||||
# Example:
|
||||
#
|
||||
# form_for @user do |f|
|
||||
# f.simple_fields_for :posts do |posts_form|
|
||||
# # Here you have all simple_form methods available
|
||||
# posts_form.input :title
|
||||
# end
|
||||
# end
|
||||
def simple_fields_for(*args, &block)
|
||||
options = args.extract_options!
|
||||
options[:builder] = SimpleForm::FormBuilder
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
module SimpleForm
|
||||
module ActionViewExtensions
|
||||
# This modules create simple form wrappers around default form_for,
|
||||
# fields_for and remote_form_for.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# simple_form_for @user do |f|
|
||||
# f.input :name, :hint => 'My hint'
|
||||
# end
|
||||
#
|
||||
module FormHelper
|
||||
[:form_for, :fields_for, :remote_form_for].each do |helper|
|
||||
class_eval <<-METHOD, __FILE__, __LINE__
|
||||
|
|
|
@ -20,6 +20,8 @@ module SimpleForm
|
|||
@component = component
|
||||
end
|
||||
|
||||
# Generate component content and call next component in the stack. When a
|
||||
# component is invalid it will be skipped.
|
||||
def call
|
||||
return @component.call unless valid?
|
||||
content + @component.call
|
||||
|
@ -37,17 +39,56 @@ module SimpleForm
|
|||
self.class.basename
|
||||
end
|
||||
|
||||
# Default html options for a component. Passed as a parameter for simple
|
||||
# form component using component name as follows:
|
||||
#
|
||||
# label_html => {}
|
||||
# input_html => {}
|
||||
# hint_html => {}
|
||||
# error_html => {}
|
||||
# wrapper_html => {}
|
||||
def component_html_options
|
||||
options[:"#{basename}_html"] || {}
|
||||
end
|
||||
|
||||
# Renders default content tag for components, using default html class
|
||||
# and user defined parameters.
|
||||
# Default component tag can be configured in SimpleForm.component_tag.
|
||||
def component_tag(content)
|
||||
html_options = component_html_options.reverse_merge(:class => basename)
|
||||
html_options = component_html_options
|
||||
html_options[:class] = "#{basename} #{html_options[:class]}".strip
|
||||
template.content_tag(SimpleForm.component_tag, content, html_options)
|
||||
end
|
||||
|
||||
# Lookup translations for components using I18n, based on object name,
|
||||
# actual action and attribute name. Lookup priority as follows:
|
||||
#
|
||||
# simple_form.{type}.{model}.{action}.{attribute}
|
||||
# simple_form.{type}.{model}.{attribute}
|
||||
# simple_form.{type}.{attribute}
|
||||
#
|
||||
# Type 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(default='')
|
||||
lookups = [ :"#{object_name}.#{attribute}", :"#{attribute}", default ]
|
||||
action = template.params[:action] if template.respond_to?(:params)
|
||||
lookups = []
|
||||
lookups << :"#{object_name}.#{action}.#{attribute}" if action.present?
|
||||
lookups << :"#{object_name}.#{attribute}"
|
||||
lookups << :"#{attribute}"
|
||||
lookups << default
|
||||
I18n.t(lookups.shift, :scope => :"simple_form.#{basename.to_s.pluralize}", :default => lookups)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
module SimpleForm
|
||||
module Components
|
||||
# General error component. Responsible for verifying whether an object
|
||||
# exists and there are errors on the attribute being generated. If errors
|
||||
# exists then the component will be rendered, otherwise will be skipped.
|
||||
class Error < Base
|
||||
def valid?
|
||||
object && !hidden_input? && !errors.blank?
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
module SimpleForm
|
||||
module Components
|
||||
# Basic hint component, which verifies whether a user has defined a hint
|
||||
# either on the input or through i18n lookup. If no hint is found, the
|
||||
# component is skipped.
|
||||
class Hint < Base
|
||||
def valid?
|
||||
!hidden_input? && !hint.blank?
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module SimpleForm
|
||||
module Components
|
||||
# Default input component, responsible for mapping column attributes from
|
||||
# database to inputs to be rendered.
|
||||
class Input < Base
|
||||
include RequiredHelpers
|
||||
extend I18nCache
|
||||
|
@ -19,6 +21,10 @@ module SimpleForm
|
|||
# Numeric types
|
||||
map_type :integer, :float, :decimal, :to => :text_field
|
||||
|
||||
# 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.true", :default => 'Yes'), true],
|
||||
|
@ -26,6 +32,8 @@ module SimpleForm
|
|||
end
|
||||
end
|
||||
|
||||
# Generate the input through the mapped option. Apply correct behaviors
|
||||
# for collections and add options whenever the input requires it.
|
||||
def content
|
||||
options[:options] ||= {}
|
||||
mapping = self.class.mappings[input_type]
|
||||
|
@ -41,6 +49,9 @@ module SimpleForm
|
|||
|
||||
protected
|
||||
|
||||
# Applies default collection behavior, mapping the default collection to
|
||||
# boolean collection if it was not set, and defining default include_blank
|
||||
# option
|
||||
def apply_collection_behavior(args)
|
||||
collection = (options[:collection] || self.class.boolean_collection).to_a
|
||||
detect_collection_methods(collection, options)
|
||||
|
@ -49,10 +60,13 @@ module SimpleForm
|
|||
args.push(collection, options[:value_method], options[:label_method])
|
||||
end
|
||||
|
||||
# Apply default behavior for inputs that need extra options, such as date
|
||||
# and time selects.
|
||||
def apply_options_behavior(args)
|
||||
args << options[:options]
|
||||
end
|
||||
|
||||
# Adds default html options to the input based on db column information.
|
||||
def apply_html_options(args)
|
||||
html_options = component_html_options
|
||||
|
||||
|
@ -63,6 +77,11 @@ module SimpleForm
|
|||
args << html_options
|
||||
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
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
module SimpleForm
|
||||
module Components
|
||||
# Responsible for rendering label with default options, such as required
|
||||
# and translation using i18n. By default all fields are required, and label
|
||||
# will prepend a default string with a required mark in all labels. You can
|
||||
# disable the required option just passing :required => false in your
|
||||
# label/input declaration. You can also use i18n to change the required
|
||||
# text, mark or entire html string that is generated.
|
||||
class Label < Base
|
||||
include RequiredHelpers
|
||||
extend I18nCache
|
||||
|
@ -24,20 +30,29 @@ module SimpleForm
|
|||
!hidden_input?
|
||||
end
|
||||
|
||||
# Overwrite html for option if the user has changed input id, so the label
|
||||
# will always point correctly to the input. Renders a default label.
|
||||
def content
|
||||
html_options = component_html_options
|
||||
html_options[:for] = options[:input_html][:id] if options.key?(:input_html)
|
||||
@builder.label(attribute, label_text, html_options)
|
||||
end
|
||||
|
||||
# Prepends the required text to label if it is required. The user is able
|
||||
# to pass a label with the :label option, or it will fallback to label
|
||||
# lookup.
|
||||
def label_text
|
||||
required_text << (options[:label] || translate_label)
|
||||
end
|
||||
|
||||
# Default required text when attribute is required.
|
||||
def required_text
|
||||
attribute_required? ? self.class.translate_required_html.dup : ''
|
||||
end
|
||||
|
||||
# Attempts to translate the label using default i18n lookup. If no
|
||||
# translation is found, fallback to human_attribute_name if it is
|
||||
# available or just use the attribute itself humanized.
|
||||
def translate_label
|
||||
default = if object.class.respond_to?(:human_attribute_name)
|
||||
object.class.human_attribute_name(attribute.to_s)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module SimpleForm
|
||||
module Components
|
||||
# Wrapper component. The last the will be executed by default, responsible
|
||||
# for wrapping the entire stack in a wrapper tag if it is configured.
|
||||
class Wrapper < Base
|
||||
include RequiredHelpers
|
||||
|
||||
|
@ -12,4 +14,4 @@ module SimpleForm
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,65 @@
|
|||
module SimpleForm
|
||||
class FormBuilder < ActionView::Helpers::FormBuilder
|
||||
attr_reader :template, :object_name, :object, :attribute, :column,
|
||||
:input_type, :options
|
||||
attr_reader :template, :object_name, :object, :attribute, :column, :input_type, :options
|
||||
|
||||
# Creates a input with all components.
|
||||
# Basic input helper, combines all components in the stack to generate input
|
||||
# html based on options the user define and some guesses through
|
||||
# database column information. By default a call to input will generate
|
||||
# label + input + hint (when defined) + errors (when exists), and all can be
|
||||
# configured inside a wrapper html.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # Imagine @user has error "can't be blank" on name
|
||||
# simple_form_for @user do |f|
|
||||
# f.input :name, :hint => 'My hint'
|
||||
# end
|
||||
#
|
||||
# This is the output html (only the input portion, not the form):
|
||||
# <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" />
|
||||
# <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.
|
||||
#
|
||||
# You have some options for the input to enable/disable some functions:
|
||||
#
|
||||
# :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
|
||||
# prompt and/or include blank. Such options are given in the :options key.
|
||||
#
|
||||
# f.input :created_at, :options => { :include_blank => true }
|
||||
#
|
||||
# == 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
|
||||
#
|
||||
def input(attribute, options={})
|
||||
@attribute, @options = attribute, options
|
||||
@column = find_attribute_column
|
||||
@input_type = default_input_type
|
||||
define_simple_form_attributes(attribute, options)
|
||||
|
||||
component = SimpleForm.terminator
|
||||
SimpleForm.components.reverse.each do |klass|
|
||||
|
@ -67,13 +119,75 @@ module SimpleForm
|
|||
@template.send(:"#{type}_tag", value, options)
|
||||
end
|
||||
|
||||
# Creates an error tag based on the given attribute, only when the attribute
|
||||
# contains errors. All the given options are sent as :error_html.
|
||||
#
|
||||
# == Examples
|
||||
#
|
||||
# f.error :name
|
||||
# f.error :name, :id => "cool_error"
|
||||
def error(attribute, options={})
|
||||
define_simple_form_attributes(attribute, :error_html => options)
|
||||
SimpleForm::Components::Error.new(self, SimpleForm.terminator).call
|
||||
end
|
||||
|
||||
# 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"
|
||||
#
|
||||
def hint(attribute, options={})
|
||||
attribute, options[:hint] = nil, attribute if attribute.is_a?(String)
|
||||
define_simple_form_attributes(attribute, :hint => options.delete(:hint), :hint_html => options)
|
||||
SimpleForm::Components::Hint.new(self, SimpleForm.terminator).call
|
||||
end
|
||||
|
||||
# Creates a default label tag for the given attribute. You can give a label
|
||||
# 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"
|
||||
#
|
||||
def label(attribute, *args)
|
||||
return super if args.first.is_a?(String)
|
||||
options = args.extract_options!
|
||||
define_simple_form_attributes(attribute, :label => options.delete(:label),
|
||||
:label_html => options, :required => options.delete(:required))
|
||||
SimpleForm::Components::Label.new(self, SimpleForm.terminator).call
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Setup default simple form attributes.
|
||||
def define_simple_form_attributes(attribute, options)
|
||||
@options = options
|
||||
|
||||
if @attribute = attribute
|
||||
@column = find_attribute_column
|
||||
@input_type = default_input_type
|
||||
end
|
||||
end
|
||||
|
||||
# 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.
|
||||
def default_input_type
|
||||
return @options[:as].to_sym if @options[:as]
|
||||
return :select if @options[:collection]
|
||||
|
||||
input_type = column.try(:type)
|
||||
input_type = @column.try(:type)
|
||||
|
||||
case input_type
|
||||
when :timestamp
|
||||
|
@ -85,6 +199,7 @@ module SimpleForm
|
|||
end
|
||||
end
|
||||
|
||||
# Finds the database column for the given attribute
|
||||
def find_attribute_column
|
||||
@object.column_for_attribute(@attribute) if @object.respond_to?(:column_for_attribute)
|
||||
end
|
||||
|
|
|
@ -6,9 +6,8 @@ en:
|
|||
update: 'Update {{model}}'
|
||||
submit: 'Submit {{model}}'
|
||||
required:
|
||||
text: "required"
|
||||
mark: "*"
|
||||
text: 'required'
|
||||
mark: '*'
|
||||
# You can uncomment the line below if you need to overwrite the whole required html.
|
||||
# When using html, text and mark won't be used.
|
||||
# html: '<abbr title="required">*</abbr> '
|
||||
|
||||
# html: '<abbr title="required">*</abbr> '
|
|
@ -1,5 +1,6 @@
|
|||
module SimpleForm
|
||||
module RequiredHelpers
|
||||
# Attribute is always required, unless the user has defined the opposite.
|
||||
def attribute_required?
|
||||
options[:required] != false
|
||||
end
|
||||
|
@ -8,14 +9,18 @@ module SimpleForm
|
|||
attribute_required? ? :required : :optional
|
||||
end
|
||||
|
||||
# Creates default required classes for attributes, such as .string and
|
||||
# .decimal, based on input type, and required class
|
||||
def default_css_classes(merge_class=nil)
|
||||
"#{input_type} #{required_class} #{merge_class}".strip
|
||||
end
|
||||
|
||||
# When components may be required, default component html options always
|
||||
# must include default css classes.
|
||||
def component_html_options
|
||||
html_options = super
|
||||
html_options[:class] = default_css_classes(html_options[:class])
|
||||
html_options
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -41,4 +41,9 @@ class ErrorTest < ActionView::TestCase
|
|||
with_error_for @user, :age, :numeric
|
||||
assert_select 'span.error', 'is not a number and must be greater than 18'
|
||||
end
|
||||
|
||||
test 'error should be able to pass html options' do
|
||||
with_error_for @user, :name, :string, :error_html => { :id => 'error', :class => 'yay' }
|
||||
assert_select 'span#error.error.yay'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,9 +38,19 @@ class ErrorTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
test 'hint should use i18n based on model, action, and attribute to lookup translation' do
|
||||
store_translations(:en, :simple_form => { :hints => { :user => {
|
||||
:edit => { :name => 'Content of this input will be truncated...' }
|
||||
} } }) do
|
||||
params.merge!(:action => 'edit')
|
||||
with_hint_for @user, :name, :string
|
||||
assert_select 'span.hint', 'Content of this input will be truncated...'
|
||||
end
|
||||
end
|
||||
|
||||
test 'hint should use i18n with model and attribute to lookup translation' do
|
||||
store_translations(:en, :simple_form => { :hints => { :user => { :name =>
|
||||
'Content of this input will be capitalized...'
|
||||
store_translations(:en, :simple_form => { :hints => { :user => {
|
||||
:name => 'Content of this input will be capitalized...'
|
||||
} } }) do
|
||||
with_hint_for @user, :name, :string
|
||||
assert_select 'span.hint', 'Content of this input will be capitalized...'
|
||||
|
@ -48,8 +58,8 @@ class ErrorTest < ActionView::TestCase
|
|||
end
|
||||
|
||||
test 'hint should use i18n just with attribute to lookup translation' do
|
||||
store_translations(:en, :simple_form => { :hints => { :name =>
|
||||
'Content of this input will be downcased...'
|
||||
store_translations(:en, :simple_form => { :hints => {
|
||||
:name => 'Content of this input will be downcased...'
|
||||
} }) do
|
||||
with_hint_for @user, :name, :string
|
||||
assert_select 'span.hint', 'Content of this input will be downcased...'
|
||||
|
@ -60,4 +70,9 @@ class ErrorTest < ActionView::TestCase
|
|||
with_hint_for :project, :name, :string, :hint => 'Test without object'
|
||||
assert_select 'span.hint', 'Test without object'
|
||||
end
|
||||
|
||||
test 'hint should be able to pass html options' do
|
||||
with_hint_for @user, :name, :string, :hint => 'Yay!', :hint_html => { :id => 'hint', :class => 'yay' }
|
||||
assert_select 'span#hint.hint.yay'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,6 +39,16 @@ class LabelTest < ActionView::TestCase
|
|||
assert_select 'label[for=user_description]', /User Description!/
|
||||
end
|
||||
|
||||
test 'label should use i18n based on model, action, and attribute to lookup translation' do
|
||||
store_translations(:en, :simple_form => { :labels => { :user => {
|
||||
:new => { :description => 'Nova descrição' }
|
||||
} } } ) do
|
||||
params.merge!(:action => 'new')
|
||||
with_label_for @user, :description, :text
|
||||
assert_select 'label[for=user_description]', /Nova descrição/
|
||||
end
|
||||
end
|
||||
|
||||
test 'label should use i18n based on model and attribute to lookup translation' do
|
||||
store_translations(:en, :simple_form => { :labels => { :user => {
|
||||
:description => 'Descrição'
|
||||
|
|
|
@ -155,8 +155,8 @@ class FormBuilderTest < ActionView::TestCase
|
|||
|
||||
test 'builder wrapping tag allow custom options to be given' do
|
||||
swap SimpleForm, :wrapper_tag => :p do
|
||||
with_form_for @user, :name, :wrapper_html => { :id => "super_cool" }
|
||||
assert_select 'form p#super_cool.required.string'
|
||||
with_form_for @user, :name, :wrapper_html => { :id => "super_cool", :class => 'yay' }
|
||||
assert_select 'form p#super_cool.required.string.yay'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -192,6 +192,69 @@ class FormBuilderTest < ActionView::TestCase
|
|||
assert_select 'form input.decimal#project_budget'
|
||||
end
|
||||
|
||||
# ERRORS
|
||||
test 'builder should generate an error component tag for the attribute' do
|
||||
simple_form_for @user do |f|
|
||||
concat f.error :name
|
||||
end
|
||||
assert_select 'span.error', "can't be blank"
|
||||
end
|
||||
|
||||
test 'builder should allow passing options to error tag' do
|
||||
simple_form_for @user do |f|
|
||||
concat f.error :name, :id => 'name_error'
|
||||
end
|
||||
assert_select 'span.error#name_error', "can't be blank"
|
||||
end
|
||||
|
||||
# HINTS
|
||||
test 'builder should generate a hint component tag for the attribute' do
|
||||
store_translations(:en, :simple_form => { :hints => { :user => { :name => "Add your name" }}}) do
|
||||
simple_form_for @user do |f|
|
||||
concat f.hint :name
|
||||
end
|
||||
assert_select 'span.hint', 'Add your name'
|
||||
end
|
||||
end
|
||||
|
||||
test 'builder should generate a hint component tag for the given text' do
|
||||
simple_form_for @user do |f|
|
||||
concat f.hint 'Hello World!'
|
||||
end
|
||||
assert_select 'span.hint', 'Hello World!'
|
||||
end
|
||||
|
||||
test 'builder should allow passing options to hint tag' do
|
||||
simple_form_for @user do |f|
|
||||
concat f.hint :name, :hint => 'Hello World!', :id => 'name_hint'
|
||||
end
|
||||
assert_select 'span.hint#name_hint', 'Hello World!'
|
||||
end
|
||||
|
||||
# LABELS
|
||||
test 'builder should generate a label component tag for the attribute' do
|
||||
simple_form_for @user do |f|
|
||||
concat f.label :name
|
||||
end
|
||||
assert_select 'label.string[for=user_name]', /Name/
|
||||
end
|
||||
|
||||
test 'builder should allow passing options to label tag' do
|
||||
simple_form_for @user do |f|
|
||||
concat f.label :name, :label => 'My label', :id => 'name_label'
|
||||
end
|
||||
assert_select 'label.string.required#name_label', /My label/
|
||||
end
|
||||
|
||||
test 'builder should fallback to default label when string is given' do
|
||||
simple_form_for @user do |f|
|
||||
concat f.label :name, 'Nome do usuário'
|
||||
end
|
||||
assert_select 'label', 'Nome do usuário'
|
||||
assert_no_select 'label.string'
|
||||
end
|
||||
|
||||
# BUTTONS
|
||||
test 'builder should create buttons' do
|
||||
with_button_for :post, :submit
|
||||
assert_select 'form input[type=submit][value=Submit Post]'
|
||||
|
|
|
@ -49,6 +49,10 @@ class ActionView::TestCase
|
|||
false
|
||||
end
|
||||
|
||||
def params
|
||||
@params ||= {}
|
||||
end
|
||||
|
||||
def user_path(*args)
|
||||
'/users'
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue