formtastic 2.0.0.rc3 → 2.0.0.rc4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|