formtastic 1.2.4 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (189) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +46 -0
  5. data/.yardopts +1 -0
  6. data/Appraisals +43 -0
  7. data/CHANGELOG +54 -0
  8. data/DEPRECATIONS +52 -0
  9. data/Gemfile +3 -0
  10. data/README.md +629 -0
  11. data/RELEASE_PROCESS +6 -0
  12. data/Rakefile +35 -0
  13. data/app/assets/stylesheets/formtastic.css +289 -0
  14. data/app/assets/stylesheets/formtastic_ie6.css +33 -0
  15. data/app/assets/stylesheets/formtastic_ie7.css +23 -0
  16. data/formtastic.gemspec +42 -0
  17. data/gemfiles/rails_3.2.gemfile +9 -0
  18. data/gemfiles/rails_4.0.4.gemfile +8 -0
  19. data/gemfiles/rails_4.1.gemfile +8 -0
  20. data/gemfiles/rails_4.2.gemfile +8 -0
  21. data/gemfiles/rails_4.gemfile +8 -0
  22. data/gemfiles/rails_5.0.gemfile +8 -0
  23. data/gemfiles/rails_edge.gemfile +15 -0
  24. data/lib/formtastic.rb +40 -1945
  25. data/lib/formtastic/action_class_finder.rb +18 -0
  26. data/lib/formtastic/actions.rb +11 -0
  27. data/lib/formtastic/actions/base.rb +156 -0
  28. data/lib/formtastic/actions/button_action.rb +67 -0
  29. data/lib/formtastic/actions/buttonish.rb +17 -0
  30. data/lib/formtastic/actions/input_action.rb +70 -0
  31. data/lib/formtastic/actions/link_action.rb +88 -0
  32. data/lib/formtastic/deprecation.rb +42 -0
  33. data/lib/formtastic/engine.rb +11 -0
  34. data/lib/formtastic/form_builder.rb +124 -0
  35. data/lib/formtastic/helpers.rb +16 -0
  36. data/lib/formtastic/helpers/action_helper.rb +162 -0
  37. data/lib/formtastic/helpers/actions_helper.rb +168 -0
  38. data/lib/formtastic/helpers/enum.rb +13 -0
  39. data/lib/formtastic/helpers/errors_helper.rb +81 -0
  40. data/lib/formtastic/helpers/fieldset_wrapper.rb +80 -0
  41. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  42. data/lib/formtastic/helpers/form_helper.rb +203 -0
  43. data/lib/formtastic/helpers/input_helper.rb +407 -0
  44. data/lib/formtastic/helpers/inputs_helper.rb +411 -0
  45. data/lib/formtastic/helpers/reflection.rb +37 -0
  46. data/lib/formtastic/html_attributes.rb +32 -0
  47. data/lib/formtastic/i18n.rb +4 -2
  48. data/lib/formtastic/input_class_finder.rb +18 -0
  49. data/lib/formtastic/inputs.rb +39 -0
  50. data/lib/formtastic/inputs/base.rb +76 -0
  51. data/lib/formtastic/inputs/base/associations.rb +31 -0
  52. data/lib/formtastic/inputs/base/choices.rb +108 -0
  53. data/lib/formtastic/inputs/base/collections.rb +159 -0
  54. data/lib/formtastic/inputs/base/database.rb +22 -0
  55. data/lib/formtastic/inputs/base/datetime_pickerish.rb +85 -0
  56. data/lib/formtastic/inputs/base/errors.rb +58 -0
  57. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  58. data/lib/formtastic/inputs/base/hints.rb +31 -0
  59. data/lib/formtastic/inputs/base/html.rb +53 -0
  60. data/lib/formtastic/inputs/base/labelling.rb +52 -0
  61. data/lib/formtastic/inputs/base/naming.rb +42 -0
  62. data/lib/formtastic/inputs/base/numeric.rb +50 -0
  63. data/lib/formtastic/inputs/base/options.rb +17 -0
  64. data/lib/formtastic/inputs/base/placeholder.rb +17 -0
  65. data/lib/formtastic/inputs/base/stringish.rb +38 -0
  66. data/lib/formtastic/inputs/base/timeish.rb +241 -0
  67. data/lib/formtastic/inputs/base/validations.rb +215 -0
  68. data/lib/formtastic/inputs/base/wrapping.rb +50 -0
  69. data/lib/formtastic/inputs/boolean_input.rb +118 -0
  70. data/lib/formtastic/inputs/check_boxes_input.rb +197 -0
  71. data/lib/formtastic/inputs/color_input.rb +42 -0
  72. data/lib/formtastic/inputs/country_input.rb +86 -0
  73. data/lib/formtastic/inputs/datalist_input.rb +41 -0
  74. data/lib/formtastic/inputs/date_picker_input.rb +93 -0
  75. data/lib/formtastic/inputs/date_select_input.rb +34 -0
  76. data/lib/formtastic/inputs/datetime_picker_input.rb +103 -0
  77. data/lib/formtastic/inputs/datetime_select_input.rb +12 -0
  78. data/lib/formtastic/inputs/email_input.rb +41 -0
  79. data/lib/formtastic/inputs/file_input.rb +42 -0
  80. data/lib/formtastic/inputs/hidden_input.rb +62 -0
  81. data/lib/formtastic/inputs/number_input.rb +88 -0
  82. data/lib/formtastic/inputs/password_input.rb +41 -0
  83. data/lib/formtastic/inputs/phone_input.rb +42 -0
  84. data/lib/formtastic/inputs/radio_input.rb +163 -0
  85. data/lib/formtastic/inputs/range_input.rb +95 -0
  86. data/lib/formtastic/inputs/search_input.rb +41 -0
  87. data/lib/formtastic/inputs/select_input.rb +235 -0
  88. data/lib/formtastic/inputs/string_input.rb +36 -0
  89. data/lib/formtastic/inputs/text_input.rb +48 -0
  90. data/lib/formtastic/inputs/time_picker_input.rb +99 -0
  91. data/lib/formtastic/inputs/time_select_input.rb +38 -0
  92. data/lib/formtastic/inputs/time_zone_input.rb +58 -0
  93. data/lib/formtastic/inputs/url_input.rb +41 -0
  94. data/lib/formtastic/localized_string.rb +17 -0
  95. data/lib/formtastic/localizer.rb +152 -0
  96. data/lib/formtastic/namespaced_class_finder.rb +99 -0
  97. data/lib/formtastic/util.rb +35 -16
  98. data/lib/formtastic/version.rb +3 -0
  99. data/lib/generators/formtastic/form/form_generator.rb +64 -37
  100. data/lib/generators/formtastic/input/input_generator.rb +46 -0
  101. data/lib/generators/formtastic/install/install_generator.rb +13 -5
  102. data/lib/generators/templates/_form.html.erb +10 -4
  103. data/lib/generators/templates/_form.html.haml +8 -4
  104. data/lib/generators/templates/_form.html.slim +8 -0
  105. data/lib/generators/templates/formtastic.rb +77 -44
  106. data/lib/generators/templates/input.rb +19 -0
  107. data/lib/locale/en.yml +3 -0
  108. data/sample/basic_inputs.html +224 -0
  109. data/sample/config.ru +69 -0
  110. data/sample/index.html +14 -0
  111. data/spec/action_class_finder_spec.rb +12 -0
  112. data/spec/actions/button_action_spec.rb +63 -0
  113. data/spec/actions/generic_action_spec.rb +521 -0
  114. data/spec/actions/input_action_spec.rb +59 -0
  115. data/spec/actions/link_action_spec.rb +92 -0
  116. data/spec/builder/custom_builder_spec.rb +116 -0
  117. data/spec/builder/error_proc_spec.rb +27 -0
  118. data/spec/builder/semantic_fields_for_spec.rb +142 -0
  119. data/spec/fast_spec_helper.rb +12 -0
  120. data/spec/generators/formtastic/form/form_generator_spec.rb +131 -0
  121. data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
  122. data/spec/generators/formtastic/install/install_generator_spec.rb +47 -0
  123. data/spec/helpers/action_helper_spec.rb +19 -0
  124. data/spec/helpers/actions_helper_spec.rb +143 -0
  125. data/spec/helpers/form_helper_spec.rb +218 -0
  126. data/spec/helpers/input_helper_spec.rb +6 -0
  127. data/spec/helpers/inputs_helper_spec.rb +655 -0
  128. data/spec/helpers/namespaced_action_helper_spec.rb +43 -0
  129. data/spec/helpers/namespaced_input_helper_spec.rb +36 -0
  130. data/spec/helpers/reflection_helper_spec.rb +32 -0
  131. data/spec/helpers/semantic_errors_helper_spec.rb +112 -0
  132. data/spec/i18n_spec.rb +210 -0
  133. data/spec/input_class_finder_spec.rb +10 -0
  134. data/spec/inputs/base/collections_spec.rb +76 -0
  135. data/spec/inputs/base/validations_spec.rb +342 -0
  136. data/spec/inputs/boolean_input_spec.rb +254 -0
  137. data/spec/inputs/check_boxes_input_spec.rb +546 -0
  138. data/spec/inputs/color_input_spec.rb +97 -0
  139. data/spec/inputs/country_input_spec.rb +133 -0
  140. data/spec/inputs/custom_input_spec.rb +55 -0
  141. data/spec/inputs/datalist_input_spec.rb +61 -0
  142. data/spec/inputs/date_picker_input_spec.rb +449 -0
  143. data/spec/inputs/date_select_input_spec.rb +235 -0
  144. data/spec/inputs/datetime_picker_input_spec.rb +490 -0
  145. data/spec/inputs/datetime_select_input_spec.rb +193 -0
  146. data/spec/inputs/email_input_spec.rb +85 -0
  147. data/spec/inputs/file_input_spec.rb +89 -0
  148. data/spec/inputs/hidden_input_spec.rb +135 -0
  149. data/spec/inputs/include_blank_spec.rb +78 -0
  150. data/spec/inputs/label_spec.rb +149 -0
  151. data/spec/inputs/number_input_spec.rb +815 -0
  152. data/spec/inputs/password_input_spec.rb +99 -0
  153. data/spec/inputs/phone_input_spec.rb +85 -0
  154. data/spec/inputs/placeholder_spec.rb +71 -0
  155. data/spec/inputs/radio_input_spec.rb +328 -0
  156. data/spec/inputs/range_input_spec.rb +505 -0
  157. data/spec/inputs/readonly_spec.rb +50 -0
  158. data/spec/inputs/search_input_spec.rb +84 -0
  159. data/spec/inputs/select_input_spec.rb +615 -0
  160. data/spec/inputs/string_input_spec.rb +260 -0
  161. data/spec/inputs/text_input_spec.rb +187 -0
  162. data/spec/inputs/time_picker_input_spec.rb +455 -0
  163. data/spec/inputs/time_select_input_spec.rb +248 -0
  164. data/spec/inputs/time_zone_input_spec.rb +143 -0
  165. data/spec/inputs/url_input_spec.rb +85 -0
  166. data/spec/inputs/with_options_spec.rb +43 -0
  167. data/spec/localizer_spec.rb +130 -0
  168. data/spec/namespaced_class_finder_spec.rb +79 -0
  169. data/spec/spec.opts +2 -0
  170. data/spec/spec_helper.rb +525 -0
  171. data/spec/support/custom_macros.rb +564 -0
  172. data/spec/support/deprecation.rb +6 -0
  173. data/spec/support/shared_examples.rb +1313 -0
  174. data/spec/support/specialized_class_finder_shared_example.rb +27 -0
  175. data/spec/support/test_environment.rb +31 -0
  176. data/spec/util_spec.rb +66 -0
  177. metadata +434 -161
  178. data/README.textile +0 -682
  179. data/generators/form/USAGE +0 -16
  180. data/generators/form/form_generator.rb +0 -111
  181. data/generators/formtastic/formtastic_generator.rb +0 -26
  182. data/init.rb +0 -5
  183. data/lib/formtastic/layout_helper.rb +0 -12
  184. data/lib/formtastic/railtie.rb +0 -14
  185. data/lib/generators/templates/formtastic.css +0 -145
  186. data/lib/generators/templates/formtastic_changes.css +0 -14
  187. data/lib/generators/templates/rails2/_form.html.erb +0 -5
  188. data/lib/generators/templates/rails2/_form.html.haml +0 -4
  189. data/rails/init.rb +0 -2
