formtastic 2.0.0.rc3 → 2.0.0.rc4

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 (43) hide show
  1. data/.travis.yml +7 -0
  2. data/CHANGELOG +26 -0
  3. data/Gemfile +2 -0
  4. data/README.textile +0 -1
  5. data/RELEASE_PROCESS +0 -1
  6. data/Rakefile +1 -1
  7. data/app/assets/stylesheets/formtastic.css +3 -3
  8. data/app/assets/stylesheets/formtastic_ie6.css +7 -1
  9. data/app/assets/stylesheets/formtastic_ie7.css +7 -1
  10. data/formtastic.gemspec +0 -15
  11. data/lib/formtastic/form_builder.rb +2 -0
  12. data/lib/formtastic/helpers/errors_helper.rb +22 -0
  13. data/lib/formtastic/helpers/form_helper.rb +18 -16
  14. data/lib/formtastic/helpers/input_helper.rb +9 -7
  15. data/lib/formtastic/helpers/inputs_helper.rb +11 -3
  16. data/lib/formtastic/helpers/reflection.rb +5 -1
  17. data/lib/formtastic/inputs/base/collections.rb +7 -0
  18. data/lib/formtastic/inputs/base/html.rb +1 -1
  19. data/lib/formtastic/inputs/base/timeish.rb +7 -2
  20. data/lib/formtastic/inputs/base/validations.rb +39 -8
  21. data/lib/formtastic/inputs/check_boxes_input.rb +3 -3
  22. data/lib/formtastic/inputs/file_input.rb +4 -4
  23. data/lib/formtastic/inputs/number_input.rb +1 -1
  24. data/lib/formtastic/inputs/radio_input.rb +1 -1
  25. data/lib/formtastic/inputs/time_input.rb +25 -3
  26. data/lib/formtastic/version.rb +1 -1
  27. data/lib/generators/templates/formtastic.rb +10 -1
  28. data/spec/builder/errors_spec.rb +10 -0
  29. data/spec/builder/semantic_fields_for_spec.rb +77 -36
  30. data/spec/helpers/form_helper_spec.rb +32 -0
  31. data/spec/helpers/input_helper_spec.rb +196 -102
  32. data/spec/helpers/inputs_helper_spec.rb +85 -73
  33. data/spec/helpers/reflection_helper_spec.rb +32 -0
  34. data/spec/inputs/check_boxes_input_spec.rb +21 -6
  35. data/spec/inputs/date_input_spec.rb +20 -0
  36. data/spec/inputs/datetime_input_spec.rb +30 -11
  37. data/spec/inputs/label_spec.rb +8 -0
  38. data/spec/inputs/number_input_spec.rb +298 -2
  39. data/spec/inputs/radio_input_spec.rb +5 -6
  40. data/spec/inputs/string_input_spec.rb +22 -5
  41. data/spec/inputs/time_input_spec.rb +51 -7
  42. data/spec/spec_helper.rb +64 -12
  43. metadata +11 -9
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - ree
4
+ - 1.9.2
5
+ - 1.9.3
6
+ env:
7
+ - DEFER_GC=false
data/CHANGELOG CHANGED
@@ -1,3 +1,29 @@
1
+ 2.0.0.rc4
2
+
3
+ * Fixed that TimeInput was not rendering hidden y/m/d inputs by default.
4
+ * Fixed test suite under Rails 3.1.0.rc5
5
+ * Fixed false and blank fragment labels on date/time inputs producing unsafe HTML.
6
+ * Fixed that inputs were 'required' withput considering `:on => :create` validations
7
+ * Fixed that collections of strings in CheckBoxesInput were ot being correctly checked be checked if they match the model
8
+ * Fixed that the required attribute was added to the choices in a :radio or :check_boxes input, instead of just the parent input wrapper
9
+ * Fixed semantic_fields_for when used with a hash-like model
10
+ * Fixed that models without defined validations (even if validators_on exists) should not be considered required
11
+ * Fixed min/max attributes when the validation uses a Proc
12
+ * Fixed that inputs should not be considered required if :allow_blank => true is set on validates_inclusion_of
13
+ * Fixed that inputs should not be considered required if if either :allow_blank => true, :minimum is > 0, or :within's least value is > 0 is set on validates_length_of
14
+ * Fixed a typo in the config template
15
+ * Fixed semantic_fields_for to work with Rails 3 *and* 3.1's method sigs (I hope), many thanks to the simple_form guys for figuring this out
16
+ * Changed HTML5 `step` attribute to default to "any" instead of "1"
17
+ * Added CarrierWave support for to file input detection
18
+ * Added a configuration 'perform_browser_validations' to opt out of HTML5 browser validations.
19
+ * Added more support for mongo documents, including MongoMapper-specific reflection capability
20
+ * Added IE specific stylesheets & moved those styles out of main stylesheet, include them yourself if needed
21
+ * Added a new configuration to opt out of HTML5 required attribute
22
+
23
+ 2.0.0.rc3
24
+
25
+ * Fixed that .label class was incorrectly applied to <label> tags inside a .choice on radio and checkbox inputs (#599)
26
+
1
27
  2.0.0.rc2
2
28
 
3
29
  * Fixed install instructions in readme to reflect 2.0.0.rc1
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source :rubygems
2
2
 
3
+ gem 'rake', '< 0.9'
4
+
3
5
  gemspec
data/README.textile CHANGED
@@ -586,7 +586,6 @@ h2. Dependencies
586
586
 
587
587
  There are none, but...
588
588
 
589
- * if you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin is installed, you won't have to specify the @:required@ option (it checks the validations on the model instead).
590
589
  * if you want to use the @:country@ input, you'll need to install the "country-select plugin":https://github.com/chrislerum/country_select (or any other country_select plugin with the same API).
591
590
  * "rspec":http://github.com/dchelimsky/rspec/, "rspec_hpricot_matchers":http://rubyforge.org/projects/rspec-hpricot/ and "rcov":http://github.com/relevance/rcov gems (plus any of their own dependencies) are required for the test suite.
592
591
 
data/RELEASE_PROCESS CHANGED
@@ -1,5 +1,4 @@
1
1
  # edit version.rb
2
- # edit gemspec release date
3
2
  git ci formtastic.gemspec -m "new gemspec" # commit changes
4
3
  git tag X.X.X # tag the new version in the code base too
5
4
  gem build formtastic.gemspec # build the gem
data/Rakefile CHANGED
@@ -44,7 +44,7 @@ end
44
44
 
45
45
  RCov::VerifyTask.new(:verify_coverage) do |t|
46
46
  t.require_exact_threshold = false
47
- t.threshold = 95
47
+ t.threshold = (RUBY_VERSION == "1.8.7" ? 95 : 0)
48
48
  end
49
49
 
50
50
  desc "Run all examples and verify coverage"
@@ -83,7 +83,7 @@ This stylesheet forms part of the Formtastic Rails Plugin
83
83
  /* BUTTONS
84
84
  --------------------------------------------------------------------------------------------------*/
85
85
  .formtastic .buttons {
86
- overflow:hidden; zoom:1; /* clear containing floats */
86
+ overflow:hidden; /* clear containing floats */
87
87
  padding-left:25%;
88
88
  }
89
89
 
@@ -96,11 +96,11 @@ This stylesheet forms part of the Formtastic Rails Plugin
96
96
  /* INPUTS
97
97
  --------------------------------------------------------------------------------------------------*/
98
98
  .formtastic .inputs {
99
- overflow:hidden; zoom:1; /* clear containing floats */
99
+ overflow:hidden; /* clear containing floats */
100
100
  }
101
101
 
102
102
  .formtastic .input {
103
- overflow:hidden; zoom:1; /* clear containing floats */
103
+ overflow:hidden; /* clear containing floats */
104
104
  padding:0.5em 0; /* padding and negative margin juggling is for Firefox */
105
105
  margin-top:-0.5em;
106
106
  margin-bottom:1em;
@@ -24,4 +24,10 @@
24
24
  /* fragment (eg year, month, day) appear a few pixels too far to the left*/
25
25
  .formtastic .fragment {
26
26
  padding-left:3px;
27
- }
27
+ }
28
+
29
+ .formtastic .buttons,
30
+ .formtastic .inputs,
31
+ .formtastic .input {
32
+ zoom:1;
33
+ }
@@ -14,4 +14,10 @@
14
14
  size:15px;
15
15
  margin-left:0;
16
16
  margin-right:0;
17
- }
17
+ }
18
+
19
+ .formtastic .buttons,
20
+ .formtastic .inputs,
21
+ .formtastic .input {
22
+ zoom:1;
23
+ }
data/formtastic.gemspec CHANGED
@@ -17,21 +17,6 @@ Gem::Specification.new do |s|
17
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
18
  s.require_paths = ["lib"]
