formtastic 2.2.1 → 2.3.0.rc

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 (50) hide show
  1. data/.travis.yml +17 -13
  2. data/Appraisals +6 -3
  3. data/CHANGELOG +4 -0
  4. data/Gemfile +1 -1
  5. data/README.textile +14 -23
  6. data/Rakefile +0 -17
  7. data/app/assets/stylesheets/formtastic.css +2 -1
  8. data/formtastic.gemspec +4 -3
  9. data/gemfiles/{rails-3.0.gemfile → rails_3.0.gemfile} +1 -1
  10. data/gemfiles/{rails-3.1.gemfile → rails_3.1.gemfile} +1 -1
  11. data/gemfiles/{rails-3.2.gemfile → rails_3.2.gemfile} +1 -1
  12. data/gemfiles/rails_4.gemfile +7 -0
  13. data/gemfiles/rails_edge.gemfile +7 -0
  14. data/lib/formtastic/form_builder.rb +7 -0
  15. data/lib/formtastic/helpers/fieldset_wrapper.rb +1 -1
  16. data/lib/formtastic/helpers/form_helper.rb +8 -10
  17. data/lib/formtastic/helpers/input_helper.rb +1 -0
  18. data/lib/formtastic/inputs/base.rb +0 -1
  19. data/lib/formtastic/inputs/base/collections.rb +15 -4
  20. data/lib/formtastic/inputs/base/labelling.rb +4 -7
  21. data/lib/formtastic/inputs/base/numeric.rb +1 -1
  22. data/lib/formtastic/inputs/base/options.rb +1 -1
  23. data/lib/formtastic/inputs/base/timeish.rb +12 -4
  24. data/lib/formtastic/inputs/boolean_input.rb +6 -6
  25. data/lib/formtastic/inputs/check_boxes_input.rb +7 -2
  26. data/lib/formtastic/inputs/datetime_picker_input.rb +3 -3
  27. data/lib/formtastic/inputs/select_input.rb +4 -2
  28. data/lib/formtastic/util.rb +4 -0
  29. data/lib/formtastic/version.rb +1 -1
  30. data/lib/generators/templates/formtastic.rb +3 -3
  31. data/sample/basic_inputs.html +1 -1
  32. data/spec/builder/semantic_fields_for_spec.rb +0 -1
  33. data/spec/helpers/action_helper_spec.rb +1 -1
  34. data/spec/helpers/form_helper_spec.rb +30 -0
  35. data/spec/helpers/input_helper_spec.rb +1 -1
  36. data/spec/helpers/inputs_helper_spec.rb +13 -15
  37. data/spec/inputs/boolean_input_spec.rb +7 -0
  38. data/spec/inputs/check_boxes_input_spec.rb +31 -1
  39. data/spec/inputs/date_select_input_spec.rb +8 -0
  40. data/spec/inputs/datetime_picker_input_spec.rb +14 -14
  41. data/spec/inputs/datetime_select_input_spec.rb +8 -0
  42. data/spec/inputs/label_spec.rb +11 -0
  43. data/spec/inputs/select_input_spec.rb +21 -4
  44. data/spec/inputs/time_select_input_spec.rb +9 -1
  45. data/spec/spec_helper.rb +74 -13
  46. data/spec/support/custom_macros.rb +3 -2
  47. data/spec/support/test_environment.rb +2 -0
  48. metadata +116 -40
  49. data/gemfiles/rails-4.gemfile +0 -8
  50. data/lib/tasks/verify_rcov.rb +0 -44
@@ -50,7 +50,10 @@ module Formtastic
50
50
  label_html_options
51
51
  )
52
52
  end
53
-
53
+
54
+ # TODO: why are we merging `input_html_options` and then making some of the irrelevant ones `nil`?
55
+ # Seems like we should be selectively including from input_html_options (a whitelist) instead of
56
+ # excluding (blacklist).
54
57
  def label_html_options
55
58
  prev = super
