simple_form 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. data/.gitignore +2 -0
  2. data/.gitmodules +3 -0
  3. data/.travis.yml +6 -0
  4. data/CHANGELOG.rdoc +109 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.lock +82 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.rdoc +180 -53
  9. data/Rakefile +27 -0
  10. data/lib/generators/simple_form/USAGE +1 -1
  11. data/lib/generators/simple_form/install_generator.rb +5 -6
  12. data/lib/generators/simple_form/templates/_form.html.erb +2 -12
  13. data/lib/generators/simple_form/templates/_form.html.haml +10 -0
  14. data/lib/generators/simple_form/templates/_form.html.slim +10 -0
  15. data/lib/generators/simple_form/templates/en.yml +14 -0
  16. data/lib/generators/simple_form/templates/simple_form.rb +66 -12
  17. data/lib/simple_form/action_view_extensions/builder.rb +99 -40
  18. data/lib/simple_form/action_view_extensions/form_helper.rb +29 -6
  19. data/lib/simple_form/components/errors.rb +16 -6
  20. data/lib/simple_form/components/hints.rb +2 -2
  21. data/lib/simple_form/components/label_input.rb +13 -0
  22. data/lib/simple_form/components/labels.rb +5 -7
  23. data/lib/simple_form/components/placeholders.rb +22 -0
  24. data/lib/simple_form/components/wrapper.rb +19 -2
  25. data/lib/simple_form/components.rb +6 -4
  26. data/lib/simple_form/error_notification.rb +42 -0
  27. data/lib/simple_form/form_builder.rb +187 -72
  28. data/lib/simple_form/has_errors.rb +14 -0
  29. data/lib/simple_form/inputs/base.rb +106 -24
  30. data/lib/simple_form/inputs/block_input.rb +3 -2
  31. data/lib/simple_form/inputs/boolean_input.rb +22 -0
  32. data/lib/simple_form/inputs/collection_input.rb +46 -17
  33. data/lib/simple_form/inputs/date_time_input.rb +11 -5
  34. data/lib/simple_form/inputs/hidden_input.rb +11 -2
  35. data/lib/simple_form/inputs/mapping_input.rb +12 -6
  36. data/lib/simple_form/inputs/numeric_input.rb +55 -6
  37. data/lib/simple_form/inputs/priority_input.rb +5 -1
  38. data/lib/simple_form/inputs/string_input.rb +25 -11
  39. data/lib/simple_form/inputs.rb +1 -0
  40. data/lib/simple_form/map_type.rb +9 -6
  41. data/lib/simple_form/version.rb +1 -1
  42. data/lib/simple_form.rb +92 -8
  43. data/simple_form.gemspec +22 -0
  44. data/test/action_view_extensions/builder_test.rb +187 -51
  45. data/test/action_view_extensions/form_helper_test.rb +17 -5
  46. data/test/components/error_test.rb +23 -12
  47. data/test/components/hint_test.rb +4 -8
  48. data/test/components/label_test.rb +79 -11
  49. data/test/components/wrapper_test.rb +63 -0
  50. data/test/discovery_inputs.rb +21 -0
  51. data/test/error_notification_test.rb +62 -0
  52. data/test/form_builder_test.rb +332 -35
  53. data/test/inputs_test.rb +516 -53
  54. data/test/support/misc_helpers.rb +32 -4
  55. data/test/support/mock_controller.rb +4 -4
  56. data/test/support/models.rb +75 -11
  57. data/test/test_helper.rb +28 -12
  58. metadata +51 -11
@@ -5,14 +5,20 @@ module SimpleForm
5
5
  @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options)
6
6
  end
7
7
 
8
+ private
9
+
10
+ def has_required?
11
+ false
12
+ end
13
+
8
14
  def label_target
9
15
  case input_type
10
- when :date, :datetime
11
- "#{attribute_name}_1i"
12
- when :time
13
- "#{attribute_name}_4i"
16
+ when :date, :datetime
17
+ "#{attribute_name}_1i"
18
+ when :time
19
+ "#{attribute_name}_4i"
14
20
  end
15
21
  end
16
22
  end
17
23
  end
18
- end
24
+ end
@@ -1,11 +1,20 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
- # Handles hidden input.
4
3
  class HiddenInput < Base
5
4
  def render
6
5
  @builder.hidden_field(attribute_name, input_html_options)
7
6
  end
8
7
  alias :input :render
8
+
9
+ protected
10
+
11
+ def attribute_required?
12
+ false
13
+ end
14
+
15
+ def required_class
16
+ nil
17
+ end
9
18
  end
10
19
  end