19
19
 
20
- s.post_install_message = %q{
21
- ========================================================================
22
- Thanks for installing Formtastic!
23
- ------------------------------------------------------------------------
24
- You can now (optionally) run the generator to copy a config initializer
25
- (and some stylesheets, in Rails 3.0.x) into your application:
26
- rails generate formtastic:install
27
-
28
- Find out more and get involved:
29
- http://github.com/justinfrench/formtastic
30
- http://rdoc.info/github/justinfrench/formtastic/master/frames
31
- http://groups.google.com.au/group/formtastic
32
- http://twitter.com/formtastic
33
- ========================================================================
34
- }
35
20
  s.rdoc_options = ["--charset=UTF-8"]
36
21
  s.extra_rdoc_files = ["README.textile"]
37
22
 
@@ -28,6 +28,8 @@ module Formtastic
28
28
  configure :default_inline_error_class, 'inline-errors'
29
29
  configure :default_error_list_class, 'errors'
30
30
  configure :default_hint_class, 'inline-hints'
31
+ configure :use_required_attribute, true
32
+ configure :perform_browser_validations, true
31
33
 
32
34
  attr_reader :template
33
35
 
@@ -89,6 +89,28 @@ module Formtastic
89
89
 
90
90
 
91
91
  protected
92
+
93
+ # @deprecated This should be removed with inline_errors_for in 2.1
94
+ def error_sentence(errors, options = {})
95
+ error_class = options[:error_class] || default_inline_error_class
96
+ template.content_tag(:p, Formtastic::Util.html_safe(errors.to_sentence.untaint), :class => error_class)
97
+ end
98
+
99
+ # @deprecated This should be removed with inline_errors_for in 2.1
100
+ def error_list(errors, options = {})
101
+ error_class = options[:error_class] || default_error_list_class
102
+ list_elements = []
103
+ errors.each do |error|
104
+ list_elements << template.content_tag(:li, Formtastic::Util.html_safe(error.untaint))
105
+ end
106
+ template.content_tag(:ul, Formtastic::Util.html_safe(list_elements.join("\n")), :class => error_class)
107
+ end
108
+
109
+ # @deprecated This should be removed with inline_errors_for in 2.1
110
+ def error_first(errors, options = {})
111
+ error_class = options[:error_class] || default_inline_error_class
112
+ template.content_tag(:p, Formtastic::Util.html_safe(errors.first.untaint), :class => error_class)
113
+ end
92
114
 