56
59
  prev[:class] = prev[:class] - ['label']
@@ -59,6 +62,7 @@ module Formtastic
59
62
  prev.merge(
60
63
  :id => nil,
61
64
  :name => nil,
65
+ :tabindex => nil,
62
66
  :for => input_html_options[:id]
63
67
  )
64
68
  )
@@ -97,11 +101,7 @@ module Formtastic
97
101
  end
98
102
 
99
103
  def checked?
100
- if defined? ActionView::Helpers::InstanceTag
101
- object && ActionView::Helpers::InstanceTag.check_box_checked?(object.send(method), checked_value)
102
- else
103
- object && boolean_checked?(object.send(method), checked_value)
104
- end
104
+ object && boolean_checked?(object.send(method), checked_value)
105
105
  end
106
106
 
107
107
  private
@@ -44,6 +44,7 @@ module Formtastic
44
44
  # <%= f.input :categories, :as => :check_boxes, :collection => [["Ruby", "ruby"], ["Rails", "rails"]] %>
45
45
  # <%= f.input :categories, :as => :check_boxes, :collection => [["Ruby", "1"], ["Rails", "2"]] %>
46
46
  # <%= f.input :categories, :as => :check_boxes, :collection => [["Ruby", 1], ["Rails", 2]] %>
47
+ # <%= f.input :categories, :as => :check_boxes, :collection => [["Ruby", 1, {'data-attr' => 'attr-value'}]] %>
47
48
  # <%= f.input :categories, :as => :check_boxes, :collection => 1..5 %>
48
49
  #
49
50
  # @example `:hidden_fields` can be used to skip Rails' rendering of a hidden field before every checkbox
@@ -127,7 +128,7 @@ module Formtastic
127
128
  value = choice_value(choice)
128
129
  builder.check_box(
129
130
  association_primary_key || method,
130
- input_html_options.merge(:id => choice_input_dom_id(choice), :name => input_name, :disabled => disabled?(value), :required => false),
131
+ extra_html_options(choice).merge(:id => choice_input_dom_id(choice), :name => input_name, :disabled => disabled?(value), :required => false),
131
132
  value,
132
133
  unchecked_value
133
134
  )
@@ -139,10 +140,14 @@ module Formtastic
139
140
  input_name,
140
141
  value,
141
142
  checked?(value),
142
- input_html_options.merge(:id => choice_input_dom_id(choice), :disabled => disabled?(value), :required => false)
143
+ extra_html_options(choice).merge(:id => choice_input_dom_id(choice), :disabled => disabled?(value), :required => false)
143
144
  )
144
145
  end
145
146
 
147
+ def extra_html_options(choice)
148
+ input_html_options.merge(custom_choice_html_options(choice))
149
+ end
150
+
146
151
  def checked?(value)
147
152
  selected_values.include?(value)
148
153
  end
@@ -89,12 +89,12 @@ module Formtastic
89
89
  def value
90
90
  return options[:input_html][:value] if options[:input_html] && options[:input_html].key?(:value)
91
91
  val = object.send(method)
92
- return val.strftime("%Y-%m-%d %H:%M") if val.is_a?(Time)
93
- return "#{val.year}-#{val.month}-#{val.day} 00:00" if val.is_a?(Date)
92
+ return val.strftime("%Y-%m-%dT%H:%M:%S") if val.is_a?(Time)
93
+ return "#{val.year}-#{val.month}-#{val.day}T00:00:00" if val.is_a?(Date)
94
94
  return val if val.nil?
95
95
  val.to_s
96
96
  end
97
97
 
98
98
  end
99
99
  end
100
- end
100
+ end
@@ -198,8 +198,10 @@ module Formtastic
198
198
  def extra_input_html_options
199
199
  {
200
200
  :multiple => multiple?,
201
- :name => multiple? ? input_html_options_name_multiple : input_html_options_name
201
+ :name => (multiple? && Rails::VERSION::MAJOR >= 3) ? input_html_options_name_multiple : input_html_options_name
202
202
  }
