formtastic-bootstrap 1.0.0

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 (72) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +123 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +159 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/formtastic-bootstrap.gemspec +128 -0
  10. data/lib/action_view/helpers/text_field_date_helper.rb +166 -0
  11. data/lib/formtastic-bootstrap.rb +5 -0
  12. data/lib/formtastic-bootstrap/form_builder.rb +38 -0
  13. data/lib/formtastic-bootstrap/helpers.rb +19 -0
  14. data/lib/formtastic-bootstrap/helpers/buttons_helper.rb +47 -0
  15. data/lib/formtastic-bootstrap/helpers/fieldset_wrapper.rb +37 -0
  16. data/lib/formtastic-bootstrap/helpers/input_helper.rb +12 -0
  17. data/lib/formtastic-bootstrap/helpers/inputs_helper.rb +36 -0
  18. data/lib/formtastic-bootstrap/inputs.rb +28 -0
  19. data/lib/formtastic-bootstrap/inputs/base.rb +22 -0
  20. data/lib/formtastic-bootstrap/inputs/base/choices.rb +49 -0
  21. data/lib/formtastic-bootstrap/inputs/base/errors.rb +48 -0
  22. data/lib/formtastic-bootstrap/inputs/base/hints.rb +27 -0
  23. data/lib/formtastic-bootstrap/inputs/base/html.rb +21 -0
  24. data/lib/formtastic-bootstrap/inputs/base/labelling.rb +18 -0
  25. data/lib/formtastic-bootstrap/inputs/base/stringish.rb +18 -0
  26. data/lib/formtastic-bootstrap/inputs/base/timeish.rb +35 -0
  27. data/lib/formtastic-bootstrap/inputs/base/wrapping.rb +56 -0
  28. data/lib/formtastic-bootstrap/inputs/boolean_input.rb +33 -0
  29. data/lib/formtastic-bootstrap/inputs/check_boxes_input.rb +35 -0
  30. data/lib/formtastic-bootstrap/inputs/date_input.rb +16 -0
  31. data/lib/formtastic-bootstrap/inputs/datetime_input.rb +19 -0
  32. data/lib/formtastic-bootstrap/inputs/email_input.rb +15 -0
  33. data/lib/formtastic-bootstrap/inputs/file_input.rb +14 -0
  34. data/lib/formtastic-bootstrap/inputs/hidden_input.rb +12 -0
  35. data/lib/formtastic-bootstrap/inputs/number_input.rb +15 -0
  36. data/lib/formtastic-bootstrap/inputs/password_input.rb +15 -0
  37. data/lib/formtastic-bootstrap/inputs/phone_input.rb +15 -0
  38. data/lib/formtastic-bootstrap/inputs/radio_input.rb +32 -0
  39. data/lib/formtastic-bootstrap/inputs/range_input.rb +15 -0
  40. data/lib/formtastic-bootstrap/inputs/search_input.rb +15 -0
  41. data/lib/formtastic-bootstrap/inputs/select_input.rb +14 -0
  42. data/lib/formtastic-bootstrap/inputs/string_input.rb +15 -0
  43. data/lib/formtastic-bootstrap/inputs/text_input.rb +14 -0
  44. data/lib/formtastic-bootstrap/inputs/time_input.rb +16 -0
  45. data/lib/formtastic-bootstrap/inputs/url_input.rb +14 -0
  46. data/spec/builder/errors_spec.rb +214 -0
  47. data/spec/builder/semantic_fields_for_spec.rb +130 -0
  48. data/spec/helpers/input_helper_spec.rb +956 -0
  49. data/spec/helpers/inputs_helper_spec.rb +577 -0
  50. data/spec/inputs/boolean_input_spec.rb +193 -0
  51. data/spec/inputs/check_boxes_input_spec.rb +439 -0
  52. data/spec/inputs/date_input_spec.rb +147 -0
  53. data/spec/inputs/datetime_input_spec.rb +101 -0
  54. data/spec/inputs/email_input_spec.rb +59 -0
  55. data/spec/inputs/file_input_spec.rb +63 -0
  56. data/spec/inputs/hidden_input_spec.rb +122 -0
  57. data/spec/inputs/number_input_spec.rb +787 -0
  58. data/spec/inputs/password_input_spec.rb +73 -0
  59. data/spec/inputs/phone_input_spec.rb +59 -0
  60. data/spec/inputs/radio_input_spec.rb +240 -0
  61. data/spec/inputs/range_input_spec.rb +479 -0
  62. data/spec/inputs/search_input_spec.rb +59 -0
  63. data/spec/inputs/select_input_spec.rb +567 -0
  64. data/spec/inputs/string_input_spec.rb +182 -0
  65. data/spec/inputs/text_input_spec.rb +163 -0
  66. data/spec/inputs/time_input_spec.rb +206 -0
  67. data/spec/inputs/url_input_spec.rb +59 -0
  68. data/spec/spec_helper.rb +24 -0
  69. data/spec/support/custom_macros.rb +704 -0
  70. data/spec/support/depracation.rb +6 -0
  71. data/spec/support/formtastic_spec_helper.rb +382 -0
  72. metadata +204 -0
