formtastic 2.2.1 → 2.3.0.rc

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