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.
- data/.travis.yml +7 -0
- data/CHANGELOG +26 -0
- data/Gemfile +2 -0
- data/README.textile +0 -1
- data/RELEASE_PROCESS +0 -1
- data/Rakefile +1 -1
- data/app/assets/stylesheets/formtastic.css +3 -3
- data/app/assets/stylesheets/formtastic_ie6.css +7 -1
- data/app/assets/stylesheets/formtastic_ie7.css +7 -1
- data/formtastic.gemspec +0 -15
- data/lib/formtastic/form_builder.rb +2 -0
- data/lib/formtastic/helpers/errors_helper.rb +22 -0
- data/lib/formtastic/helpers/form_helper.rb +18 -16
- data/lib/formtastic/helpers/input_helper.rb +9 -7
- data/lib/formtastic/helpers/inputs_helper.rb +11 -3
- data/lib/formtastic/helpers/reflection.rb +5 -1
- data/lib/formtastic/inputs/base/collections.rb +7 -0
- data/lib/formtastic/inputs/base/html.rb +1 -1
- data/lib/formtastic/inputs/base/timeish.rb +7 -2
- data/lib/formtastic/inputs/base/validations.rb +39 -8
- data/lib/formtastic/inputs/check_boxes_input.rb +3 -3
- data/lib/formtastic/inputs/file_input.rb +4 -4
- data/lib/formtastic/inputs/number_input.rb +1 -1
- data/lib/formtastic/inputs/radio_input.rb +1 -1
- data/lib/formtastic/inputs/time_input.rb +25 -3
- data/lib/formtastic/version.rb +1 -1
- data/lib/generators/templates/formtastic.rb +10 -1
- data/spec/builder/errors_spec.rb +10 -0
- data/spec/builder/semantic_fields_for_spec.rb +77 -36
- data/spec/helpers/form_helper_spec.rb +32 -0
- data/spec/helpers/input_helper_spec.rb +196 -102
- data/spec/helpers/inputs_helper_spec.rb +85 -73
- data/spec/helpers/reflection_helper_spec.rb +32 -0
- data/spec/inputs/check_boxes_input_spec.rb +21 -6
- data/spec/inputs/date_input_spec.rb +20 -0
- data/spec/inputs/datetime_input_spec.rb +30 -11
- data/spec/inputs/label_spec.rb +8 -0
- data/spec/inputs/number_input_spec.rb +298 -2
- data/spec/inputs/radio_input_spec.rb +5 -6
- data/spec/inputs/string_input_spec.rb +22 -5
- data/spec/inputs/time_input_spec.rb +51 -7
- data/spec/spec_helper.rb +64 -12
- metadata +11 -9
data/.travis.yml
ADDED
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
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
data/Rakefile
CHANGED
@@ -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;
|
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;
|
99
|
+
overflow:hidden; /* clear containing floats */
|
100
100
|
}
|
101
101
|
|
102
102
|
.formtastic .input {
|
103
|
-
overflow:hidden;
|
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;
|
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.
|
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
|
319
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
74
|
-
|
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
|
-
|
88
|
-
|
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
|
-
|
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
|
-
|
125
|
-
|
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
|