93
115
  def error_keys(method, options)
94
116
  @methods_for_error ||= {}
@@ -140,24 +140,10 @@ module Formtastic
140
140
  #
141
141
  # @option *args [String] :namespace
142
142
  def semantic_form_for(record_or_name_or_array, *args, &proc)
143
- form_helper_wrapper(:form_for, record_or_name_or_array, *args, &proc)
144
- end
145
-
146
- # Wrapper around Rails' own `fields_for` helper to set the `:builder` option to
147
- # `Formtastic::FormBuilder`.
148
- #
149
- # @see #semantic_form_for
150
- def semantic_fields_for(record_or_name_or_array, *args, &proc)
151
- form_helper_wrapper(:fields_for, record_or_name_or_array, *args, &proc)
152
- end
153
-
154
- protected
155
-
156
- # @todo pretty sure some of this (like HTML classes and record naming are exlusive to `form_for`)
157
- def form_helper_wrapper(rails_helper_method_name, record_or_name_or_array, *args, &proc)
158
143
  options = args.extract_options!
159
144
  options[:builder] ||= @@builder
160
145
  options[:html] ||= {}
146
+ options[:html][:novalidate] = !builder.perform_browser_validations unless options[:html].key?(:novalidate)
161
147
  @@builder.custom_namespace = options[:namespace].to_s
