Refactor Inputs::Base into helpers and make :on based on the resource status.

This commit is contained in:
José Valim 2011-09-03 12:00:23 +02:00
parent 5cdf47bf72
commit a4655ba845
16 changed files with 193 additions and 110 deletions

View File

@ -2,7 +2,7 @@
SimpleForm.setup do |config|
# Components used by the form builder to generate a complete input. You can remove
# any of them, change the order, or even add your own components to the stack.
# config.components = [ :placeholder, :maxlength, :label_input, :hint, :error ]
# config.components = [ :placeholder, :label_input, :hint, :error ]
# Default tag used on hints.
# config.hint_tag = :span

View File

@ -7,6 +7,7 @@ module SimpleForm
autoload :ErrorNotification, 'simple_form/error_notification'
autoload :FormBuilder, 'simple_form/form_builder'
autoload :HasErrors, 'simple_form/has_errors'
autoload :Helpers, 'simple_form/helpers'
autoload :I18nCache, 'simple_form/i18n_cache'
autoload :Inputs, 'simple_form/inputs'
autoload :MapType, 'simple_form/map_type'
@ -45,7 +46,7 @@ module SimpleForm
# Components used by the form builder.
mattr_accessor :components
@@components = [ :placeholder, :maxlength, :label_input, :hint, :error ]
@@components = [ :placeholder, :label_input, :hint, :error ]
# Series of attemps to detect a default label method for collection.
mattr_accessor :collection_label_methods

View File

@ -6,6 +6,5 @@ module SimpleForm
autoload :Labels, 'simple_form/components/labels'
autoload :Placeholders, 'simple_form/components/placeholders'
autoload :Wrapper, 'simple_form/components/wrapper'
autoload :Maxlength, 'simple_form/components/maxlength'
end
end

View File

@ -2,8 +2,7 @@ module SimpleForm
module Components
module Placeholders
def placeholder
# This components is disabled by default.
nil
nil # This components is disabled by default.
end
private

View File

@ -0,0 +1,8 @@
module SimpleForm
module Helpers
autoload :Maxlength, 'simple_form/helpers/maxlength'
autoload :Pattern, 'simple_form/helpers/pattern'
autoload :Validators, 'simple_form/helpers/validators'
autoload :Required, 'simple_form/helpers/required'
end
end

View File

@ -1,18 +1,12 @@
module SimpleForm
module Components
module Maxlength
def maxlength
# This components is disabled by default.
nil
end
module Helpers
# Helper methods for maxlength.
module Maxlength #:nodoc:
private
def active_maxlength
if SimpleForm.html5
input_html_options[:maxlength] ||= maximum_length_from_validation || limit
end
nil
def add_maxlength!
input_html_options[:maxlength] ||= maximum_length_from_validation || limit if SimpleForm.html5
end
def maximum_length_from_validation

View File

@ -0,0 +1,25 @@
module SimpleForm
module Helpers
# Helper methods for pattern.
module Pattern #:nodoc:
private
def add_pattern!
input_html_options[:pattern] ||= pattern_source if validate_pattern?
end
def validate_pattern?
has_validators? && SimpleForm.html5 &&
SimpleForm.browser_validations && pattern_validator.present?
end
def pattern_source
pattern_validator.options[:with].source
end
def pattern_validator
@pattern_validator ||= attribute_validators.find { |v| ActiveModel::Validations::FormatValidator === v }
end
end
end
end

View File

@ -0,0 +1,36 @@
module SimpleForm
module Helpers
module Required
private
def attribute_required?
@required
end
def calculate_required
if !options[:required].nil?
options[:required]
elsif has_validators?
(attribute_validators + reflection_validators).any? do |v|
v.kind == :presence && valid_validator?(v)
end
else
attribute_required_by_default?
end
end
# Whether this input is valid for HTML 5 required attribute.
def has_required?
attribute_required? && SimpleForm.html5 && SimpleForm.browser_validations
end
def attribute_required_by_default?
SimpleForm.required_by_default
end
def required_class
attribute_required? ? :required : :optional
end
end
end
end

