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
@@ -127,7 +127,7 @@ module Formtastic
|
|
127
127
|
value = choice_value(choice)
|
128
128
|
builder.check_box(
|
129
129
|
association_primary_key || method,
|
130
|
-
input_html_options.merge(:id => choice_input_dom_id(choice), :name => input_name, :disabled => disabled?(value)),
|
130
|
+
input_html_options.merge(:id => choice_input_dom_id(choice), :name => input_name, :disabled => disabled?(value), :required => false),
|
131
131
|
value,
|
132
132
|
unchecked_value
|
133
133
|
)
|
@@ -139,7 +139,7 @@ module Formtastic
|
|
139
139
|
input_name,
|
140
140
|
value,
|
141
141
|
checked?(value),
|
142
|
-
input_html_options.merge(:id => choice_input_dom_id(choice), :disabled => disabled?(value))
|
142
|
+
input_html_options.merge(:id => choice_input_dom_id(choice), :disabled => disabled?(value), :required => false)
|
143
143
|
)
|
144
144
|
end
|
145
145
|
|
@@ -154,7 +154,7 @@ module Formtastic
|
|
154
154
|
def selected_values
|
155
155
|
if object.respond_to?(method)
|
156
156
|
selected_items = [object.send(method)].compact.flatten
|
157
|
-
[*selected_items.map { |o|
|
157
|
+
[*selected_items.map { |o| send_or_call_or_object(value_method, o) }].compact
|
158
158
|
else
|
159
159
|
[]
|
160
160
|
end
|
@@ -13,7 +13,7 @@ module Formtastic
|
|
13
13
|
#
|
14
14
|
# <%= semantic_form_for(@user, :html => { :multipart => true }) do |f| %>
|
15
15
|
# <%= f.inputs do %>
|
16
|
-
# <%= f.input :
|
16
|
+
# <%= f.input :avatar, :as => :file %>
|
17
17
|
# <% end %>
|
18
18
|
# <% end %>
|
19
19
|
#
|
@@ -21,8 +21,8 @@ module Formtastic
|
|
21
21
|
# <fieldset>
|
22
22
|
# <ol>
|
23
23
|
# <li class="email">
|
24
|
-
# <label for="
|
25
|
-
# <input type="
|
24
|
+
# <label for="user_avatar">Avatar</label>
|
25
|
+
# <input type="file" id="user_avatar" name="user[avatar]">
|
26
26
|
# </li>
|
27
27
|
# </ol>
|
28
28
|
# </fieldset>
|
@@ -39,4 +39,4 @@ module Formtastic
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
|
-
end
|
42
|
+
end
|
@@ -140,7 +140,7 @@ module Formtastic
|
|
140
140
|
|
141
141
|
def choice_html(choice)
|
142
142
|
template.content_tag(:label,
|
143
|
-
builder.radio_button(input_name, choice_value(choice), input_html_options.merge(choice_html_options(choice))) <<
|
143
|
+
builder.radio_button(input_name, choice_value(choice), input_html_options.merge(choice_html_options(choice)).merge(:required => false)) <<
|
144
144
|
choice_label(choice),
|
145
145
|
label_html_options.merge(:for => choice_input_dom_id(choice), :class => nil)
|
146
146
|
)
|
@@ -1,16 +1,38 @@
|
|
1
1
|
module Formtastic
|
2
2
|
module Inputs
|
3
3
|
# Outputs a series of select boxes for the fragments that make up a time (hour, minute, second).
|
4
|
+
# Unless `:ignore_date` is true, it will render hidden inputs for the year, month and day as
|
5
|
+
# well, defaulting to `Time.current` if the form object doesn't have a value, much like Rails'
|
6
|
+
# own `time_select`.
|
4
7
|
#
|
5
8
|
# @see Formtastic::Inputs::Timeish Timeish module for documetation of date, time and datetime input options.
|
6
9
|
class TimeInput
|
7
10
|
include Base
|
8
11
|
include Base::Timeish
|
9
12
|
|
10
|
-
# we don't want year / month / day fragments
|
11
|
-
def
|
12
|
-
|
13
|
+
# we don't want year / month / day fragments if :ignore_date => true
|
14
|
+
def fragments
|
15
|
+
time_fragments
|
13
16
|
end
|
17
|
+
|
18
|
+
def value_or_default_value
|
19
|
+
value ? value : Time.current
|
20
|
+
end
|
21
|
+
|
22
|
+
def fragment_value(fragment)
|
23
|
+
value_or_default_value.send(fragment)
|
24
|
+
end
|
25
|
+
|
26
|
+
def hidden_fragments
|
27
|
+
if !options[:ignore_date]
|
28
|
+
date_fragments.map do |fragment|
|
29
|
+
template.hidden_field_tag("#{object_name}[#{fragment_name(fragment)}]", fragment_value(fragment), :id => fragment_id(fragment), :disabled => input_html_options[:disabled] )
|
30
|
+
end.join.html_safe
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
14
36
|
end
|
15
37
|
end
|
16
38
|
end
|
data/lib/formtastic/version.rb
CHANGED
@@ -66,8 +66,17 @@
|
|
66
66
|
# Specifies if labels/hints for input fields automatically be looked up using I18n.
|
67
67
|
# Default value: true. Overridden for specific fields by setting value to true,
|
68
68
|
# i.e. :label => true, or :hint => true (or opposite depending on initialized value)
|
69
|
-
# Formtastic::
|
69
|
+
# Formtastic::FormBuilder.i18n_lookups_by_default = false
|
70
70
|
|
71
71
|
# You can add custom inputs or override parts of Formtastic by subclassing Formtastic::FormBuilder and
|
72
72
|
# specifying that class here. Defaults to Formtastic::FormBuilder.
|
73
73
|
# Formtastic::Helpers::FormHelper.builder = MyCustomBuilder
|
74
|
+
|
75
|
+
# You can opt out of Formtastic's use of the HTML5 `required` attribute on `<input>`, `<select>`
|
76
|
+
# and `<textarea>` tags by setting this to false (defaults to true).
|
77
|
+
# Formtastic::FormBuilder.use_required_attribute = false
|
78
|
+
|
79
|
+
# You can opt out of new HTML5 browser validations (for things like email and url inputs) by setting
|
80
|
+
# this to false. Doing so will add a `novalidate` attribute to the `<form>` tag.
|
81
|
+
# See http://diveintohtml5.org/forms.html#validation for more info.
|
82
|
+
# Formtastic::FormBuilder.perform_browser_validations = true
|
data/spec/builder/errors_spec.rb
CHANGED
@@ -107,6 +107,16 @@ describe 'Formtastic::FormBuilder#errors_on' do
|
|
107
107
|
output_buffer.should_not have_tag('p.inine-errors')
|
108
108
|
output_buffer.should_not have_tag('ul.errors')
|
109
109
|
end
|
110
|
+
|
111
|
+
it 'should allow calling deprecated errors_on and inline_errors_for helpers' do
|
112
|
+
Formtastic::FormBuilder.inline_errors = :sentence
|
113
|
+
with_deprecation_silenced do
|
114
|
+
concat(semantic_form_for(@new_post) do |builder|
|
115
|
+
builder.errors_on :title
|
116
|
+
builder.inline_errors_for :title
|
117
|
+
end)
|
118
|
+
end
|
119
|
+
end
|
110
120
|
|
111
121
|
end
|
112
122
|
|
@@ -11,51 +11,93 @@ describe 'Formtastic::FormBuilder#fields_for' do
|
|
11
11
|
@new_post.stub!(:author).and_return(::Author.new)
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
context 'outside a form_for block' do
|
15
|
+
it 'yields an instance of FormHelper.builder' do
|
16
|
+
semantic_fields_for(@new_post) do |nested_builder|
|
17
|
+
nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
|
18
|
+
end
|
19
|
+
semantic_fields_for(@new_post.author) do |nested_builder|
|
20
|
+
nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
|
21
|
+
end
|
22
|
+
semantic_fields_for(:author, @new_post.author) do |nested_builder|
|
23
|
+
nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
|
24
|
+
end
|
25
|
+
semantic_fields_for(:author, @hash_backed_author) do |nested_builder|
|
17
26
|
nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
|
18
27
|
end
|
19
28
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
29
|
+
|
30
|
+
it 'should respond to input' do
|
31
|
+
semantic_fields_for(@new_post) do |nested_builder|
|
32
|
+
nested_builder.respond_to?(:input).should be_true
|
33
|
+
end
|
34
|
+
semantic_fields_for(@new_post.author) do |nested_builder|
|
35
|
+
nested_builder.respond_to?(:input).should be_true
|
36
|
+
end
|
37
|
+
semantic_fields_for(:author, @new_post.author) do |nested_builder|
|
38
|
+
nested_builder.respond_to?(:input).should be_true
|
39
|
+
end
|
40
|
+
semantic_fields_for(:author, @hash_backed_author) do |nested_builder|
|
41
|
+
nested_builder.respond_to?(:input).should be_true
|
26
42
|
end
|
27
43
|
end
|
28
44
|
end
|
29
45
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
46
|
+
context 'within a form_for block' do
|
47
|
+
it 'yields an instance of FormHelper.builder' do
|
48
|
+
semantic_form_for(@new_post) do |builder|
|
49
|
+
builder.semantic_fields_for(:author) do |nested_builder|
|
50
|
+
nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
|
51
|
+
end
|
34
52
|
end
|
35
53
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
54
|
+
|
55
|
+
it 'yields an instance of FormHelper.builder with hash-like model' do
|
56
|
+
semantic_form_for(:user) do |builder|
|
57
|
+
builder.semantic_fields_for(:author, @hash_backed_author) do |nested_builder|
|
58
|
+
nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'nests the object name' do
|
64
|
+
semantic_form_for(@new_post) do |builder|
|
65
|
+
builder.semantic_fields_for(@bob) do |nested_builder|
|
66
|
+
nested_builder.object_name.should == 'post[author]'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'supports passing collection as second parameter' do
|
72
|
+
semantic_form_for(@new_post) do |builder|
|
73
|
+
builder.semantic_fields_for(:author, [@fred,@bob]) do |nested_builder|
|
74
|
+
nested_builder.object_name.should =~ /post\[author_attributes\]\[\d+\]/
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should sanitize html id for li tag' do
|
80
|
+
@bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
|
81
|
+
concat(semantic_form_for(@new_post) do |builder|
|
82
|
+
concat(builder.semantic_fields_for(@bob, :index => 1) do |nested_builder|
|
83
|
+
concat(nested_builder.inputs(:login))
|
84
|
+
end)
|
43
85
|
end)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
86
|
+
output_buffer.should have_tag('form fieldset.inputs #post_author_1_login_input')
|
87
|
+
# Not valid selector, so using good ol' regex
|
88
|
+
output_buffer.should_not =~ /id="post\[author\]_1_login_input"/
|
89
|
+
# <=> output_buffer.should_not have_tag('form fieldset.inputs #post[author]_1_login_input')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should use namespace provided in nested fields' do
|
93
|
+
@bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
|
94
|
+
concat(semantic_form_for(@new_post, :namespace => 'context2') do |builder|
|
95
|
+
concat(builder.semantic_fields_for(@bob, :index => 1) do |nested_builder|
|
96
|
+
concat(nested_builder.inputs(:login))
|
97
|
+
end)
|
56
98
|
end)
|
57
|
-
|
58
|
-
|
99
|
+
output_buffer.should have_tag('form fieldset.inputs #context2_post_author_1_login_input')
|
100
|
+
end
|
59
101
|
end
|
60
102
|
|
61
103
|
context "when I rendered my own hidden id input" do
|
@@ -68,7 +110,7 @@ describe 'Formtastic::FormBuilder#fields_for' do
|
|
68
110
|
@fred.stub!(:posts_attributes=)
|
69
111
|
|
70
112
|
concat(semantic_form_for(@fred) do |builder|
|
71
|
-
concat(builder.
|
113
|
+
concat(builder.semantic_fields_for(:posts) do |nested_builder|
|
72
114
|
concat(nested_builder.input(:id, :as => :hidden))
|
73
115
|
concat(nested_builder.input(:title))
|
74
116
|
end)
|
@@ -85,4 +127,3 @@ describe 'Formtastic::FormBuilder#fields_for' do
|
|
85
127
|
end
|
86
128
|
|
87
129
|
end
|
88
|
-
|
@@ -23,6 +23,38 @@ describe 'FormHelper' do
|
|
23
23
|
end)
|
24
24
|
output_buffer.should have_tag("form.formtastic")
|
25
25
|
end
|
26
|
+
|
27
|
+
it 'adds a "novalidate" attribute to the generated form when configured to do so' do
|
28
|
+
with_config :perform_browser_validations, true do
|
29
|
+
concat(semantic_form_for(@new_post, :url => '/hello') do |builder|
|
30
|
+
end)
|
31
|
+
output_buffer.should_not have_tag("form[@novalidate]")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'omits a "novalidate" attribute to the generated form when configured to do so' do
|
36
|
+
with_config :perform_browser_validations, false do
|
37
|
+
concat(semantic_form_for(@new_post, :url => '/hello') do |builder|
|
38
|
+
end)
|
39
|
+
output_buffer.should have_tag("form[@novalidate]")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'allows form HTML to override "novalidate" attribute when configure to not show' do
|
44
|
+
with_config :perform_browser_validations, false do
|
45
|
+
concat(semantic_form_for(@new_post, :url => '/hello', :html => { :novalidate => true }) do |builder|
|
46
|
+
end)
|
47
|
+
output_buffer.should have_tag("form[@novalidate]")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'allows form HTML to override "novalidate" attribute when configure to show' do
|
52
|
+
with_config :perform_browser_validations, true do
|
53
|
+
concat(semantic_form_for(@new_post, :url => '/hello', :html => { :novalidate => false }) do |builder|
|
54
|
+
end)
|
55
|
+
output_buffer.should_not have_tag("form[@novalidate]")
|
56
|
+
end
|
57
|
+
end
|
26
58
|
|
27
59
|
it 'adds a class of "xyz" to the generated form' do
|
28
60
|
Formtastic::Helpers::FormHelper.default_form_class = 'xyz'
|
@@ -8,18 +8,18 @@ describe 'Formtastic::FormBuilder#input' do
|
|
8
8
|
before do
|
9
9
|
@output_buffer = ''
|
10
10
|
mock_everything
|
11
|
-
|
11
|
+
|
12
12
|
@errors = mock('errors')
|
13
13
|
@errors.stub!(:[]).and_return([])
|
14
14
|
@new_post.stub!(:errors).and_return(@errors)
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
after do
|
18
18
|
::I18n.backend.reload!
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
describe 'arguments and options' do
|
22
|
-
|
22
|
+
|
23
23
|
it 'should require the first argument (the method on form\'s object)' do
|
24
24
|
lambda {
|
25
25
|
concat(semantic_form_for(@new_post) do |builder|
|
@@ -31,7 +31,7 @@ describe 'Formtastic::FormBuilder#input' do
|
|
31
31
|
describe ':required option' do
|
32
32
|
|
33
33
|
describe 'when true' do
|
34
|
-
|
34
|
+
|
35
35
|
it 'should set a "required" class' do
|
36
36
|
with_config :required_string, " required yo!" do
|
37
37
|
concat(semantic_form_for(@new_post) do |builder|
|
@@ -114,21 +114,21 @@ describe 'Formtastic::FormBuilder#input' do
|
|
114
114
|
before do
|
115
115
|
@new_post.stub!(:class).and_return(::PostModel)
|
116
116
|
end
|
117
|
-
|
117
|
+
|
118
118
|
after do
|
119
119
|
@new_post.stub!(:class).and_return(::Post)
|
120
120
|
end
|
121
121
|
describe 'and validates_presence_of was called for the method' do
|
122
122
|
it 'should be required' do
|
123
|
-
|
123
|
+
|
124
124
|
@new_post.class.should_receive(:validators_on).with(:title).any_number_of_times.and_return([
|
125
125
|
active_model_presence_validator([:title])
|
126
126
|
])
|
127
|
-
|
127
|
+
|
128
128
|
@new_post.class.should_receive(:validators_on).with(:body).any_number_of_times.and_return([
|
129
129
|
active_model_presence_validator([:body], {:if => true})
|
130
130
|
])
|
131
|
-
|
131
|
+
|
132
132
|
concat(semantic_form_for(@new_post) do |builder|
|
133
133
|
concat(builder.input(:title))
|
134
134
|
concat(builder.input(:body))
|
@@ -136,84 +136,169 @@ describe 'Formtastic::FormBuilder#input' do
|
|
136
136
|
output_buffer.should have_tag('form li.required')
|
137
137
|
output_buffer.should_not have_tag('form li.optional')
|
138
138
|
end
|
139
|
-
|
139
|
+
|
140
|
+
it 'should be required when there is :on => :create option on create' do
|
141
|
+
with_config :required_string, " required yo!" do
|
142
|
+
@new_post.class.should_receive(:validators_on).with(:title).any_number_of_times.and_return([
|
143
|
+
active_model_presence_validator([:title], {:on => :create})
|
144
|
+
])
|
145
|
+
concat(semantic_form_for(@new_post) do |builder|
|
146
|
+
concat(builder.input(:title))
|
147
|
+
end)
|
148
|
+
output_buffer.should have_tag('form li.required')
|
149
|
+
output_buffer.should_not have_tag('form li.optional')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should be required when there is :on => :save option on create' do
|
154
|
+
with_config :required_string, " required yo!" do
|
155
|
+
@new_post.class.should_receive(:validators_on).with(:title).any_number_of_times.and_return([
|
156
|
+
active_model_presence_validator([:title], {:on => :save})
|
157
|
+
])
|
158
|
+
concat(semantic_form_for(@new_post) do |builder|
|
159
|
+
concat(builder.input(:title))
|
160
|
+
end)
|
161
|
+
output_buffer.should have_tag('form li.required')
|
162
|
+
output_buffer.should_not have_tag('form li.optional')
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should be required when there is :on => :save option on update' do
|
167
|
+
with_config :required_string, " required yo!" do
|
168
|
+
@fred.class.should_receive(:validators_on).with(:login).any_number_of_times.and_return([
|
169
|
+
active_model_presence_validator([:login], {:on => :save})
|
170
|
+
])
|
171
|
+
concat(semantic_form_for(@fred) do |builder|
|
172
|
+
concat(builder.input(:login))
|
173
|
+
end)
|
174
|
+
output_buffer.should have_tag('form li.required')
|
175
|
+
output_buffer.should_not have_tag('form li.optional')
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should not be required when there is :on => :create option on update' do
|
180
|
+
@fred.class.should_receive(:validators_on).with(:login).any_number_of_times.and_return([
|
181
|
+
active_model_presence_validator([:login], {:on => :create})
|
182
|
+
])
|
183
|
+
concat(semantic_form_for(@fred) do |builder|
|
184
|
+
concat(builder.input(:login))
|
185
|
+
end)
|
186
|
+
output_buffer.should_not have_tag('form li.required')
|
187
|
+
output_buffer.should have_tag('form li.optional')
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should not be required when there is :on => :update option on create' do
|
191
|
+
@new_post.class.should_receive(:validators_on).with(:title).any_number_of_times.and_return([
|
192
|
+
active_model_presence_validator([:title], {:on => :update})
|
193
|
+
])
|
194
|
+
concat(semantic_form_for(@new_post) do |builder|
|
195
|
+
concat(builder.input(:title))
|
196
|
+
end)
|
197
|
+
output_buffer.should_not have_tag('form li.required')
|
198
|
+
output_buffer.should have_tag('form li.optional')
|
199
|
+
end
|
200
|
+
|
140
201
|
it 'should be not be required if the optional :if condition is not satisifed' do
|
141
|
-
|
202
|
+
presence_should_be_required(:required => false, :tag => :body, :options => { :if => false })
|
142
203
|
end
|
143
|
-
|
204
|
+
|
144
205
|
it 'should not be required if the optional :if proc evaluates to false' do
|
145
|
-
|
206
|
+
presence_should_be_required(:required => false, :tag => :body, :options => { :if => proc { |record| false } })
|
146
207
|
end
|
147
|
-
|
208
|
+
|
148
209
|
it 'should be required if the optional :if proc evaluates to true' do
|
149
|
-
|
210
|
+
presence_should_be_required(:required => true, :tag => :body, :options => { :if => proc { |record| true } })
|
150
211
|
end
|
151
|
-
|
212
|
+
|
152
213
|
it 'should not be required if the optional :unless proc evaluates to true' do
|
153
|
-
|
214
|
+
presence_should_be_required(:required => false, :tag => :body, :options => { :unless => proc { |record| true } })
|
154
215
|
end
|
155
|
-
|
216
|
+
|
156
217
|
it 'should be required if the optional :unless proc evaluates to false' do
|
157
|
-
|
218
|
+
presence_should_be_required(:required => true, :tag => :body, :options => { :unless => proc { |record| false } })
|
158
219
|
end
|
159
|
-
|
220
|
+
|
160
221
|
it 'should be required if the optional :if with a method string evaluates to true' do
|
161
222
|
@new_post.should_receive(:required_condition).and_return(true)
|
162
|
-
|
223
|
+
presence_should_be_required(:required => true, :tag => :body, :options => { :if => :required_condition })
|
163
224
|
end
|
164
|
-
|
225
|
+
|
165
226
|
it 'should be required if the optional :if with a method string evaluates to false' do
|
166
227
|
@new_post.should_receive(:required_condition).and_return(false)
|
167
|
-
|
228
|
+
presence_should_be_required(:required => false, :tag => :body, :options => { :if => :required_condition })
|
168
229
|
end
|
169
|
-
|
170
|
-
it 'should
|
230
|
+
|
231
|
+
it 'should be required if the optional :unless with a method string evaluates to false' do
|
171
232
|
@new_post.should_receive(:required_condition).and_return(false)
|
172
|
-
|
233
|
+
presence_should_be_required(:required => true, :tag => :body, :options => { :unless => :required_condition })
|
173
234
|
end
|
174
|
-
|
175
|
-
it 'should be required if the optional :unless with a method string evaluates to true' do
|
235
|
+
|
236
|
+
it 'should not be required if the optional :unless with a method string evaluates to true' do
|
176
237
|
@new_post.should_receive(:required_condition).and_return(true)
|
177
|
-
|
238
|
+
presence_should_be_required(:required => false, :tag => :body, :options => { :unless => :required_condition })
|
178
239
|
end
|
179
240
|
end
|
180
|
-
|
241
|
+
|
181
242
|
describe 'and validates_inclusion_of was called for the method' do
|
182
243
|
it 'should be required' do
|
183
244
|
@new_post.class.should_receive(:validators_on).with(:published).any_number_of_times.and_return([
|
184
245
|
active_model_inclusion_validator([:published], {:in => [false, true]})
|
185
246
|
])
|
186
|
-
|
187
|
-
concat(semantic_form_for(@new_post) do |builder|
|
188
|
-
concat(builder.input(:published))
|
189
|
-
end)
|
190
|
-
output_buffer.should have_tag('form li.required')
|
191
|
-
output_buffer.should_not have_tag('form li.optional')
|
247
|
+
should_be_required(:tag => :published, :required => true)
|
192
248
|
end
|
193
|
-
|
249
|
+
|
194
250
|
it 'should not be required if allow_blank is true' do
|
195
251
|
@new_post.class.should_receive(:validators_on).with(:published).any_number_of_times.and_return([
|
196
252
|
active_model_inclusion_validator([:published], {:in => [false, true], :allow_blank => true})
|
197
253
|
])
|
198
|
-
|
199
|
-
concat(semantic_form_for(@new_post) do |builder|
|
200
|
-
concat(builder.input(:published))
|
201
|
-
end)
|
202
|
-
output_buffer.should_not have_tag('form li.required')
|
203
|
-
output_buffer.should have_tag('form li.optional')
|
254
|
+
should_be_required(:tag => :published, :required => false)
|
204
255
|
end
|
205
256
|
end
|
206
|
-
|
257
|
+
|
258
|
+
describe 'and validates_length_of was called for the method' do
|
259
|
+
it 'should be required if minimum is set' do
|
260
|
+
length_should_be_required(:tag => :title, :required => true, :options => {:minimum => 1})
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'should be required if :within is set' do
|
264
|
+
length_should_be_required(:tag => :title, :required => true, :options => {:within => 1..5})
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'should not be required if :within allows zero length' do
|
268
|
+
length_should_be_required(:tag => :title, :required => false, :options => {:within => 0..5})
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'should not be required if only :minimum is zero' do
|
272
|
+
length_should_be_required(:tag => :title, :required => false, :options => {:minimum => 0})
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'should not be required if only :minimum is not set' do
|
276
|
+
length_should_be_required(:tag => :title, :required => false, :options => {:maximum => 5})
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'should not be required if allow_blank is true' do
|
280
|
+
length_should_be_required(:tag => :published, :required => false, :options => {:allow_blank => true})
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def add_presence_validator(options)
|
285
|
+
@new_post.class.stub!(:validators_on).with(options[:tag]).and_return([
|
286
|
+
active_model_presence_validator([options[:tag]], options[:options])
|
287
|
+
])
|
288
|
+
end
|
289
|
+
|
290
|
+
def add_length_validator(options)
|
291
|
+
@new_post.class.should_receive(:validators_on).with(options[:tag]).any_number_of_times {[
|
292
|
+
active_model_length_validator([options[:tag]], options[:options])
|
293
|
+
]}
|
294
|
+
end
|
295
|
+
|
207
296
|
# TODO make a matcher for this?
|
208
297
|
def should_be_required(options)
|
209
|
-
@new_post.class.stub!(:validators_on).with(:body).and_return([
|
210
|
-
active_model_presence_validator([:body], options[:options])
|
211
|
-
])
|
212
|
-
|
213
298
|
concat(semantic_form_for(@new_post) do |builder|
|
214
|
-
concat(builder.input(:
|
299
|
+
concat(builder.input(options[:tag]))
|
215
300
|
end)
|
216
|
-
|
301
|
+
|
217
302
|
if options[:required]
|
218
303
|
output_buffer.should_not have_tag('form li.optional')
|
219
304
|
output_buffer.should have_tag('form li.required')
|
@@ -222,24 +307,34 @@ describe 'Formtastic::FormBuilder#input' do
|
|
222
307
|
output_buffer.should_not have_tag('form li.required')
|
223
308
|
end
|
224
309
|
end
|
225
|
-
|
310
|
+
|
311
|
+
def presence_should_be_required(options)
|
312
|
+
add_presence_validator(options)
|
313
|
+
should_be_required(options)
|
314
|
+
end
|
315
|
+
|
316
|
+
def length_should_be_required(options)
|
317
|
+
add_length_validator(options)
|
318
|
+
should_be_required(options)
|
319
|
+
end
|
320
|
+
|
226
321
|
# TODO JF reversed this during refactor, need to make sure
|
227
322
|
describe 'and there are no requirement validations on the method' do
|
228
323
|
before do
|
229
324
|
@new_post.class.should_receive(:validators_on).with(:title).and_return([])
|
230
325
|
end
|
231
|
-
|
232
|
-
it 'should
|
326
|
+
|
327
|
+
it 'should not be required' do
|
233
328
|
concat(semantic_form_for(@new_post) do |builder|
|
234
329
|
concat(builder.input(:title))
|
235
330
|
end)
|
236
|
-
output_buffer.
|
237
|
-
output_buffer.
|
331
|
+
output_buffer.should_not have_tag('form li.required')
|
332
|
+
output_buffer.should have_tag('form li.optional')
|
238
333
|
end
|
239
334
|
end
|
240
|
-
|
335
|
+
|
241
336
|
end
|
242
|
-
|
337
|
+
|
243
338
|
describe 'and an object without :validators_on' do
|
244
339
|
|
245
340
|
it 'should use the default value' do
|
@@ -304,99 +399,98 @@ describe 'Formtastic::FormBuilder#input' do
|
|
304
399
|
@new_post.stub!(:column_for_attribute).with(:aws_instance_id).and_return(mock('column', :type => :integer))
|
305
400
|
default_input_type(:integer, :aws_instance_id).should == :number
|
306
401
|
end
|
307
|
-
|
402
|
+
|
308
403
|
it 'should default to :select for associations' do
|
309
404
|
@new_post.class.stub!(:reflect_on_association).with(:user_id).and_return(mock('ActiveRecord::Reflection::AssociationReflection'))
|
310
405
|
@new_post.class.stub!(:reflect_on_association).with(:section_id).and_return(mock('ActiveRecord::Reflection::AssociationReflection'))
|
311
406
|
default_input_type(:integer, :user_id).should == :select
|
312
407
|
default_input_type(:integer, :section_id).should == :select
|
313
408
|
end
|
314
|
-
|
409
|
+
|
315
410
|
it 'should default to :password for :string column types with "password" in the method name' do
|
316
411
|
default_input_type(:string, :password).should == :password
|
317
412
|
default_input_type(:string, :hashed_password).should == :password
|
318
413
|
default_input_type(:string, :password_hash).should == :password
|
319
414
|
end
|
320
|
-
|
415
|
+
|
321
416
|
it 'should default to :text for :text column types' do
|
322
417
|
default_input_type(:text).should == :text
|
323
418
|
end
|
324
|
-
|
419
|
+
|
325
420
|
it 'should default to :date for :date column types' do
|
326
421
|
default_input_type(:date).should == :date
|
327
422
|
end
|
328
|
-
|
423
|
+
|
329
424
|
it 'should default to :datetime for :datetime and :timestamp column types' do
|
330
425
|
default_input_type(:datetime).should == :datetime
|
331
426
|
default_input_type(:timestamp).should == :datetime
|
332
427
|
end
|
333
|
-
|
428
|
+
|
334
429
|
it 'should default to :time for :time column types' do
|
335
430
|
default_input_type(:time).should == :time
|
336
431
|
end
|
337
|
-
|
432
|
+
|
338
433
|
it 'should default to :boolean for :boolean column types' do
|
339
434
|
default_input_type(:boolean).should == :boolean
|
340
435
|
end
|
341
|
-
|
436
|
+
|
342
437
|
it 'should default to :string for :string column types' do
|
343
438
|
default_input_type(:string).should == :string
|
344
439
|
end
|
345
|
-
|
440
|
+
|
346
441
|
it 'should default to :number for :integer, :float and :decimal column types' do
|
347
442
|
default_input_type(:integer).should == :number
|
348
443
|
default_input_type(:float).should == :number
|
349
444
|
default_input_type(:decimal).should == :number
|
350
445
|
end
|
351
|
-
|
446
|
+
|
352
447
|
it 'should default to :country for :string columns named country' do
|
353
448
|
default_input_type(:string, :country).should == :country
|
354
449
|
end
|
355
|
-
|
450
|
+
|
356
451
|
it 'should default to :email for :string columns matching email' do
|
357
452
|
default_input_type(:string, :email).should == :email
|
358
453
|
default_input_type(:string, :customer_email).should == :email
|
359
454
|
default_input_type(:string, :email_work).should == :email
|
360
455
|
end
|
361
|
-
|
456
|
+
|
362
457
|
it 'should default to :url for :string columns named url or website' do
|
363
458
|
default_input_type(:string, :url).should == :url
|
364
459
|
default_input_type(:string, :website).should == :url
|
365
460
|
default_input_type(:string, :my_url).should == :url
|
366
461
|
default_input_type(:string, :hurl).should_not == :url
|
367
462
|
end
|
368
|
-
|
463
|
+
|
369
464
|
it 'should default to :phone for :string columns named phone or fax' do
|
370
465
|
default_input_type(:string, :phone).should == :phone
|
371
466
|
default_input_type(:string, :fax).should == :phone
|
372
467
|
end
|
373
|
-
|
468
|
+
|
374
469
|
it 'should default to :search for :string columns named search' do
|
375
470
|
default_input_type(:string, :search).should == :search
|
376
471
|
end
|
377
|
-
|
472
|
+
|
378
473
|
describe 'defaulting to file column' do
|
379
474
|
Formtastic::FormBuilder.file_methods.each do |method|
|
380
475
|
it "should default to :file for attributes that respond to ##{method}" do
|
381
|
-
@new_post.stub!(:column_for_attribute).and_return(nil)
|
382
476
|
column = mock('column')
|
383
|
-
|
477
|
+
|
384
478
|
Formtastic::FormBuilder.file_methods.each do |test|
|
385
479
|
### TODO: Check if this is ok
|
386
480
|
column.stub!(method).with(test).and_return(method == test)
|
387
481
|
end
|
388
|
-
|
482
|
+
|
389
483
|
@new_post.should_receive(method).and_return(column)
|
390
|
-
|
484
|
+
|
391
485
|
semantic_form_for(@new_post) do |builder|
|
392
486
|
builder.send(:default_input_type, method).should == :file
|
393
487
|
end
|
394
488
|
end
|
395
489
|
end
|
396
|
-
|
490
|
+
|
397
491
|
end
|
398
492
|
end
|
399
|
-
|
493
|
+
|
400
494
|
it 'should call the corresponding input class with .to_html' do
|
401
495
|
[:select, :time_zone, :radio, :date, :datetime, :time, :boolean, :check_boxes, :hidden, :string, :password, :number, :text, :file].each do |input_style|
|
402
496
|
@new_post.stub!(:generic_column_name)
|
@@ -405,7 +499,7 @@ describe 'Formtastic::FormBuilder#input' do
|
|
405
499
|
input_instance = mock('Input instance')
|
406
500
|
input_class = "#{input_style.to_s}_input".classify
|
407
501
|
input_constant = "Formtastic::Inputs::#{input_class}".constantize
|
408
|
-
|
502
|
+
|
409
503
|
input_constant.should_receive(:new).and_return(input_instance)
|
410
504
|
input_instance.should_receive(:to_html).and_return("some HTML")
|
411
505
|
|
@@ -738,10 +832,10 @@ describe 'Formtastic::FormBuilder#input' do
|
|
738
832
|
end
|
739
833
|
|
740
834
|
describe ':collection option' do
|
741
|
-
|
835
|
+
|
742
836
|
it "should be required on polymorphic associations" do
|
743
837
|
@new_post.stub!(:commentable)
|
744
|
-
@new_post.class.stub!(:reflections).and_return({
|
838
|
+
@new_post.class.stub!(:reflections).and_return({
|
745
839
|
:commentable => mock('macro_reflection', :options => { :polymorphic => true }, :macro => :belongs_to)
|
746
840
|
})
|
747
841
|
@new_post.stub!(:column_for_attribute).with(:commentable).and_return(
|
@@ -750,37 +844,37 @@ describe 'Formtastic::FormBuilder#input' do
|
|
750
844
|
@new_post.class.stub!(:reflect_on_association).with(:commentable).and_return(
|
751
845
|
mock('reflection', :macro => :belongs_to, :options => { :polymorphic => true })
|
752
846
|
)
|
753
|
-
expect {
|
847
|
+
expect {
|
754
848
|
concat(semantic_form_for(@new_post) do |builder|
|
755
|
-
concat(builder.inputs do
|
849
|
+
concat(builder.inputs do
|
756
850
|
concat(builder.input :commentable)
|
757
851
|
end)
|
758
852
|
end)
|
759
853
|
}.to raise_error(Formtastic::PolymorphicInputWithoutCollectionError)
|
760
854
|
end
|
761
|
-
|
855
|
+
|
762
856
|
end
|
763
857
|
|
764
858
|
end
|
765
|
-
|
859
|
+
|
766
860
|
describe 'options re-use' do
|
767
|
-
|
861
|
+
|
768
862
|
it 'should retain :as option when re-using the same options hash' do
|
769
863
|
my_options = { :as => :string }
|
770
864
|
output = ''
|
771
|
-
|
865
|
+
|
772
866
|
concat(semantic_form_for(@new_post) do |builder|
|
773
867
|
concat(builder.input(:title, my_options))
|
774
868
|
concat(builder.input(:publish_at, my_options))
|
775
869
|
end)
|
776
870
|
output_buffer.should have_tag 'li.string', :count => 2
|
777
871
|
end
|
778
|
-
|
779
|
-
|
872
|
+
|
873
|
+
|
780
874
|
end
|
781
|
-
|
875
|
+
|
782
876
|
describe 'instantiating an input class' do
|
783
|
-
|
877
|
+
|
784
878
|
context 'when a class does not exist' do
|
785
879
|
it "should raise an error" do
|
786
880
|
lambda {
|
@@ -790,9 +884,9 @@ describe 'Formtastic::FormBuilder#input' do
|
|
790
884
|
}.should raise_error(Formtastic::UnknownInputError)
|
791
885
|
end
|
792
886
|
end
|
793
|
-
|
887
|
+
|
794
888
|
context 'when a customized top-level class does not exist' do
|
795
|
-
|
889
|
+
|
796
890
|
it 'should instantiate the Formtastic input' do
|
797
891
|
input = mock('input', :to_html => 'some HTML')
|
798
892
|
Formtastic::Inputs::StringInput.should_receive(:new).and_return(input)
|
@@ -800,26 +894,26 @@ describe 'Formtastic::FormBuilder#input' do
|
|
800
894
|
builder.input(:title, :as => :string)
|
801
895
|
end)
|
802
896
|
end
|
803
|
-
|
897
|
+
|
804
898
|
end
|
805
|
-
|
899
|
+
|
806
900
|
describe 'when a top-level input class exists' do
|
807
901
|
it "should instantiate the top-level input instead of the Formtastic one" do
|
808
902
|
class ::StringInput < Formtastic::Inputs::StringInput
|
809
903
|
end
|
810
|
-
|
904
|
+
|
811
905
|
input = mock('input', :to_html => 'some HTML')
|
812
906
|
Formtastic::Inputs::StringInput.should_not_receive(:new).and_return(input)
|
813
907
|
::StringInput.should_receive(:new).and_return(input)
|
814
|
-
|
908
|
+
|
815
909
|
concat(semantic_form_for(@new_post) do |builder|
|
816
910
|
builder.input(:title, :as => :string)
|
817
911
|
end)
|
818
912
|
end
|
819
913
|
end
|
820
|
-
|
914
|
+
|
821
915
|
describe 'when instantiated multiple times with the same input type' do
|
822
|
-
|
916
|
+
|
823
917
|
it "should be cached (not calling the internal methods)" do
|
824
918
|
# TODO this is really tied to the underlying implementation
|
825
919
|
concat(semantic_form_for(@new_post) do |builder|
|
@@ -828,10 +922,10 @@ describe 'Formtastic::FormBuilder#input' do
|
|
828
922
|
builder.input(:title, :as => :string)
|
829
923
|
end)
|
830
924
|
end
|
831
|
-
|
925
|
+
|
832
926
|
end
|
833
|
-
|
927
|
+
|
834
928
|
end
|
835
|
-
|
929
|
+
|
836
930
|
end
|
837
931
|
|