162
148
 
163
149
  singularizer = defined?(ActiveModel::Naming.singular) ? ActiveModel::Naming.method(:singular) : ActionController::RecordIdentifier.method(:singular_class_name)
@@ -172,10 +158,26 @@ module Formtastic
172
158
  options[:html][:class] = class_names.join(" ")
173
159
 
174
160
  with_custom_field_error_proc do
175
- self.send(rails_helper_method_name, record_or_name_or_array, *(args << options), &proc)
161
+ self.form_for(record_or_name_or_array, *(args << options), &proc)
176
162
  end
177
163
  end
178
164
 
165
+ # Wrapper around Rails' own `fields_for` helper to set the `:builder` option to
166
+ # `Formtastic::FormBuilder`.
167
+ #
168
+ # @see #semantic_form_for
169
+ def semantic_fields_for(record_name, record_object = nil, options = {}, &block)
170
+ options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
171
+ options[:builder] ||= @@builder
172
+ @@builder.custom_namespace = options[:namespace].to_s # TODO needed?
173
+
174
+ with_custom_field_error_proc do
175
+ self.fields_for(record_name, record_object, options, &block)
176
+ end
177
+ end
178
+
179
+ protected
180
+
179
181
  # Override the default ActiveRecordHelper behaviour of wrapping the input.
180
182
  # This gets taken care of semantically by adding an error class to the LI tag
181
183
  # containing the input.
@@ -267,6 +267,9 @@ module Formtastic
267
267
 
268
268
  protected
269
269
 
270
+ # First try if we can detect special things like :file. With CarrierWave the method does have
271
+ # an underlying column so we don't want :string to get selected.
272
+ #
270
273
  # For methods that have a database column, take a best guess as to what the input method
271
274
  # should be. In most cases, it will just return the column type (eg :string), but for special
272
275
  # cases it will simplify (like the case of :integer, :float & :decimal to :number), or do
@@ -275,6 +278,12 @@ module Formtastic
275
278
  # If there is no column for the method (eg "virtual columns" with an attr_accessor), the
276
279
  # default is a :string, a similar behaviour to Rails' scaffolding.
277
280
  def default_input_type(method, options = {}) #:nodoc:
281
+ if @object
282
+ return :select if reflection_for(method)
283
+
284
+ return :file if is_file?(method, options)
285
+ end
286
+
278
287
  if column = column_for(method)
279
288
  # Special cases where the column type doesn't map to an input method.
280
289
  case column.type
@@ -300,12 +309,6 @@ module Formtastic
300
309
  # Try 3: Assume the input name will be the same as the column type (e.g. string_input).
301
310
  return column.type
302
311
  else
303
- if @object
304
- return :select if reflection_for(method)
305
-
306
- return :file if is_file?(method, options)
307
- end
308
-
309
312
  return :select if options.key?(:collection)
310
313
  return :password if method.to_s =~ /password/
311
314
  return :string
@@ -363,4 +366,3 @@ module Formtastic
363
366
  end
364
367
  end
365
368
  end
366
-
@@ -314,9 +314,17 @@ module Formtastic
314
314
  end
315
315
 
316
316
  def fieldset_contents_from_column_list(columns)
