Move components to a folder named components.
This commit is contained in:
parent
727f29567a
commit
65ba8dabbd
|
@ -2,7 +2,9 @@ require 'simple_form/action_view_extensions/form_helper'
|
||||||
require 'simple_form/action_view_extensions/builder'
|
require 'simple_form/action_view_extensions/builder'
|
||||||
|
|
||||||
module SimpleForm
|
module SimpleForm
|
||||||
|
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 :MapType, 'simple_form/map_type'
|
autoload :MapType, 'simple_form/map_type'
|
||||||
|
autoload :RequiredComponent, 'simple_form/required_component'
|
||||||
end
|
end
|
|
@ -1,50 +0,0 @@
|
||||||
module SimpleForm
|
|
||||||
class AbstractComponent
|
|
||||||
attr_reader :builder, :attribute, :input_type, :options
|
|
||||||
|
|
||||||
def self.basename
|
|
||||||
@basename ||= name.split("::").last.underscore.to_sym
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(builder, attribute, input_type, options)
|
|
||||||
@builder = builder
|
|
||||||
@attribute = attribute
|
|
||||||
@input_type = input_type
|
|
||||||
@options = options
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate
|
|
||||||
return "" unless valid?
|
|
||||||
component_tag(content).to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def template
|
|
||||||
@builder.template
|
|
||||||
end
|
|
||||||
|
|
||||||
def object
|
|
||||||
@builder.object
|
|
||||||
end
|
|
||||||
|
|
||||||
def hidden_input?
|
|
||||||
@input_type == :hidden
|
|
||||||
end
|
|
||||||
|
|
||||||
def basename
|
|
||||||
self.class.basename
|
|
||||||
end
|
|
||||||
|
|
||||||
def component_tag(content)
|
|
||||||
template.content_tag(:span, content, :class => basename)
|
|
||||||
end
|
|
||||||
|
|
||||||
def translate(default='')
|
|
||||||
lookups = [ :"#{@builder.object_name}.#{@attribute}", :"#{@attribute}", default ]
|
|
||||||
I18n.t(lookups.shift, :scope => :"simple_form.#{basename.to_s.pluralize}", :default => lookups)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Components
|
||||||
|
autoload :Base, 'simple_form/components/base'
|
||||||
|
autoload :Error, 'simple_form/components/error'
|
||||||
|
autoload :Hint, 'simple_form/components/hint'
|
||||||
|
autoload :Input, 'simple_form/components/input'
|
||||||
|
autoload :Label, 'simple_form/components/label'
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,52 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Components
|
||||||
|
class Base
|
||||||
|
attr_reader :builder, :attribute, :input_type, :options
|
||||||
|
|
||||||
|
def self.basename
|
||||||
|
@basename ||= name.split("::").last.underscore.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(builder, attribute, input_type, options)
|
||||||
|
@builder = builder
|
||||||
|
@attribute = attribute
|
||||||
|
@input_type = input_type
|
||||||
|
@options = options
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate
|
||||||
|
return "" unless valid?
|
||||||
|
component_tag(content).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def template
|
||||||
|
@builder.template
|
||||||
|
end
|
||||||
|
|
||||||
|
def object
|
||||||
|
@builder.object
|
||||||
|
end
|
||||||
|
|
||||||
|
def hidden_input?
|
||||||
|
@input_type == :hidden
|
||||||
|
end
|
||||||
|
|
||||||
|
def basename
|
||||||
|
self.class.basename
|
||||||
|
end
|
||||||
|
|
||||||
|
def component_tag(content)
|
||||||
|
template.content_tag(:span, content, :class => basename)
|
||||||
|
end
|
||||||
|
|
||||||
|
def translate(default='')
|
||||||
|
lookups = [ :"#{@builder.object_name}.#{@attribute}", :"#{@attribute}", default ]
|
||||||
|
I18n.t(lookups.shift, :scope => :"simple_form.#{basename.to_s.pluralize}", :default => lookups)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Components
|
||||||
|
class Error < Base
|
||||||
|
def valid?
|
||||||
|
!hidden_input? && !errors.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
def errors
|
||||||
|
@errors ||= object.errors[@attribute]
|
||||||
|
end
|
||||||
|
|
||||||
|
def content
|
||||||
|
Array(errors).to_sentence
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Components
|
||||||
|
class Hint < Base
|
||||||
|
def valid?
|
||||||
|
!hidden_input? && !content.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
def content
|
||||||
|
@content ||= @options[:hint] || translate
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,68 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Components
|
||||||
|
class Input < Base
|
||||||
|
extend I18nCache
|
||||||
|
extend MapType
|
||||||
|
include RequiredComponent
|
||||||
|
|
||||||
|
map_type :boolean, :to => :check_box
|
||||||
|
map_type :text, :to => :text_area
|
||||||
|
map_type :datetime, :to => :datetime_select, :options => true
|
||||||
|
map_type :date, :to => :date_select, :options => true
|
||||||
|
map_type :time, :to => :time_select, :options => true
|
||||||
|
map_type :password, :to => :password_field
|
||||||
|
map_type :hidden, :to => :hidden_field
|
||||||
|
map_type :select, :to => :collection_select, :options => true, :collection => true
|
||||||
|
map_type :radio, :to => :collection_radio, :collection => true
|
||||||
|
map_type :numeric, :to => :text_field
|
||||||
|
map_type :string, :to => :text_field
|
||||||
|
|
||||||
|
def self.boolean_collection
|
||||||
|
i18n_cache :boolean_collection do
|
||||||
|
[ [I18n.t(:"simple_form.true", :default => 'Yes'), true],
|
||||||
|
[I18n.t(:"simple_form.false", :default => 'No'), false] ]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate
|
||||||
|
html_options = @options[:html] || {}
|
||||||
|
html_options[:class] = default_css_classes(html_options[:class])
|
||||||
|
@options[:options] ||= {}
|
||||||
|
|
||||||
|
mapping = self.class.mappings[@input_type]
|
||||||
|
raise "Invalid input type #{@input_type.inspect}" unless mapping
|
||||||
|
|
||||||
|
args = [ @attribute ]
|
||||||
|
|
||||||
|
if mapping.collection
|
||||||
|
collection = @options[:collection] || self.class.boolean_collection
|
||||||
|
detect_collection_methods(collection, @options)
|
||||||
|
args.push(collection, @options[:value_method], @options[:label_method])
|
||||||
|
end
|
||||||
|
|
||||||
|
args << @options[:options] if mapping.options
|
||||||
|
args << html_options
|
||||||
|
|
||||||
|
@builder.send(mapping.method, *args)
|
||||||
|
end
|
||||||
|
|
||||||
|
def detect_collection_methods(collection, options)
|
||||||
|
sample = collection.first
|
||||||
|
|
||||||
|
if sample.is_a?(Array) # TODO Test me
|
||||||
|
options[:label_method] ||= :first
|
||||||
|
options[:value_method] ||= :last
|
||||||
|
elsif sample.is_a?(String) # TODO Test me
|
||||||
|
options[:label_method] ||= :to_s
|
||||||
|
options[:value_method] ||= :to_s
|
||||||
|
elsif sample.is_a?(Numeric) # TODO Test me (including selected)
|
||||||
|
options[:label_method] ||= :to_s
|
||||||
|
options[:value_method] ||= :to_i
|
||||||
|
else # TODO Implement collection label methods or something similar
|
||||||
|
options[:label_method] ||= :to_s
|
||||||
|
options[:value_method] ||= :to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,48 @@
|
||||||
|
module SimpleForm
|
||||||
|
module Components
|
||||||
|
class Label < Base
|
||||||
|
extend I18nCache
|
||||||
|
include RequiredComponent
|
||||||
|
|
||||||
|
def self.translate_required_string
|
||||||
|
i18n_cache :translate_required_string do
|
||||||
|
I18n.t(:"simple_form.required.string", :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
|
||||||
|
|
||||||
|
def generate
|
||||||
|
return '' unless valid?
|
||||||
|
html_options = { :class => default_css_classes }
|
||||||
|
html_options[:for] = @options[:html][:id] if @options.key?(:html)
|
||||||
|
@builder.label(@attribute, label_text, html_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def label_text
|
||||||
|
required_text << (@options[:label] || translate_label)
|
||||||
|
end
|
||||||
|
|
||||||
|
def required_text
|
||||||
|
attribute_required? ? self.class.translate_required_string.dup : ''
|
||||||
|
end
|
||||||
|
|
||||||
|
def translate_label
|
||||||
|
default = object.try(:human_attribute_name, @attribute.to_s) || @attribute.to_s.humanize
|
||||||
|
translate(default)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,15 +0,0 @@
|
||||||
module SimpleForm
|
|
||||||
class Error < AbstractComponent
|
|
||||||
def valid?
|
|
||||||
!hidden_input? && !errors.blank?
|
|
||||||
end
|
|
||||||
|
|
||||||
def errors
|
|
||||||
@errors ||= object.errors[@attribute]
|
|
||||||
end
|
|
||||||
|
|
||||||
def content
|
|
||||||
Array(errors).to_sentence
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,24 +1,17 @@
|
||||||
require 'simple_form/abstract_component'
|
|
||||||
require 'simple_form/label'
|
|
||||||
require 'simple_form/input'
|
|
||||||
require 'simple_form/hint'
|
|
||||||
require 'simple_form/error'
|
|
||||||
|
|
||||||
module SimpleForm
|
module SimpleForm
|
||||||
class FormBuilder < ActionView::Helpers::FormBuilder
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
||||||
# Components used by the folder builder. By default is:
|
# Components used by the folder builder.
|
||||||
# [SimpleForm::Label, SimpleForm::Input, SimpleForm::Hint, SimpleForm::Error]
|
# By default is [:label, :input, :hint, :error].
|
||||||
cattr_accessor :components, :instance_writer => false
|
cattr_accessor :components, :instance_writer => false
|
||||||
@@components = [SimpleForm::Label, SimpleForm::Input, SimpleForm::Hint, SimpleForm::Error]
|
@@components = [
|
||||||
|
SimpleForm::Components::Label, SimpleForm::Components::Input,
|
||||||
|
SimpleForm::Components::Hint, SimpleForm::Components::Error
|
||||||
|
]
|
||||||
|
|
||||||
# Make the template accessible for components
|
# Make the template accessible for components
|
||||||
attr_reader :template
|
attr_reader :template
|
||||||
|
|
||||||
def input(attribute, options={})
|
def input(attribute, options={})
|
||||||
# TODO Do this makes sense since we are delegating to components?
|
|
||||||
options.assert_valid_keys(:as, :label, :required, :hint, :options, :html,
|
|
||||||
:collection, :label_method, :value_method)
|
|
||||||
|
|
||||||
input_type = (options[:as] || default_input_type(attribute, options)).to_sym
|
input_type = (options[:as] || default_input_type(attribute, options)).to_sym
|
||||||
|
|
||||||
pieces = self.components.collect do |klass|
|
pieces = self.components.collect do |klass|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
module SimpleForm
|
|
||||||
class Hint < AbstractComponent
|
|
||||||
def valid?
|
|
||||||
!hidden_input? && !content.blank?
|
|
||||||
end
|
|
||||||
|
|
||||||
def content
|
|
||||||
@content ||= @options[:hint] || translate
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,71 +0,0 @@
|
||||||
require 'simple_form/required_component'
|
|
||||||
require 'simple_form/i18n_cache'
|
|
||||||
require 'simple_form/map_type'
|
|
||||||
|
|
||||||
module SimpleForm
|
|
||||||
class Input < AbstractComponent
|
|
||||||
extend I18nCache
|
|
||||||
extend MapType
|
|
||||||
include RequiredComponent
|
|
||||||
|
|
||||||
map_type :boolean, :to => :check_box
|
|
||||||
map_type :text, :to => :text_area
|
|
||||||
map_type :datetime, :to => :datetime_select, :options => true
|
|
||||||
map_type :date, :to => :date_select, :options => true
|
|
||||||
map_type :time, :to => :time_select, :options => true
|
|
||||||
map_type :password, :to => :password_field
|
|
||||||
map_type :hidden, :to => :hidden_field
|
|
||||||
map_type :select, :to => :collection_select, :options => true, :collection => true
|
|
||||||
map_type :radio, :to => :collection_radio, :collection => true
|
|
||||||
map_type :numeric, :to => :text_field
|
|
||||||
map_type :string, :to => :text_field
|
|
||||||
|
|
||||||
def self.boolean_collection
|
|
||||||
i18n_cache :boolean_collection do
|
|
||||||
[ [I18n.t(:"simple_form.true", :default => 'Yes'), true],
|
|
||||||
[I18n.t(:"simple_form.false", :default => 'No'), false] ]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate
|
|
||||||
html_options = @options[:html] || {}
|
|
||||||
html_options[:class] = default_css_classes(html_options[:class])
|
|
||||||
@options[:options] ||= {}
|
|
||||||
|
|
||||||
mapping = self.class.mappings[@input_type]
|
|
||||||
raise "Invalid input type #{@input_type.inspect}" unless mapping
|
|
||||||
|
|
||||||
args = [ @attribute ]
|
|
||||||
|
|
||||||
if mapping.collection
|
|
||||||
collection = @options[:collection] || self.class.boolean_collection
|
|
||||||
detect_collection_methods(collection, @options)
|
|
||||||
args.push(collection, @options[:value_method], @options[:label_method])
|
|
||||||
end
|
|
||||||
|
|
||||||
args << @options[:options] if mapping.options
|
|
||||||
args << html_options
|
|
||||||
|
|
||||||
@builder.send(mapping.method, *args)
|
|
||||||
end
|
|
||||||
|
|
||||||
def detect_collection_methods(collection, options)
|
|
||||||
sample = collection.first
|
|
||||||
|
|
||||||
if sample.is_a?(Array) # TODO Test me
|
|
||||||
options[:label_method] ||= :first
|
|
||||||
options[:value_method] ||= :last
|
|
||||||
elsif sample.is_a?(String) # TODO Test me
|
|
||||||
options[:label_method] ||= :to_s
|
|
||||||
options[:value_method] ||= :to_s
|
|
||||||
elsif sample.is_a?(Numeric) # TODO Test me (including selected)
|
|
||||||
options[:label_method] ||= :to_s
|
|
||||||
options[:value_method] ||= :to_i
|
|
||||||
else # TODO Implement collection label methods or something similar
|
|
||||||
options[:label_method] ||= :to_s
|
|
||||||
options[:value_method] ||= :to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,49 +0,0 @@
|
||||||
require 'simple_form/required_component'
|
|
||||||
require 'simple_form/i18n_cache'
|
|
||||||
|
|
||||||
module SimpleForm
|
|
||||||
class Label < AbstractComponent
|
|
||||||
extend SimpleForm::I18nCache
|
|
||||||
include RequiredComponent
|
|
||||||
|
|
||||||
def self.translate_required_string
|
|
||||||
i18n_cache :translate_required_string do
|
|
||||||
I18n.t(:"simple_form.required.string", :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
|
|
||||||
|
|
||||||
def generate
|
|
||||||
return '' unless valid?
|
|
||||||
html_options = { :class => default_css_classes }
|
|
||||||
html_options[:for] = @options[:html][:id] if @options.key?(:html)
|
|
||||||
@builder.label(@attribute, label_text, html_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def label_text
|
|
||||||
required_text << (@options[:label] || translate_label)
|
|
||||||
end
|
|
||||||
|
|
||||||
def required_text
|
|
||||||
attribute_required? ? self.class.translate_required_string.dup : ''
|
|
||||||
end
|
|
||||||
|
|
||||||
def translate_label
|
|
||||||
default = object.try(:human_attribute_name, @attribute.to_s) || @attribute.to_s.humanize
|
|
||||||
translate(default)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -3,15 +3,7 @@ require 'test_helper'
|
||||||
class InputTest < ActionView::TestCase
|
class InputTest < ActionView::TestCase
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
SimpleForm::Input.reset_i18n_cache :boolean_collection
|
SimpleForm::Components::Input.reset_i18n_cache :boolean_collection
|
||||||
end
|
|
||||||
|
|
||||||
test 'input should verify options hash' do
|
|
||||||
assert_raise ArgumentError do
|
|
||||||
simple_form_for @user do |f|
|
|
||||||
concat f.input :name, :invalid_param => true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'input should generate a default text field' do
|
test 'input should generate a default text field' do
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'test_helper'
|
||||||
class LabelTest < ActionView::TestCase
|
class LabelTest < ActionView::TestCase
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
SimpleForm::Label.reset_i18n_cache :translate_required_string
|
SimpleForm::Components::Label.reset_i18n_cache :translate_required_string
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'input should generate a label with the text field' do
|
test 'input should generate a label with the text field' do
|
||||||
|
|
Loading…
Reference in New Issue