11
- end
20
+ end
@@ -4,8 +4,6 @@ module SimpleForm
4
4
  class MappingInput < Base
5
5
  extend MapType
6
6
 
7
- map_type :boolean, :to => :check_box
8
- map_type :password, :to => :password_field
9
7
  map_type :text, :to => :text_area
10
8
  map_type :file, :to => :file_field
11
9
 
@@ -13,10 +11,18 @@ module SimpleForm
13
11
  @builder.send(input_method, attribute_name, input_html_options)
14
12
  end
15
13
 
16
- def input_method
17
- method = self.class.mappings[input_type]
18
- raise "Could not find method for #{input_type.inspect}" unless method
19
- method
14
+ private
15
+
16
+ def has_placeholder?
17
+ (text? || password?) && placeholder_present?
18
+ end
19
+
20
+ def password?
21
+ input_type == :password
22
+ end
23
+
24
+ def text?
25
+ input_type == :text
20
26
  end
21
27
  end
22
28
  end
@@ -2,15 +2,64 @@ module SimpleForm
2
2
  module Inputs
3
3
  class NumericInput < Base
4
4
  def input
5
+ input_html_options[:type] ||= "number" if SimpleForm.html5
6
+ input_html_options[:size] ||= SimpleForm.default_input_size
7
+ input_html_options[:step] ||= integer? ? 1 : "any" if SimpleForm.html5
8
+ infer_attributes_from_validations! if SimpleForm.html5
5
9
  @builder.text_field(attribute_name, input_html_options)
6
10
  end
7
11
 
8
- def input_html_options
9
- input_options = super
10
- input_options[:class] = "numeric #{input_options[:class]}"
11
- input_options[:size] ||= SimpleForm.default_input_size
12
- input_options
12
+ def input_html_classes
13
+ super.unshift("numeric")
14
+ end
15
+
16
+ protected
17
+
18
+ def has_placeholder?
19
+ placeholder_present?
20
+ end
21
+
22
+ def infer_attributes_from_validations!
23
+ return unless has_validators?
24
+
25
+ numeric_validator = find_numericality_validator or return
26
+ validator_options = numeric_validator.options
27
+
28
+ input_html_options[:min] ||= minimum_value(validator_options)
29
+ input_html_options[:max] ||= maximum_value(validator_options)
30
+ end
31
+
32
+ def integer?
33
+ input_type == :integer
34
+ end
35
+
36
+ def minimum_value(validator_options)
37
+ if integer? && validator_options.key?(:greater_than)
38
+ evaluate_validator_option(validator_options[:greater_than]) + 1
39
+ else
40
+ evaluate_validator_option(validator_options[:greater_than_or_equal_to])
41
+ end
42
+ end
43
+
44
+ def maximum_value(validator_options)
45
+ if integer? && validator_options.key?(:less_than)
46
+ evaluate_validator_option(validator_options[:less_than]) - 1
47
+ else
48
+ evaluate_validator_option(validator_options[:less_than_or_equal_to])
49
+ end
50
+ end
51
+
52
+ def find_numericality_validator
53
+ attribute_validators.find { |v| ActiveModel::Validations::NumericalityValidator === v }
54
+ end
55
+
56
+ private
57
+
58
+ def evaluate_validator_option(option)
59
+ return option if option.is_a?(Numeric)
60
+ return object.send(option) if option.is_a?(Symbol)
61
+ return option.call(object) if option.respond_to?(:call)
13
62
  end
14
63
  end
15
64
  end
16
- end
65
+ end
@@ -12,9 +12,13 @@ module SimpleForm
12
12
 
13
13
  protected
14
14
 
15
+ def has_required?
16
+ false
17
+ end
18
+
15
19
  def skip_include_blank?
16
20
  super || input_priority.present?
17
21
  end
18
22
  end
19
23
  end
20
- end
24
+ end
@@ -1,21 +1,23 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class StringInput < Base
4
- def input
5
- @builder.text_field(attribute_name, input_html_options)
6
- end
4
+ extend MapType
7
5
 
8
- def input_html_options
9
- input_options = super
10
- input_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
11
- input_options[:maxlength] ||= limit if limit
6
+ map_type :string, :email, :search, :tel, :url, :to => :text_field
7
+ map_type :password, :to => :password_field
12
8
 
13
- unless input_type == :string
14
- input_options[:type] ||= input_type
15
- input_options[:class] = "string #{input_options[:class]}"
9
+ def input
10
+ input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
11
+ input_html_options[:maxlength] ||= limit if limit && SimpleForm.html5
12
+ if password? || SimpleForm.html5
13
+ input_html_options[:type] ||= input_type unless string?
16
14
  end
