forme 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1 -0
- data/MIT-LICENSE +18 -0
- data/README.rdoc +231 -0
- data/Rakefile +97 -0
- data/lib/forme.rb +1178 -0
- data/lib/forme/sinatra.rb +89 -0
- data/lib/forme/version.rb +9 -0
- data/lib/sequel/plugins/forme.rb +435 -0
- data/spec/forme_spec.rb +579 -0
- data/spec/sequel_plugin_spec.rb +405 -0
- data/spec/sinatra_integration_spec.rb +96 -0
- data/spec/spec_helper.rb +2 -0
- metadata +93 -0
data/spec/forme_spec.rb
ADDED
@@ -0,0 +1,579 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
describe "Forme plain forms" do
|
5
|
+
def sel(opts, s)
|
6
|
+
opts.map{|o| "<option #{'selected="selected" ' if o == s}value=\"#{o}\">#{sprintf("%02i", o)}</option>"}.join
|
7
|
+
end
|
8
|
+
|
9
|
+
before do
|
10
|
+
@f = Forme::Form.new
|
11
|
+
end
|
12
|
+
|
13
|
+
specify "Forme.version should return version string" do
|
14
|
+
Forme.version.should =~ /\A\d+\.\d+\.\d+\z/
|
15
|
+
end
|
16
|
+
|
17
|
+
specify "should create a simple input tags" do
|
18
|
+
@f.input(:text).to_s.should == '<input type="text"/>'
|
19
|
+
@f.input(:radio).to_s.should == '<input type="radio"/>'
|
20
|
+
@f.input(:password).to_s.should == '<input type="password"/>'
|
21
|
+
@f.input(:checkbox).to_s.should == '<input type="checkbox"/>'
|
22
|
+
@f.input(:submit).to_s.should == '<input type="submit"/>'
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "should use :name option as attribute" do
|
26
|
+
@f.input(:text, :name=>"foo").to_s.should == '<input name="foo" type="text"/>'
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "should use :id option as attribute" do
|
30
|
+
@f.input(:text, :id=>"foo").to_s.should == '<input id="foo" type="text"/>'
|
31
|
+
end
|
32
|
+
|
33
|
+
specify "should use :class option as attribute" do
|
34
|
+
@f.input(:text, :class=>"foo").to_s.should == '<input class="foo" type="text"/>'
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "should use :value option as attribute" do
|
38
|
+
@f.input(:text, :value=>"foo").to_s.should == '<input type="text" value="foo"/>'
|
39
|
+
end
|
40
|
+
|
41
|
+
specify "should use :placeholder option as attribute" do
|
42
|
+
@f.input(:text, :placeholder=>"foo").to_s.should == '<input placeholder="foo" type="text"/>'
|
43
|
+
end
|
44
|
+
|
45
|
+
specify "should allow arbitrary attributes using the :attr option" do
|
46
|
+
@f.input(:text, :attr=>{:bar=>"foo"}).to_s.should == '<input bar="foo" type="text"/>'
|
47
|
+
end
|
48
|
+
|
49
|
+
specify "should convert the :data option into attributes" do
|
50
|
+
@f.input(:text, :data=>{:bar=>"foo"}).to_s.should == '<input data-bar="foo" type="text"/>'
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "should not have standard options override the :attr option" do
|
54
|
+
@f.input(:text, :name=>:bar, :attr=>{:name=>"foo"}).to_s.should == '<input name="foo" type="text"/>'
|
55
|
+
end
|
56
|
+
|
57
|
+
specify "should combine :class standard option with :attr option" do
|
58
|
+
@f.input(:text, :class=>:bar, :attr=>{:class=>"foo"}).to_s.should == '<input class="foo bar" type="text"/>'
|
59
|
+
end
|
60
|
+
|
61
|
+
specify "should not have :data options override the :attr option" do
|
62
|
+
@f.input(:text, :data=>{:bar=>"baz"}, :attr=>{:"data-bar"=>"foo"}).to_s.should == '<input data-bar="foo" type="text"/>'
|
63
|
+
end
|
64
|
+
|
65
|
+
specify "should create hidden input with value 0 for each checkbox with a name" do
|
66
|
+
@f.input(:checkbox, :name=>"foo").to_s.should == '<input name="foo" type="hidden" value="0"/><input name="foo" type="checkbox"/>'
|
67
|
+
end
|
68
|
+
|
69
|
+
specify "should not create hidden input with value 0 for each checkbox with a name if :no_hidden option is used" do
|
70
|
+
@f.input(:checkbox, :name=>"foo", :no_hidden=>true).to_s.should == '<input name="foo" type="checkbox"/>'
|
71
|
+
end
|
72
|
+
|
73
|
+
specify "should create hidden input with _hidden appened to id for each checkbox with a name and id" do
|
74
|
+
@f.input(:checkbox, :name=>"foo", :id=>"bar").to_s.should == '<input id="bar_hidden" name="foo" type="hidden" value="0"/><input id="bar" name="foo" type="checkbox"/>'
|
75
|
+
end
|
76
|
+
|
77
|
+
specify "should create hidden input with value f for each checkbox with a name and value t" do
|
78
|
+
@f.input(:checkbox, :name=>"foo", :value=>"t").to_s.should == '<input name="foo" type="hidden" value="f"/><input name="foo" type="checkbox" value="t"/>'
|
79
|
+
end
|
80
|
+
|
81
|
+
specify "should use :hidden_value option for value of hidden input for checkbox" do
|
82
|
+
@f.input(:checkbox, :name=>"foo", :hidden_value=>"no").to_s.should == '<input name="foo" type="hidden" value="no"/><input name="foo" type="checkbox"/>'
|
83
|
+
end
|
84
|
+
|
85
|
+
specify "should handle :checked option" do
|
86
|
+
@f.input(:checkbox, :checked=>true).to_s.should == '<input checked="checked" type="checkbox"/>'
|
87
|
+
@f.input(:checkbox, :checked=>false).to_s.should == '<input type="checkbox"/>'
|
88
|
+
end
|
89
|
+
|
90
|
+
specify "should create textarea tag" do
|
91
|
+
@f.input(:textarea).to_s.should == '<textarea></textarea>'
|
92
|
+
end
|
93
|
+
|
94
|
+
specify "should create select tag" do
|
95
|
+
@f.input(:select).to_s.should == '<select></select>'
|
96
|
+
end
|
97
|
+
|
98
|
+
specify "should create date tag" do
|
99
|
+
@f.input(:date).to_s.should == '<input type="date"/>'
|
100
|
+
end
|
101
|
+
|
102
|
+
specify "should not error for input type :input" do
|
103
|
+
@f.input(:input).to_s.should == '<input type="input"/>'
|
104
|
+
end
|
105
|
+
|
106
|
+
specify "should use multiple select boxes for dates if the :as=>:select option is given" do
|
107
|
+
@f.input(:date, :name=>"foo", :id=>"bar", :as=>:select, :value=>Date.new(2011, 6, 5)).to_s.should == %{<select id="bar_year" name="foo[year]">#{sel(1900..2050, 2011)}</select>-<select id="bar_month" name="foo[month]">#{sel(1..12, 6)}</select>-<select id="bar_day" name="foo[day]">#{sel(1..31, 5)}</select>}
|
108
|
+
end
|
109
|
+
|
110
|
+
specify "should have explicit labeler and trtd wrapper work with multiple select boxes for dates" do
|
111
|
+
@f.input(:date, :name=>"foo", :id=>"bar", :as=>:select, :value=>Date.new(2011, 6, 5), :wrapper=>:trtd, :labeler=>:explicit, :label=>'Baz').to_s.should == %{<tr><td><label for="bar_year">Baz</label></td><td><select id="bar_year" name="foo[year]">#{sel(1900..2050, 2011)}</select>-<select id="bar_month" name="foo[month]">#{sel(1..12, 6)}</select>-<select id="bar_day" name="foo[day]">#{sel(1..31, 5)}</select></td></tr>}
|
112
|
+
end
|
113
|
+
|
114
|
+
specify "should use multiple select boxes for datetimes if the :as=>:select option is given" do
|
115
|
+
@f.input(:datetime, :name=>"foo", :id=>"bar", :as=>:select, :value=>DateTime.new(2011, 6, 5, 4, 3, 2)).to_s.should == %{<select id="bar_year" name="foo[year]">#{sel(1900..2050, 2011)}</select>-<select id="bar_month" name="foo[month]">#{sel(1..12, 6)}</select>-<select id="bar_day" name="foo[day]">#{sel(1..31, 5)}</select> <select id="bar_hour" name="foo[hour]">#{sel(0..23, 4)}</select>:<select id="bar_minute" name="foo[minute]">#{sel(0..59, 3)}</select>:<select id="bar_second" name="foo[second]">#{sel(0..59, 2)}</select>}
|
116
|
+
end
|
117
|
+
|
118
|
+
specify "should create select tag with options" do
|
119
|
+
@f.input(:select, :options=>[1, 2, 3], :selected=>2).to_s.should == '<select><option>1</option><option selected="selected">2</option><option>3</option></select>'
|
120
|
+
@f.input(:select, :options=>[1, 2, 3], :value=>2).to_s.should == '<select><option>1</option><option selected="selected">2</option><option>3</option></select>'
|
121
|
+
end
|
122
|
+
|
123
|
+
specify "should create select tag with options and values" do
|
124
|
+
@f.input(:select, :options=>[[:a, 1], [:b, 2], [:c, 3]], :selected=>2).to_s.should == '<select><option value="1">a</option><option selected="selected" value="2">b</option><option value="3">c</option></select>'
|
125
|
+
end
|
126
|
+
|
127
|
+
specify "should create select tag with options and values with hashes" do
|
128
|
+
@f.input(:select, :options=>[[:a, {:foo=>1}], [:b, {:bar=>4, :value=>2}], [:c, {:baz=>3}]], :selected=>2).to_s.should == '<select><option foo="1">a</option><option bar="4" selected="selected" value="2">b</option><option baz="3">c</option></select>'
|
129
|
+
end
|
130
|
+
|
131
|
+
specify "should create select tag with options and values using given method" do
|
132
|
+
@f.input(:select, :options=>[[:a, 1], [:b, 2], [:c, 3]], :text_method=>:last, :selected=>2).to_s.should == '<select><option>1</option><option selected="selected">2</option><option>3</option></select>'
|
133
|
+
@f.input(:select, :options=>[[:a, 1], [:b, 2], [:c, 3]], :text_method=>:last, :value_method=>:first, :selected=>:b).to_s.should == '<select><option value="a">1</option><option selected="selected" value="b">2</option><option value="c">3</option></select>'
|
134
|
+
end
|
135
|
+
|
136
|
+
specify "should use html attributes specified in options" do
|
137
|
+
@f.input(:text, :value=>'foo', :name=>'bar').to_s.should == '<input name="bar" type="text" value="foo"/>'
|
138
|
+
@f.input(:textarea, :value=>'foo', :name=>'bar').to_s.should == '<textarea name="bar">foo</textarea>'
|
139
|
+
@f.input(:select, :name=>'bar', :options=>[1, 2, 3]).to_s.should == '<select name="bar"><option>1</option><option>2</option><option>3</option></select>'
|
140
|
+
end
|
141
|
+
|
142
|
+
specify "should support :add_blank option for select inputs" do
|
143
|
+
@f.input(:select, :options=>[[:b, 2], [:c, 3]], :add_blank=>true, :value=>2).to_s.should == '<select><option value=""></option><option selected="selected" value="2">b</option><option value="3">c</option></select>'
|
144
|
+
end
|
145
|
+
|
146
|
+
specify "should use :add_blank option value as prompt if it is a String" do
|
147
|
+
@f.input(:select, :options=>[[:b, 2], [:c, 3]], :add_blank=>"Prompt Here", :value=>2).to_s.should == '<select><option value="">Prompt Here</option><option selected="selected" value="2">b</option><option value="3">c</option></select>'
|
148
|
+
end
|
149
|
+
|
150
|
+
specify "radio and checkbox inputs should handle :checked option" do
|
151
|
+
@f.input(:radio, :checked=>true).to_s.should == '<input checked="checked" type="radio"/>'
|
152
|
+
@f.input(:radio, :checked=>false).to_s.should == '<input type="radio"/>'
|
153
|
+
@f.input(:checkbox, :checked=>true).to_s.should == '<input checked="checked" type="checkbox"/>'
|
154
|
+
@f.input(:checkbox, :checked=>false).to_s.should == '<input type="checkbox"/>'
|
155
|
+
end
|
156
|
+
|
157
|
+
specify "inputs should handle :required option" do
|
158
|
+
@f.input(:text, :required=>true).to_s.should == '<input required="required" type="text"/>'
|
159
|
+
@f.input(:text, :required=>false).to_s.should == '<input type="text"/>'
|
160
|
+
end
|
161
|
+
|
162
|
+
specify "inputs should handle :disabled option" do
|
163
|
+
@f.input(:text, :disabled=>true).to_s.should == '<input disabled="disabled" type="text"/>'
|
164
|
+
@f.input(:text, :disabled=>false).to_s.should == '<input type="text"/>'
|
165
|
+
end
|
166
|
+
|
167
|
+
specify "inputs should not include options with nil values" do
|
168
|
+
@f.input(:text, :name=>nil).to_s.should == '<input type="text"/>'
|
169
|
+
@f.input(:textarea, :name=>nil).to_s.should == '<textarea></textarea>'
|
170
|
+
end
|
171
|
+
|
172
|
+
specify "inputs should include options with false values" do
|
173
|
+
@f.input(:text, :name=>false).to_s.should == '<input name="false" type="text"/>'
|
174
|
+
end
|
175
|
+
|
176
|
+
specify "should automatically create a label if a :label option is used" do
|
177
|
+
@f.input(:text, :label=>'Foo', :value=>'foo').to_s.should == '<label>Foo: <input type="text" value="foo"/></label>'
|
178
|
+
end
|
179
|
+
|
180
|
+
specify "should automatically note the input has errors if :error option is used" do
|
181
|
+
@f.input(:text, :error=>'Bad Stuff!', :value=>'foo').to_s.should == '<input class="error" type="text" value="foo"/><span class="error_message">Bad Stuff!</span>'
|
182
|
+
end
|
183
|
+
|
184
|
+
specify "should add to existing :class option if :error option is used" do
|
185
|
+
@f.input(:text, :error=>'Bad Stuff!', :class=>'bar', :value=>'foo').to_s.should == '<input class="bar error" type="text" value="foo"/><span class="error_message">Bad Stuff!</span>'
|
186
|
+
end
|
187
|
+
|
188
|
+
specify "#open should return an opening tag" do
|
189
|
+
@f.open(:action=>'foo', :method=>'post').to_s.should == '<form action="foo" method="post">'
|
190
|
+
end
|
191
|
+
|
192
|
+
specify "#close should return a closing tag" do
|
193
|
+
@f.close.to_s.should == '</form>'
|
194
|
+
end
|
195
|
+
|
196
|
+
specify "#button should return a submit tag" do
|
197
|
+
@f.button.to_s.should == '<input type="submit"/>'
|
198
|
+
end
|
199
|
+
|
200
|
+
specify "#button should accept an options hash" do
|
201
|
+
@f.button(:name=>'foo', :value=>'bar').to_s.should == '<input name="foo" type="submit" value="bar"/>'
|
202
|
+
end
|
203
|
+
|
204
|
+
specify "#button should accept a string to use as a value" do
|
205
|
+
@f.button('foo').to_s.should == '<input type="submit" value="foo"/>'
|
206
|
+
end
|
207
|
+
|
208
|
+
specify "#tag should return a serialized_tag" do
|
209
|
+
@f.tag(:textarea).to_s.should == '<textarea></textarea>'
|
210
|
+
@f.tag(:textarea, :name=>:foo).to_s.should == '<textarea name="foo"></textarea>'
|
211
|
+
@f.tag(:textarea, {:name=>:foo}, :bar).to_s.should == '<textarea name="foo">bar</textarea>'
|
212
|
+
end
|
213
|
+
|
214
|
+
specify "should have an #inputs method for multiple inputs wrapped in a fieldset" do
|
215
|
+
@f.inputs([:textarea, :text]).to_s.should == '<fieldset class="inputs"><textarea></textarea><input type="text"/></fieldset>'
|
216
|
+
end
|
217
|
+
|
218
|
+
specify "should have default #inputs method accept an :attr option" do
|
219
|
+
@f.inputs([:textarea, :text], :legend=>'Inputs', :attr=>{:class=>'foo', :bar=>'baz'}).to_s.should == '<fieldset bar="baz" class="foo inputs"><legend>Inputs</legend><textarea></textarea><input type="text"/></fieldset>'
|
220
|
+
end
|
221
|
+
|
222
|
+
specify "should have default #inputs method accept a :legend option" do
|
223
|
+
@f.inputs([:textarea, :text], :legend=>'Inputs').to_s.should == '<fieldset class="inputs"><legend>Inputs</legend><textarea></textarea><input type="text"/></fieldset>'
|
224
|
+
end
|
225
|
+
|
226
|
+
specify "should have default #inputs method accept a :legend_attr option" do
|
227
|
+
@f.inputs([:textarea, :text], :legend=>'Inputs', :legend_attr=>{:class=>'foo'}).to_s.should == '<fieldset class="inputs"><legend class="foo">Inputs</legend><textarea></textarea><input type="text"/></fieldset>'
|
228
|
+
end
|
229
|
+
|
230
|
+
specify "should have an #inputs method take a block and yield to it" do
|
231
|
+
@f.inputs{@f.input(:textarea); @f.input(:text)}.to_s.should == '<fieldset class="inputs"><textarea></textarea><input type="text"/></fieldset>'
|
232
|
+
end
|
233
|
+
|
234
|
+
specify "should have an #inputs method work with both args and block" do
|
235
|
+
@f.inputs([:textarea]){@f.input(:text)}.to_s.should == '<fieldset class="inputs"><textarea></textarea><input type="text"/></fieldset>'
|
236
|
+
end
|
237
|
+
|
238
|
+
specify "should have an #inputs method support array arguments and creating inputs with the array as argument list" do
|
239
|
+
@f.inputs([[:textarea, {:name=>'foo'}], [:text, {:id=>'bar'}]]).to_s.should == '<fieldset class="inputs"><textarea name="foo"></textarea><input id="bar" type="text"/></fieldset>'
|
240
|
+
end
|
241
|
+
|
242
|
+
specify "should escape tag content" do
|
243
|
+
@f.tag(:div, {}, ['<p></p>']).to_s.should == '<div><p></p></div>'
|
244
|
+
end
|
245
|
+
|
246
|
+
specify "should not escape raw tag content" do
|
247
|
+
@f.tag(:div, {}, ['<p></p>'.extend(Forme::Raw)]).to_s.should == '<div><p></p></div>'
|
248
|
+
end
|
249
|
+
|
250
|
+
specify "should escape tag content in attribute values" do
|
251
|
+
@f.tag(:div, :foo=>'<p></p>').to_s.should == '<div foo="<p></p>"></div>'
|
252
|
+
end
|
253
|
+
|
254
|
+
specify "should not escape raw tag content in attribute values" do
|
255
|
+
@f.tag(:div, :foo=>'<p></p>'.extend(Forme::Raw)).to_s.should == '<div foo="<p></p>"></div>'
|
256
|
+
end
|
257
|
+
|
258
|
+
specify "should format dates, times, and datetimes in ISO format" do
|
259
|
+
@f.tag(:div, :foo=>Date.new(2011, 6, 5)).to_s.should == '<div foo="2011-06-05"></div>'
|
260
|
+
@f.tag(:div, :foo=>DateTime.new(2011, 6, 5, 4, 3, 2)).to_s.should == '<div foo="2011-06-05 04:03:02+00:00"></div>'
|
261
|
+
@f.tag(:div, :foo=>Time.utc(2011, 6, 5, 4, 3, 2)).to_s.should =~ /<div foo="2011-06-05 04:03:02(GMT|UTC)"><\/div>/
|
262
|
+
end
|
263
|
+
|
264
|
+
specify "inputs should accept a :wrapper option to use a custom wrapper" do
|
265
|
+
@f.input(:text, :wrapper=>:li).to_s.should == '<li><input type="text"/></li>'
|
266
|
+
end
|
267
|
+
|
268
|
+
specify "inputs should accept a :wrapper_attr option to use custom wrapper attributes" do
|
269
|
+
@f.input(:text, :wrapper=>:li, :wrapper_attr=>{:class=>"foo"}).to_s.should == '<li class="foo"><input type="text"/></li>'
|
270
|
+
end
|
271
|
+
|
272
|
+
specify "inputs should accept a :formatter option to use a custom formatter" do
|
273
|
+
@f.input(:text, :formatter=>:readonly, :value=>'1', :label=>'Foo').to_s.should == '<label>Foo: <span>1</span></label>'
|
274
|
+
@f.input(:text, :formatter=>:default, :value=>'1', :label=>'Foo').to_s.should == '<label>Foo: <input type="text" value="1"/></label>'
|
275
|
+
end
|
276
|
+
|
277
|
+
specify "inputs should accept a :labeler option to use a custom labeler" do
|
278
|
+
@f.input(:textarea, :labeler=>:explicit, :label=>'bar', :id=>:foo).to_s.should == '<label for="foo">bar</label><textarea id="foo"></textarea>'
|
279
|
+
end
|
280
|
+
|
281
|
+
specify "inputs should accept a :error_handler option to use a custom error_handler" do
|
282
|
+
@f.input(:textarea, :error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}, :error=>'bar', :id=>:foo).to_s.should == '<textarea id="foo"></textarea>!!! bar'
|
283
|
+
end
|
284
|
+
|
285
|
+
specify "#inputs should accept a :inputs_wrapper option to use a custom inputs_wrapper" do
|
286
|
+
@f.inputs([:textarea], :inputs_wrapper=>:ol).to_s.should == '<ol><textarea></textarea></ol>'
|
287
|
+
end
|
288
|
+
|
289
|
+
specify "inputs should accept a :wrapper=>nil option to not use a wrapper" do
|
290
|
+
Forme::Form.new(:wrapper=>:li).input(:text, :wrapper=>nil).to_s.should == '<input type="text"/>'
|
291
|
+
end
|
292
|
+
|
293
|
+
specify "inputs should accept a :labeler=>nil option to not use a labeler" do
|
294
|
+
@f.input(:textarea, :labeler=>nil, :label=>'bar', :id=>:foo).to_s.should == '<textarea id="foo"></textarea>'
|
295
|
+
end
|
296
|
+
|
297
|
+
specify "inputs should accept a :error_handler=>nil option to not use an error_handler" do
|
298
|
+
@f.input(:textarea, :error_handler=>nil, :error=>'bar', :id=>:foo).to_s.should == '<textarea id="foo"></textarea>'
|
299
|
+
end
|
300
|
+
|
301
|
+
specify "#inputs should accept a :inputs_wrapper=>nil option to not use an inputs_wrapper" do
|
302
|
+
@f.form{|f| f.inputs([:textarea], :inputs_wrapper=>nil)}.to_s.should == '<form><textarea></textarea></form>'
|
303
|
+
end
|
304
|
+
|
305
|
+
specify "invalid custom transformers should raise an Error" do
|
306
|
+
proc{Forme::Form.new(:wrapper=>Object.new)}.should raise_error(Forme::Error)
|
307
|
+
proc{@f.input(:textarea, :wrapper=>Object.new).to_s}.should raise_error(Forme::Error)
|
308
|
+
proc{@f.input(:textarea, :formatter=>nil).to_s}.should raise_error(Forme::Error)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
describe "Forme custom" do
|
313
|
+
specify "formatters can be specified as a proc" do
|
314
|
+
Forme::Form.new(:formatter=>proc{|i| i.form._tag(:textarea, i.opts.map{|k,v| [v.upcase, k.to_s.downcase]})}).input(:text, :name=>'foo').to_s.should == '<textarea FOO="name"></textarea>'
|
315
|
+
end
|
316
|
+
|
317
|
+
specify "serializers can be specified as a proc" do
|
318
|
+
Forme::Form.new(:serializer=>proc{|t| "#{t.type} = #{t.opts.inspect}"}).input(:textarea, :name=>'foo').to_s.should == 'textarea = {:name=>"foo"}'
|
319
|
+
end
|
320
|
+
|
321
|
+
specify "labelers can be specified as a proc" do
|
322
|
+
Forme::Form.new(:labeler=>proc{|t, i| ["#{i.opts[:label]}: ", t]}).input(:textarea, :name=>'foo', :label=>'bar').to_s.should == 'bar: <textarea name="foo"></textarea>'
|
323
|
+
end
|
324
|
+
|
325
|
+
specify "error_handlers can be specified as a proc" do
|
326
|
+
Forme::Form.new(:error_handler=>proc{|t, i| [t, "!!! #{i.opts[:error]}"]}).input(:textarea, :name=>'foo', :error=>'bar').to_s.should == '<textarea name="foo"></textarea>!!! bar'
|
327
|
+
end
|
328
|
+
|
329
|
+
specify "wrappers can be specified as a proc" do
|
330
|
+
Forme::Form.new(:wrapper=>proc{|t, i| t.tag(:div, {:bar=>i.opts[:name]}, t)}).input(:textarea, :name=>'foo').to_s.should == '<div bar="foo"><textarea name="foo"></textarea></div>'
|
331
|
+
end
|
332
|
+
|
333
|
+
specify "inputs_wrappers can be specified as a proc" do
|
334
|
+
Forme::Form.new(:inputs_wrapper=>proc{|f, opts, &block| f.tag(:div, &block)}).inputs([:textarea]).to_s.should == '<div><textarea></textarea></div>'
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
describe "Forme built-in custom" do
|
339
|
+
specify "transformers should raise if the there is no matching transformer" do
|
340
|
+
proc{Forme::Form.new(:formatter=>:foo)}.should raise_error(Forme::Error)
|
341
|
+
end
|
342
|
+
|
343
|
+
specify "formatter: disabled disables all inputs unless :disabled=>false option" do
|
344
|
+
Forme::Form.new(:formatter=>:disabled).input(:textarea).to_s.should == '<textarea disabled="disabled"></textarea>'
|
345
|
+
Forme::Form.new(:formatter=>:disabled).input(:textarea, :disabled=>false).to_s.should == '<textarea></textarea>'
|
346
|
+
end
|
347
|
+
|
348
|
+
specify "formatter: readonly uses spans for most input fields and disables radio/checkbox fields" do
|
349
|
+
Forme::Form.new(:formatter=>:readonly).input(:textarea, :label=>"Foo", :value=>"Bar").to_s.should == "<label>Foo: <span>Bar</span></label>"
|
350
|
+
Forme::Form.new(:formatter=>:readonly).input(:text, :label=>"Foo", :value=>"Bar").to_s.should == "<label>Foo: <span>Bar</span></label>"
|
351
|
+
Forme::Form.new(:formatter=>:readonly).input(:radio, :label=>"Foo", :value=>"Bar").to_s.should == "<label><input disabled=\"disabled\" type=\"radio\" value=\"Bar\"/> Foo</label>"
|
352
|
+
Forme::Form.new(:formatter=>:readonly).input(:radio, :label=>"Foo", :value=>"Bar", :checked=>true).to_s.should == "<label><input checked=\"checked\" disabled=\"disabled\" type=\"radio\" value=\"Bar\"/> Foo</label>"
|
353
|
+
Forme::Form.new(:formatter=>:readonly).input(:checkbox, :label=>"Foo", :value=>"Bar").to_s.should == "<label><input disabled=\"disabled\" type=\"checkbox\" value=\"Bar\"/> Foo</label>"
|
354
|
+
Forme::Form.new(:formatter=>:readonly).input(:checkbox, :label=>"Foo", :value=>"Bar", :checked=>true).to_s.should == "<label><input checked=\"checked\" disabled=\"disabled\" type=\"checkbox\" value=\"Bar\"/> Foo</label>"
|
355
|
+
Forme::Form.new(:formatter=>:readonly).input(:select, :label=>"Foo", :options=>[1, 2, 3], :value=>2).to_s.should == "<label>Foo: <span>2</span></label>"
|
356
|
+
end
|
357
|
+
|
358
|
+
specify "labeler: explicit uses an explicit label with for attribute" do
|
359
|
+
Forme::Form.new(:labeler=>:explicit).input(:textarea, :id=>'foo', :label=>'bar').to_s.should == '<label for="foo">bar</label><textarea id="foo"></textarea>'
|
360
|
+
end
|
361
|
+
|
362
|
+
specify "labeler: explicit should handle tags with errors" do
|
363
|
+
Forme::Form.new(:labeler=>:explicit).input(:text, :error=>'Bad Stuff!', :value=>'f', :id=>'foo', :label=>'bar').to_s.should == '<label for="foo">bar</label><input class="error" id="foo" type="text" value="f"/><span class="error_message">Bad Stuff!</span>'
|
364
|
+
end
|
365
|
+
|
366
|
+
specify "wrapper: li wraps tag in an li" do
|
367
|
+
Forme::Form.new(:wrapper=>:li).input(:textarea, :id=>'foo').to_s.should == '<li><textarea id="foo"></textarea></li>'
|
368
|
+
end
|
369
|
+
|
370
|
+
specify "wrapper: p wraps tag in an p" do
|
371
|
+
Forme::Form.new(:wrapper=>:p).input(:textarea, :id=>'foo').to_s.should == '<p><textarea id="foo"></textarea></p>'
|
372
|
+
end
|
373
|
+
|
374
|
+
specify "wrapper: div wraps tag in an div" do
|
375
|
+
Forme::Form.new(:wrapper=>:div).input(:textarea, :id=>'foo').to_s.should == '<div><textarea id="foo"></textarea></div>'
|
376
|
+
end
|
377
|
+
|
378
|
+
specify "wrapper: span wraps tag in an span" do
|
379
|
+
Forme::Form.new(:wrapper=>:span).input(:textarea, :id=>'foo').to_s.should == '<span><textarea id="foo"></textarea></span>'
|
380
|
+
end
|
381
|
+
|
382
|
+
specify "wrapper: trtd wraps tag in an tr/td" do
|
383
|
+
Forme::Form.new(:wrapper=>:trtd).input(:textarea, :id=>'foo').to_s.should == '<tr><td><textarea id="foo"></textarea></td></tr>'
|
384
|
+
end
|
385
|
+
|
386
|
+
specify "wrapper: trtd supports multiple tags in separate tds" do
|
387
|
+
Forme::Form.new(:wrapper=>:trtd, :labeler=>:explicit).input(:textarea, :id=>'foo', :label=>'Foo').to_s.should == '<tr><td><label for="foo">Foo</label></td><td><textarea id="foo"></textarea></td></tr>'
|
388
|
+
end
|
389
|
+
|
390
|
+
specify "wrapper: trtd should use at most 2 td tags" do
|
391
|
+
Forme::Form.new(:wrapper=>:trtd, :labeler=>:explicit).input(:textarea, :id=>'foo', :label=>'Foo', :error=>'Bar').to_s.should == '<tr><td><label for="foo">Foo</label></td><td><textarea class="error" id="foo"></textarea><span class="error_message">Bar</span></td></tr>'
|
392
|
+
end
|
393
|
+
|
394
|
+
specify "inputs_wrapper: ol wraps tags in an ol" do
|
395
|
+
Forme::Form.new(:inputs_wrapper=>:ol, :wrapper=>:li).inputs([:textarea]).to_s.should == '<ol><li><textarea></textarea></li></ol>'
|
396
|
+
end
|
397
|
+
|
398
|
+
specify "inputs_wrapper: fieldset_ol wraps tags in a fieldset and an ol" do
|
399
|
+
Forme::Form.new(:inputs_wrapper=>:fieldset_ol, :wrapper=>:li).inputs([:textarea]).to_s.should == '<fieldset class="inputs"><ol><li><textarea></textarea></li></ol></fieldset>'
|
400
|
+
end
|
401
|
+
|
402
|
+
specify "inputs_wrapper: fieldset_ol supports a :legend option" do
|
403
|
+
Forme.form({}, :inputs_wrapper=>:fieldset_ol, :wrapper=>:li, :legend=>'Foo', :inputs=>[:textarea]).to_s.should == '<form><fieldset class="inputs"><legend>Foo</legend><ol><li><textarea></textarea></li></ol></fieldset></form>'
|
404
|
+
end
|
405
|
+
|
406
|
+
specify "inputs_wrapper: div wraps tags in a div" do
|
407
|
+
Forme::Form.new(:inputs_wrapper=>:div, :wrapper=>:span).inputs([:textarea]).to_s.should == '<div><span><textarea></textarea></span></div>'
|
408
|
+
end
|
409
|
+
|
410
|
+
specify "inputs_wrapper: table wraps tags in an table" do
|
411
|
+
Forme::Form.new(:inputs_wrapper=>:table, :wrapper=>:trtd).inputs([:textarea]).to_s.should == '<table><tr><td><textarea></textarea></td></tr></table>'
|
412
|
+
end
|
413
|
+
|
414
|
+
specify "serializer: html_usa formats dates and datetimes in American format without timezones" do
|
415
|
+
Forme::Form.new(:serializer=>:html_usa).tag(:div, :foo=>Date.new(2011, 6, 5)).to_s.should == '<div foo="06/05/2011"></div>'
|
416
|
+
Forme::Form.new(:serializer=>:html_usa).tag(:div, :foo=>DateTime.new(2011, 6, 5, 16, 3, 2)).to_s.should == '<div foo="06/05/2011 04:03:02PM"></div>'
|
417
|
+
Forme::Form.new(:serializer=>:html_usa).tag(:div, :foo=>Time.utc(2011, 6, 5, 4, 3, 2)).to_s.should == '<div foo="06/05/2011 04:03:02AM"></div>'
|
418
|
+
end
|
419
|
+
|
420
|
+
specify "serializer: text uses plain text output instead of html" do
|
421
|
+
Forme::Form.new(:serializer=>:text).input(:textarea, :label=>"Foo", :value=>"Bar").to_s.should == "Foo: Bar\n\n"
|
422
|
+
Forme::Form.new(:serializer=>:text).input(:text, :label=>"Foo", :value=>"Bar").to_s.should == "Foo: Bar\n\n"
|
423
|
+
Forme::Form.new(:serializer=>:text).input(:radio, :label=>"Foo", :value=>"Bar").to_s.should == "___ Foo\n"
|
424
|
+
Forme::Form.new(:serializer=>:text).input(:radio, :label=>"Foo", :value=>"Bar", :checked=>true).to_s.should == "_X_ Foo\n"
|
425
|
+
Forme::Form.new(:serializer=>:text).input(:checkbox, :label=>"Foo", :value=>"Bar").to_s.should == "___ Foo\n"
|
426
|
+
Forme::Form.new(:serializer=>:text).input(:checkbox, :label=>"Foo", :value=>"Bar", :checked=>true).to_s.should == "_X_ Foo\n"
|
427
|
+
Forme::Form.new(:serializer=>:text).input(:select, :label=>"Foo", :options=>[1, 2, 3], :value=>2).to_s.should == "Foo: \n___ 1\n_X_ 2\n___ 3\n\n"
|
428
|
+
Forme::Form.new(:serializer=>:text).input(:password, :label=>"Pass").to_s.should == "Pass: ********\n\n"
|
429
|
+
Forme::Form.new(:serializer=>:text).button().to_s.should == ""
|
430
|
+
Forme::Form.new(:serializer=>:text).inputs([[:textarea, {:label=>"Foo", :value=>"Bar"}]], :legend=>'Baz').to_s.should == "Baz\n---\nFoo: Bar\n\n"
|
431
|
+
Forme::Form.new(:serializer=>:text).tag(:p){|f| f.input(:textarea, :label=>"Foo", :value=>"Bar")}.to_s.should == "Foo: Bar\n\n"
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
describe "Forme registering custom transformers" do
|
436
|
+
specify "should have #register_transformer register a transformer object for later use" do
|
437
|
+
Forme.register_transformer(:wrapper, :div, proc{|t, i| t.tag(:div, {}, [t])})
|
438
|
+
Forme::Form.new(:wrapper=>:div).input(:textarea).to_s.should == '<div><textarea></textarea></div>'
|
439
|
+
end
|
440
|
+
|
441
|
+
specify "should have #register_transformer register a transformer block for later use" do
|
442
|
+
Forme.register_transformer(:wrapper, :div1){|t, i| t.tag(:div1, {}, [t])}
|
443
|
+
Forme::Form.new(:wrapper=>:div1).input(:textarea).to_s.should == '<div1><textarea></textarea></div1>'
|
444
|
+
end
|
445
|
+
|
446
|
+
specify "should have #register_transformer raise an error if given a block and an object" do
|
447
|
+
proc do
|
448
|
+
Forme.register_transformer(:wrapper, :div1, proc{|t, i| t}){|t| t.tag(:div1, {}, [t])}
|
449
|
+
end.should raise_error(Forme::Error)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
describe "Forme configurations" do
|
454
|
+
after do
|
455
|
+
Forme.default_config = :default
|
456
|
+
end
|
457
|
+
|
458
|
+
specify "config: :formastic uses fieldset_ol inputs_wrapper and li wrapper, and explicit labeler" do
|
459
|
+
Forme::Form.new(:config=>:formtastic).inputs([[:textarea, {:id=>:foo, :label=>'Foo'}]]).to_s.should == '<fieldset class="inputs"><ol><li><label for="foo">Foo</label><textarea id="foo"></textarea></li></ol></fieldset>'
|
460
|
+
end
|
461
|
+
|
462
|
+
specify "should be able to set a default configuration with Forme.default_config=" do
|
463
|
+
Forme.default_config = :formtastic
|
464
|
+
Forme::Form.new.inputs([[:textarea, {:id=>:foo, :label=>'Foo'}]]).to_s.should == '<fieldset class="inputs"><ol><li><label for="foo">Foo</label><textarea id="foo"></textarea></li></ol></fieldset>'
|
465
|
+
end
|
466
|
+
|
467
|
+
specify "should have #register_config register a configuration for later use" do
|
468
|
+
Forme.register_config(:foo, :wrapper=>:li, :labeler=>:explicit)
|
469
|
+
Forme::Form.new(:config=>:foo).input(:textarea, :id=>:foo, :label=>'Foo').to_s.should == '<li><label for="foo">Foo</label><textarea id="foo"></textarea></li>'
|
470
|
+
end
|
471
|
+
|
472
|
+
specify "should have #register_config support a :base option to base it on an existing config" do
|
473
|
+
Forme.register_config(:foo2, :labeler=>:default, :base=>:formtastic)
|
474
|
+
Forme::Form.new(:config=>:foo2).inputs([[:textarea, {:id=>:foo, :label=>'Foo'}]]).to_s.should == '<fieldset class="inputs"><ol><li><label>Foo: <textarea id="foo"></textarea></label></li></ol></fieldset>'
|
475
|
+
end
|
476
|
+
|
477
|
+
end
|
478
|
+
|
479
|
+
describe "Forme object forms" do
|
480
|
+
specify "should handle a simple case" do
|
481
|
+
obj = Class.new{def forme_input(form, field, opts) form._input(:text, :name=>"obj[#{field}]", :id=>"obj_#{field}", :value=>"#{field}_foo") end}.new
|
482
|
+
Forme::Form.new(obj).input(:field).to_s.should == '<input id="obj_field" name="obj[field]" type="text" value="field_foo"/>'
|
483
|
+
end
|
484
|
+
|
485
|
+
specify "should handle more complex case with multiple different types and opts" do
|
486
|
+
obj = Class.new do
|
487
|
+
def self.name() "Foo" end
|
488
|
+
|
489
|
+
attr_reader :x, :y
|
490
|
+
|
491
|
+
def initialize(x, y)
|
492
|
+
@x, @y = x, y
|
493
|
+
end
|
494
|
+
def forme_input(form, field, opts={})
|
495
|
+
t = opts[:type]
|
496
|
+
t ||= (field == :x ? :textarea : :text)
|
497
|
+
s = field.to_s
|
498
|
+
form._input(t, {:label=>s.upcase, :name=>"foo[#{s}]", :id=>"foo_#{s}", :value=>send(field)}.merge!(opts))
|
499
|
+
end
|
500
|
+
end.new('&foo', 3)
|
501
|
+
f = Forme::Form.new(obj)
|
502
|
+
f.input(:x).to_s.should == '<label>X: <textarea id="foo_x" name="foo[x]">&foo</textarea></label>'
|
503
|
+
f.input(:y, :attr=>{:brain=>'no'}).to_s.should == '<label>Y: <input brain="no" id="foo_y" name="foo[y]" type="text" value="3"/></label>'
|
504
|
+
end
|
505
|
+
|
506
|
+
specify "should handle case where obj doesn't respond to forme_input" do
|
507
|
+
Forme::Form.new([:foo]).input(:first).to_s.should == '<input id="first" name="first" type="text" value="foo"/>'
|
508
|
+
obj = Class.new{attr_accessor :foo}.new
|
509
|
+
obj.foo = 'bar'
|
510
|
+
Forme::Form.new(obj).input(:foo).to_s.should == '<input id="foo" name="foo" type="text" value="bar"/>'
|
511
|
+
end
|
512
|
+
|
513
|
+
specify "should respect opts hash when obj doesn't respond to forme_input" do
|
514
|
+
Forme::Form.new([:foo]).input(:first, :name=>'bar').to_s.should == '<input id="first" name="bar" type="text" value="foo"/>'
|
515
|
+
Forme::Form.new([:foo]).input(:first, :id=>'bar').to_s.should == '<input id="bar" name="first" type="text" value="foo"/>'
|
516
|
+
Forme::Form.new([:foo]).input(:first, :value=>'bar').to_s.should == '<input id="first" name="first" type="text" value="bar"/>'
|
517
|
+
Forme::Form.new([:foo]).input(:first, :attr=>{:x=>'bar'}).to_s.should == '<input id="first" name="first" type="text" value="foo" x="bar"/>'
|
518
|
+
end
|
519
|
+
|
520
|
+
specify "should handle obj passed in via :obj hash key" do
|
521
|
+
Forme::Form.new(:obj=>[:foo]).input(:first).to_s.should == '<input id="first" name="first" type="text" value="foo"/>'
|
522
|
+
end
|
523
|
+
|
524
|
+
specify "should be able to turn off obj handling per input using :obj=>nil option" do
|
525
|
+
Forme::Form.new([:foo]).input(:checkbox, :name=>"foo", :hidden_value=>"no", :obj=>nil).to_s.should == '<input name="foo" type="hidden" value="no"/><input name="foo" type="checkbox"/>'
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
describe "Forme.form DSL" do
|
530
|
+
specify "should return a form tag" do
|
531
|
+
Forme.form.to_s.should == '<form></form>'
|
532
|
+
end
|
533
|
+
|
534
|
+
specify "should yield a Form object to the block" do
|
535
|
+
Forme.form{|f| f.should be_a_kind_of(Forme::Form)}
|
536
|
+
end
|
537
|
+
|
538
|
+
specify "should have inputs called instead the block be added to the existing form" do
|
539
|
+
Forme.form{|f| f.input(:text)}.to_s.should == '<form><input type="text"/></form>'
|
540
|
+
end
|
541
|
+
|
542
|
+
specify "should be able to nest inputs inside tags" do
|
543
|
+
Forme.form{|f| f.tag(:div){f.input(:text)}}.to_s.should == '<form><div><input type="text"/></div></form>'
|
544
|
+
Forme.form{|f| f.tag(:div){f.tag(:fieldset){f.input(:text)}}}.to_s.should == '<form><div><fieldset><input type="text"/></fieldset></div></form>'
|
545
|
+
Forme.form{|f| f.tag(:div){f.tag(:fieldset){f.tag(:span){f.input(:text)}}}}.to_s.should == '<form><div><fieldset><span><input type="text"/></span></fieldset></div></form>'
|
546
|
+
Forme.form{|f| f.tag(:div){f.input(:text)}; f.input(:radio)}.to_s.should == '<form><div><input type="text"/></div><input type="radio"/></form>'
|
547
|
+
Forme.form{|f| f.tag(:div){f.tag(:fieldset){f.input(:text); f.input(:radio)}; f.input(:checkbox)}}.to_s.should == '<form><div><fieldset><input type="text"/><input type="radio"/></fieldset><input type="checkbox"/></div></form>'
|
548
|
+
end
|
549
|
+
|
550
|
+
specify "should handle an :inputs option to automatically create inputs" do
|
551
|
+
Forme.form({}, :inputs=>[:text, :textarea]).to_s.should == '<form><fieldset class="inputs"><input type="text"/><textarea></textarea></fieldset></form>'
|
552
|
+
end
|
553
|
+
|
554
|
+
specify "should handle a :legend option if inputs is used" do
|
555
|
+
Forme.form({}, :inputs=>[:text, :textarea], :legend=>'Foo').to_s.should == '<form><fieldset class="inputs"><legend>Foo</legend><input type="text"/><textarea></textarea></fieldset></form>'
|
556
|
+
end
|
557
|
+
|
558
|
+
specify "should still work with a block if :inputs is used" do
|
559
|
+
Forme.form({}, :inputs=>[:text]){|f| f.input(:textarea)}.to_s.should == '<form><fieldset class="inputs"><input type="text"/></fieldset><textarea></textarea></form>'
|
560
|
+
end
|
561
|
+
|
562
|
+
specify "should handle an :button option to automatically create a button" do
|
563
|
+
Forme.form({}, :button=>'Foo').to_s.should == '<form><input type="submit" value="Foo"/></form>'
|
564
|
+
end
|
565
|
+
|
566
|
+
specify "should allow :button option value to be a hash" do
|
567
|
+
Forme.form({}, :button=>{:value=>'Foo', :name=>'bar'}).to_s.should == '<form><input name="bar" type="submit" value="Foo"/></form>'
|
568
|
+
end
|
569
|
+
|
570
|
+
specify "should handle an :button option work with a block" do
|
571
|
+
Forme.form({}, :button=>'Foo'){|f| f.input(:textarea)}.to_s.should == '<form><textarea></textarea><input type="submit" value="Foo"/></form>'
|
572
|
+
end
|
573
|
+
|
574
|
+
specify "should have an :button and :inputs option work together" do
|
575
|
+
Forme.form({}, :inputs=>[:text, :textarea], :button=>'Foo').to_s.should == '<form><fieldset class="inputs"><input type="text"/><textarea></textarea></fieldset><input type="submit" value="Foo"/></form>'
|
576
|
+
end
|
577
|
+
|
578
|
+
end
|
579
|
+
|