View File

@ -0,0 +1,40 @@
module SimpleForm
module Helpers
module Validators
private
def has_validators?
attribute_name && object.class.respond_to?(:validators_on)
end
def attribute_validators
object.class.validators_on(attribute_name)
end
def reflection_validators
reflection ? object.class.validators_on(reflection.name) : []
end
def valid_validator?(validator)
!conditional_validators?(validator) && action_validator_match?(validator)
end
def conditional_validators?(validator)
validator.options.include?(:if) || validator.options.include?(:unless)
end
def action_validator_match?(validator)
return true if !validator.options.include?(:on)
case validator.options[:on]
when :save
true
when :create
!object.persisted?
when :update
object.persisted?
end
end
end
end
end

View File

@ -9,12 +9,16 @@ module SimpleForm
:update => :edit
}
include SimpleForm::Helpers::Required
include SimpleForm::Helpers::Validators
include SimpleForm::Helpers::Maxlength
include SimpleForm::Helpers::Pattern
include SimpleForm::Components::Errors
include SimpleForm::Components::Hints
include SimpleForm::Components::LabelInput
include SimpleForm::Components::Placeholders
include SimpleForm::Components::Wrapper
include SimpleForm::Components::Maxlength
# Enables certain components support to the given input.
def self.enable(*args)
@ -63,7 +67,11 @@ module SimpleForm
wrap(content)
end
protected
private
def add_size!
input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
end
def limit
column && column.limit
@ -73,59 +81,10 @@ module SimpleForm
options[:components] || SimpleForm.components
end
def attribute_required?
@required
end
def calculate_required
if !options[:required].nil?
options[:required]
elsif has_validators?
(attribute_validators + reflection_validators).any? do |v|
v.kind == :presence && !conditional_validators?(v) && action_validators?(v)
end
else
attribute_required_by_default?
end
end
# Whether this input is valid for HTML 5 required attribute.
def has_required?
attribute_required? && SimpleForm.html5 && SimpleForm.browser_validations
end
def has_autofocus?
options[:autofocus]
end
def has_validators?
attribute_name && object.class.respond_to?(:validators_on)
end
def attribute_validators
object.class.validators_on(attribute_name)
end
def reflection_validators
reflection ? object.class.validators_on(reflection.name) : []
end
def conditional_validators?(validator)
validator.options.include?(:if) || validator.options.include?(:unless)
end
def action_validators?(validator)
!validator.options.include?(:on) || ACTIONS[validator.options[:on].to_sym] == lookup_action
end
def attribute_required_by_default?
SimpleForm.required_by_default
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
@ -216,11 +175,6 @@ module SimpleForm
action = action.to_sym
ACTIONS[action] || action
end
def input_method
self.class.mappings[input_type] or
raise("Could not find method for #{input_type.inspect}")
end
end
end
end

View File

@ -1,10 +1,11 @@
module SimpleForm
module Inputs
class PasswordInput < Base
enable :maxlength, :placeholder
enable :placeholder
def input
input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
add_size!
add_maxlength!
@builder.password_field(attribute_name, input_html_options)
end
end

View File

@ -1,12 +1,13 @@
module SimpleForm
module Inputs
class StringInput < Base
enable :maxlength, :placeholder
enable :placeholder
def input
input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
input_html_options[:pattern] ||= pattern_validator if validate_pattern?
input_html_options[:type] ||= input_type if SimpleForm.html5 && !string?
input_html_options[:type] ||= input_type if SimpleForm.html5 && !string?
add_maxlength!
add_pattern!
add_size!
@builder.text_field(attribute_name, input_html_options)
end
@ -14,24 +15,11 @@ module SimpleForm
string? ? super : super.unshift("string")
end
protected
private
def string?
input_type == :string
end
def validate_pattern?
return unless has_validators?
SimpleForm.html5 && SimpleForm.browser_validations && find_pattern_validator.present?
end
def pattern_validator
find_pattern_validator.options[:with].source
end
def find_pattern_validator
attribute_validators.find { |v| ActiveModel::Validations::FormatValidator === v }
end
end
end
end