@@ -0,0 +1,15 @@
1
+ module FormtasticBootstrap
2
+ module Inputs
3
+ class RangeInput < Formtastic::Inputs::RangeInput
4
+ include Base
5
+ include Base::Stringish
6
+
7
+ def to_html
8
+ generic_input_wrapping do
9
+ builder.range_field(method, input_html_options)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module FormtasticBootstrap
2
+ module Inputs
3
+ class SearchInput < Formtastic::Inputs::SearchInput
4
+ include Base
5
+ include Base::Stringish
6
+
7
+ def to_html
8
+ generic_input_wrapping do
9
+ builder.search_field(method, input_html_options)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module FormtasticBootstrap
2
+ module Inputs
3
+ class SelectInput < Formtastic::Inputs::SelectInput
4
+ include Base
5
+
6
+ def to_html
7
+ generic_input_wrapping do
8
+ options[:group_by] ? grouped_select_html : select_html
9
+ end
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module FormtasticBootstrap
2
+ module Inputs
3
+ class StringInput < Formtastic::Inputs::StringInput
4
+ include Base
5
+ include Base::Stringish
6
+
7
+ def to_html
8
+ generic_input_wrapping do
9
+ builder.text_field(method, input_html_options)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module FormtasticBootstrap
2
+ module Inputs
3
+ class TextInput < Formtastic::Inputs::TextInput
4
+ include Base
5
+
6
+ def to_html
7
+ generic_input_wrapping do
8
+ builder.text_area(method, input_html_options)
9
+ end
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module FormtasticBootstrap
2
+ module Inputs
3
+ class TimeInput < Formtastic::Inputs::TimeInput
4
+ include Base
5
+ include Base::Stringish
6
+ include Base::Timeish
7
+
8
+ def to_html
9
+ generic_input_wrapping do
10
+ time_input_html
11
+ end
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ module FormtasticBootstrap
2
+ module Inputs
3
+ class UrlInput < Formtastic::Inputs::UrlInput
4
+ include Base
5
+ include Base::Stringish
6
+
7
+ def to_html
8
+ generic_input_wrapping do
9
+ builder.url_field(method, input_html_options)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,214 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'Formtastic::FormBuilder#errors_on' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before(:each) do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ Formtastic::Helpers::FormHelper.builder = FormtasticBootstrap::FormBuilder
12
+ @title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
13
+ @errors = mock('errors')
14
+ @new_post.stub!(:errors).and_return(@errors)
15
+
16
+ @orig_inline_errors = FormtasticBootstrap::FormBuilder.inline_errors
17
+ @orig_inline_error_class = FormtasticBootstrap::FormBuilder.default_inline_error_class
18
+ @orig_error_list_class = FormtasticBootstrap::FormBuilder.default_error_list_class
19
+ end
20
+
21
+ after(:each) do
22
+ FormtasticBootstrap::FormBuilder.inline_errors = @orig_inline_errors
23
+ FormtasticBootstrap::FormBuilder.default_inline_error_class = @orig_inline_error_class
24
+ FormtasticBootstrap::FormBuilder.default_error_list_class = @orig_error_list_class
25
+ end
26
+
27
+ describe 'when there are errors' do
28
+ before do
29
+ @errors.stub!(:[]).with(:title).and_return(@title_errors)
30
+ end
31
+
32
+ # after do
33
+ # FormtasticBootstrap::FormBuilder.default_inline_error_class = 'inline-errors'
34
+ # FormtasticBootstrap::FormBuilder.default_inline_error_class = 'help-inline'
35
+ # FormtasticBootstrap::FormBuilder.default_error_list_class = 'errors'
36
+ # end
37
+
38
+ it 'should render a paragraph with the errors joined into a sentence when inline_errors config is :sentence' do
39
+ FormtasticBootstrap::FormBuilder.inline_errors = :sentence
40
+ concat(semantic_form_for(@new_post) do |builder|
41
+ builder.input :title
42
+ end)
43
+ output_buffer.should have_tag('span.help-inline', @title_errors.to_sentence)
44
+ end
45
+
46
+ it 'should render a paragraph with a overridden default class' do
47
+ FormtasticBootstrap::FormBuilder.default_inline_error_class = 'better-errors'
48
+ concat(semantic_form_for(@new_post) do |builder|
49
+ builder.input(:title)
50
+ end)
51
+ output_buffer.should have_tag('span.better-errors', @title_errors.to_sentence)
52
+ end
53
+
54
+ it 'should render a paragraph with the errors joined into a sentence when inline_errors config is :sentence with a customized error class' do
55
+ FormtasticBootstrap::FormBuilder.inline_errors = :sentence
56
+ concat(semantic_form_for(@new_post) do |builder|
57
+ builder.input(:title, :error_class => 'better-errors')
58
+ end)
59
+ output_buffer.should have_tag('span.better-errors', @title_errors.to_sentence)
60
+ end
61
+
62
+ it 'should render an unordered list with the class errors when inline_errors config is :list' do
63
+ FormtasticBootstrap::FormBuilder.inline_errors = :list
64
+ concat(semantic_form_for(@new_post) do |builder|
65
+ builder.input(:title)
66
+ end)
67
+ output_buffer.should have_tag('ul.errors')
68
+ @title_errors.each do |error|
69
+ output_buffer.should have_tag('ul.errors li', error)
70
+ end
71
+ end
72
+
73
+ it 'should render an unordered list with the class overridden default class' do
74
+ FormtasticBootstrap::FormBuilder.inline_errors = :list
75
+ FormtasticBootstrap::FormBuilder.default_error_list_class = "better-errors"
76
+ concat(semantic_form_for(@new_post) do |builder|
77
+ builder.input :title
78
+ end)
79
+ output_buffer.should have_tag('ul.better-errors')
80
+ @title_errors.each do |error|
81
+ output_buffer.should have_tag('ul.better-errors li', error)
82
+ end
83
+ end
84
+
85
+ it 'should render an unordered list with the class errors when inline_errors config is :list with a customized error class' do
86
+ FormtasticBootstrap::FormBuilder.inline_errors = :list
87
+ concat(semantic_form_for(@new_post) do |builder|
88
+ builder.input :title, :error_class => "better-errors"
89
+ end)
90
+ output_buffer.should have_tag('ul.better-errors')
91
+ @title_errors.each do |error|
92
+ output_buffer.should have_tag('ul.better-errors li', error)
93
+ end
94
+ end
95
+
96
+ it 'should render a paragraph with the first error when inline_errors config is :first' do
97
+ FormtasticBootstrap::FormBuilder.inline_errors = :first
98
+ concat(semantic_form_for(@new_post) do |builder|
99
+ builder.input :title
100
+ end)
101
+ output_buffer.should have_tag('span.help-inline', @title_errors.first)
102
+ end
103
+
104
+ it 'should render a paragraph with the first error when inline_errors config is :first with a customized error class' do
105
+ FormtasticBootstrap::FormBuilder.inline_errors = :first
106
+ concat(semantic_form_for(@new_post) do |builder|
107
+ builder.input :title, :error_class => "better-errors"
108
+ end)
109
+ output_buffer.should have_tag('span.better-errors', @title_errors.first)
110
+ end
111
+
112
+ it 'should return nil when inline_errors config is :none' do
113
+ FormtasticBootstrap::FormBuilder.inline_errors = :none
114
+ concat(semantic_form_for(@new_post) do |builder|
115
+ builder.input :title
116
+ end)
117
+ output_buffer.should_not have_tag('span.help-inline')
118
+ output_buffer.should_not have_tag('span.help-block')
119
+ output_buffer.should_not have_tag('ul.errors')
120
+ end
121
+
122
+ it 'should allow calling deprecated errors_on and inline_errors_for helpers' do
123
+ FormtasticBootstrap::FormBuilder.inline_errors = :sentence
124
+ with_deprecation_silenced do
125
+ concat(semantic_form_for(@new_post) do |builder|
126
+ builder.errors_on :title
127
+ builder.inline_errors_for :title
128
+ end)
129
+ end
130
+ end
131
+ end
132
+
133
+ describe 'when there are no errors (nil)' do
134
+ before do
135
+ @errors.stub!(:[]).with(:title).and_return(nil)
136
+ end
137
+
138
+ it 'should return nil when inline_errors config is :sentence, :list or :none' do
139
+ with_deprecation_silenced do
140
+ [:sentence, :list, :none].each do |config|
141
+ with_config :inline_errors, config do
142
+ semantic_form_for(@new_post) do |builder|
143
+ builder.errors_on(:title).should be_nil
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ describe 'when there are no errors (empty array)' do
152
+
153
+ before(:each) do
154
+ @errors.stub!(:[]).with(:title).and_return([])
155
+ end
156
+
157
+ it 'should return nil when inline_errors config is :sentence, :list or :none' do
158
+ with_deprecation_silenced do
159
+ [:sentence, :list, :none].each do |config|
160
+ FormtasticBootstrap::FormBuilder.inline_errors = config
161
+ semantic_form_for(@new_post) do |builder|
162
+ builder.errors_on(:title).should be_nil
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ describe 'when file type columns have errors' do
170
+ it "should list errors added on metadata fields" do
171
+ @errors.stub!(:[]).with(:document_file_name).and_return(['must be provided'])
172
+ @errors.stub!(:[]).with(:document_file_size).and_return(['must be less than 4mb'])
173
+ @errors.stub!(:[]).with(:document_content_type).and_return(['must be an image'])
174
+ @errors.stub!(:[]).with(:document).and_return(nil)
175
+
176
+ with_config :inline_errors, :sentence do
177
+ concat(semantic_form_for(@new_post) do |builder|
178
+ concat(builder.input(:document))
179
+ end)
180
+ end
181
+ output_buffer.should have_tag("div.error")
182
+ output_buffer.should have_tag('span.help-inline', (['must be an image','must be provided', 'must be less than 4mb']).to_sentence)
183
+ end
184
+ end
185
+
186
+ describe 'when there are errors on the association and column' do
187
+
188
+ it "should list all unique errors" do
189
+ ::Post.stub!(:reflections).and_return({:author => mock('reflection', :options => {}, :macro => :belongs_to)})
190
+
191
+ @errors.stub!(:[]).with(:author).and_return(['must not be blank'])
192
+ @errors.stub!(:[]).with(:author_id).and_return(['is already taken', 'must not be blank']) # note the duplicate of association
193
+
194
+ with_config :inline_errors, :list do
195
+ concat(semantic_form_for(@new_post) do |builder|
196
+ concat(builder.input(:author))
197
+ end)
198
+ end
199
+ output_buffer.should have_tag("ul.errors li", /must not be blank/, :count => 1)
200
+ output_buffer.should have_tag("ul.errors li", /is already taken/, :count => 1)
201
+ end
202
+
203
+ end
204
+
205
+ describe "when there are errors on a has_many association" do
206
+ it "should include the association ids error messages" do
207
+ semantic_form_for(@new_post) do |builder|
208
+ builder.send(:error_keys, :sub_posts, {}).should include(:sub_post_ids)
209
+ end
210
+ end
211
+ end
212
+
213
+ end
214
+
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'FormtasticBootstrap::FormBuilder#fields_for' do
4
+
5
+ include FormtasticSpecHelper
6
+
7
+ before do
8
+ @output_buffer = ''
9
+ mock_everything
10
+ @new_post.stub!(:author).and_return(::Author.new)
11
+
12
+ Formtastic::Helpers::FormHelper.builder = FormtasticBootstrap::FormBuilder
13
+ end
14
+
15
+ context 'outside a form_for block' do
16
+ it 'yields an instance of FormHelper.builder' do
17
+ semantic_fields_for(@new_post) do |nested_builder|
18
+ nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
19
+ end
20
+ semantic_fields_for(@new_post.author) do |nested_builder|
21
+ nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
22
+ end
23
+ semantic_fields_for(:author, @new_post.author) do |nested_builder|
24
+ nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
25
+ end
26
+ semantic_fields_for(:author, @hash_backed_author) do |nested_builder|
27
+ nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
28
+ end
29
+ end
30
+
31
+ it 'should respond to input' do
32
+ semantic_fields_for(@new_post) do |nested_builder|
33
+ nested_builder.respond_to?(:input).should be_true
34
+ end
35
+ semantic_fields_for(@new_post.author) do |nested_builder|
36
+ nested_builder.respond_to?(:input).should be_true
37
+ end
38
+ semantic_fields_for(:author, @new_post.author) do |nested_builder|
39
+ nested_builder.respond_to?(:input).should be_true
40
+ end
41
+ semantic_fields_for(:author, @hash_backed_author) do |nested_builder|
42
+ nested_builder.respond_to?(:input).should be_true
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'within a form_for block' do
48
+ it 'yields an instance of FormHelper.builder' do
49
+ semantic_form_for(@new_post) do |builder|
50
+ builder.semantic_fields_for(:author) do |nested_builder|
51
+ nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
52
+ end
53
+ end
54
+ end
55
+
56
+ it 'yields an instance of FormHelper.builder with hash-like model' do
57
+ semantic_form_for(:user) do |builder|
58
+ builder.semantic_fields_for(:author, @hash_backed_author) do |nested_builder|
59
+ nested_builder.class.should == Formtastic::Helpers::FormHelper.builder
60
+ end
61
+ end
62
+ end
63
+
64
+ it 'nests the object name' do
65
+ semantic_form_for(@new_post) do |builder|
66
+ builder.semantic_fields_for(@bob) do |nested_builder|
67
+ nested_builder.object_name.should == 'post[author]'
68
+ end
69
+ end
70
+ end
71
+
72
+ it 'supports passing collection as second parameter' do
73
+ semantic_form_for(@new_post) do |builder|
74
+ builder.semantic_fields_for(:author, [@fred,@bob]) do |nested_builder|
75
+ nested_builder.object_name.should =~ /post\[author_attributes\]\[\d+\]/
76
+ end
77
+ end
78
+ end
79
+
80
+ it 'should sanitize html id for li tag' do
81
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
82
+ concat(semantic_form_for(@new_post) do |builder|
83
+ concat(builder.semantic_fields_for(@bob, :index => 1) do |nested_builder|
84
+ concat(nested_builder.inputs(:login))
85
+ end)
86
+ end)
87
+ output_buffer.should have_tag('form fieldset.inputs #post_author_1_login_input')
88
+ # Not valid selector, so using good ol' regex
89
+ output_buffer.should_not =~ /id="post\[author\]_1_login_input"/
90
+ # <=> output_buffer.should_not have_tag('form fieldset.inputs #post[author]_1_login_input')
91
+ end
92
+
93
+ it 'should use namespace provided in nested fields' do
94
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
95
+ concat(semantic_form_for(@new_post, :namespace => 'context2') do |builder|
96
+ concat(builder.semantic_fields_for(@bob, :index => 1) do |nested_builder|
97
+ concat(nested_builder.inputs(:login))
98
+ end)
99
+ end)
100
+ output_buffer.should have_tag('form fieldset.inputs #context2_post_author_1_login_input')
101
+ end
102
+ end
103
+
104
+ context "when I rendered my own hidden id input" do
105
+
106
+ before do
107
+ output_buffer.replace ''
108
+
109
+ @fred.posts.size.should == 1
110
+ @fred.posts.first.stub!(:persisted?).and_return(true)
111
+ @fred.stub!(:posts_attributes=)
112
+
113
+ concat(semantic_form_for(@fred) do |builder|
114
+ concat(builder.semantic_fields_for(:posts) do |nested_builder|
115
+ concat(nested_builder.input(:id, :as => :hidden))
116
+ concat(nested_builder.input(:title))
117
+ end)
118
+ end)
119
+ end
120
+
121
+ it "should only render one hidden input (my one)" do
122
+ output_buffer.should have_tag 'input#author_posts_attributes_0_id', :count => 1
123
+ end
124
+
125
+ it "should render the hidden input inside an div.hidden" do
126
+ output_buffer.should have_tag 'div.hidden div.input input#author_posts_attributes_0_id'
127
+ end
128
+ end
129
+
130
+ end