317
- columns.collect do |method|
318
- if @object && (@object.class.reflect_on_association(method.to_sym) && @object.class.reflect_on_association(method.to_sym).options[:polymorphic] == true)
319
- raise PolymorphicInputWithoutCollectionError.new("Please provide a collection for :#{method} input (you'll need to use block form syntax). Inputs for polymorphic associations can only be used when an explicit :collection is provided.")
317
+ columns.collect do |method|
318
+ if @object
319
+ if @object.class.respond_to?(:reflect_on_association)
320
+ if (@object.class.reflect_on_association(method.to_sym) && @object.class.reflect_on_association(method.to_sym).options[:polymorphic] == true)
321
+ raise PolymorphicInputWithoutCollectionError.new("Please provide a collection for :#{method} input (you'll need to use block form syntax). Inputs for polymorphic associations can only be used when an explicit :collection is provided.")
322
+ end
323
+ elsif @object.class.respond_to?(:associations)
324
+ if (@object.class.associations(method.to_sym) && @object.class.associations(method.to_sym).options[:polymorphic] == true)
325
+ raise PolymorphicInputWithoutCollectionError.new("Please provide a collection for :#{method} input (you'll need to use block form syntax). Inputs for polymorphic associations can only be used when an explicit :collection is provided.")
326
+ end
327
+ end
320
328
  end
321
329
  input(method.to_sym)
322
330
  end
@@ -5,7 +5,11 @@ module Formtastic
5
5
  # If an association method is passed in (f.input :author) try to find the
6
6
  # reflection object.
7
7
  def reflection_for(method) #:nodoc:
8
- @object.class.reflect_on_association(method) if @object.class.respond_to?(:reflect_on_association)
8
+ if @object.class.respond_to?(:reflect_on_association)
9
+ @object.class.reflect_on_association(method)
10
+ elsif @object.class.respond_to?(:associations) # MongoMapper uses the 'associations(method)' instead
11
+ @object.class.associations(method)
12
+ end
9
13
  end
10
14
 
11
15
  def association_macro_for_method(method) #:nodoc:
@@ -87,6 +87,13 @@ module Formtastic
87
87
  object.send(duck)
88
88
  end
89
89
  end
90
+
91
+ # Avoids an issue where `send_or_call` can be a String and duck can be something simple like
92
+ # `:first`, which obviously String responds to.
93
+ def send_or_call_or_object(duck, object)
94
+ return object if object.is_a?(String) # TODO what about other classes etc?
95
+ send_or_call(duck, object)
96
+ end
90
97
 
91
98
  end
92
99
  end
@@ -21,7 +21,7 @@ module Formtastic
21
21
  def input_html_options
22
22
  {
23
23
  :id => dom_id,
24
- :required => required?,
24
+ :required => required_attribute?,
25
25
  :autofocus => autofocus?
26
26
  }.merge(options[:input_html] || {})
27
27
  end
@@ -90,6 +90,7 @@ module Formtastic
90
90
  def to_html
91
91
  input_wrapping do
92
92
  fragments_wrapping do
93
+ hidden_fragments <<
93
94
  fragments_label <<
94
95
  template.content_tag(:ol,
95
96
  fragments.map do |fragment|
@@ -147,7 +148,7 @@ module Formtastic
147
148
 
148
149
  def fragment_label_html(fragment)
149
150
  text = fragment_label(fragment)
150
- text.blank? ? "" : template.content_tag(:label, text, :for => fragment_id(fragment))
151
+ text.blank? ? "".html_safe : template.content_tag(:label, text, :for => fragment_id(fragment))
151
152
  end
152
153
 
153
154
  def value
@@ -196,7 +197,7 @@ module Formtastic
196
197
  :class => "label"
197
198
  )
198
199
  else
199
- ""
200
+ "".html_safe
200
201
  end
201
202
  end
202
203
 
@@ -206,6 +207,10 @@ module Formtastic
206
207
  )
207
208
  end
208
209
 