203
+
204
+
203
205
  end
204
206
 
205
207
  def input_html_options_name
@@ -232,4 +234,4 @@ module Formtastic
232
234
 
233
235
  end
234
236
  end
235
- end
237
+ end
@@ -20,6 +20,10 @@ module Formtastic
20
20
  text
21
21
  end
22
22
  end
23
+
24
+ def rails3?
25
+ ::Rails::VERSION::MAJOR == 3
26
+ end
23
27
 
24
28
  end
25
29
  end
@@ -1,3 +1,3 @@
1
1
  module Formtastic
2
- VERSION = "2.2.1"
2
+ VERSION = "2.3.0.rc"
3
3
  end
@@ -67,10 +67,10 @@
67
67
  # Formtastic::Helpers::FormHelper.builder = MyCustomBuilder
68
68
 
69
69
  # You can opt-in to Formtastic's use of the HTML5 `required` attribute on `<input>`, `<select>`
70
- # and `<textarea>` tags by setting this to false (defaults to true).
71
- # Formtastic::FormBuilder.use_required_attribute = true
70
+ # and `<textarea>` tags by setting this to true (defaults to false).
71
+ # Formtastic::FormBuilder.use_required_attribute = false
72
72
 
73
73
  # You can opt-in to new HTML5 browser validations (for things like email and url inputs) by setting
74
- # this to false. Doing so will add a `novalidate` attribute to the `<form>` tag.
74
+ # this to true. Doing so will add a `novalidate` attribute to the `<form>` tag.
75
75
  # See http://diveintohtml5.org/forms.html#validation for more info.
76
76
  # Formtastic::FormBuilder.perform_browser_validations = true
@@ -215,7 +215,7 @@
215
215
  <button id="gem_submit" type="reset">Reset</button>
216
216
  </li>
217
217
  <li class="action link_action">
218
- <a href="#">Cancel</button>
218
+ <a href="#">Cancel</a>
219
219
  </li>
220
220
  </ol>
221
221
  </fieldset>
@@ -122,7 +122,6 @@ describe 'Formtastic::FormBuilder#fields_for' do
122
122
  @fred.posts.size.should == 1
123
123
  @fred.posts.first.stub!(:persisted?).and_return(true)
124
124
  @fred.stub!(:posts_attributes=)