17
15
 
18
- input_options
16
+ @builder.send(input_method, attribute_name, input_html_options)
17
+ end
18
+
19
+ def input_html_classes
20
+ string? ? super : super.unshift("string")
19
21
  end
20
22
 
21
23
  protected
@@ -23,6 +25,18 @@ module SimpleForm
23
25
  def limit
24
26
  column && column.limit
25
27
  end
28
+
29
+ def has_placeholder?
30
+ placeholder_present?
31
+ end
32
+
33
+ def string?
34
+ input_type == :string
35
+ end
36
+
37
+ def password?
38
+ input_type == :password
39
+ end
26
40
  end
27
41
  end
28
42
  end
@@ -2,6 +2,7 @@ module SimpleForm
2
2
  module Inputs
3
3
  autoload :Base, 'simple_form/inputs/base'
4
4
  autoload :BlockInput, 'simple_form/inputs/block_input'
5
+ autoload :BooleanInput, 'simple_form/inputs/boolean_input'
5
6
  autoload :CollectionInput, 'simple_form/inputs/collection_input'
6
7
  autoload :DateTimeInput, 'simple_form/inputs/date_time_input'
7
8
  autoload :HiddenInput, 'simple_form/inputs/hidden_input'
@@ -1,13 +1,16 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
1
3
  module SimpleForm
2
4
  module MapType
3
- def mappings
4
- @mappings ||= {}
5
+ def self.extended(base)
6
+ base.class_attribute :mappings
7
+ base.mappings = {}
5
8
  end
6
9
 
7
10
  def map_type(*types)
8
- options = types.extract_options!
9
- raise ArgumentError, "You need to give :to as option to map_type" unless options[:to]
10
- types.each { |t| mappings[t] = options[:to] }
11
+ map_to = types.extract_options![:to]
12
+ raise ArgumentError, "You need to give :to as option to map_type" unless map_to
13
+ self.mappings = mappings.merge types.each_with_object({}) { |t, m| m[t] = map_to }
11
14
  end
12
15
  end
13
- end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module SimpleForm
2
- VERSION = "1.2.0".freeze
2
+ VERSION = "1.4.0".freeze
3
3
  end
data/lib/simple_form.rb CHANGED
@@ -3,23 +3,49 @@ require 'simple_form/action_view_extensions/form_helper'
3
3
  require 'simple_form/action_view_extensions/builder'
4
4
 
5
5
  module SimpleForm
6
- autoload :Components, 'simple_form/components'
7
- autoload :FormBuilder, 'simple_form/form_builder'
8
- autoload :I18nCache, 'simple_form/i18n_cache'
9
- autoload :Inputs, 'simple_form/inputs'
10
- autoload :MapType, 'simple_form/map_type'
6
+ autoload :Components, 'simple_form/components'
7
+ autoload :ErrorNotification, 'simple_form/error_notification'
8
+ autoload :FormBuilder, 'simple_form/form_builder'
9
+ autoload :HasErrors, 'simple_form/has_errors'
10
+ autoload :I18nCache, 'simple_form/i18n_cache'
11
+ autoload :Inputs, 'simple_form/inputs'
12
+ autoload :MapType, 'simple_form/map_type'
11
13
 
12
14
  # Default tag used on hints.
13
15
  mattr_accessor :hint_tag
14
16
  @@hint_tag = :span
15
17
 
18
+ # CSS class to add to all hint tags.
19
+ mattr_accessor :hint_class
20
+ @@hint_class = :hint
21
+
16
22
  # Default tag used on errors.
17
23
  mattr_accessor :error_tag
18
24
  @@error_tag = :span
19
25
 
26
+ # CSS class to add to all error tags.
27
+ mattr_accessor :error_class
28
+ @@error_class = :error
29
+
30
+ # Method used to tidy up errors.
31
+ mattr_accessor :error_method
32
+ @@error_method = :first
33
+
34
+ # Default tag used for error notification helper.
35
+ mattr_accessor :error_notification_tag
36
+ @@error_notification_tag = :p
37
+
38
+ # CSS class to add for error notification helper.
39
+ mattr_accessor :error_notification_class
40
+ @@error_notification_class = :error_notification
41
+
42
+ # ID to add for error notification helper.
43
+ mattr_accessor :error_notification_id
44
+ @@error_notification_id = nil
45
+
20
46
  # Components used by the form builder.
21
47
  mattr_accessor :components
22
- @@components = [ :label, :input, :hint, :error ]
48
+ @@components = [ :placeholder, :label_input, :hint, :error ]
23
49
 