210
+ def hidden_fragments
211
+ "".html_safe
212
+ end
213
+
209
214
  end
210
215
  end
211
216
  end
@@ -27,7 +27,7 @@ module Formtastic
27
27
  validator_relevant?(validator)
28
28
  end
29
29
  else
30
- []
30
+ nil
31
31
  end
32
32
  end
33
33
 
@@ -70,8 +70,15 @@ module Formtastic
70
70
  # We can't determine an appropriate value for :greater_than with a float/decimal column
71
71
  raise IndeterminableMinimumAttributeError if validation.options[:greater_than] && column? && [:float, :decimal].include?(column.type)
72
72
 
73
- return validation.options[:greater_than_or_equal_to] if validation.options[:greater_than_or_equal_to]
74
- return (validation.options[:greater_than] + 1) if validation.options[:greater_than]
73
+ if validation.options[:greater_than_or_equal_to]
74
+ return (validation.options[:greater_than_or_equal_to].call(object)) if validation.options[:greater_than_or_equal_to].kind_of?(Proc)
75
+ return (validation.options[:greater_than_or_equal_to])
76
+ end
77
+
78
+ if validation.options[:greater_than]
79
+ return (validation.options[:greater_than].call(object) + 1) if validation.options[:greater_than].kind_of?(Proc)
80
+ return (validation.options[:greater_than] + 1)
81
+ end
75
82
  end
76
83
  end
77
84
 
@@ -84,8 +91,15 @@ module Formtastic
84
91
  # We can't determine an appropriate value for :greater_than with a float/decimal column
85
92
  raise IndeterminableMaximumAttributeError if validation.options[:less_than] && column? && [:float, :decimal].include?(column.type)
86
93
 
87
- return validation.options[:less_than_or_equal_to] if validation.options[:less_than_or_equal_to]
88
- return (validation.options[:less_than] - 1) if validation.options[:less_than]
94
+ if validation.options[:less_than_or_equal_to]
95
+ return (validation.options[:less_than_or_equal_to].call(object)) if validation.options[:less_than_or_equal_to].kind_of?(Proc)
96
+ return (validation.options[:less_than_or_equal_to])
97
+ end
98
+
99
+ if validation.options[:less_than]
100
+ return ((validation.options[:less_than].call(object)) - 1) if validation.options[:less_than].kind_of?(Proc)
101
+ return (validation.options[:less_than] - 1)
102
+ end
89
103
  end
90
104
  end
91
105
 
@@ -112,7 +126,7 @@ module Formtastic
112
126
  end
113
127
 
114
128
  def validations?
115
- !validations.empty?
129
+ validations != nil
116
130
  end
117
131
 
118
132
  def required?
@@ -121,14 +135,31 @@ module Formtastic
121
135
  return false if not_required_through_negated_validation?
122
136
  if validations?
123
137
  validations.select { |validator|
124
- [:presence, :inclusion, :length].include?(validator.kind) &&
125
- validator.options[:allow_blank] != true
138
+ if validator.options.key?(:on)
139
+ return false if (validator.options[:on] != :save) && ((object.new_record? && validator.options[:on] != :create) || (!object.new_record? && validator.options[:on] != :update))
140
+ end
141
+ case validator.kind
142
+ when :presence
143
+ true
144
+ when :inclusion
145
+ validator.options[:allow_blank] != true
146
+ when :length
147
+ validator.options[:allow_blank] != true &&
148
+ validator.options[:minimum].to_i > 0 ||
149
+ validator.options[:within].try(:first).to_i > 0
150
+ else
151
+ false
152
+ end
126
153
  }.any?
127
154
  else
128
155
  return responds_to_global_required? && !!builder.all_fields_required_by_default
129
156
  end
130
157
  end
131
158
 
159
+ def required_attribute?
160
+ required? && builder.use_required_attribute
161
+ end
162
+
132
163
  def not_required_through_negated_validation?
133
164
  @not_required_through_negated_validation
134
165
  end