formtastic-bootstrap 1.0.0

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