View File

@ -1,9 +1,10 @@
module SimpleForm
module Inputs
class TextInput < Base
enable :maxlength, :placeholder
enable :placeholder
def input
add_maxlength!
@builder.text_area(attribute_name, input_html_options)
end
end

View File

@ -338,6 +338,8 @@ class FormBuilderTest < ActionView::TestCase
assert_select 'input.optional#user_name'
end
# VALIDATORS :if :unless
test 'builder input should not be required when ActiveModel::Validations is included and if option is present' do
with_form_for @validating_user, :age
assert_no_select 'input.required'
@ -345,22 +347,6 @@ class FormBuilderTest < ActionView::TestCase
assert_select 'input.optional#validating_user_age'
end
test 'builder input should be required when validation is on create' do
@controller.action_name = "new"
with_form_for @validating_user, :action
assert_select 'input.required'
assert_select 'input[required]'
assert_select 'input.required[required]#validating_user_action'
end
test 'builder input should not be required when validation is on other action' do
@controller.action_name = "edit"
with_form_for @validating_user, :action
assert_no_select 'input.required'
assert_no_select 'input[required]'
assert_select 'input.optional#validating_user_action'
end
test 'builder input should not be required when ActiveModel::Validations is included and unless option is present' do
with_form_for @validating_user, :amount
assert_no_select 'input.required'
@ -368,7 +354,53 @@ class FormBuilderTest < ActionView::TestCase
assert_select 'input.optional#validating_user_amount'
end
# VALIDATORS :on
test 'builder input should be required when validation is on create and is not persisted' do
@validating_user.new_record!
with_form_for @validating_user, :action
assert_select 'input.required'
assert_select 'input[required]'
assert_select 'input.required[required]#validating_user_action'
end
test 'builder input should not be required when validation is on create and is persisted' do
with_form_for @validating_user, :action
assert_no_select 'input.required'
assert_no_select 'input[required]'
assert_select 'input.optional#validating_user_action'
end
test 'builder input should be required when validation is on save' do
with_form_for @validating_user, :credit_limit
assert_select 'input.required'
assert_select 'input[required]'
assert_select 'input.required[required]#validating_user_credit_limit'
@validating_user.new_record!
with_form_for @validating_user, :credit_limit
assert_select 'input.required'
assert_select 'input[required]'
assert_select 'input.required[required]#validating_user_credit_limit'
end
test 'builder input should be required when validation is on update and is persisted' do
with_form_for @validating_user, :phone_number
assert_select 'input.required'
assert_select 'input[required]'
assert_select 'input.required[required]#validating_user_phone_number'
end
test 'builder input should not be required when validation is on update and is not persisted' do
@validating_user.new_record!
with_form_for @validating_user, :phone_number
assert_no_select 'input.required'
assert_no_select 'input[required]'
assert_select 'input.optional#validating_user_phone_number'
end
# WRAPPERS
test 'builder support wrapping around an specific tag' do
swap SimpleForm, :wrapper_tag => :p do
with_form_for @user, :name

View File

@ -130,7 +130,11 @@ class ValidatingUser < User
validates :company, :presence => true
validates :age, :presence => true, :if => Proc.new { |user| user.name }
validates :amount, :presence => true, :unless => Proc.new { |user| user.age }
validates :action, :presence => true, :on => :create
validates :action, :presence => true, :on => :create
validates :credit_limit, :presence => true, :on => :save
validates :phone_number, :presence => true, :on => :update
validates_numericality_of :age,
:greater_than_or_equal_to => 18,
:less_than_or_equal_to => 99,

View File

@ -89,5 +89,6 @@ class ActionView::TestCase
alias :users_path :user_path
alias :super_user_path :user_path
alias :validating_user_path :user_path
alias :validating_users_path :user_path
alias :other_validating_user_path :user_path
end