125
-
126
125
  concat(semantic_form_for(@fred) do |builder|
127
126
  concat(builder.semantic_fields_for(:posts) do |nested_builder|
128
127
  concat(nested_builder.input(:id, :as => :hidden))
@@ -281,7 +281,7 @@ describe 'Formtastic::FormBuilder#action' do
281
281
  end
282
282
 
283
283
  action = mock('action', :to_html => 'some HTML')
284
- Formtastic::Actions::ButtonAction.should_not_receive(:new).and_return(action)
284
+ Formtastic::Actions::ButtonAction.should_not_receive(:new)
285
285
  ::ButtonAction.should_receive(:new).and_return(action)
286
286
 
287
287
  concat(semantic_form_for(@new_post) do |builder|
@@ -62,6 +62,13 @@ describe 'FormHelper' do
62
62
  end)
63
63
  output_buffer.should have_tag("form.xyz")
64
64
  end
65
+
66
+ it 'omits the leading spaces from the classes in the generated form when the default class is nil' do
67
+ Formtastic::Helpers::FormHelper.default_form_class = nil
68
+ concat(semantic_form_for(::Post.new, :as => :post, :url => '/hello') do |builder|
69
+ end)
70
+ output_buffer.should have_tag("form[class='post']")
71
+ end
65
72
 
66
73
  it 'adds class matching the object name to the generated form when a symbol is provided' do
67
74
  concat(semantic_form_for(@new_post, :url => '/hello') do |builder|
@@ -135,6 +142,29 @@ describe 'FormHelper' do
135
142
  end
136
143
  end
137
144
 
145
+ describe ActionView::Base.field_error_proc do
146
+ it 'is set to no-op wrapper by default' do
147
+ semantic_form_for(@new_post, :url => '/hello') do |builder|
148
+ ::ActionView::Base.field_error_proc.call("html", nil).should == "html"
149
+ end
150
+ end
151
+
152
+ it 'is set to the configured custom field_error_proc' do
153
+ field_error_proc = mock()
154
+ Formtastic::Helpers::FormHelper.field_error_proc = field_error_proc
155
+ semantic_form_for(@new_post, :url => '/hello') do |builder|
156
+ ::ActionView::Base.field_error_proc.should == field_error_proc
157
+ end
158
+ end
159
+
160
+ it 'is restored to its original value after the form is rendered' do
161
+ lambda do
162
+ Formtastic::Helpers::FormHelper.field_error_proc = proc {""}
163
+ semantic_form_for(@new_post, :url => '/hello') { |builder| }
164
+ end.should_not change(::ActionView::Base, :field_error_proc)
165
+ end
166
+ end
167
+
138
168
  describe "with :builder option" do
139
169
  it "yields an instance of the given builder" do
140
170
  class MyAwesomeCustomBuilder < Formtastic::FormBuilder
@@ -907,7 +907,7 @@ describe 'Formtastic::FormBuilder#input' do
907
907
  end
908
908
 
909
909
  input = mock('input', :to_html => 'some HTML')
910
- Formtastic::Inputs::StringInput.should_not_receive(:new).and_return(input)
910
+ Formtastic::Inputs::StringInput.should_not_receive(:new)
911
911
  ::StringInput.should_receive(:new).and_return(input)
912
912
 
913
913
  concat(semantic_form_for(@new_post) do |builder|
@@ -57,6 +57,7 @@ describe 'Formtastic::FormBuilder#inputs' do
57
57
 
58
58
  before do
59
59
  @new_post.stub!(:respond_to?).and_return(true, true)
60
+ @new_post.stub!(:respond_to?).with(:empty?).and_return(false)
60
61
  @new_post.stub!(:author).and_return(@bob)
61
62
  end
62
63
 
@@ -270,7 +271,8 @@ describe 'Formtastic::FormBuilder#inputs' do
270
271
  concat(inputs)
271
272
  end)
272
273
  end
273
-
274
+
275
+ # TODO: looks like the block isn't being called for the last assertion here
274
276
  it 'should render a fieldset with a legend inside the form' do
275
277
  output_buffer.should have_tag("form fieldset legend", /^#{@legend_text}$/)
276
278
  output_buffer.should have_tag("form fieldset legend", /^#{@legend_text_using_name}$/)
@@ -296,21 +298,18 @@ describe 'Formtastic::FormBuilder#inputs' do
296
298
  }
297
299
  }
298
300
  concat(semantic_form_for(@new_post) do |builder|
299
- inputs = builder.inputs :advanced_options do
300
- end
301
- concat(inputs)
302
- inputs =builder.inputs :name => :advanced_options_using_name do
303
- end
304
- concat(inputs)
305
- inputs = builder.inputs :title => :advanced_options_using_title do
306
- end
307
- concat(inputs)
308
- inputs = builder.inputs :nested_forms_title, :for => :authors do |nf|
309
- end
310
- concat(inputs)
301
+ concat(builder.inputs(:advanced_options) do
302
+ end)
303
+ concat(builder.inputs(:name => :advanced_options_using_name) do
304
+ end)
305
+ concat(builder.inputs(:title => :advanced_options_using_title) do
306
+ end)
307
+ concat(builder.inputs(:nested_forms_title, :for => :authors) do |nf|
308
+ end)
311
309
  end)
312
310
  end
313
-
311
+
312
+ # TODO: looks like the block isn't being called for the last assertion here
314
313
  it 'should render a fieldset with a localized legend inside the form' do
315
314
  output_buffer.should have_tag("form fieldset legend", /^#{@localized_legend_text}$/)
316
315
  output_buffer.should have_tag("form fieldset legend", /^#{@localized_legend_text_using_name}$/)
@@ -344,7 +343,6 @@ describe 'Formtastic::FormBuilder#inputs' do
344
343
  before do
345
344
  ::Post.stub!(:reflections).and_return({:author => mock('reflection', :options => {}, :macro => :belongs_to),
346
345
  :comments => mock('reflection', :options => {}, :macro => :has_many) })
347
- ::Author.stub!(:find).and_return([@fred, @bob])
348
346
 
349
347
  @new_post.stub!(:title)
350
348
  @new_post.stub!(:body)
@@ -150,6 +150,13 @@ describe 'boolean input' do
150
150
  output_buffer.should have_tag('form li label input[@type="checkbox"]')
151
151
  output_buffer.should have_tag('form li label input[@name="project[allow_comments]"]')
152
152
  end
153
+
154
+ it 'should not pass input_html options down to the label html' do
155
+ concat(semantic_form_for(@new_post) do |builder|
156
+ builder.input(:title, :as => :boolean, :input_html => { :tabindex => 2 })
157
+ end)
158
+ output_buffer.should_not have_tag('label[tabindex]')
159
+ end
153
160
 
154
161
  context "when required" do
155
162
 
@@ -441,7 +441,37 @@ describe 'check_boxes input' do
441
441
 
442
442
  it "should not check any items" do
443
443
  output_buffer.should have_tag('form li input[@checked]', :count => 0)
444
- end
444
+ end
445
+
446
+ describe "and the attribute has values" do
447
+ before do
448
+ @fred.stub(:posts) { [1] }
449
+
450
+ concat(semantic_form_for(@fred) do |builder|
451
+ concat(builder.input(:posts, :as => :check_boxes, :collection => @_collection))
452
+ end)
453
+ end
454
+
455
+ it "should check the appropriate items" do
456
+ output_buffer.should have_tag("form li input[@value='1'][@checked]")
457
+ end
458
+ end
459
+
460
+ describe "and the collection includes html options" do
461
+ before do
462
+ @_collection = [["First", 1, {'data-test' => 'test-data'}], ["Second", 2, {'data-test2' => 'test-data2'}]]
463
+
464
+ concat(semantic_form_for(@fred) do |builder|
465
+ concat(builder.input(:posts, :as => :check_boxes, :collection => @_collection))
466
+ end)
467
+ end
468
+
469
+ it "should have injected the html attributes" do
470
+ @_collection.each do |v|
471
+ output_buffer.should have_tag("form li input[@value='#{v[1]}'][@#{v[2].keys[0]}='#{v[2].values[0]}']")
472
+ end
473
+ end
474
+ end
445
475
  end
446
476
 
447
477
  end
@@ -147,6 +147,14 @@ describe 'date select input' do
147
147
  end
148
148
 
149
149
  end
150
+
151
+ it "should not display labels for any fields when :labels is falsy" do
152
+ output_buffer.replace ''
153
+ concat(semantic_form_for(@new_post) do |builder|
154
+ concat(builder.input(:created_at, :as => :date_select, :labels => false))
155
+ end)
156
+ output_buffer.should have_tag('form li.date_select fieldset ol li label', :count => 0)
157
+ end
150
158
  end
151
159
 
152
160
  describe "when required" do
@@ -145,10 +145,10 @@ describe 'datetime_picker input' do
145
145
  it "can be set from :input_html options" do
146
146
  concat(
147
147
  semantic_form_for(@new_post) do |f|
148
- concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11 23:00" }))
148
+ concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11T23:00:00" }))
149
149
  end
150
150
  )
151
- output_buffer.should have_tag "input[value='1111-11-11 23:00']"
151
+ output_buffer.should have_tag "input[value='1111-11-11T23:00:00']"
152
152
  end
153
153
 
154
154
  end
@@ -160,22 +160,22 @@ describe 'datetime_picker input' do
160
160
  @new_post.stub!(:publish_at).and_return(@date)
161
161
  end
162
162
 
163
- it "renders the date as YYYY-MM-DD 00:00" do
163
+ it "renders the date as YYYY-MM-DDT00:00:00" do
164
164
  concat(
165
165
  semantic_form_for(@new_post) do |f|
166
166
  concat(f.input(:publish_at, :as => :datetime_picker ))
167
167
  end
168
168
  )
169
- output_buffer.should have_tag "input[value='#{@date.to_s} 00:00']"
169
+ output_buffer.should have_tag "input[value='2000-11-11T00:00:00']"
170
170
  end
171
171
 
172
172
  it "can be set from :input_html options" do
173
173
  concat(
174
174
  semantic_form_for(@new_post) do |f|
175
- concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11 00:00" }))
175
+ concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11T00:00:00" }))
176
176
  end
177
177
  )
178
- output_buffer.should have_tag "input[value='1111-11-11 00:00']"
178
+ output_buffer.should have_tag "input[value='1111-11-11T00:00:00']"
179
179
  end
180
180
 
181
181
  end
@@ -193,16 +193,16 @@ describe 'datetime_picker input' do
193
193
  concat(f.input(:publish_at, :as => :datetime_picker ))
194
194
  end
195
195
  )
196
- output_buffer.should have_tag "input[value='2000-11-11 11:11']"
196
+ output_buffer.should have_tag "input[value='2000-11-11T11:11:11']"
197
197
  end
198
198
 
199
199
  it "can be set from :input_html options" do
200
200
  concat(
201
201
  semantic_form_for(@new_post) do |f|
202
- concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11 11:11" }))
202
+ concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11T11:11:11" }))
203
203
  end
204
204
  )
205
- output_buffer.should have_tag "input[value='1111-11-11 11:11']"
205
+ output_buffer.should have_tag "input[value='1111-11-11T11:11:11']"
206
206
  end
207
207
 
208
208
  end
@@ -225,10 +225,10 @@ describe 'datetime_picker input' do
225
225
  it "can be set from :input_html options" do
226
226
  concat(
227
227
  semantic_form_for(@new_post) do |f|
228
- concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11 11:11" }))
228
+ concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11T11:11:11" }))
229
229
  end
230
230
  )
231
- output_buffer.should have_tag "input[value='1111-11-11 11:11']"
231
+ output_buffer.should have_tag "input[value='1111-11-11T11:11:11']"
232
232
  end
233
233
 
234
234
  end
@@ -251,10 +251,10 @@ describe 'datetime_picker input' do
251
251
  it "can be set from :input_html options" do
252
252
  concat(
253
253
  semantic_form_for(@new_post) do |f|
254
- concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11 11:11" }))
254
+ concat(f.input(:publish_at, :as => :datetime_picker, :input_html => { :value => "1111-11-11T11:11:11" }))
255
255
  end
256
256
  )
257
- output_buffer.should have_tag "input[value='1111-11-11 11:11']"
257
+ output_buffer.should have_tag "input[value='1111-11-11T11:11:11']"
258
258
  end
259
259
 
260
260
  end
@@ -487,4 +487,4 @@ describe 'datetime_picker input' do
487
487
  end
488
488
  end
489
489
 
490
- end
490
+ end
@@ -156,6 +156,14 @@ describe 'datetime select input' do
156
156
  output_buffer.should_not include("&gt;")
157
157
  end
158
158
  end
159
+
160
+ it "should not display labels for any fields when :labels is falsy" do
161
+ output_buffer.replace ''
162
+ concat(semantic_form_for(@new_post) do |builder|
163
+ concat(builder.input(:created_at, :as => :datetime_select, :labels => false))
164
+ end)
165
+ output_buffer.should have_tag('form li.datetime_select fieldset ol li label', :count => 0)
166
+ end
159
167
  end
160
168
 
161
169
  describe "when required" do