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,76 @@
1
+ require 'fast_spec_helper'
2
+ require 'inputs/base/collections'
3
+
4
+ class MyInput
5
+ include Formtastic::Inputs::Base::Collections
6
+ end
7
+
8
+ describe MyInput do
9
+ let(:builder) { double }
10
+ let(:template) { double }
11
+ let(:model_class) { double }
12
+ let(:model) { double(:class => model_class) }
13
+ let(:model_name) { "post" }
14
+ let(:method) { double }
15
+ let(:options) { Hash.new }
16
+
17
+ let(:instance) { MyInput.new(builder, template, model, model_name, method, options) }
18
+
19
+ # class Whatever < ActiveRecord::Base
20
+ # enum :status => [:active, :archived]
21
+ # end
22
+ #
23
+ # Whatever.statuses
24
+ #
25
+ # Whatever.new.status
26
+ #
27
+ # f.input :status
28
+ describe "#collection_from_enum" do
29
+
30
+ let(:method) { :status }
31
+
32
+ context "when an enum is defined for the method" do
33
+ before do
34
+ statuses = ActiveSupport::HashWithIndifferentAccess.new("active"=>0, "inactive"=>1)
35
+ model_class.stub(:statuses) { statuses }
36
+ model.stub(:defined_enums) { {"status" => statuses } }
37
+ end
38
+
39
+ context 'no translations available' do
40
+ it 'returns an Array of EnumOption objects based on the enum options hash' do
41
+ instance.collection_from_enum.should eq [["Active", "active"],["Inactive", "inactive"]]
42
+ end
43
+ end
44
+
45
+ context 'with translations' do
46
+ before do
47
+ ::I18n.backend.store_translations :en, :activerecord => {
48
+ :attributes => {
49
+ :post => {
50
+ :statuses => {
51
+ :active => "I am active",
52
+ :inactive => "I am inactive"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ end
58
+ it 'returns an Array of EnumOption objects based on the enum options hash' do
59
+ instance.collection_from_enum.should eq [["I am active", "active"],["I am inactive", "inactive"]]
60
+ end
61
+
62
+ after do
63
+ ::I18n.backend.store_translations :en, {}
64
+ end
65
+ end
66
+ end
67
+
68
+ context "when an enum is not defined" do
69
+ it 'returns nil' do
70
+ instance.collection_from_enum.should eq nil
71
+ end
72
+ end
73
+ end
74
+
75
+ end
76
+
@@ -0,0 +1,342 @@
1
+ require 'fast_spec_helper'
2
+ require 'inputs/base/validations'
3
+
4
+ class MyInput
5
+ include Formtastic::Inputs::Base::Validations
6
+ end
7
+
8
+ describe MyInput do
9
+ let(:builder) { double }
10
+ let(:template) { double }
11
+ let(:model_class) { double }
12
+ let(:model) { double(:class => model_class) }
13
+ let(:model_name) { "post" }
14
+ let(:method) { double }
15
+ let(:options) { Hash.new }
16
+ let(:validator) { double }
17
+ let(:instance) { MyInput.new(builder, template, model, model_name, method, options) }
18
+
19
+ describe '#required?' do
20
+ context 'with a single validator' do
21
+ before :each do
22
+ allow(instance).to receive(:validations?).and_return(:true)
23
+ allow(instance).to receive(:validations).and_return([validator])
24
+ end
25
+
26
+ context 'with options[:required] being true' do
27
+ let(:options) { {required: true} }
28
+
29
+ it 'is required' do
30
+ expect(instance.required?).to be_truthy
31
+ end
32
+ end
33
+
34
+ context 'with options[:required] being false' do
35
+ let(:options) { {required: false} }
36
+
37
+ it 'is not required' do
38
+ expect(instance.required?).to be_falsey
39
+ end
40
+ end
41
+
42
+ context 'with negated validation' do
43
+ it 'is not required' do
44
+ instance.not_required_through_negated_validation!
45
+ expect(instance.required?).to be_falsey
46
+ end
47
+ end
48
+
49
+ context 'with presence validator' do
50
+ let (:validator) { double(options: {}, kind: :presence) }
51
+
52
+ it 'is required' do
53
+ expect(instance.required?).to be_truthy
54
+ end
55
+
56
+ context 'with options[:on] as symbol' do
57
+ context 'with save context' do
58
+ let (:validator) { double(options: {on: :save}, kind: :presence) }
59
+
60
+ it 'is required' do
61
+ expect(instance.required?).to be_truthy
62
+ end
63
+ end
64
+
65
+ context 'with create context' do
66
+ let (:validator) { double(options: {on: :create}, kind: :presence) }
67
+
68
+ it 'is required for new records' do
69
+ allow(model).to receive(:new_record?).and_return(true)
70
+ expect(instance.required?).to be_truthy
71
+ end
72
+
73
+ it 'is not required for existing records' do
74
+ allow(model).to receive(:new_record?).and_return(false)
75
+ expect(instance.required?).to be_falsey
76
+ end
77
+ end
78
+
79
+ context 'with update context' do
80
+ let (:validator) { double(options: {on: :update}, kind: :presence) }
81
+
82
+ it 'is not required for new records' do
83
+ allow(model).to receive(:new_record?).and_return(true)
84
+ expect(instance.required?).to be_falsey
85
+ end
86
+
87
+ it 'is required for existing records' do
88
+ allow(model).to receive(:new_record?).and_return(false)
89
+ expect(instance.required?).to be_truthy
90
+ end
91
+ end
92
+ end
93
+
94
+ context 'with options[:on] as array' do
95
+ context 'with save context' do
96
+ let (:validator) { double(options: {on: [:save]}, kind: :presence) }
97
+
98
+ it 'is required' do
99
+ expect(instance.required?).to be_truthy
100
+ end
101
+ end
102
+
103
+ context 'with create context' do
104
+ let (:validator) { double(options: {on: [:create]}, kind: :presence) }
105
+
106
+ it 'is required for new records' do
107
+ allow(model).to receive(:new_record?).and_return(true)
108
+ expect(instance.required?).to be_truthy
109
+ end
110
+
111
+ it 'is not required for existing records' do
112
+ allow(model).to receive(:new_record?).and_return(false)
113
+ expect(instance.required?).to be_falsey
114
+ end
115
+ end
116
+
117
+ context 'with update context' do
118
+ let (:validator) { double(options: {on: [:update]}, kind: :presence) }
119
+
120
+ it 'is not required for new records' do
121
+ allow(model).to receive(:new_record?).and_return(true)
122
+ expect(instance.required?).to be_falsey
123
+ end
124
+
125
+ it 'is required for existing records' do
126
+ allow(model).to receive(:new_record?).and_return(false)
127
+ expect(instance.required?).to be_truthy
128
+ end
129
+ end
130
+
131
+ context 'with save and create context' do
132
+ let (:validator) { double(options: {on: [:save, :create]}, kind: :presence) }
133
+
134
+ it 'is required for new records' do
135
+ allow(model).to receive(:new_record?).and_return(true)
136
+ expect(instance.required?).to be_truthy
137
+ end
138
+
139
+ it 'is required for existing records' do
140
+ allow(model).to receive(:new_record?).and_return(false)
141
+ expect(instance.required?).to be_truthy
142
+ end
143
+ end
144
+
145
+ context 'with save and update context' do
146
+ let (:validator) { double(options: {on: [:save, :create]}, kind: :presence) }
147
+
148
+ it 'is required for new records' do
149
+ allow(model).to receive(:new_record?).and_return(true)
150
+ expect(instance.required?).to be_truthy
151
+ end
152
+
153
+ it 'is required for existing records' do
154
+ allow(model).to receive(:new_record?).and_return(false)
155
+ expect(instance.required?).to be_truthy
156
+ end
157
+ end
158
+
159
+ context 'with create and update context' do
160
+ let (:validator) { double(options: {on: [:create, :update]}, kind: :presence) }
161
+
162
+ it 'is required for new records' do
163
+ allow(model).to receive(:new_record?).and_return(true)
164
+ expect(instance.required?).to be_truthy
165
+ end
166
+
167
+ it 'is required for existing records' do
168
+ allow(model).to receive(:new_record?).and_return(false)
169
+ expect(instance.required?).to be_truthy
170
+ end
171
+ end
172
+
173
+ context 'with save and other context' do
174
+ let (:validator) { double(options: {on: [:save, :foo]}, kind: :presence) }
175
+
176
+ it 'is required for new records' do
177
+ allow(model).to receive(:new_record?).and_return(true)
178
+ expect(instance.required?).to be_truthy
179
+ end
180
+
181
+ it 'is required for existing records' do
182
+ allow(model).to receive(:new_record?).and_return(false)
183
+ expect(instance.required?).to be_truthy
184
+ end
185
+ end
186
+
187
+ context 'with create and other context' do
188
+ let (:validator) { double(options: {on: [:create, :foo]}, kind: :presence) }
189
+
190
+ it 'is required for new records' do
191
+ allow(model).to receive(:new_record?).and_return(true)
192
+ expect(instance.required?).to be_truthy
193
+ end
194
+
195
+ it 'is not required for existing records' do
196
+ allow(model).to receive(:new_record?).and_return(false)
197
+ expect(instance.required?).to be_falsey
198
+ end
199
+ end
200
+
201
+ context 'with update and other context' do
202
+ let (:validator) { double(options: {on: [:update, :foo]}, kind: :presence) }
203
+
204
+ it 'is not required for new records' do
205
+ allow(model).to receive(:new_record?).and_return(true)
206
+ expect(instance.required?).to be_falsey
207
+ end
208
+
209
+ it 'is required for existing records' do
210
+ allow(model).to receive(:new_record?).and_return(false)
211
+ expect(instance.required?).to be_truthy
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ context 'with inclusion validator' do
218
+ context 'with allow blank' do
219
+ let (:validator) { double(options: {allow_blank: true}, kind: :inclusion) }
220
+
221
+ it 'is not required' do
222
+ expect(instance.required?).to be_falsey
223
+ end
224
+ end
225
+
226
+ context 'without allow blank' do
227
+ let (:validator) { double(options: {allow_blank: false}, kind: :inclusion) }
228
+
229
+ it 'is required' do
230
+ expect(instance.required?).to be_truthy
231
+ end
232
+ end
233
+ end
234
+
235
+ context 'with a length validator' do
236
+ context 'with allow blank' do
237
+ let (:validator) { double(options: {allow_blank: true}, kind: :length) }
238
+
239
+ it 'is not required' do
240
+ expect(instance.required?).to be_falsey
241
+ end
242
+ end
243
+
244
+ context 'without allow blank' do
245
+ let (:validator) { double(options: {allow_blank: false}, kind: :length) }
246
+
247
+ it 'is not required' do
248
+ expect(instance.required?).to be_falsey
249
+ end
250
+
251
+ context 'with a minimum > 0' do
252
+ let (:validator) { double(options: {allow_blank: false, minimum: 1}, kind: :length) }
253
+
254
+ it 'is required' do
255
+ expect(instance.required?).to be_truthy
256
+ end
257
+ end
258
+
259
+ context 'with a minimum <= 0' do
260
+ let (:validator) { double(options: {allow_blank: false, minimum: 0}, kind: :length) }
261
+
262
+ it 'is not required' do
263
+ expect(instance.required?).to be_falsey
264
+ end
265
+ end
266
+
267
+ context 'with a defined range starting with > 0' do
268
+ let (:validator) { double(options: {allow_blank: false, within: 1..5}, kind: :length) }
269
+
270
+ it 'is required' do
271
+ expect(instance.required?).to be_truthy
272
+ end
273
+ end
274
+
275
+ context 'with a defined range starting with <= 0' do
276
+ let (:validator) { double(options: {allow_blank: false, within: 0..5}, kind: :length) }
277
+
278
+ it 'is not required' do
279
+ expect(instance.required?).to be_falsey
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ context 'with another validator' do
286
+ let (:validator) { double(options: {allow_blank: true}, kind: :foo) }
287
+
288
+ it 'is not required' do
289
+ expect(instance.required?).to be_falsey
290
+ end
291
+ end
292
+ end
293
+
294
+ context 'with multiple validators' do
295
+ context 'with a on create presence validator and a on update presence validator' do
296
+ let (:validator1) { double(options: {on: :create}, kind: :presence) }
297
+ let (:validator2) { double(options: {}, kind: :presence) }
298
+
299
+ before :each do
300
+ allow(model).to receive(:new_record?).and_return(false)
301
+ allow(instance).to receive(:validations?).and_return(:true)
302
+ allow(instance).to receive(:validations).and_return([validator1, validator2])
303
+ end
304
+
305
+ it 'is required' do
306
+ expect(instance.required?).to be_truthy
307
+ end
308
+ end
309
+
310
+ context 'with a on create presence validator and a presence validator' do
311
+ let (:validator1) { double(options: {on: :create}, kind: :presence) }
312
+ let (:validator2) { double(options: {}, kind: :presence) }
313
+
314
+ before :each do
315
+ allow(model).to receive(:new_record?).and_return(false)
316
+ allow(instance).to receive(:validations?).and_return(:true)
317
+ allow(instance).to receive(:validations).and_return([validator1, validator2])
318
+ end
319
+
320
+ it 'is required' do
321
+ expect(instance.required?).to be_truthy
322
+ end
323
+ end
324
+
325
+ context 'with a on create presence validator and a allow blank inclusion validator' do
326
+ let (:validator1) { double(options: {on: :create}, kind: :presence) }
327
+ let (:validator2) { double(options: {allow_blank: true}, kind: :inclusion) }
328
+
329
+ before :each do
330
+ allow(model).to receive(:new_record?).and_return(false)
331
+ allow(instance).to receive(:validations?).and_return(:true)
332
+ allow(instance).to receive(:validations).and_return([validator1, validator2])
333
+ end
334
+
335
+ it 'is required' do
336
+ expect(instance.required?).to be_falsey
337
+ end
338
+ end
339
+ end
340
+ end
341
+ end
342
+
@@ -0,0 +1,254 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'boolean input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe 'generic' do
14
+ before do
15
+ concat(semantic_form_for(@new_post) do |builder|
16
+ concat(builder.input(:allow_comments, :as => :boolean))
17
+ end)
18
+ end
19
+
20
+ it_should_have_input_wrapper_with_class("boolean")
21
+ it_should_have_input_wrapper_with_class(:input)
22
+ it_should_have_input_wrapper_with_id("post_allow_comments_input")
23
+ it_should_apply_error_logic_for_input_type(:boolean)
24
+
25
+ it 'should generate a label containing the input' do
26
+ output_buffer.should_not have_tag('label.label')
27
+ output_buffer.should have_tag('form li label', :count => 1)
28
+ output_buffer.should have_tag('form li label[@for="post_allow_comments"]')
29
+ output_buffer.should have_tag('form li label', /Allow comments/)
30
+ output_buffer.should have_tag('form li label input[@type="checkbox"]', :count => 1)
31
+ output_buffer.should have_tag('form li input[@type="hidden"]', :count => 1)
32
+ output_buffer.should_not have_tag('form li label input[@type="hidden"]', :count => 1) # invalid HTML5
33
+ end
34
+
35
+ it 'should not add a "name" attribute to the label' do
36
+ output_buffer.should_not have_tag('form li label[@name]')
37
+ end
38
+
39
+ it 'should generate a checkbox input' do
40
+ output_buffer.should have_tag('form li label input')
41
+ output_buffer.should have_tag('form li label input#post_allow_comments')
42
+ output_buffer.should have_tag('form li label input[@type="checkbox"]')
43
+ output_buffer.should have_tag('form li label input[@name="post[allow_comments]"]')
44
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="1"]')
45
+ end
46
+
47
+ it 'should generate a checked input if object.method returns true' do
48
+ output_buffer.should have_tag('form li label input[@checked="checked"]')
49
+ output_buffer.should have_tag('form li input[@name="post[allow_comments]"]', :count => 2)
50
+ output_buffer.should have_tag('form li input#post_allow_comments', :count => 1)
51
+ end
52
+ end
53
+
54
+ it 'should generate a checked input if :input_html is passed :checked => checked' do
55
+ concat(semantic_form_for(@new_post) do |builder|
56
+ concat(builder.input(:answer_comments, :as => :boolean, :input_html => {:checked => 'checked'}))
57
+ end)
58
+ output_buffer.should have_tag('form li label input[@checked="checked"]')
59
+ end
60
+
61
+ it 'should name the hidden input with the :name html_option' do
62
+ concat(semantic_form_for(@new_post) do |builder|
63
+ concat(builder.input(:answer_comments, :as => :boolean, :input_html => { :name => "foo" }))
64
+ end)
65
+
66
+ output_buffer.should have_tag('form li input[@type="checkbox"][@name="foo"]', :count => 1)
67
+ output_buffer.should have_tag('form li input[@type="hidden"][@name="foo"]', :count => 1)
68
+ end
69
+
70
+ it 'should name the hidden input with the :name html_option' do
71
+ concat(semantic_form_for(@new_post) do |builder|
72
+ concat(builder.input(:answer_comments, :as => :boolean, :input_html => { :name => "foo" }))
73
+ end)
74
+
75
+ output_buffer.should have_tag('form li input[@type="checkbox"][@name="foo"]', :count => 1)
76
+ output_buffer.should have_tag('form li input[@type="hidden"][@name="foo"]', :count => 1)
77
+ end
78
+
79
+ it "should generate a disabled input and hidden input if :input_html is passed :disabled => 'disabled' " do
80
+ concat(semantic_form_for(@new_post) do |builder|
81
+ concat(builder.input(:allow_comments, :as => :boolean, :input_html => {:disabled => 'disabled'}))
82
+ end)
83
+ output_buffer.should have_tag('form li label input[@disabled="disabled"]', :count => 1)
84
+ output_buffer.should have_tag('form li input[@type="hidden"][@disabled="disabled"]', :count => 1)
85
+ end
86
+
87
+ it 'should generate an input[id] with matching label[for] when id passed in :input_html' do
88
+ concat(semantic_form_for(@new_post) do |builder|
89
+ concat(builder.input(:allow_comments, :as => :boolean, :input_html => {:id => 'custom_id'}))
90
+ end)
91
+ output_buffer.should have_tag('form li label input[@id="custom_id"]')
92
+ output_buffer.should have_tag('form li label[@for="custom_id"]')
93
+ end
94
+
95
+ it 'should allow checked and unchecked values to be sent' do
96
+ concat(semantic_form_for(@new_post) do |builder|
97
+ concat(builder.input(:allow_comments, :as => :boolean, :checked_value => 'checked', :unchecked_value => 'unchecked'))
98
+ end)
99
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="checked"]:not([@unchecked_value][@checked_value])')
100
+ output_buffer.should have_tag('form li input[@type="hidden"][@value="unchecked"]')
101
+ output_buffer.should_not have_tag('form li label input[@type="hidden"]') # invalid HTML5
102
+ end
103
+
104
+ it 'should generate a checked input if object.method returns checked value' do
105
+ @new_post.stub(:allow_comments).and_return('yes')
106
+
107
+ concat(semantic_form_for(@new_post) do |builder|
108
+ concat(builder.input(:allow_comments, :as => :boolean, :checked_value => 'yes', :unchecked_value => 'no'))
109
+ end)
110
+
111
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="yes"][@checked="checked"]')
112
+ end
113
+
114
+ it 'should not generate a checked input if object.method returns unchecked value' do
115
+ @new_post.stub(:allow_comments).and_return('no')
116
+
117
+ concat(semantic_form_for(@new_post) do |builder|
118
+ concat(builder.input(:allow_comments, :as => :boolean, :checked_value => 'yes', :unchecked_value => 'no'))
119
+ end)
120
+
121
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="yes"]:not([@checked])')
122
+ end
123
+
124
+ it 'should generate a checked input if object.method returns checked value' do
125
+ @new_post.stub(:allow_comments).and_return('yes')
126
+
127
+ concat(semantic_form_for(@new_post) do |builder|
128
+ concat(builder.input(:allow_comments, :as => :boolean, :checked_value => 'yes', :unchecked_value => 'no'))
129
+ end)
130
+
131
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="yes"][@checked="checked"]')
132
+ end
133
+
134
+ it 'should generate a checked input for boolean database values compared to string checked values' do
135
+ @new_post.stub(:foo).and_return(1)
136
+
137
+ concat(semantic_form_for(@new_post) do |builder|
138
+ concat(builder.input(:foo, :as => :boolean))
139
+ end)
140
+
141
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="1"][@checked="checked"]')
142
+ end
143
+
144
+ it 'should generate a checked input if object.method returns checked value when inverted' do
145
+ @new_post.stub(:allow_comments).and_return(0)
146
+
147
+ concat(semantic_form_for(@new_post) do |builder|
148
+ concat(builder.input(:allow_comments, :as => :boolean, :checked_value => 0, :unchecked_value => 1))
149
+ end)
150
+
151
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="0"][@checked="checked"]')
152
+ end
153
+
154
+ it 'should not generate a checked input if object.method returns unchecked value' do
155
+ @new_post.stub(:allow_comments).and_return('no')
156
+
157
+ concat(semantic_form_for(@new_post) do |builder|
158
+ concat(builder.input(:allow_comments, :as => :boolean, :checked_value => 'yes', :unchecked_value => 'no'))
159
+ end)
160
+
161
+ output_buffer.should have_tag('form li label input[@type="checkbox"][@value="yes"]:not([@checked])')
162
+ end
163
+
164
+ it 'should generate a label and a checkbox even if no object is given' do
165
+ concat(semantic_form_for(:project, :url => 'http://test.host') do |builder|
166
+ concat(builder.input(:allow_comments, :as => :boolean))
167
+ end)
168
+
169
+ output_buffer.should have_tag('form li label[@for="project_allow_comments"]')
170
+ output_buffer.should have_tag('form li label', /Allow comments/)
171
+ output_buffer.should have_tag('form li label input[@type="checkbox"]')
172
+
173
+ output_buffer.should have_tag('form li label input#project_allow_comments')
174
+ output_buffer.should have_tag('form li label input[@type="checkbox"]')
175
+ output_buffer.should have_tag('form li label input[@name="project[allow_comments]"]')
176
+ end
177
+
178
+ it 'should not pass input_html options down to the label html' do
179
+ concat(semantic_form_for(@new_post) do |builder|
180
+ builder.input(:title, :as => :boolean, :input_html => { :tabindex => 2, :x => "X" })
181
+ end)
182
+ output_buffer.should_not have_tag('label[tabindex]')
183
+ output_buffer.should_not have_tag('label[x]')
184
+ end
185
+
186
+ context "when required" do
187
+
188
+ it "should add the required attribute to the input's html options" do
189
+ with_config :use_required_attribute, true do
190
+ concat(semantic_form_for(@new_post) do |builder|
191
+ concat(builder.input(:title, :as => :boolean, :required => true))
192
+ end)
193
+ output_buffer.should have_tag("input[@required]")
194
+ end
195
+ end
196
+
197
+ it "should not add the required attribute to the boolean fields input's html options" do
198
+ with_config :use_required_attribute, true do
199
+ concat(semantic_form_for(@new_post) do |builder|
200
+ concat(builder.input(:title, :as => :boolean))
201
+ end)
202
+ output_buffer.should_not have_tag("input[@required]")
203
+ end
204
+ end
205
+
206
+ end
207
+
208
+ describe "when namespace is provided" do
209
+
210
+ before do
211
+ @output_buffer = ''
212
+ mock_everything
213
+
214
+ concat(semantic_form_for(@new_post, :namespace => "context2") do |builder|
215
+ concat(builder.input(:allow_comments, :as => :boolean))
216
+ end)
217
+ end
218
+
219
+ it_should_have_input_wrapper_with_id("context2_post_allow_comments_input")
220
+ it_should_have_an_inline_label_for("context2_post_allow_comments")
221
+
222
+ end
223
+
224
+ describe "when index is provided" do
225
+
226
+ before do
227
+ @output_buffer = ''
228
+ mock_everything
229
+
230
+ concat(semantic_form_for(@new_post) do |builder|
231
+ concat(builder.fields_for(:author, :index => 3) do |author|
232
+ concat(author.input(:name, :as => :boolean))
233
+ end)
234
+ end)
235
+ end
236
+
237
+ it 'should index the id of the wrapper' do
238
+ output_buffer.should have_tag("li#post_author_attributes_3_name_input")
239
+ end
240
+
241
+ it 'should index the id of the input tag' do
242
+ output_buffer.should have_tag("input#post_author_attributes_3_name")
243
+ end
244
+
245
+ it 'should index the name of the hidden input' do
246
+ output_buffer.should have_tag("input[@type='hidden'][@name='post[author_attributes][3][name]']")
247
+ end
248
+
249
+ it 'should index the name of the checkbox input' do
250
+ output_buffer.should have_tag("input[@type='checkbox'][@name='post[author_attributes][3][name]']")
251
+ end
252
+
253
+ end
254
+ end