@@ -0,0 +1,218 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'FormHelper' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe '#semantic_form_for' do
14
+
15
+ it 'yields an instance of Formtastic::FormBuilder' do
16
+ semantic_form_for(@new_post, :url => '/hello') do |builder|
17
+ builder.class.should == Formtastic::FormBuilder
18
+ end
19
+ end
20
+
21
+ it 'adds a class of "formtastic" to the generated form' do
22
+ concat(semantic_form_for(@new_post, :url => '/hello') do |builder|
23
+ end)
24
+ output_buffer.should have_tag("form.formtastic")
25
+ end
26
+
27
+ it 'does not add "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 'adds "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 configured to validate' 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 configured to not validate' 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
58
+
59
+ it 'adds a class of "xyz" to the generated form' do
60
+ Formtastic::Helpers::FormHelper.default_form_class = 'xyz'
61
+ concat(semantic_form_for(::Post.new, :as => :post, :url => '/hello') do |builder|
62
+ end)
63
+ output_buffer.should have_tag("form.xyz")
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
72
+
73
+ it 'adds class matching the object name to the generated form when a symbol is provided' do
74
+ concat(semantic_form_for(@new_post, :url => '/hello') do |builder|
75
+ end)
76
+ output_buffer.should have_tag("form.post")
77
+
78
+ concat(semantic_form_for(:project, :url => '/hello') do |builder|
79
+ end)
80
+ output_buffer.should have_tag("form.project")
81
+ end
82
+
83
+ it 'adds class matching the :as option when provided' do
84
+ concat(semantic_form_for(@new_post, :as => :message, :url => '/hello') do |builder|
85
+ end)
86
+ output_buffer.should have_tag("form.message")
87
+
88
+ concat(semantic_form_for([:admins, @new_post], :as => :message, :url => '/hello') do |builder|
89
+ end)
90
+ output_buffer.should have_tag("form.message")
91
+ end
92
+
93
+ it 'adds class matching the object\'s class to the generated form when an object is provided' do
94
+ concat(semantic_form_for(@new_post) do |builder|
95
+ end)
96
+ output_buffer.should have_tag("form.post")
97
+ end
98
+
99
+ it 'adds a namespaced class to the generated form' do
100
+ concat(semantic_form_for(::Namespaced::Post.new, :url => '/hello') do |builder|
101
+ end)
102
+ output_buffer.should have_tag("form.namespaced_post")
103
+ end
104
+
105
+ it 'adds a customized class to the generated form' do
106
+ Formtastic::Helpers::FormHelper.default_form_model_class_proc = lambda { |model_class_name| "#{model_class_name}_form" }
107
+ concat(semantic_form_for(@new_post, :url => '/hello') do |builder|
108
+ end)
109
+ output_buffer.should have_tag("form.post_form")
110
+
111
+ concat(semantic_form_for(:project, :url => '/hello') do |builder|
112
+ end)
113
+ output_buffer.should have_tag("form.project_form")
114
+ end
115
+
116
+ describe 'allows :html options' do
117
+ before(:each) do
118
+ concat(semantic_form_for(@new_post, :url => '/hello', :html => { :id => "something-special", :class => "something-extra", :multipart => true }) do |builder|
119
+ end)
120
+ end
121
+
122
+ it 'to add a id of "something-special" to generated form' do
123
+ output_buffer.should have_tag("form#something-special")
124
+ end
125
+
126
+ it 'to add a class of "something-extra" to generated form' do
127
+ output_buffer.should have_tag("form.something-extra")
128
+ end
129
+
130
+ it 'to add enctype="multipart/form-data"' do
131
+ output_buffer.should have_tag('form[@enctype="multipart/form-data"]')
132
+ end
133
+ end
134
+
135
+ it 'can be called with a resource-oriented style' do
136
+ semantic_form_for(@new_post) do |builder|
137
+ builder.object.class.should == ::Post
138
+ builder.object_name.should == "post"
139
+ end
140
+ end
141
+
142
+ it 'can be called with a generic style and instance variable' do
143
+ semantic_form_for(@new_post, :as => :post, :url => new_post_path) do |builder|
144
+ builder.object.class.should == ::Post
145
+ builder.object_name.to_s.should == "post" # TODO: is this forced .to_s a bad assumption somewhere?
146
+ end
147
+ end
148
+
149
+ it 'can be called with a generic style and inline object' do
150
+ semantic_form_for(@new_post, :url => new_post_path) do |builder|
151
+ builder.object.class.should == ::Post
152
+ builder.object_name.to_s.should == "post" # TODO: is this forced .to_s a bad assumption somewhere?
153
+ end
154
+ end
155
+
156
+ describe 'ActionView::Base.field_error_proc' do
157
+ it 'is set to no-op wrapper by default' do
158
+ semantic_form_for(@new_post, :url => '/hello') do |builder|
159
+ ::ActionView::Base.field_error_proc.call("html", nil).should == "html"
160
+ end
161
+ end
162
+
163
+ it 'is set to the configured custom field_error_proc' do
164
+ field_error_proc = double()
165
+ Formtastic::Helpers::FormHelper.formtastic_field_error_proc = field_error_proc
166
+ semantic_form_for(@new_post, :url => '/hello') do |builder|
167
+ ::ActionView::Base.field_error_proc.should == field_error_proc
168
+ end
169
+ end
170
+
171
+ it 'is restored to its original value after the form is rendered' do
172
+ lambda do
173
+ Formtastic::Helpers::FormHelper.formtastic_field_error_proc = proc {""}
174
+ semantic_form_for(@new_post, :url => '/hello') { |builder| }
175
+ end.should_not change(::ActionView::Base, :field_error_proc)
176
+ end
177
+ end
178
+
179
+ describe "with :builder option" do
180
+ it "yields an instance of the given builder" do
181
+ class MyAwesomeCustomBuilder < Formtastic::FormBuilder
182
+ end
183
+ semantic_form_for(@new_post, :url => '/hello', :builder => MyAwesomeCustomBuilder) do |builder|
184
+ builder.class.should == MyAwesomeCustomBuilder
185
+ end
186
+ end
187
+ end
188
+
189
+ describe 'with :namespace option' do
190
+ it "should set the custom_namespace" do
191
+ semantic_form_for(@new_post, :namespace => 'context2') do |builder|
192
+ expect(builder.dom_id_namespace).to eq('context2')
193
+ end
194
+ end
195
+ end
196
+
197
+ describe 'without :namespace option' do
198
+ it 'defaults to class settings' do
199
+ expect(Formtastic::FormBuilder).to receive(:custom_namespace).and_return('context2')
200
+
201
+ semantic_form_for(@new_post) do |builder|
202
+ expect(builder.dom_id_namespace).to eq('context2')
203
+ end
204
+ end
205
+ end
206
+
207
+ end
208
+
209
+ describe '#semantic_fields_for' do
210
+ it 'yields an instance of Formtastic::FormBuilder' do
211
+ semantic_fields_for(@new_post) do |builder|
212
+ builder.class.should.kind_of?(Formtastic::FormBuilder)
213
+ end
214
+ end
215
+ end
216
+
217
+ end
218
+
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'Formtastic::FormBuilder#input' do
5
+ it_behaves_like 'Input Helper' # from spec/support/shared_examples.rb
6
+ end
@@ -0,0 +1,655 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'Formtastic::FormBuilder#inputs' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe 'with a block (block forms syntax)' do
14
+
15
+ describe 'when no options are provided' do
16
+ before do
17
+ output_buffer.replace 'before_builder' # clear the output buffer and sets before_builder
18
+ concat(semantic_form_for(@new_post) do |builder|
19
+ @inputs_output = builder.inputs do
20
+ concat('hello')
21
+ end
22
+ end)
23
+ end
24
+
25
+ it 'should output just the content wrapped in inputs, not the whole template' do
26
+ output_buffer.should =~ /before_builder/
27
+ @inputs_output.should_not =~ /before_builder/
28
+ end
29
+
30
+ it 'should render a fieldset inside the form, with a class of "inputs"' do
31
+ output_buffer.should have_tag("form fieldset.inputs")
32
+ end
33
+
34
+ it 'should render an ol inside the fieldset' do
35
+ output_buffer.should have_tag("form fieldset.inputs ol")
36
+ end
37
+
38
+ it 'should render the contents of the block inside the ol' do
39
+ output_buffer.should have_tag("form fieldset.inputs ol", /hello/)
40
+ end
41
+
42
+ it 'should not render a legend inside the fieldset' do
43
+ output_buffer.should_not have_tag("form fieldset.inputs legend")
44
+ end
45
+
46
+ it 'should render a fieldset even if no object is given' do
47
+ concat(semantic_form_for(:project, :url => 'http://test.host/') do |builder|
48
+ @inputs_output = builder.inputs do
49
+ concat('bye')
50
+ end
51
+ end)
52
+ output_buffer.should have_tag("form fieldset.inputs ol", /bye/)
53
+ end
54
+ end
55
+
56
+ describe 'when a :for option is provided' do
57
+
58
+ before do
59
+ @new_post.stub(:respond_to?).and_return(true, true)
60
+ @new_post.stub(:respond_to?).with(:empty?).and_return(false)
61
+ @new_post.stub(:author).and_return(@bob)
62
+ end
63
+
64
+ it 'should render nested inputs' do
65
+ @bob.stub(:column_for_attribute).and_return(double('column', :type => :string, :limit => 255))
66
+
67
+ concat(semantic_form_for(@new_post) do |builder|
68
+ inputs = builder.inputs :for => [:author, @bob] do |bob_builder|
69
+ concat(bob_builder.input(:login))
70
+ end
71
+ concat(inputs)
72
+ end)
73
+ output_buffer.should have_tag("form fieldset.inputs #post_author_attributes_login")
74
+ output_buffer.should_not have_tag("form fieldset.inputs #author_login")
75
+ end
76
+
77
+ it 'should concat rendered nested inputs to the template' do
78
+ @bob.stub(:column_for_attribute).and_return(double('column', :type => :string, :limit => 255))
79
+
80
+ concat(semantic_form_for(@new_post) do |builder|
81
+ builder.inputs :for => [:author, @bob] do |bob_builder|
82
+ concat(bob_builder.input(:login))
83
+ end
84
+ end)
85
+
86
+ output_buffer.should have_tag("form fieldset.inputs #post_author_attributes_login")
87
+ output_buffer.should_not have_tag("form fieldset.inputs #author_login")
88
+
89
+ end
90
+
91
+ describe "as a symbol representing the association name" do
92
+
93
+ it 'should nest the inputs with an _attributes suffix on the association name' do
94
+ concat(semantic_form_for(@new_post) do |post|
95
+ inputs = post.inputs :for => :author do |author|
96
+ concat(author.input(:login))
97
+ end
98
+ concat(inputs)
99
+ end)
100
+ output_buffer.should have_tag("form input[@name='post[author_attributes][login]']")
101
+ end
102
+
103
+ end
104
+
105
+ describe "as a symbol representing a has_many association name" do
106
+ before do
107
+ @new_post.stub(:authors).and_return([@bob, @fred])
108
+ @new_post.stub(:authors_attributes=)
109
+ end
110
+
111
+ it 'should nest the inputs with a fieldset, legend and :name input for each item' do
112
+ concat(semantic_form_for(@new_post) do |post|
113
+ post.inputs :for => :authors, :name => '%i' do |author|
114
+ concat(author.input(:login))
115
+ end
116
+ end)
117
+
118
+ output_buffer.should have_tag("form fieldset.inputs", :count => 2)
119
+ output_buffer.should have_tag("form fieldset.inputs legend", :count => 2)
120
+ output_buffer.should have_tag("form fieldset.inputs legend", "1", :count => 1)
121
+ output_buffer.should have_tag("form fieldset.inputs legend", "2")
122
+ output_buffer.should have_tag("form input[@name='post[authors_attributes][0][login]']")
123
+ output_buffer.should have_tag("form input[@name='post[authors_attributes][1][login]']")
124
+ output_buffer.should_not have_tag('form fieldset[@name]')
125
+ end
126
+
127
+ it 'should include an indexed :label input for each item' do
128
+ concat(semantic_form_for(@new_post) do |post|
129
+ post.inputs :for => :authors do |author, index|
130
+ concat(author.input(:login, :label => "#{index}", :required => false))
131
+ end
132
+ end)
133
+
134
+ output_buffer.should have_tag("form fieldset.inputs label", "1", :count => 1)
135
+ output_buffer.should have_tag("form fieldset.inputs label", "2", :count => 1)
136
+ output_buffer.should_not have_tag('form fieldset legend')
137
+ end
138
+ end
139
+
140
+ describe 'as an array containing the a symbole for the association name and the associated object' do
141
+
142
+ it 'should nest the inputs with an _attributes suffix on the association name' do
143
+ concat(semantic_form_for(@new_post) do |post|
144
+ inputs = post.inputs :for => [:author, @new_post.author] do |author|
145
+ concat(author.input(:login))
146
+ end
147
+ concat(inputs)
148
+ end)
149
+ output_buffer.should have_tag("form input[@name='post[author_attributes][login]']")
150
+ end
151
+
152
+ end
153
+
154
+ describe 'as an associated object' do
155
+
156
+ it 'should not nest the inputs with an _attributes suffix' do
157
+ concat(semantic_form_for(@new_post) do |post|
158
+ inputs = post.inputs :for => @new_post.author do |author|
159
+ concat(author.input(:login))
160
+ end
161
+ concat(inputs)
162
+ end)
163
+ output_buffer.should have_tag("form input[@name='post[author][login]']")
164
+ end
165
+
166
+ end
167
+
168
+ it 'should raise an error if :for and block with no argument is given' do
169
+ semantic_form_for(@new_post) do |builder|
170
+ proc {
171
+ builder.inputs(:for => [:author, @bob]) do
172
+ #
173
+ end
174
+ }.should raise_error(ArgumentError, 'You gave :for option with a block to inputs method, ' <<
175
+ 'but the block does not accept any argument.')
176
+ end
177
+ end
178
+
179
+ it 'should pass options down to semantic_fields_for' do
180
+ @bob.stub(:column_for_attribute).and_return(double('column', :type => :string, :limit => 255))
181
+
182
+ concat(semantic_form_for(@new_post) do |builder|
183
+ inputs = builder.inputs :for => [:author, @bob], :for_options => { :index => 10 } do |bob_builder|
184
+ concat(bob_builder.input(:login))
185
+ end
186
+ concat(inputs)
187
+ end)
188
+
189
+ output_buffer.should have_tag('form fieldset ol li #post_author_attributes_10_login')
190
+ end
191
+
192
+ it 'should not add builder as a fieldset attribute tag' do
193
+ concat(semantic_form_for(@new_post) do |builder|
194
+ inputs = builder.inputs :for => [:author, @bob], :for_options => { :index => 10 } do |bob_builder|
195
+ concat('input')
196
+ end
197
+ concat(inputs)
198
+ end)
199
+
200
+ output_buffer.should_not have_tag('fieldset[@builder="Formtastic::Helpers::FormHelper"]')
201
+ end
202
+
203
+ it 'should send parent_builder as an option to allow child index interpolation for legends' do
204
+ concat(semantic_form_for(@new_post) do |builder|
205
+ builder.instance_variable_set('@nested_child_index', 0)
206
+ inputs = builder.inputs :for => [:author, @bob], :name => 'Author #%i' do |bob_builder|
207
+ concat('input')
208
+ end
209
+ concat(inputs)
210
+ end)
211
+
212
+ output_buffer.should have_tag('fieldset legend', 'Author #1')
213
+ end
214
+
215
+ it 'should also provide child index interpolation for legends when nested child index is a hash' do
216
+ concat(semantic_form_for(@new_post) do |builder|
217
+ builder.instance_variable_set('@nested_child_index', :author => 10)
218
+ inputs = builder.inputs :for => [:author, @bob], :name => 'Author #%i' do |bob_builder|
219
+ concat('input')
220
+ end
221
+ concat(inputs)
222
+ end)
223
+
224
+ output_buffer.should have_tag('fieldset legend', 'Author #11')
225
+ end
226
+
227
+ it 'should send parent_builder as an option to allow child index interpolation for labels' do
228
+ concat(semantic_form_for(@new_post) do |builder|
229
+ builder.instance_variable_set('@nested_child_index', 'post[author_attributes]' => 0)
230
+ inputs = builder.inputs :for => [:author, @bob] do |bob_builder, index|
231
+ concat(bob_builder.input(:name, :label => "Author ##{index}", :required => false))
232
+ end
233
+ concat(inputs)
234
+ end)
235
+
236
+ output_buffer.should have_tag('fieldset label', 'Author #1')
237
+ end
238
+
239
+ it 'should also provide child index interpolation for labels when nested child index is a hash' do
240
+ concat(semantic_form_for(@new_post) do |builder|
241
+ builder.instance_variable_set('@nested_child_index', 'post[author_attributes]' => 10)
242
+ inputs = builder.inputs :for => [:author, @bob] do |bob_builder, index|
243
+ concat(bob_builder.input(:name, :label => "Author ##{index}", :required => false))
244
+ end
245
+ concat(inputs)
246
+ end)
247
+
248
+ output_buffer.should have_tag('fieldset label', 'Author #11')
249
+ end
250
+ end
251
+
252
+ describe 'when a :name or :title option is provided' do
253
+ describe 'and is a string' do
254
+ before do
255
+ @legend_text = "Advanced options"
256
+ @legend_text_using_name = "Advanced options 2"
257
+ @legend_text_using_title = "Advanced options 3"
258
+ @nested_forms_legend_text = "This is a nested form title"
259
+ concat(semantic_form_for(@new_post) do |builder|
260
+ inputs = builder.inputs @legend_text do
261
+ end
262
+ concat(inputs)
263
+ inputs = builder.inputs :name => @legend_text_using_name do
264
+ end
265
+ concat(inputs)
266
+ inputs = builder.inputs :title => @legend_text_using_title do
267
+ end
268
+ concat(inputs)
269
+ inputs = builder.inputs @nested_forms_legend_text, :for => :authors do |nf|
270
+ end
271
+ concat(inputs)
272
+ end)
273
+ end
274
+
275
+ # TODO: looks like the block isn't being called for the last assertion here
276
+ it 'should render a fieldset with a legend inside the form' do
277
+ output_buffer.should have_tag("form fieldset legend", /^#{@legend_text}$/)
278
+ output_buffer.should have_tag("form fieldset legend", /^#{@legend_text_using_name}$/)
279
+ output_buffer.should have_tag("form fieldset legend", /^#{@legend_text_using_title}$/)
280
+ output_buffer.should have_tag("form fieldset legend", /^#{@nested_forms_legend_text}$/)
281
+ end
282
+ end
283
+
284
+ describe 'and is a symbol' do
285
+ before do
286
+ @localized_legend_text = "Localized advanced options"
287
+ @localized_legend_text_using_name = "Localized advanced options 2"
288
+ @localized_legend_text_using_title = "Localized advanced options 3"
289
+ @localized_nested_forms_legend_text = "This is a localized nested form title"
290
+ ::I18n.backend.store_translations :en, :formtastic => {
291
+ :titles => {
292
+ :post => {
293
+ :advanced_options => @localized_legend_text,
294
+ :advanced_options_using_name => @localized_legend_text_using_name,
295
+ :advanced_options_using_title => @localized_legend_text_using_title,
296
+ :nested_forms_title => @localized_nested_forms_legend_text
297
+ }
298
+ }
299
+ }
300
+ concat(semantic_form_for(@new_post) do |builder|
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)
309
+ end)
310
+ end
311
+
312
+ # TODO: looks like the block isn't being called for the last assertion here
313
+ it 'should render a fieldset with a localized legend inside the form' do
314
+ output_buffer.should have_tag("form fieldset legend", /^#{@localized_legend_text}$/)
315
+ output_buffer.should have_tag("form fieldset legend", /^#{@localized_legend_text_using_name}$/)
316
+ output_buffer.should have_tag("form fieldset legend", /^#{@localized_legend_text_using_title}$/)
317
+ output_buffer.should have_tag("form fieldset legend", /^#{@localized_nested_forms_legend_text}$/)
318
+ end
319
+ end
320
+ end
321
+
322
+ describe 'when other options are provided' do
323
+ before do
324
+ @id_option = 'advanced'
325
+ @class_option = 'wide'
326
+
327
+ concat(semantic_form_for(@new_post) do |builder|
328
+ builder.inputs :id => @id_option, :class => @class_option do
329
+ end
330
+ end)
331
+ end
332
+
333
+ it 'should pass the options into the fieldset tag as attributes' do
334
+ output_buffer.should have_tag("form fieldset##{@id_option}")
335
+ output_buffer.should have_tag("form fieldset.#{@class_option}")
336
+ end
337
+ end
338
+
339
+ end
340
+
341
+ describe 'without a block' do
342
+
343
+ before do
344
+ ::Post.stub(:reflections).and_return({:author => double('reflection', :options => {}, :macro => :belongs_to),
345
+ :comments => double('reflection', :options => {}, :macro => :has_many) })
346
+
347
+ @new_post.stub(:title)
348
+ @new_post.stub(:body)
349
+ @new_post.stub(:author_id)
350
+
351
+ @new_post.stub(:column_for_attribute).with(:title).and_return(double('column', :type => :string, :limit => 255))
352
+ @new_post.stub(:column_for_attribute).with(:body).and_return(double('column', :type => :text))
353
+ @new_post.stub(:column_for_attribute).with(:created_at).and_return(double('column', :type => :datetime))
354
+ @new_post.stub(:column_for_attribute).with(:author).and_return(nil)
355
+ end
356
+
357
+ describe 'with no args (quick forms syntax)' do
358
+ before do
359
+ concat(semantic_form_for(@new_post) do |builder|
360
+ concat(builder.inputs)
361
+ end)
362
+ end
363
+
364
+ it 'should render a form' do
365
+ output_buffer.should have_tag('form')
366
+ end
367
+
368
+ it 'should render a fieldset inside the form' do
369
+ output_buffer.should have_tag('form > fieldset.inputs')
370
+ end
371
+
372
+ it 'should not render a legend in the fieldset' do
373
+ output_buffer.should_not have_tag('form > fieldset.inputs > legend')
374
+ end
375
+
376
+ it 'should render an ol in the fieldset' do
377
+ output_buffer.should have_tag('form > fieldset.inputs > ol')
378
+ end
379
+
380
+ it 'should render a list item in the ol for each column and reflection' do
381
+ # Remove the :has_many macro and :created_at column
382
+ count = ::Post.content_columns.size + ::Post.reflections.size - 2
383
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li', :count => count)
384
+ end
385
+
386
+ it 'should render a string list item for title' do
387
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.string')
388
+ end
389
+
390
+ it 'should render a text list item for body' do
391
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.text')
392
+ end
393
+
394
+ it 'should render a select list item for author_id' do
395
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.select', :count => 1)
396
+ end
397
+
398
+ it 'should not render timestamps inputs by default' do
399
+ output_buffer.should_not have_tag('form > fieldset.inputs > ol > li.datetime')
400
+ end
401
+
402
+ context "with a polymorphic association" do
403
+
404
+ before do
405
+ @new_post.stub(:commentable)
406
+ @new_post.class.stub(:reflections).and_return({
407
+ :commentable => double('macro_reflection', :options => { :polymorphic => true }, :macro => :belongs_to)
408
+ })
409
+ @new_post.stub(:column_for_attribute).with(:commentable).and_return(
410
+ double('column', :type => :integer)
411
+ )
412
+ end
413
+
414
+ it 'should not render an input for the polymorphic association (the collection class cannot be guessed)' do
415
+ concat(semantic_form_for(@new_post) do |builder|
416
+ concat(builder.inputs)
417
+ end)
418
+ output_buffer.should_not have_tag('li#post_commentable_input')
419
+ end
420
+
421
+ end
422
+ end
423
+
424
+ describe 'with column names as args (short hand forms syntax)' do
425
+ describe 'and an object is given' do
426
+ it 'should render a form with a fieldset containing two list items' do
427
+ concat(semantic_form_for(@new_post) do |builder|
428
+ concat(builder.inputs(:title, :body))
429
+ end)
430
+
431
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li', :count => 2)
432
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.string')
433
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.text')
434
+ end
435
+ end
436
+
437
+ describe 'and no object is given' do
438
+ it 'should render a form with a fieldset containing two list items' do
439
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
440
+ concat(builder.inputs(:title, :body))
441
+ end)
442
+
443
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.string', :count => 2)
444
+ end
445
+ end
446
+
447
+ context "with a polymorphic association" do
448
+
449
+ it 'should raise an error for polymorphic associations (the collection class cannot be guessed)' do
450
+ @new_post.stub(:commentable)
451
+ @new_post.class.stub(:reflections).and_return({
452
+ :commentable => double('macro_reflection', :options => { :polymorphic => true }, :macro => :belongs_to)
453
+ })
454
+ @new_post.stub(:column_for_attribute).with(:commentable).and_return(
455
+ double('column', :type => :integer)
456
+ )
457
+ @new_post.class.stub(:reflect_on_association).with(:commentable).and_return(
458
+ double('reflection', :macro => :belongs_to, :options => { :polymorphic => true })
459
+ )
460
+
461
+ expect {
462
+ concat(semantic_form_for(@new_post) do |builder|
463
+ concat(builder.inputs :commentable)
464
+ end)
465
+ }.to raise_error(Formtastic::PolymorphicInputWithoutCollectionError)
466
+ end
467
+
468
+ end
469
+
470
+ end
471
+
472
+ describe 'when a :for option is provided' do
473
+ describe 'and an object is given' do
474
+ it 'should render nested inputs' do
475
+ @bob.stub(:column_for_attribute).and_return(double('column', :type => :string, :limit => 255))
476
+ concat(semantic_form_for(@new_post) do |builder|
477
+ concat(builder.inputs(:login, :for => @bob))
478
+ end)
479
+
480
+ output_buffer.should have_tag("form fieldset.inputs #post_author_login")
481
+ output_buffer.should_not have_tag("form fieldset.inputs #author_login")
482
+ end
483
+ end
484
+
485
+ describe 'and no object is given' do
486
+ it 'should render nested inputs' do
487
+ concat(semantic_form_for(:project, :url => 'http://test.host/') do |builder|
488
+ concat(builder.inputs(:login, :for => @bob))
489
+ end)
490
+ output_buffer.should have_tag("form fieldset.inputs #project_author_login")
491
+ output_buffer.should_not have_tag("form fieldset.inputs #project_login")
492
+ end
493
+ end
494
+ end
495
+
496
+ describe 'with column names and an options hash as args' do
497
+ before do
498
+ concat(semantic_form_for(@new_post) do |builder|
499
+ @legend_text_using_option = "Legendary Legend Text"
500
+ @legend_text_using_arg = "Legendary Legend Text 2"
501
+ concat(builder.inputs(:title, :body, :name => @legend_text_using_option, :id => "my-id"))
502
+ concat(builder.inputs(@legend_text_using_arg, :title, :body, :id => "my-id-2"))
503
+ end)
504
+ end
505
+
506
+ it 'should render a form with a fieldset containing two list items' do
507
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li', :count => 4)
508
+ end
509
+
510
+ it 'should pass the options down to the fieldset' do
511
+ output_buffer.should have_tag('form > fieldset#my-id.inputs')
512
+ end
513
+
514
+ it 'should use the special :name option as a text for the legend tag' do
515
+ output_buffer.should have_tag('form > fieldset#my-id.inputs > legend', /^#{@legend_text_using_option}$/)
516
+ output_buffer.should have_tag('form > fieldset#my-id-2.inputs > legend', /^#{@legend_text_using_arg}$/)
517
+ end
518
+ end
519
+
520
+ describe 'when a :except option is provided' do
521
+ describe 'and an object is given' do
522
+ it 'should render a form with a fieldset containing only string item' do
523
+ concat(semantic_form_for(@new_post) do |builder|
524
+ concat(builder.inputs :except => :body)
525
+ end)
526
+
527
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li', :count => 2)
528
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.string')
529
+ output_buffer.should_not have_tag('form > fieldset.inputs > ol > li.text')
530
+ end
531
+
532
+ it 'should render a form with a fieldset containing only text item' do
533
+ concat(semantic_form_for(@new_post) do |builder|
534
+ concat(builder.inputs :except => :title)
535
+ end)
536
+
537
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li', :count => 2)
538
+ output_buffer.should_not have_tag('form > fieldset.inputs > ol > li.string')
539
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.text')
540
+ end
541
+ end
542
+
543
+ describe 'and no object is given' do
544
+ it 'should render a form with a fieldset containing two list items' do
545
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
546
+ concat(builder.inputs(:title, :body))
547
+ end)
548
+
549
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li.string', :count => 2)
550
+ end
551
+ end
552
+ end
553
+ end
554
+
555
+ describe 'nesting' do
556
+
557
+ context "when not nested" do
558
+ it "should not wrap the inputs in an li block" do
559
+ concat(semantic_form_for(@new_post) do |builder|
560
+ concat(builder.inputs do
561
+ end)
562
+ end)
563
+ output_buffer.should_not have_tag('form > li')
564
+ end
565
+ end
566
+
567
+ context "when nested (with block)" do
568
+ it "should wrap the nested inputs in an li block to maintain HTML validity" do
569
+ concat(semantic_form_for(@new_post) do |builder|
570
+ concat(builder.inputs do
571
+ concat(builder.inputs do
572
+ end)
573
+ end)
574
+ end)
575
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li > fieldset.inputs > ol')
576
+ end
577
+ end
578
+
579
+ context "when nested (with block and :for)" do
580
+ it "should wrap the nested inputs in an li block to maintain HTML validity" do
581
+ concat(semantic_form_for(@new_post) do |builder|
582
+ concat(builder.inputs do
583
+ concat(builder.inputs(:for => :author) do |author_builder|
584
+ end)
585
+ end)
586
+ end)
587
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li > fieldset.inputs > ol')
588
+ end
589
+ end
590
+
591
+ context "when nested (without block)" do
592
+ it "should wrap the nested inputs in an li block to maintain HTML validity" do
593
+ concat(semantic_form_for(@new_post) do |builder|
594
+ concat(builder.inputs do
595
+ concat(builder.inputs(:title))
596
+ end)
597
+ end)
598
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li > fieldset.inputs > ol')
599
+ end
600
+ end
601
+
602
+ context "when nested (without block, with :for)" do
603
+ it "should wrap the nested inputs in an li block to maintain HTML validity" do
604
+ concat(semantic_form_for(@new_post) do |builder|
605
+ concat(builder.inputs do
606
+ concat(builder.inputs(:name, :for => :author))
607
+ end)
608
+ end)
609
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li > fieldset.inputs > ol')
610
+ end
611
+ end
612
+
613
+ context "when double nested" do
614
+ it "should wrap the nested inputs in an li block to maintain HTML validity" do
615
+ concat(semantic_form_for(@new_post) do |builder|
616
+ concat(builder.inputs do
617
+ concat(builder.inputs do
618
+ concat(builder.inputs do
619
+ end)
620
+ end)
621
+ end)
622
+ end)
623
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li > fieldset.inputs > ol > li > fieldset.inputs > ol')
624
+ end
625
+ end
626
+
627
+ context "when several are nested" do
628
+ it "should wrap each of the nested inputs in an li block to maintain HTML validity" do
629
+ concat(semantic_form_for(@new_post) do |builder|
630
+ concat(builder.inputs do
631
+ concat(builder.inputs do
632
+ end)
633
+ concat(builder.inputs do
634
+ end)
635
+ end)
636
+ end)
637
+ output_buffer.should have_tag('form > fieldset.inputs > ol > li > fieldset.inputs > ol', :count => 2)
638
+ end
639
+ end
640
+
641
+ end
642
+
643
+ describe 'when using MongoMapper associations ' do
644
+ def generate_form
645
+ semantic_form_for(@new_mm_post) do |builder|
646
+ builder.inputs :title, :sub_posts
647
+ end
648
+ end
649
+ it "should throw PolymorphicInputWithoutCollectionError on sub_posts" do
650
+ ::MongoPost.should_receive(:associations).at_least(3).times
651
+ expect { generate_form }.to raise_error(Formtastic::PolymorphicInputWithoutCollectionError)
652
+ end
653
+ end
654
+
655
+ end