formtastic 1.2.4 → 3.1.5

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 (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