24
50
  # Series of attemps to detect a default label method for collection.
25
51
  mattr_accessor :collection_label_methods
@@ -29,18 +55,63 @@ module SimpleForm
29
55
  mattr_accessor :collection_value_methods
30
56
  @@collection_value_methods = [ :id, :to_s ]
31
57
 
32
- # You can wrap all inputs in a pre-defined tag. By default is nil.
58
+ # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
59
+ mattr_accessor :collection_wrapper_tag
60
+ @@collection_wrapper_tag = nil
61
+
62
+ # You can wrap each item in a collection of radio/check boxes with a tag, defaulting to none.
63
+ mattr_accessor :item_wrapper_tag
64
+ @@item_wrapper_tag = :span
65
+
66
+ # You can wrap all inputs in a pre-defined tag. Default is a div.
33
67
  mattr_accessor :wrapper_tag
34
68
  @@wrapper_tag = :div
35
69
 
70
+ # You can define the class to use on all wrappers. Default is input.
71
+ mattr_accessor :wrapper_class
72
+ @@wrapper_class = :input
73
+
74
+ # You can define the class to add to the wrapper when the field has errors. Default is field_with_errors.
75
+ mattr_accessor :wrapper_error_class
76
+ @@wrapper_error_class = :field_with_errors
77
+
36
78
  # How the label text should be generated altogether with the required text.
37
79
  mattr_accessor :label_text
38
80
  @@label_text = lambda { |label, required| "#{required} #{label}" }
39
81
 
82
+ # You can define the class to use on all labels. Default is nil.
83
+ mattr_accessor :label_class
84
+ @@label_class = nil
85
+
86
+ # You can define the class to use on all forms. Default is simple_form.
87
+ mattr_accessor :form_class
88
+ @@form_class = :simple_form
89
+
90
+ # Whether attributes are required by default (or not).
91
+ mattr_accessor :required_by_default
92
+ @@required_by_default = true
93
+
94
+ # Tell browsers whether to use default HTML5 validations (novalidate option).
95
+ mattr_accessor :browser_validations
96
+ @@browser_validations = true
97
+
98
+ # Determines whether HTML5 types (:email, :url, :search, :tel) and attributes
99
+ # (e.g. required) are used or not. True by default.
100
+ # Having this on in non-HTML5 compliant sites can cause odd behavior in
101
+ # HTML5-aware browsers such as Chrome.
102
+ mattr_accessor :html5
103
+ @@html5 = true
104
+
40
105
  # Collection of methods to detect if a file type was given.
41
106
  mattr_accessor :file_methods
42
107
  @@file_methods = [ :mounted_as, :file?, :public_filename ]
43
108
 
109
+ # Custom mappings for input types. This should be a hash containing a regexp
110
+ # to match as key, and the input type that will be used when the field name
111
+ # matches the regexp as value, such as { /count/ => :integer }.
112
+ mattr_accessor :input_mappings
113
+ @@input_mappings = nil
114
+
44
115
  # Default priority for time_zone inputs.
45
116
  mattr_accessor :time_zone_priority
46
117
  @@time_zone_priority = nil
@@ -53,7 +124,20 @@ module SimpleForm
53
124
  mattr_accessor :default_input_size
54
125
  @@default_input_size = 50
55
126
 
56
- # Default way to setup SimpleForm. Run script/generate simple_form_install
127
+ # When off, do not use translations in hint, labels and placeholders.
128
+ # It is a small performance improvement if you are not using such features.
129
+ mattr_accessor :translate
130
+ @@translate = true
131
+
132
+ # Automatically discover new inputs in Rails' autoload path.
133
+ mattr_accessor :inputs_discovery
134
+ @@inputs_discovery = true
135
+
136
+ # Cache simple form inputs discovery
137
+ mattr_accessor :cache_discovery
138
+ @@cache_discovery = !Rails.env.development?
139
+
140
+ # Default way to setup SimpleForm. Run rails generate simple_form:install
57
141
  # to create a fresh initializer with all configuration values.
58
142
  def self.setup
59
143
  yield self
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "simple_form/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "simple_form"
7
+ s.version = SimpleForm::VERSION.dup
8
+ s.platform = Gem::Platform::RUBY
9
+ s.summary = "Forms made easy!"
10
+ s.email = "contact@plataformatec.com.br"
11
+ s.homepage = "http://github.com/plataformatec/simple_form"
12
+ s.description = "Forms made easy!"
13
+ s.authors = ['José Valim', 'Carlos Antônio']
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.test_files -= Dir["test/support/country_select/**/*"]
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.rubyforge_project = "simple_form"
22
+ end