Merge branch 'refactoring'
This commit is contained in:
commit
604aac88ce
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
* Sample CSS
|
* Sample CSS
|
||||||
* Add default size support
|
* Add default size support
|
||||||
* Allow block to be given to overwrite content
|
|
||||||
|
|
||||||
== Validations
|
== Validations
|
||||||
|
|
||||||
|
|
|
@ -6,26 +6,26 @@ module SimpleForm
|
||||||
autoload :Components, 'simple_form/components'
|
autoload :Components, 'simple_form/components'
|
||||||
autoload :FormBuilder, 'simple_form/form_builder'
|
autoload :FormBuilder, 'simple_form/form_builder'
|
||||||
autoload :I18nCache, 'simple_form/i18n_cache'
|
autoload :I18nCache, 'simple_form/i18n_cache'
|
||||||
|
autoload :Inputs, 'simple_form/inputs'
|
||||||
autoload :MapType, 'simple_form/map_type'
|
autoload :MapType, 'simple_form/map_type'
|
||||||
autoload :RequiredHelpers, 'simple_form/required_helpers'
|
|
||||||
|
|
||||||
# Default tag used in components.
|
# Default tag used in hints.
|
||||||
mattr_accessor :component_tag
|
mattr_accessor :hint_tag
|
||||||
@@component_tag = :span
|
@@hint_tag = :span
|
||||||
|
|
||||||
|
# Default tag used in errors.
|
||||||
|
mattr_accessor :error_tag
|
||||||
|
@@error_tag = :span
|
||||||
|
|
||||||
# Components used by the form builder.
|
# Components used by the form builder.
|
||||||
mattr_accessor :components
|
mattr_accessor :components
|
||||||
@@components = [
|
@@components = [ :label, :input, :hint, :error ]
|
||||||
SimpleForm::Components::Wrapper, SimpleForm::Components::Label,
|
|
||||||
SimpleForm::Components::Input, SimpleForm::Components::Hint,
|
|
||||||
SimpleForm::Components::Error
|
|
||||||
]
|
|
||||||
|
|
||||||
# Series of attemps to detect a default label method for collection
|
# Series of attemps to detect a default label method for collection.
|
||||||
mattr_accessor :collection_label_methods
|
mattr_accessor :collection_label_methods
|
||||||
@@collection_label_methods = [ :to_label, :name, :title, :to_s ]
|
@@collection_label_methods = [ :to_label, :name, :title, :to_s ]
|
||||||
|
|
||||||
# Series of attemps to detect a default value method for collection
|
# Series of attemps to detect a default value method for collection.
|
||||||
mattr_accessor :collection_value_methods
|
mattr_accessor :collection_value_methods
|
||||||
@@collection_value_methods = [ :id, :to_s ]
|
@@collection_value_methods = [ :id, :to_s ]
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ module SimpleForm
|
||||||
# * disabled => the value or values that should be disabled. Accepts a single
|
# * disabled => the value or values that should be disabled. Accepts a single
|
||||||
# item or an array of items.
|
# item or an array of items.
|
||||||
#
|
#
|
||||||
def collection_check_box(attribute, collection, value_method, text_method, options={}, html_options={})
|
def collection_check_boxes(attribute, collection, value_method, text_method, options={}, html_options={})
|
||||||
collection.inject('') do |result, item|
|
collection.inject('') do |result, item|
|
||||||
value = item.send value_method
|
value = item.send value_method
|
||||||
text = item.send text_method
|
text = item.send text_method
|
||||||
|
@ -77,7 +77,7 @@ module SimpleForm
|
||||||
default_html_options[:multiple] = true
|
default_html_options[:multiple] = true
|
||||||
|
|
||||||
result << check_box(attribute, default_html_options, value, '') <<
|
result << check_box(attribute, default_html_options, value, '') <<
|
||||||
label("#{attribute}_#{value}", text, :class => "collection_check_box")
|
label("#{attribute}_#{value}", text, :class => "collection_check_boxes")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
module SimpleForm
|
module SimpleForm
|
||||||
module Components
|
module Components
|
||||||
autoload :Base, 'simple_form/components/base'
|
autoload :Errors, 'simple_form/components/errors'
|
||||||
autoload :Error, 'simple_form/components/error'
|
autoload :Hints, 'simple_form/components/hints'
|
||||||
autoload :Hint, 'simple_form/components/hint'
|
autoload :Labels, 'simple_form/components/labels'
|
||||||
autoload :Input, 'simple_form/components/input'
|
|
||||||
autoload :Label, 'simple_form/components/label'
|
|
||||||
autoload :Wrapper, 'simple_form/components/wrapper'
|
autoload :Wrapper, 'simple_form/components/wrapper'
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,113 +0,0 @@
|
||||||
module SimpleForm
|
|
||||||
module Components
|
|
||||||
# The component is the core of SimpleForm. SimpleForm can be customized simply
|
|
||||||
# with the addition of new components to the component stack. A component just
|
|
||||||
# need to be initialized with two values, the builder and the next component to
|
|
||||||
# be invoked and respond to call.
|
|
||||||
#
|
|
||||||
# The Base component is a raw component with some helpers and a default behavior
|
|
||||||
# of prepending the content available in the method content.
|
|
||||||
class Base
|
|
||||||
delegate :template, :object, :object_name, :attribute_name, :column,
|
|
||||||
:reflection, :input_type, :options, :to => :@builder
|
|
||||||
|
|
||||||
# When action is create or update, we still should use new and edit
|
|
||||||
ACTIONS = {
|
|
||||||
:create => :new,
|
|
||||||
:update => :edit
|
|
||||||
}
|
|
||||||
|
|
||||||
def self.basename
|
|
||||||
@basename ||= name.split("::").last.underscore.to_sym
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(builder, component)
|
|
||||||
@builder = builder
|
|
||||||
@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
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def hidden_input?
|
|
||||||
input_type == :hidden
|
|
||||||
end
|
|
||||||
|
|
||||||
def basename
|
|
||||||
self.class.basename
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find reflection name when available, otherwise use attribute
|
|
||||||
def reflection_or_attribute_name
|
|
||||||
reflection ? reflection.name : attribute_name
|
|
||||||
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
|
|
||||||
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 = []
|
|
||||||
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.#{basename.to_s.pluralize}", :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
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,28 +0,0 @@
|
||||||
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 than the component will be rendered, otherwise will be skipped.
|
|
||||||
class Error < Base
|
|
||||||
def valid?
|
|
||||||
object && !hidden_input? && !errors.blank?
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
def content
|
|
||||||
component_tag errors.to_sentence
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Components
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,20 +0,0 @@
|
||||||
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?
|
|
||||||
end
|
|
||||||
|
|
||||||
def hint
|
|
||||||
@hint ||= options[:hint] || translate
|
|
||||||
end
|
|
||||||
|
|
||||||
def content
|
|
||||||
component_tag hint
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Components
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,122 +0,0 @@
|
||||||
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
|
|
||||||
extend MapType
|
|
||||||
|
|
||||||
map_type :boolean, :to => :check_box
|
|
||||||
map_type :string, :to => :text_field
|
|
||||||
map_type :password, :to => :password_field
|
|
||||||
map_type :text, :to => :text_area
|
|
||||||
map_type :file, :to => :file_field
|
|
||||||
map_type :hidden, :to => :hidden_field
|
|
||||||
|
|
||||||
# Numeric types
|
|
||||||
map_type :integer, :float, :decimal, :to => :text_field
|
|
||||||
|
|
||||||
# Date/time types
|
|
||||||
map_type :datetime, :to => :datetime_select, :options => true
|
|
||||||
map_type :date, :to => :date_select, :options => true
|
|
||||||
map_type :time, :to => :time_select, :options => true
|
|
||||||
|
|
||||||
# Collection types
|
|
||||||
map_type :select, :to => :collection_select, :options => true, :collection => true
|
|
||||||
map_type :radio, :to => :collection_radio, :options => true, :collection => true
|
|
||||||
map_type :check_boxes, :to => :collection_check_box, :options => true, :collection => true
|
|
||||||
|
|
||||||
# With priority zones
|
|
||||||
map_type :country, :to => :country_select, :options => true, :with_priority => true
|
|
||||||
map_type :time_zone, :to => :time_zone_select, :options => true, :with_priority => true
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Generate the input through the mapped option. Apply correct behaviors
|
|
||||||
# for collections and add options whenever the input requires it.
|
|
||||||
def content
|
|
||||||
mapping = self.class.mappings[input_type]
|
|
||||||
raise "Invalid input type #{input_type.inspect}" unless mapping
|
|
||||||
|
|
||||||
args = [ attribute_name ]
|
|
||||||
apply_with_priority_behavior(args) if mapping.with_priority
|
|
||||||
apply_collection_behavior(args) if mapping.collection
|
|
||||||
apply_options_behavior(args) if mapping.options
|
|
||||||
apply_html_options(args)
|
|
||||||
|
|
||||||
@builder.send(mapping.method, *args)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# Applies priority behavior to configured types.
|
|
||||||
def apply_with_priority_behavior(args)
|
|
||||||
priorities = options[:priority] || SimpleForm.send(:"#{input_type}_priority")
|
|
||||||
args.push(priorities)
|
|
||||||
end
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
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)
|
|
||||||
options[:include_blank] = true unless skip_include_blank?
|
|
||||||
args << options
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds default html options to the input based on db column information.
|
|
||||||
def apply_html_options(args)
|
|
||||||
html_options = component_html_options
|
|
||||||
|
|
||||||
if column && [:string, :password, :decimal, :float].include?(input_type)
|
|
||||||
html_options[:maxlength] ||= column.limit
|
|
||||||
end
|
|
||||||
|
|
||||||
args << html_options
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if :include_blank must be included by default.
|
|
||||||
def skip_include_blank?
|
|
||||||
options.key?(:prompt) || options.key?(:include_blank) || 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
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,89 +0,0 @@
|
||||||
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
|
|
||||||
|
|
||||||
def self.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 self.translate_required_text
|
|
||||||
I18n.t(:"simple_form.required.text", :default => 'required')
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.translate_required_mark
|
|
||||||
I18n.t(:"simple_form.required.mark", :default => '*')
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid?
|
|
||||||
!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(label_target, text, html_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Map attribute to specific name when dealing with date/time/timestamp,
|
|
||||||
# ensuring label will always be "clickable". For better accessibility.
|
|
||||||
def label_target
|
|
||||||
case input_type
|
|
||||||
when :date, :datetime
|
|
||||||
"#{attribute_name}_1i"
|
|
||||||
when :time
|
|
||||||
"#{attribute_name}_4i"
|
|
||||||
else
|
|
||||||
attribute_name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# The method that actually generates the label. This can be overwriten using
|
|
||||||
# a SimpleForm configuration value:
|
|
||||||
#
|
|
||||||
# SimpleForm.label_text = lambda do |label_text, required_text|
|
|
||||||
# required_text + label_text + ":"
|
|
||||||
# end
|
|
||||||
def text
|
|
||||||
SimpleForm.label_text.call(label_text, required_text)
|
|
||||||
end
|
|
||||||
|
|
||||||
# The user is able to pass a label with the :label option, or it will
|
|
||||||
# fallback to label lookup.
|
|
||||||
def label_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(reflection_or_attribute_name.to_s)
|
|
||||||
else
|
|
||||||
attribute_name.to_s.humanize
|
|
||||||
end
|
|
||||||
|
|
||||||
translate(default)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Components
|
||||||
|
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
|
||||||
|
|
||||||
|
def label_target
|
||||||
|
attribute_name
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
translate(:labels, default)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,19 +1,21 @@
|
||||||
module SimpleForm
|
module SimpleForm
|
||||||
module Components
|
module Components
|
||||||
# Wrapper component. The last the will be executed by default, responsible
|
module Wrapper
|
||||||
# for wrapping the entire stack in a wrapper tag if it is configured.
|
def wrap(content)
|
||||||
class Wrapper < Base
|
if wrapper_tag && options[:wrapper] != false
|
||||||
include RequiredHelpers
|
template.content_tag(wrapper_tag, content, wrapper_html_options)
|
||||||
|
|
||||||
def call
|
|
||||||
tag = options[:wrapper] || SimpleForm.wrapper_tag
|
|
||||||
|
|
||||||
if tag
|
|
||||||
template.content_tag(tag, @component.call, component_html_options)
|
|
||||||
else
|
else
|
||||||
@component.call
|
content
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def wrapper_tag
|
||||||
|
options[:wrapper_tag] || SimpleForm.wrapper_tag
|
||||||
|
end
|
||||||
|
|
||||||
|
def wrapper_html_options
|
||||||
|
html_options_for(:wrapper, input_type, required_class)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -3,7 +3,14 @@ module SimpleForm
|
||||||
attr_reader :template, :object_name, :object, :attribute_name, :column,
|
attr_reader :template, :object_name, :object, :attribute_name, :column,
|
||||||
:reflection, :input_type, :options
|
:reflection, :input_type, :options
|
||||||
|
|
||||||
TERMINATOR = lambda { "" }
|
extend MapType
|
||||||
|
include SimpleForm::Inputs
|
||||||
|
|
||||||
|
map_type :boolean, :password, :text, :file, :to => SimpleForm::Inputs::MappingInput
|
||||||
|
map_type :string, :integer, :decimal, :float, :to => SimpleForm::Inputs::TextFieldInput
|
||||||
|
map_type :select, :radio, :check_boxes, :to => SimpleForm::Inputs::CollectionInput
|
||||||
|
map_type :date, :time, :datetime, :to => SimpleForm::Inputs::DateTimeInput
|
||||||
|
map_type :country, :time_zone, :to => SimpleForm::Inputs::PriorityInput
|
||||||
|
|
||||||
# Basic input helper, combines all components in the stack to generate
|
# Basic input helper, combines all components in the stack to generate
|
||||||
# input html based on options the user define and some guesses through
|
# input html based on options the user define and some guesses through
|
||||||
|
@ -70,15 +77,16 @@ module SimpleForm
|
||||||
# Some inputs, as :time_zone and :country accepts a :priority option. If none is
|
# 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.
|
# given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectivelly.
|
||||||
#
|
#
|
||||||
def input(attribute_name, options={})
|
def input(attribute_name, options={}, &block)
|
||||||
define_simple_form_attributes(attribute_name, options)
|
define_simple_form_attributes(attribute_name, options)
|
||||||
|
|
||||||
component = TERMINATOR
|
if block_given?
|
||||||
SimpleForm.components.reverse.each do |klass|
|
SimpleForm::Inputs::BlockInput.new(self, block).render
|
||||||
next if @options[klass.basename] == false
|
else
|
||||||
component = klass.new(self, component)
|
klass = self.class.mappings[input_type] ||
|
||||||
|
self.class.const_get(:"#{input_type.to_s.camelize}Input")
|
||||||
|
klass.new(self).render
|
||||||
end
|
end
|
||||||
component.call
|
|
||||||
end
|
end
|
||||||
alias :attribute :input
|
alias :attribute :input
|
||||||
|
|
||||||
|
@ -117,7 +125,22 @@ module SimpleForm
|
||||||
# f.association :company, :scope => [ :public, :not_broken ]
|
# f.association :company, :scope => [ :public, :not_broken ]
|
||||||
# # Same as doing Company.public.not_broken.all
|
# # Same as doing Company.public.not_broken.all
|
||||||
#
|
#
|
||||||
def association(association, options={})
|
# == Block
|
||||||
|
#
|
||||||
|
# When a block is given, association simple behaves as a proxy to
|
||||||
|
# simple_fields_for:
|
||||||
|
#
|
||||||
|
# f.association :company do |c|
|
||||||
|
# c.input :name
|
||||||
|
# c.input :type
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# From the options above, only :collection can also be supplied.
|
||||||
|
#
|
||||||
|
def association(association, options={}, &block)
|
||||||
|
return simple_fields_for(*[association,
|
||||||
|
options.delete(:collection), options].compact, &block) if block_given?
|
||||||
|
|
||||||
raise ArgumentError, "Association cannot be used in forms not associated with an object" unless @object
|
raise ArgumentError, "Association cannot be used in forms not associated with an object" unless @object
|
||||||
|
|
||||||
options[:as] ||= :select
|
options[:as] ||= :select
|
||||||
|
@ -210,7 +233,7 @@ module SimpleForm
|
||||||
#
|
#
|
||||||
def error(attribute_name, options={})
|
def error(attribute_name, options={})
|
||||||
define_simple_form_attributes(attribute_name, :error_html => options)
|
define_simple_form_attributes(attribute_name, :error_html => options)
|
||||||
SimpleForm::Components::Error.new(self, TERMINATOR).call
|
SimpleForm::Inputs::Base.new(self).error
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a hint tag for the given attribute. Accepts a symbol indicating
|
# Creates a hint tag for the given attribute. Accepts a symbol indicating
|
||||||
|
@ -226,7 +249,7 @@ module SimpleForm
|
||||||
def hint(attribute_name, options={})
|
def hint(attribute_name, options={})
|
||||||
attribute_name, options[:hint] = nil, attribute_name if attribute_name.is_a?(String)
|
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)
|
define_simple_form_attributes(attribute_name, :hint => options.delete(:hint), :hint_html => options)
|
||||||
SimpleForm::Components::Hint.new(self, TERMINATOR).call
|
SimpleForm::Inputs::Base.new(self).hint
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a default label tag for the given attribute. You can give a label
|
# Creates a default label tag for the given attribute. You can give a label
|
||||||
|
@ -247,7 +270,7 @@ module SimpleForm
|
||||||
options = args.extract_options!
|
options = args.extract_options!
|
||||||
define_simple_form_attributes(attribute_name, :label => options.delete(:label),
|
define_simple_form_attributes(attribute_name, :label => options.delete(:label),
|
||||||
:label_html => options, :required => options.delete(:required))
|
:label_html => options, :required => options.delete(:required))
|
||||||
SimpleForm::Components::Label.new(self, TERMINATOR).call
|
SimpleForm::Inputs::Base.new(self).label
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -8,11 +8,15 @@ module SimpleForm
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_i18n_cache(key)
|
def get_i18n_cache(key)
|
||||||
instance_variable_get(:"@#{key}") || reset_i18n_cache(key)
|
if class_variable_defined?(:"@@#{key}")
|
||||||
|
class_variable_get(:"@@#{key}")
|
||||||
|
else
|
||||||
|
reset_i18n_cache(key)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_i18n_cache(key)
|
def reset_i18n_cache(key)
|
||||||
instance_variable_set(:"@#{key}", {})
|
class_variable_set(:"@@#{key}", {})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Inputs
|
||||||
|
autoload :Base, 'simple_form/inputs/base'
|
||||||
|
autoload :BlockInput, 'simple_form/inputs/block_input'
|
||||||
|
autoload :CollectionInput, 'simple_form/inputs/collection_input'
|
||||||
|
autoload :DateTimeInput, 'simple_form/inputs/date_time_input'
|
||||||
|
autoload :HiddenInput, 'simple_form/inputs/hidden_input'
|
||||||
|
autoload :MappingInput, 'simple_form/inputs/mapping_input'
|
||||||
|
autoload :PriorityInput, 'simple_form/inputs/priority_input'
|
||||||
|
autoload :TextFieldInput, 'simple_form/inputs/text_field_input'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,113 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Inputs
|
||||||
|
class Base
|
||||||
|
extend I18nCache
|
||||||
|
|
||||||
|
# When action is create or update, we still should use new and edit
|
||||||
|
ACTIONS = {
|
||||||
|
:create => :new,
|
||||||
|
:update => :edit
|
||||||
|
}
|
||||||
|
|
||||||
|
include SimpleForm::Components::Errors
|
||||||
|
include SimpleForm::Components::Hints
|
||||||
|
include SimpleForm::Components::Labels
|
||||||
|
include SimpleForm::Components::Wrapper
|
||||||
|
|
||||||
|
delegate :template, :object, :object_name, :attribute_name, :column,
|
||||||
|
:reflection, :input_type, :options, :to => :@builder
|
||||||
|
|
||||||
|
def initialize(builder)
|
||||||
|
@builder = builder
|
||||||
|
end
|
||||||
|
|
||||||
|
def input
|
||||||
|
raise NotImplementedError
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
content = SimpleForm.components.map do |component|
|
||||||
|
next if options[component] == false
|
||||||
|
send(component)
|
||||||
|
end
|
||||||
|
content.compact!
|
||||||
|
wrap(content.join).html_safe!
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Check if :include_blank must be included by default.
|
||||||
|
def skip_include_blank?
|
||||||
|
options.key?(:prompt) || options.key?(:include_blank)
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Inputs
|
||||||
|
class BlockInput < Base
|
||||||
|
def initialize(builder, block)
|
||||||
|
@builder, @block = builder, block
|
||||||
|
end
|
||||||
|
|
||||||
|
def input
|
||||||
|
template.capture(&@block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,50 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Inputs
|
||||||
|
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.yes" and
|
||||||
|
# "simple_form.no" 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
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,18 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Inputs
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Inputs
|
||||||
|
# Handles hidden input.
|
||||||
|
class HiddenInput < Base
|
||||||
|
def render
|
||||||
|
@builder.hidden_field(attribute_name, input_html_options)
|
||||||
|
end
|
||||||
|
alias :input :render
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Inputs
|
||||||
|
# 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
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,14 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Inputs
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,16 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Inputs
|
||||||
|
# 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[:maxlength] ||= column.limit if column
|
||||||
|
input_options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,4 @@
|
||||||
module SimpleForm
|
module SimpleForm
|
||||||
Mapping = Struct.new(:method, :collection, :options, :with_priority)
|
|
||||||
|
|
||||||
module MapType
|
module MapType
|
||||||
def mappings
|
def mappings
|
||||||
@mappings ||= {}
|
@mappings ||= {}
|
||||||
|
@ -9,9 +7,7 @@ module SimpleForm
|
||||||
def map_type(*types)
|
def map_type(*types)
|
||||||
options = types.extract_options!
|
options = types.extract_options!
|
||||||
raise ArgumentError, "You need to give :to as option to map_type" unless options[:to]
|
raise ArgumentError, "You need to give :to as option to map_type" unless options[:to]
|
||||||
mapping = Mapping.new(options[:to], options[:collection] || false,
|
types.each { |t| mappings[t] = options[:to] }
|
||||||
options[:options] || false, options[:with_priority] || false)
|
|
||||||
types.each { |t| mappings[t] = mapping }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,26 +0,0 @@
|
||||||
module SimpleForm
|
|
||||||
module RequiredHelpers
|
|
||||||
# Attribute is always required, unless the user has defined the opposite.
|
|
||||||
def attribute_required?
|
|
||||||
options[:required] != false
|
|
||||||
end
|
|
||||||
|
|
||||||
def required_class
|
|
||||||
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
|
|
|
@ -63,7 +63,7 @@ class BuilderTest < ActionView::TestCase
|
||||||
test 'collection check box accepts a collection and generate a serie of checkboxes for value method' do
|
test 'collection check box accepts a collection and generate a serie of checkboxes for value method' do
|
||||||
collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')]
|
collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')]
|
||||||
form_for @user do |f|
|
form_for @user do |f|
|
||||||
concat f.collection_check_box :tag_ids, collection, :id, :name
|
concat f.collection_check_boxes :tag_ids, collection, :id, :name
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_select "form input[type=hidden][name='user[tag_ids][]'][value=]"
|
assert_select "form input[type=hidden][name='user[tag_ids][]'][value=]"
|
||||||
|
@ -74,17 +74,17 @@ class BuilderTest < ActionView::TestCase
|
||||||
test 'collection check box accepts a collection and generate a serie of checkboxes with labels for label method' do
|
test 'collection check box accepts a collection and generate a serie of checkboxes with labels for label method' do
|
||||||
collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')]
|
collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')]
|
||||||
form_for @user do |f|
|
form_for @user do |f|
|
||||||
concat f.collection_check_box :tag_ids, collection, :id, :name
|
concat f.collection_check_boxes :tag_ids, collection, :id, :name
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_select 'form label.collection_check_box[for=user_tag_ids_1]', 'Tag 1'
|
assert_select 'form label.collection_check_boxes[for=user_tag_ids_1]', 'Tag 1'
|
||||||
assert_select 'form label.collection_check_box[for=user_tag_ids_2]', 'Tag 2'
|
assert_select 'form label.collection_check_boxes[for=user_tag_ids_2]', 'Tag 2'
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'collection check box accepts selected values as :checked option' do
|
test 'collection check box accepts selected values as :checked option' do
|
||||||
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
||||||
form_for @user do |f|
|
form_for @user do |f|
|
||||||
concat f.collection_check_box :tag_ids, collection, :first, :last, :checked => [1, 3]
|
concat f.collection_check_boxes :tag_ids, collection, :first, :last, :checked => [1, 3]
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_select 'form input[type=checkbox][value=1][checked=checked]'
|
assert_select 'form input[type=checkbox][value=1][checked=checked]'
|
||||||
|
@ -95,7 +95,7 @@ class BuilderTest < ActionView::TestCase
|
||||||
test 'collection check box accepts a single checked value' do
|
test 'collection check box accepts a single checked value' do
|
||||||
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
||||||
form_for @user do |f|
|
form_for @user do |f|
|
||||||
concat f.collection_check_box :tag_ids, collection, :first, :last, :checked => 3
|
concat f.collection_check_boxes :tag_ids, collection, :first, :last, :checked => 3
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_select 'form input[type=checkbox][value=3][checked=checked]'
|
assert_select 'form input[type=checkbox][value=3][checked=checked]'
|
||||||
|
@ -106,7 +106,7 @@ class BuilderTest < ActionView::TestCase
|
||||||
test 'collection check box accepts multiple disabled items' do
|
test 'collection check box accepts multiple disabled items' do
|
||||||
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
||||||
form_for @user do |f|
|
form_for @user do |f|
|
||||||
concat f.collection_check_box :tag_ids, collection, :first, :last, :disabled => [1, 3]
|
concat f.collection_check_boxes :tag_ids, collection, :first, :last, :disabled => [1, 3]
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_select 'form input[type=checkbox][value=1][disabled=disabled]'
|
assert_select 'form input[type=checkbox][value=1][disabled=disabled]'
|
||||||
|
@ -117,7 +117,7 @@ class BuilderTest < ActionView::TestCase
|
||||||
test 'collection check box accepts single disable item' do
|
test 'collection check box accepts single disable item' do
|
||||||
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
||||||
form_for @user do |f|
|
form_for @user do |f|
|
||||||
concat f.collection_check_box :tag_ids, collection, :first, :last, :disabled => 1
|
concat f.collection_check_boxes :tag_ids, collection, :first, :last, :disabled => 1
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_select 'form input[type=checkbox][value=1][disabled=disabled]'
|
assert_select 'form input[type=checkbox][value=1][disabled=disabled]'
|
||||||
|
@ -128,7 +128,7 @@ class BuilderTest < ActionView::TestCase
|
||||||
test 'collection check box accepts a proc to disabled items' do
|
test 'collection check box accepts a proc to disabled items' do
|
||||||
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
collection = (1..3).map{|i| [i, "Tag #{i}"] }
|
||||||
form_for @user do |f|
|
form_for @user do |f|
|
||||||
concat f.collection_check_box :tag_ids, collection, :first, :last, :disabled => proc { |i| i.first == 1 }
|
concat f.collection_check_boxes :tag_ids, collection, :first, :last, :disabled => proc { |i| i.first == 1 }
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_select 'form input[type=checkbox][value=1][disabled=disabled]'
|
assert_select 'form input[type=checkbox][value=1][disabled=disabled]'
|
||||||
|
@ -139,7 +139,7 @@ class BuilderTest < ActionView::TestCase
|
||||||
test 'collection check box accepts html options' do
|
test 'collection check box accepts html options' do
|
||||||
collection = [[1, 'Tag 1'], [2, 'Tag 2']]
|
collection = [[1, 'Tag 1'], [2, 'Tag 2']]
|
||||||
form_for @user do |f|
|
form_for @user do |f|
|
||||||
concat f.collection_check_box :tag_ids, collection, :first, :last, {}, :class => 'check'
|
concat f.collection_check_boxes :tag_ids, collection, :first, :last, {}, :class => 'check'
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_select 'form input.check[type=checkbox][value=1]'
|
assert_select 'form input.check[type=checkbox][value=1]'
|
||||||
|
@ -150,15 +150,15 @@ class BuilderTest < ActionView::TestCase
|
||||||
collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')]
|
collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')]
|
||||||
form_for @user do |f|
|
form_for @user do |f|
|
||||||
f.fields_for :post do |p|
|
f.fields_for :post do |p|
|
||||||
concat p.collection_check_box :tag_ids, collection, :id, :name
|
concat p.collection_check_boxes :tag_ids, collection, :id, :name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_select 'form input#user_post_tag_ids_1[type=checkbox][value=1]'
|
assert_select 'form input#user_post_tag_ids_1[type=checkbox][value=1]'
|
||||||
assert_select 'form input#user_post_tag_ids_2[type=checkbox][value=2]'
|
assert_select 'form input#user_post_tag_ids_2[type=checkbox][value=2]'
|
||||||
|
|
||||||
assert_select 'form label.collection_check_box[for=user_post_tag_ids_1]', 'Tag 1'
|
assert_select 'form label.collection_check_boxes[for=user_post_tag_ids_1]', 'Tag 1'
|
||||||
assert_select 'form label.collection_check_box[for=user_post_tag_ids_2]', 'Tag 2'
|
assert_select 'form label.collection_check_boxes[for=user_post_tag_ids_2]', 'Tag 2'
|
||||||
end
|
end
|
||||||
|
|
||||||
# SIMPLE FIELDS
|
# SIMPLE FIELDS
|
||||||
|
|
|
@ -9,28 +9,18 @@ class ErrorTest < ActionView::TestCase
|
||||||
f.input_type = type
|
f.input_type = type
|
||||||
f.options = options
|
f.options = options
|
||||||
|
|
||||||
error = SimpleForm::Components::Error.new(f, SimpleForm::FormBuilder::TERMINATOR)
|
concat(SimpleForm::Inputs::Base.new(f).error.to_s)
|
||||||
concat(error.call)
|
|
||||||
yield error if block_given?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'error should not generate content for hidden fields' do
|
|
||||||
with_error_for @user, :name, :hidden do |error|
|
|
||||||
assert error.call.blank?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'error should not generate content for attribute without errors' do
|
test 'error should not generate content for attribute without errors' do
|
||||||
with_error_for @user, :active, :boolean do |error|
|
with_error_for @user, :active, :boolean
|
||||||
assert error.call.blank?
|
assert_no_select 'span.error'
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'error should not generate messages when object is not present' do
|
test 'error should not generate messages when object is not present' do
|
||||||
with_error_for :project, :name, :string do |error|
|
with_error_for :project, :name, :string
|
||||||
assert error.call.blank?
|
assert_no_select 'span.error'
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'error should generate messages for attribute with single error' do
|
test 'error should generate messages for attribute with single error' do
|
||||||
|
|
|
@ -9,22 +9,13 @@ class HintTest < ActionView::TestCase
|
||||||
f.input_type = type
|
f.input_type = type
|
||||||
f.options = options
|
f.options = options
|
||||||
|
|
||||||
hint = SimpleForm::Components::Hint.new(f, SimpleForm::FormBuilder::TERMINATOR)
|
concat(SimpleForm::Inputs::Base.new(f).hint.to_s)
|
||||||
concat(hint.call)
|
|
||||||
yield hint if block_given?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'hint should not be generated by default' do
|
test 'hint should not be generated by default' do
|
||||||
with_hint_for @user, :name, :string do |hint|
|
with_hint_for @user, :name, :string
|
||||||
assert hint.call.blank?
|
assert_no_select 'span.hint'
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'hint should not be generated for hidden fields' do
|
|
||||||
with_hint_for @user, :name, :hidden, :hint => 'Use with care...' do |hint|
|
|
||||||
assert hint.call.blank?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'hint should be generated with input text' do
|
test 'hint should be generated with input text' do
|
||||||
|
@ -33,7 +24,7 @@ class HintTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'hint uses the current component tag set' do
|
test 'hint uses the current component tag set' do
|
||||||
swap SimpleForm, :component_tag => :p do
|
swap SimpleForm, :hint_tag => :p do
|
||||||
with_hint_for @user, :name, :string, :hint => 'Use with care...'
|
with_hint_for @user, :name, :string, :hint => 'Use with care...'
|
||||||
assert_select 'p.hint', 'Use with care...'
|
assert_select 'p.hint', 'Use with care...'
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'test_helper'
|
||||||
class LabelTest < ActionView::TestCase
|
class LabelTest < ActionView::TestCase
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
SimpleForm::Components::Label.reset_i18n_cache :translate_required_html
|
SimpleForm::Inputs::Base.reset_i18n_cache :translate_required_html
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_label_for(object, attribute_name, type, options={})
|
def with_label_for(object, attribute_name, type, options={})
|
||||||
|
@ -13,15 +13,7 @@ class LabelTest < ActionView::TestCase
|
||||||
f.input_type = type
|
f.input_type = type
|
||||||
f.options = options
|
f.options = options
|
||||||
|
|
||||||
label = SimpleForm::Components::Label.new(f, SimpleForm::FormBuilder::TERMINATOR)
|
concat(SimpleForm::Inputs::Base.new(f).label)
|
||||||
concat(label.call)
|
|
||||||
yield label if block_given?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'label should not be generated for hidden inputs' do
|
|
||||||
with_label_for @user, :name, :hidden do |label|
|
|
||||||
assert label.call.blank?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -175,19 +167,4 @@ class LabelTest < ActionView::TestCase
|
||||||
with_label_for :project, :description, :string, :required => false
|
with_label_for :project, :description, :string, :required => false
|
||||||
assert_no_select 'label.required[for=project_description]'
|
assert_no_select 'label.required[for=project_description]'
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'label should point to first option when date input type' do
|
|
||||||
with_label_for :project, :created_at, :date
|
|
||||||
assert_select 'label[for=project_created_at_1i]'
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'label should point to first option when datetime input type' do
|
|
||||||
with_label_for :project, :created_at, :datetime
|
|
||||||
assert_select 'label[for=project_created_at_1i]'
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'label should point to first option when time input type' do
|
|
||||||
with_label_for :project, :created_at, :time
|
|
||||||
assert_select 'label[for=project_created_at_4i]'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,42 +2,65 @@ require 'test_helper'
|
||||||
|
|
||||||
class FormBuilderTest < ActionView::TestCase
|
class FormBuilderTest < ActionView::TestCase
|
||||||
|
|
||||||
def with_form_for(object, attribute, options={})
|
def with_form_for(object, *args, &block)
|
||||||
simple_form_for object do |f|
|
simple_form_for object do |f|
|
||||||
concat f.input attribute, options
|
concat f.input(*args, &block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_button_for(object, *args)
|
def with_button_for(object, *args)
|
||||||
simple_form_for object do |f|
|
simple_form_for object do |f|
|
||||||
concat f.button *args
|
concat f.button(*args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_error_for(object, *args)
|
def with_error_for(object, *args)
|
||||||
simple_form_for object do |f|
|
simple_form_for object do |f|
|
||||||
concat f.error *args
|
concat f.error(*args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_hint_for(object, *args)
|
def with_hint_for(object, *args)
|
||||||
simple_form_for object do |f|
|
simple_form_for object do |f|
|
||||||
concat f.hint *args
|
concat f.hint(*args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_label_for(object, *args)
|
def with_label_for(object, *args)
|
||||||
simple_form_for object do |f|
|
simple_form_for object do |f|
|
||||||
concat f.label *args
|
concat f.label(*args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_association_for(object, *args)
|
def with_association_for(object, *args)
|
||||||
simple_form_for object do |f|
|
simple_form_for object do |f|
|
||||||
concat f.association *args
|
concat f.association(*args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# All
|
||||||
|
test 'nested simple fields should yields an instance of FormBuilder' do
|
||||||
|
simple_form_for :user do |f|
|
||||||
|
f.simple_fields_for :posts do |posts_form|
|
||||||
|
assert posts_form.instance_of?(SimpleForm::FormBuilder)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'builder input is html safe' do
|
||||||
|
simple_form_for @user do |f|
|
||||||
|
assert f.input(:name).html_safe?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'builder input should allow a block to configure input' do
|
||||||
|
with_form_for @user, :name do
|
||||||
|
concat text_field_tag :foo, :bar, :id => :cool
|
||||||
|
end
|
||||||
|
assert_no_select 'input.string'
|
||||||
|
assert_select 'input#cool'
|
||||||
|
end
|
||||||
|
|
||||||
# INPUT TYPES
|
# INPUT TYPES
|
||||||
test 'builder should generate text fields for string columns' do
|
test 'builder should generate text fields for string columns' do
|
||||||
with_form_for @user, :name
|
with_form_for @user, :name
|
||||||
|
@ -136,7 +159,7 @@ class FormBuilderTest < ActionView::TestCase
|
||||||
|
|
||||||
test 'builder should generate a input with label' do
|
test 'builder should generate a input with label' do
|
||||||
with_form_for @user, :name
|
with_form_for @user, :name
|
||||||
assert_select 'form label.string[for=user_name]'
|
assert_select 'form label.string[for=user_name]', /Name/
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'builder should be able to disable the label for a input' do
|
test 'builder should be able to disable the label for a input' do
|
||||||
|
@ -146,7 +169,12 @@ class FormBuilderTest < ActionView::TestCase
|
||||||
|
|
||||||
test 'builder should use custom label' do
|
test 'builder should use custom label' do
|
||||||
with_form_for @user, :name, :label => 'Yay!'
|
with_form_for @user, :name, :label => 'Yay!'
|
||||||
assert_no_select 'form label', 'Yay!'
|
assert_select 'form label', /Yay!/
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'builder should pass options to label' do
|
||||||
|
with_form_for @user, :name, :label_html => { :id => "cool" }
|
||||||
|
assert_select 'form label#cool', /Name/
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'builder should not generate hints for a input' do
|
test 'builder should not generate hints for a input' do
|
||||||
|
@ -166,6 +194,11 @@ class FormBuilderTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'builder should pass options to hint' do
|
||||||
|
with_form_for @user, :name, :hint => 'test', :hint_html => { :id => "cool" }
|
||||||
|
assert_select 'span.hint#cool', 'test'
|
||||||
|
end
|
||||||
|
|
||||||
test 'builder should generate errors for attribute without errors' do
|
test 'builder should generate errors for attribute without errors' do
|
||||||
with_form_for @user, :credit_limit
|
with_form_for @user, :credit_limit
|
||||||
assert_no_select 'span.errors'
|
assert_no_select 'span.errors'
|
||||||
|
@ -181,6 +214,22 @@ class FormBuilderTest < ActionView::TestCase
|
||||||
assert_no_select 'span.error'
|
assert_no_select 'span.error'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'builder should pass options to errors' do
|
||||||
|
with_form_for @user, :name, :error_html => { :id => "cool" }
|
||||||
|
assert_select 'span.error#cool', "can't be blank"
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'builder input should be required by default' do
|
||||||
|
with_form_for @user, :name
|
||||||
|
assert_select 'input.required#user_name'
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'builder input should allow disabling required' do
|
||||||
|
with_form_for @user, :name, :required => false
|
||||||
|
assert_no_select 'input.required'
|
||||||
|
assert_select 'input.optional#user_name'
|
||||||
|
end
|
||||||
|
|
||||||
# WRAPPERS
|
# WRAPPERS
|
||||||
test 'builder support wrapping around an specific tag' do
|
test 'builder support wrapping around an specific tag' do
|
||||||
swap SimpleForm, :wrapper_tag => :p do
|
swap SimpleForm, :wrapper_tag => :p do
|
||||||
|
@ -209,19 +258,11 @@ class FormBuilderTest < ActionView::TestCase
|
||||||
|
|
||||||
test 'builder allows wrapper tag to be given on demand' do
|
test 'builder allows wrapper tag to be given on demand' do
|
||||||
simple_form_for @user do |f|
|
simple_form_for @user do |f|
|
||||||
concat f.input :name, :wrapper => :b
|
concat f.input :name, :wrapper_tag => :b
|
||||||
end
|
end
|
||||||
assert_select 'form b.required.string'
|
assert_select 'form b.required.string'
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'nested simple fields should yields an instance of FormBuilder' do
|
|
||||||
simple_form_for :user do |f|
|
|
||||||
f.simple_fields_for :posts do |posts_form|
|
|
||||||
assert posts_form.instance_of?(SimpleForm::FormBuilder)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# WITHOUT OBJECT
|
# WITHOUT OBJECT
|
||||||
test 'builder should generate properly when object is not present' do
|
test 'builder should generate properly when object is not present' do
|
||||||
with_form_for :project, :name
|
with_form_for :project, :name
|
||||||
|
@ -248,7 +289,7 @@ class FormBuilderTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
# ERRORS
|
# ERRORS
|
||||||
test 'builder should generate an error component tag for the attribute' do
|
test 'builder should generate an error tag for the attribute' do
|
||||||
with_error_for @user, :name
|
with_error_for @user, :name
|
||||||
assert_select 'span.error', "can't be blank"
|
assert_select 'span.error', "can't be blank"
|
||||||
end
|
end
|
||||||
|
@ -259,7 +300,7 @@ class FormBuilderTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
# HINTS
|
# HINTS
|
||||||
test 'builder should generate a hint component tag for the attribute' do
|
test 'builder should generate a hint tag for the attribute' do
|
||||||
store_translations(:en, :simple_form => { :hints => { :user => { :name => "Add your name" }}}) do
|
store_translations(:en, :simple_form => { :hints => { :user => { :name => "Add your name" }}}) do
|
||||||
with_hint_for @user, :name
|
with_hint_for @user, :name
|
||||||
assert_select 'span.hint', 'Add your name'
|
assert_select 'span.hint', 'Add your name'
|
||||||
|
@ -277,7 +318,7 @@ class FormBuilderTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
# LABELS
|
# LABELS
|
||||||
test 'builder should generate a label component tag for the attribute' do
|
test 'builder should generate a label for the attribute' do
|
||||||
with_label_for @user, :name
|
with_label_for @user, :name
|
||||||
assert_select 'label.string[for=user_name]', /Name/
|
assert_select 'label.string[for=user_name]', /Name/
|
||||||
end
|
end
|
||||||
|
@ -369,6 +410,25 @@ class FormBuilderTest < ActionView::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'builder association with a block call simple_fields_for' do
|
||||||
|
simple_form_for @user do |f|
|
||||||
|
f.association :posts do |posts_form|
|
||||||
|
assert posts_form.instance_of?(SimpleForm::FormBuilder)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'builder association forwards collection to simple_fields_for' do
|
||||||
|
calls = 0
|
||||||
|
simple_form_for @user do |f|
|
||||||
|
f.association :company, :collection => Company.all do |c|
|
||||||
|
calls += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal calls, 3
|
||||||
|
end
|
||||||
|
|
||||||
# ASSOCIATIONS - BELONGS TO
|
# ASSOCIATIONS - BELONGS TO
|
||||||
test 'builder creates a select for belongs_to associations' do
|
test 'builder creates a select for belongs_to associations' do
|
||||||
with_association_for @user, :company
|
with_association_for @user, :company
|
||||||
|
|
|
@ -3,27 +3,16 @@ require 'test_helper'
|
||||||
class InputTest < ActionView::TestCase
|
class InputTest < ActionView::TestCase
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
SimpleForm::Components::Input.reset_i18n_cache :boolean_collection
|
SimpleForm::Inputs::CollectionInput.reset_i18n_cache :boolean_collection
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_input_for(object, attribute_name, type, options={})
|
def with_input_for(object, attribute_name, type, options={})
|
||||||
simple_form_for object do |f|
|
simple_form_for object do |f|
|
||||||
f.attribute_name = attribute_name
|
concat f.input(attribute_name, options.merge(:as => type))
|
||||||
f.column = object.column_for_attribute(attribute_name) if object.respond_to?(:column_for_attribute)
|
|
||||||
f.input_type = type
|
|
||||||
f.options = options
|
|
||||||
|
|
||||||
input = SimpleForm::Components::Input.new(f, SimpleForm::FormBuilder::TERMINATOR)
|
|
||||||
concat(input.call)
|
|
||||||
yield input if block_given?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'input should map text field to string attribute' do
|
# ALL
|
||||||
with_input_for @user, :name, :string
|
|
||||||
assert_select 'input[name=\'user[name]\'][id=user_name][value=New in Simple Form!]'
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'input should generate css class based on default input type' do
|
test 'input should generate css class based on default input type' do
|
||||||
with_input_for @user, :name, :string
|
with_input_for @user, :name, :string
|
||||||
assert_select 'input.string'
|
assert_select 'input.string'
|
||||||
|
@ -37,14 +26,10 @@ class InputTest < ActionView::TestCase
|
||||||
assert_select 'select.datetime'
|
assert_select 'select.datetime'
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'input should allow passing options to text field' do
|
# TextFieldInput
|
||||||
with_input_for @user, :name, :string, :input_html => { :class => 'my_input', :id => 'my_input' }
|
test 'input should map text field to string attribute' do
|
||||||
assert_select 'input#my_input.my_input'
|
with_input_for @user, :name, :string
|
||||||
end
|
assert_select 'input[name=\'user[name]\'][id=user_name][value=New in Simple Form!]'
|
||||||
|
|
||||||
test 'input should generate a text area for text attributes' do
|
|
||||||
with_input_for @user, :description, :text
|
|
||||||
assert_select 'textarea.text#user_description'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'input should generate an integer text field for integer attributes ' do
|
test 'input should generate an integer text field for integer attributes ' do
|
||||||
|
@ -62,6 +47,22 @@ class InputTest < ActionView::TestCase
|
||||||
assert_select 'input.decimal#user_age'
|
assert_select 'input.decimal#user_age'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'input should get options from column definition for string attributes' do
|
||||||
|
with_input_for @user, :name, :string
|
||||||
|
assert_select 'input.string[maxlength=100]'
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'input should get options from column definition for decimal attributes' do
|
||||||
|
with_input_for @user, :credit_limit, :decimal
|
||||||
|
assert_select 'input.decimal[maxlength=15]'
|
||||||
|
end
|
||||||
|
|
||||||
|
# MappingInput
|
||||||
|
test 'input should generate a text area for text attributes' do
|
||||||
|
with_input_for @user, :description, :text
|
||||||
|
assert_select 'textarea.text#user_description'
|
||||||
|
end
|
||||||
|
|
||||||
test 'input should generate a checkbox by default for boolean attributes' do
|
test 'input should generate a checkbox by default for boolean attributes' do
|
||||||
with_input_for @user, :active, :boolean
|
with_input_for @user, :active, :boolean
|
||||||
assert_select 'input[type=checkbox].boolean#user_active'
|
assert_select 'input[type=checkbox].boolean#user_active'
|
||||||
|
@ -72,17 +73,29 @@ class InputTest < ActionView::TestCase
|
||||||
assert_select 'input[type=password].password#user_password'
|
assert_select 'input[type=password].password#user_password'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'input should generate a file field' do
|
||||||
|
with_input_for @user, :name, :file
|
||||||
|
assert_select 'input#user_name[type=file]'
|
||||||
|
end
|
||||||
|
|
||||||
|
# HiddenInput
|
||||||
test 'input should generate a hidden field' do
|
test 'input should generate a hidden field' do
|
||||||
with_input_for @user, :name, :hidden
|
with_input_for @user, :name, :hidden
|
||||||
assert_no_select 'input[type=text]'
|
assert_no_select 'input[type=text]'
|
||||||
assert_select 'input#user_name[type=hidden]'
|
assert_select 'input#user_name[type=hidden]'
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'input should generate a file field' do
|
test 'hint should not be generated for hidden fields' do
|
||||||
with_input_for @user, :name, :file
|
with_input_for @user, :name, :hidden, :hint => 'Use with care...'
|
||||||
assert_select 'input#user_name[type=file]'
|
assert_no_select 'span.hint'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'label should not be generated for hidden inputs' do
|
||||||
|
with_input_for @user, :name, :hidden
|
||||||
|
assert_no_select 'label'
|
||||||
|
end
|
||||||
|
|
||||||
|
# PriorityInput
|
||||||
test 'input should generate a country select field' do
|
test 'input should generate a country select field' do
|
||||||
with_input_for @user, :country, :country
|
with_input_for @user, :country, :country
|
||||||
assert_select 'select#user_country'
|
assert_select 'select#user_country'
|
||||||
|
@ -114,6 +127,7 @@ class InputTest < ActionView::TestCase
|
||||||
assert_select 'select option[value=][disabled=disabled]'
|
assert_select 'select option[value=][disabled=disabled]'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# DateTime input
|
||||||
test 'input should generate a datetime select by default for datetime attributes' do
|
test 'input should generate a datetime select by default for datetime attributes' do
|
||||||
with_input_for @user, :created_at, :datetime
|
with_input_for @user, :created_at, :datetime
|
||||||
1.upto(5) do |i|
|
1.upto(5) do |i|
|
||||||
|
@ -167,6 +181,22 @@ class InputTest < ActionView::TestCase
|
||||||
assert_select 'select.time option', 'minuto'
|
assert_select 'select.time option', 'minuto'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'label should point to first option when date input type' do
|
||||||
|
with_input_for :project, :created_at, :date
|
||||||
|
assert_select 'label[for=project_created_at_1i]'
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'label should point to first option when datetime input type' do
|
||||||
|
with_input_for :project, :created_at, :datetime
|
||||||
|
assert_select 'label[for=project_created_at_1i]'
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'label should point to first option when time input type' do
|
||||||
|
with_input_for :project, :created_at, :time
|
||||||
|
assert_select 'label[for=project_created_at_4i]'
|
||||||
|
end
|
||||||
|
|
||||||
|
# CollectionInput
|
||||||
test 'input should generate boolean radio buttons by default for radio types' do
|
test 'input should generate boolean radio buttons by default for radio types' do
|
||||||
with_input_for @user, :active, :radio
|
with_input_for @user, :active, :radio
|
||||||
assert_select 'input[type=radio][value=true].radio#user_active_true'
|
assert_select 'input[type=radio][value=true].radio#user_active_true'
|
||||||
|
@ -281,38 +311,7 @@ class InputTest < ActionView::TestCase
|
||||||
assert_select 'label.collection_radio', 'CARLOS'
|
assert_select 'label.collection_radio', 'CARLOS'
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'input should be required by default' do
|
# With no object
|
||||||
with_input_for @user, :name, :string
|
|
||||||
assert_select 'input.required#user_name'
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'input should allow disabling required' do
|
|
||||||
with_input_for @user, :name, :string, :required => false
|
|
||||||
assert_no_select 'input.required'
|
|
||||||
assert_select 'input.optional#user_name'
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'input should get options from column definition for string attributes' do
|
|
||||||
with_input_for @user, :name, :string
|
|
||||||
assert_select 'input.string[maxlength=100]'
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'input should get options from column definition for decimal attributes' do
|
|
||||||
with_input_for @user, :credit_limit, :decimal
|
|
||||||
assert_select 'input.decimal[maxlength=15]'
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'input should get options from column definition for password attributes' do
|
|
||||||
with_input_for @user, :password, :password
|
|
||||||
assert_select 'input.password[maxlength=100]'
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'input should not generate options for different attributes' do
|
|
||||||
with_input_for @user, :description, :text
|
|
||||||
assert_select 'textarea'
|
|
||||||
assert_no_select 'textarea[maxlength]'
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'input should be generated properly when object is not present' do
|
test 'input should be generated properly when object is not present' do
|
||||||
with_input_for :project, :name, :string
|
with_input_for :project, :name, :string
|
||||||
assert_select 'input.string.required#project_name'
|
assert_select 'input.string.required#project_name'
|
|
@ -1,7 +1,6 @@
|
||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class SimpleFormTest < ActiveSupport::TestCase
|
class SimpleFormTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
test 'setup block yields self' do
|
test 'setup block yields self' do
|
||||||
SimpleForm.setup do |config|
|
SimpleForm.setup do |config|
|
||||||
assert_equal SimpleForm, config
|
assert_equal SimpleForm, config
|
||||||
|
|
|
@ -12,6 +12,10 @@ class Company < Struct.new(:id, :name)
|
||||||
return all[1..2] if options[:joins]
|
return all[1..2] if options[:joins]
|
||||||
all
|
all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new_record?
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Tag < Struct.new(:id, :name)
|
class Tag < Struct.new(:id, :name)
|
||||||
|
@ -32,6 +36,9 @@ class User < OpenStruct
|
||||||
@new_record || false
|
@new_record || false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def company_attributes=(foo)
|
||||||
|
end
|
||||||
|
|
||||||
def column_for_attribute(attribute)
|
def column_for_attribute(attribute)
|
||||||
column_type, limit = case attribute.to_sym
|
column_type, limit = case attribute.to_sym
|
||||||
when :name, :status, :password then [:string, 100]
|
when :name, :status, :password then [:string, 100]
|
||||||
|
|
Loading…
Reference in New Issue