forme 0.9.2 → 0.10.0

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.
data/lib/forme/rails.rb CHANGED
@@ -53,8 +53,8 @@ module Forme
53
53
 
54
54
  # If a block is not given, emit the inputs into the current output
55
55
  # buffer.
56
- def _inputs(*)
57
- if block_given?
56
+ def _inputs(inputs=[], opts={}) # :nodoc:
57
+ if block_given? && !opts[:subform]
58
58
  super
59
59
  else
60
60
  emit(super)
@@ -83,8 +83,8 @@ module Forme
83
83
  template.raw(tag.to_s)
84
84
  end
85
85
  end
86
-
87
- def tag_(type, attr={}, children=[])
86
+
87
+ def tag_(type, attr={}, children=[]) # :nodoc:
88
88
  tag = _tag(type, attr, children)
89
89
  emit(serializer.serialize_open(tag)) if serializer.respond_to?(:serialize_open)
90
90
  Array(tag.children).each{|c| emit(c)}
data/lib/forme/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Forme
2
2
  # Version constant, use <tt>Forme.version</tt> instead.
3
- VERSION = '0.9.2'.freeze
3
+ VERSION = '0.10.0'.freeze
4
4
 
5
5
  # Returns the version as a frozen string (e.g. '0.1.0')
6
6
  def self.version
@@ -15,18 +15,6 @@ module Sequel # :nodoc:
15
15
  # that use a <tt>Sequel::Model</tt> instance as the form's
16
16
  # +obj+.
17
17
  module SequelForm
18
- # Stack of objects used by subform. The current +obj+
19
- # is added to the top of the stack on a call to +subform+,
20
- # the nested associated object is set as the current +obj+ during the
21
- # call to +subform+, and when +subform+ returns, the top of the
22
- # stack is set as the current +obj+.
23
- attr_accessor :nested_associations
24
-
25
- # The namespaces that should be added to the id and name
26
- # attributes for the receiver's inputs. Used as a stack
27
- # by +subform+.
28
- attr_accessor :namespaces
29
-
30
18
  # Use the post method by default for Sequel forms, unless
31
19
  # overridden with the :method attribute.
32
20
  def form(attr={}, &block)
@@ -53,26 +41,31 @@ module Sequel # :nodoc:
53
41
  # :inputs :: Automatically call +inputs+ with the given values. Using
54
42
  # this, it is not required to pass a block to the method,
55
43
  # though it will still work if you do.
56
- # :legend :: If :inputs is also used, this is passed to it to override
57
- # the default :legend used. You can also use a proc as the value,
44
+ # :legend :: Overrides the default :legend used (which is based on the
45
+ # association name). You can also use a proc as the value,
58
46
  # which will called with each associated object (and the position
59
47
  # in the associated object already for *_to_many associations),
60
48
  # and should return the legend string to use for that object.
49
+ # :grid :: Sets up a table with one row per associated object, and
50
+ # one column per field.
51
+ # :labels :: When using the :grid option, override the labels that would
52
+ # be created via the :inputs option. If you are not providing
53
+ # an :inputs option or are using a block with additional inputs,
54
+ # you should specify this option.
61
55
  def subform(association, opts={}, &block)
62
56
  nested_obj = opts.has_key?(:obj) ? opts[:obj] : obj.send(association)
63
57
  ref = obj.class.association_reflection(association)
64
58
  multiple = ref.returns_array?
65
- i = -1
66
- ins = opts[:inputs]
67
- Array(nested_obj).each do |no|
68
- begin
69
- nested_associations << obj
70
- namespaces << "#{association}_attributes"
71
- namespaces << (i+=1) if multiple
72
- @obj = no
73
- emit(input(ref.associated_class.primary_key, :type=>:hidden, :label=>nil)) unless no.new?
74
- if ins
75
- options = opts.dup
59
+ grid = opts[:grid]
60
+ ns = "#{association}_attributes"
61
+
62
+ contents = proc do
63
+ send(multiple ? :each_obj : :with_obj, nested_obj, ns) do |no, i|
64
+ emit(input(ref.associated_class.primary_key, :type=>:hidden, :label=>nil, :wrapper=>nil)) unless no.new?
65
+ options = opts.dup
66
+ if grid
67
+ options.delete(:legend)
68
+ else
76
69
  if options.has_key?(:legend)
77
70
  if options[:legend].respond_to?(:call)
78
71
  options[:legend] = multiple ? options[:legend].call(no, i) : options[:legend].call(no)
@@ -84,32 +77,21 @@ module Sequel # :nodoc:
84
77
  options[:legend] = humanize(association)
85
78
  end
86
79
  end
87
- _inputs(ins, options, &block)
88
- else
89
- yield
90
80
  end
91
- ensure
92
- @obj = nested_associations.pop
93
- namespaces.pop if multiple
94
- namespaces.pop
81
+ options[:subform] = true
82
+ _inputs(options[:inputs]||[], options, &block)
95
83
  end
96
84
  end
85
+
86
+ if grid
87
+ labels = opts.fetch(:labels){opts[:inputs].map{|l, *| humanize(l)} if opts[:inputs]}
88
+ legend = opts.fetch(:legend){humanize(association)}
89
+ inputs({:inputs_wrapper=>:table, :nested_inputs_wrapper=>:tr, :wrapper=>:td, :labeler=>nil, :labels=>labels, :legend=>legend}, &contents)
90
+ else
91
+ contents.call
92
+ end
97
93
  nil
98
94
  end
99
-
100
- # Return a unique id attribute for the +field+, handling
101
- # nested attributes use.
102
- def namespaced_id(field)
103
- "#{namespaces.join('_')}_#{field}"
104
- end
105
-
106
- # Return a unique name attribute for the +field+, handling nested
107
- # attribute use. If +multiple+ is true, end the name
108
- # with [] so that param parsing will treat the name as part of an array.
109
- def namespaced_name(field, multiple=false)
110
- root, *nsps = namespaces
111
- "#{root}#{nsps.map{|n| "[#{n}]"}.join}[#{field}]#{'[]' if multiple}"
112
- end
113
95
  end
114
96
 
115
97
  # Helper class for dealing with Forme/Sequel integration.
@@ -157,8 +139,7 @@ module Sequel # :nodoc:
157
139
  type = opts[:type]
158
140
  if !type && (sch = obj.db_schema[field])
159
141
  meth = :"input_#{sch[:type]}"
160
- opts[:id] = form.namespaced_id(field) unless opts.has_key?(:id)
161
- opts[:name] = form.namespaced_name(field) unless opts.has_key?(:name)
142
+ opts[:key] = field unless opts.has_key?(:key)
162
143
  opts[:required] = true if !opts.has_key?(:required) && sch[:allow_null] == false && sch[:type] != :boolean
163
144
  handle_label(field)
164
145
 
@@ -183,8 +164,7 @@ module Sequel # :nodoc:
183
164
  raise(Error, "Unrecognized field used: #{field}") unless rt || type
184
165
  meth = :"input_#{type}"
185
166
  opts[:value] = nil unless rt || opts.has_key?(:value)
186
- opts[:id] = form.namespaced_id(field) unless opts.has_key?(:id)
187
- opts[:name] = form.namespaced_name(field, opts[:multiple]) unless opts.has_key?(:name)
167
+ opts[:key] = field unless opts.has_key?(:key)
188
168
  handle_label(field)
189
169
  if respond_to?(meth, true)
190
170
  opts.delete(:type)
@@ -217,22 +197,6 @@ module Sequel # :nodoc:
217
197
  opts[:label] = [opts[:label], form._tag(:abbr, {:title=>'required'}, '*')] if opts[:required]
218
198
  end
219
199
 
220
- # Add the label to the start of the array, returning the array.
221
- def add_label(label, array)
222
- array.unshift(form._tag(:span, {:class=>:label}, label)) if label
223
- array
224
- end
225
-
226
- # Unset the wrapper and tag_wrapper options and return a
227
- # array with the wrapper and tag_wrapper to use. The tag_wrapper
228
- # is for wrapping each individual tag.
229
- def get_wrappers
230
- tag_wrapper = opts.delete(:tag_wrapper) || :default
231
- wrapper = form.transformer(:wrapper, opts)
232
- opts.delete(:wrapper)
233
- [wrapper, tag_wrapper]
234
- end
235
-
236
200
  # Update the attributes and options for any recognized validations
237
201
  def handle_validations(f)
238
202
  m = obj.model
@@ -296,19 +260,13 @@ module Sequel # :nodoc:
296
260
  def association_many_to_one(ref)
297
261
  key = ref[:key]
298
262
  handle_errors(key)
299
- opts[:name] = form.namespaced_name(key) unless opts.has_key?(:name)
263
+ opts[:key] = key unless opts.has_key?(:key)
300
264
  opts[:value] = obj.send(key) unless opts.has_key?(:value)
301
265
  opts[:options] = association_select_options(ref) unless opts.has_key?(:options)
302
266
  if opts.delete(:as) == :radio
303
267
  handle_label(field)
304
- label = opts.delete(:label)
305
- val = opts.delete(:value)
306
- wrapper, tag_wrapper = get_wrappers
307
- radios = opts.delete(:options).map{|l, pk| _input(:radio, opts.merge(:value=>pk, :id=>"#{form.namespaced_id(key)}_#{pk}", :wrapper=>tag_wrapper, :label=>l, :label_attr=>{:class=>:option}, :checked=>(pk == val)))}
308
- add_label(label, radios)
309
- wrapper ? wrapper.call(radios, _input(:radio, opts)) : radios
268
+ _input(:radioset, opts)
310
269
  else
311
- opts[:id] = form.namespaced_id(key) unless opts.has_key?(:id)
312
270
  opts[:required] = true if !opts.has_key?(:required) && (sch = obj.model.db_schema[key]) && !sch[:allow_null]
313
271
  opts[:add_blank] = true if !opts.has_key?(:add_blank) && !(opts[:required] && opts[:value])
314
272
  handle_label(field)
@@ -327,19 +285,16 @@ module Sequel # :nodoc:
327
285
  klass = ref.associated_class
328
286
  pk = klass.primary_key
329
287
  field = "#{klass.send(:singularize, ref[:name])}_pks"
330
- opts[:name] = form.namespaced_name(field, :multiple) unless opts.has_key?(:name)
288
+ unless opts.has_key?(:key)
289
+ opts[:array] = true unless opts.has_key?(:array)
290
+ opts[:key] = field
291
+ end
331
292
  opts[:value] = obj.send(ref[:name]).map{|x| x.send(pk)} unless opts.has_key?(:value)
332
293
  opts[:options] = association_select_options(ref) unless opts.has_key?(:options)
333
294
  handle_label(field)
334
295
  if opts.delete(:as) == :checkbox
335
- label = opts.delete(:label)
336
- val = opts.delete(:value)
337
- wrapper, tag_wrapper = get_wrappers
338
- cbs = opts.delete(:options).map{|l, pk| _input(:checkbox, opts.merge(:value=>pk, :id=>"#{form.namespaced_id(field)}_#{pk}", :wrapper=>tag_wrapper, :label=>l, :label_attr=>{:class=>:option}, :checked=>val.include?(pk), :no_hidden=>true))}
339
- add_label(label, cbs)
340
- wrapper ? wrapper.call(cbs, _input(:checkbox, opts)) : cbs
296
+ _input(:checkboxset, opts)
341
297
  else
342
- opts[:id] = form.namespaced_id(field) unless opts.has_key?(:id)
343
298
  opts[:multiple] = true unless opts.has_key?(:multiple)
344
299
  _input(:select, opts)
345
300
  end
@@ -385,19 +340,14 @@ module Sequel # :nodoc:
385
340
 
386
341
  case opts[:as]
387
342
  when :radio
388
- wrapper, tag_wrapper = get_wrappers
389
- true_opts = opts.merge(:value=>opts[:true_value]||'t', :label=>opts[:true_label]||'Yes', :label_attr=>{:class=>:option}, :error=>nil, :wrapper=>tag_wrapper, :wrapper_attr=>{})
390
- false_opts = opts.merge(:value=>opts[:false_value]||'f', :label=>opts[:false_label]||'No', :label_attr=>{:class=>:option}, :wrapper=>tag_wrapper, :wrapper_attr=>{})
391
- if i = opts[:id]
392
- true_opts[:id] = "#{i}_yes"
393
- false_opts[:id] = "#{i}_no"
394
- end
395
343
  v = opts.has_key?(:value) ? opts[:value] : obj.send(field)
344
+ true_value = opts[:true_value]||'t'
345
+ false_value = opts[:false_value]||'f'
346
+ opts[:options] = [[opts[:true_label]||'Yes', {:value=>true_value, :key_id=>'yes'}], [opts[:false_label]||'No', {:value=>false_value, :key_id=>'no'}]]
396
347
  unless v.nil?
397
- (v ? true_opts : false_opts)[:checked] = true
348
+ opts[:value] = v ? true_value : false_value
398
349
  end
399
- array = add_label(opts[:label], [_input(:radio, true_opts), _input(:radio, false_opts)])
400
- wrapper ? wrapper.call(array, _input(:radio, opts)) : array
350
+ _input(:radioset, opts)
401
351
  when :select
402
352
  v = opts[:value] || obj.send(field)
403
353
  opts[:value] = (v ? 't' : 'f') unless v.nil?
@@ -487,8 +437,7 @@ module Sequel # :nodoc:
487
437
  # specific code, such as support for nested attributes.
488
438
  def forme_config(form)
489
439
  form.extend(SequelForm)
490
- form.nested_associations = []
491
- form.namespaces = [model.send(:underscore, model.name)]
440
+ form.namespaces << model.send(:underscore, model.name)
492
441
  form.extend(SinatraSequelForm) if defined?(::Forme::Sinatra::Form) && form.is_a?(::Forme::Sinatra::Form)
493
442
  end
494
443
 
@@ -0,0 +1,13 @@
1
+ require 'coverage'
2
+ require 'simplecov'
3
+
4
+ def SimpleCov.forme_coverage(opts = {})
5
+ start do
6
+ add_filter "/spec/"
7
+ add_group('Missing'){|src| src.covered_percent < 100}
8
+ add_group('Covered'){|src| src.covered_percent == 100}
9
+ yield self if block_given?
10
+ end
11
+ end
12
+
13
+ ENV.delete('COVERAGE')
data/spec/forme_spec.rb CHANGED
@@ -45,6 +45,136 @@ describe "Forme plain forms" do
45
45
  @f.input(:text, :style=>"foo").to_s.should == '<input style="foo" type="text"/>'
46
46
  end
47
47
 
48
+ specify "should use :key option as name and id attributes" do
49
+ @f.input(:text, :key=>"foo").to_s.should == '<input id="foo" name="foo" type="text"/>'
50
+ end
51
+
52
+ specify "should use :key_id option as suffix for :key option id attributes" do
53
+ @f.input(:text, :key=>"foo", :key_id=>'bar').to_s.should == '<input id="foo_bar" name="foo" type="text"/>'
54
+ end
55
+
56
+ specify "should have :key option respect :multiple option" do
57
+ @f.input(:text, :key=>"foo", :multiple=>true).to_s.should == '<input id="foo" name="foo[]" type="text"/>'
58
+ end
59
+
60
+ specify "should use :key option respect form's current namespace" do
61
+ @f.with_opts(:namespace=>['bar']) do
62
+ @f.input(:text, :key=>"foo").to_s.should == '<input id="bar_foo" name="bar[foo]" type="text"/>'
63
+ @f.input(:text, :key=>"foo", :multiple=>true).to_s.should == '<input id="bar_foo" name="bar[foo][]" type="text"/>'
64
+ @f.with_opts(:namespace=>['bar', 'baz']) do
65
+ @f.input(:text, :key=>"foo").to_s.should == '<input id="bar_baz_foo" name="bar[baz][foo]" type="text"/>'
66
+ end
67
+ end
68
+ end
69
+
70
+ specify "should consider form's :values hash for default values based on the :key option if :value is not present" do
71
+ @f.opts[:values] = {'foo'=>'baz'}
72
+ @f.input(:text, :key=>"foo").to_s.should == '<input id="foo" name="foo" type="text" value="baz"/>'
73
+ @f.input(:text, :key=>"foo", :value=>'x').to_s.should == '<input id="foo" name="foo" type="text" value="x"/>'
74
+
75
+ @f.input(:text, :key=>:foo).to_s.should == '<input id="foo" name="foo" type="text" value="baz"/>'
76
+ @f.opts[:values] = {:foo=>'baz'}
77
+ @f.input(:text, :key=>:foo).to_s.should == '<input id="foo" name="foo" type="text" value="baz"/>'
78
+ end
79
+
80
+ specify "should consider form's :values hash for default values based on the :key option when using namespaces" do
81
+ @f.opts[:values] = {'bar'=>{'foo'=>'baz'}}
82
+ @f.with_opts(:namespace=>['bar']) do
83
+ @f.input(:text, :key=>"foo").to_s.should == '<input id="bar_foo" name="bar[foo]" type="text" value="baz"/>'
84
+ @f.input(:text, :key=>"foo", :value=>'x').to_s.should == '<input id="bar_foo" name="bar[foo]" type="text" value="x"/>'
85
+ @f.input(:text, :key=>:foo).to_s.should == '<input id="bar_foo" name="bar[foo]" type="text" value="baz"/>'
86
+ end
87
+
88
+ @f.with_opts(:namespace=>[:bar]) do
89
+ @f.input(:text, :key=>:foo).to_s.should == '<input id="bar_foo" name="bar[foo]" type="text" value="baz"/>'
90
+
91
+ @f.opts[:values] = {:bar=>{:foo=>'baz'}}
92
+ @f.input(:text, :key=>:foo).to_s.should == '<input id="bar_foo" name="bar[foo]" type="text" value="baz"/>'
93
+ @f.opts[:values] = {:bar=>{}}
94
+ @f.input(:text, :key=>:foo).to_s.should == '<input id="bar_foo" name="bar[foo]" type="text"/>'
95
+ @f.opts[:values] = {}
96
+ @f.input(:text, :key=>:foo).to_s.should == '<input id="bar_foo" name="bar[foo]" type="text"/>'
97
+
98
+ @f.opts[:values] = {'bar'=>{'quux'=>{'foo'=>'baz'}}}
99
+ @f.with_opts(:namespace=>['bar', 'quux']) do
100
+ @f.input(:text, :key=>"foo").to_s.should == '<input id="bar_quux_foo" name="bar[quux][foo]" type="text" value="baz"/>'
101
+ end
102
+ end
103
+ end
104
+
105
+ specify "should support a with_obj method that changes the object and namespace for the given block" do
106
+ @f.with_obj([:a, :c], 'bar') do
107
+ @f.input(:first).to_s.should == '<input id="bar_first" name="bar[first]" type="text" value="a"/>'
108
+ @f.with_obj([:b], 'baz') do
109
+ @f.input(:first).to_s.should == '<input id="bar_baz_first" name="bar[baz][first]" type="text" value="b"/>'
110
+ end
111
+ @f.with_obj([:b], %w'baz quux') do
112
+ @f.input(:first).to_s.should == '<input id="bar_baz_quux_first" name="bar[baz][quux][first]" type="text" value="b"/>'
113
+ end
114
+ @f.with_obj([:b]) do
115
+ @f.input(:first).to_s.should == '<input id="bar_first" name="bar[first]" type="text" value="b"/>'
116
+ end
117
+ @f.input(:last).to_s.should == '<input id="bar_last" name="bar[last]" type="text" value="c"/>'
118
+ end
119
+ end
120
+
121
+ specify "should support a each_obj method that changes the object and namespace for multiple objects for the given block" do
122
+ @f.tag(:form) do
123
+ @f.each_obj([[:a, :c], [:b, :d]], 'bar') do
124
+ @f.input(:first)
125
+ @f.input(:last)
126
+ end
127
+ end.to_s.should == '<form><input id="bar_0_first" name="bar[0][first]" type="text" value="a"/><input id="bar_0_last" name="bar[0][last]" type="text" value="c"/><input id="bar_1_first" name="bar[1][first]" type="text" value="b"/><input id="bar_1_last" name="bar[1][last]" type="text" value="d"/></form>'
128
+
129
+ @f.tag(:form) do
130
+ @f.each_obj([[:a, :c], [:b, :d]], %w'bar baz') do
131
+ @f.input(:first)
132
+ @f.input(:last)
133
+ end
134
+ end.to_s.should == '<form><input id="bar_baz_0_first" name="bar[baz][0][first]" type="text" value="a"/><input id="bar_baz_0_last" name="bar[baz][0][last]" type="text" value="c"/><input id="bar_baz_1_first" name="bar[baz][1][first]" type="text" value="b"/><input id="bar_baz_1_last" name="bar[baz][1][last]" type="text" value="d"/></form>'
135
+
136
+ @f.tag(:form) do
137
+ @f.each_obj([[:a, :c], [:b, :d]]) do
138
+ @f.input(:first)
139
+ @f.input(:last)
140
+ end
141
+ end.to_s.should == '<form><input id="0_first" name="0[first]" type="text" value="a"/><input id="0_last" name="0[last]" type="text" value="c"/><input id="1_first" name="1[first]" type="text" value="b"/><input id="1_last" name="1[last]" type="text" value="d"/></form>'
142
+ end
143
+
144
+ specify "should allow overriding form inputs on a per-block basis" do
145
+ @f.input(:text).to_s.should == '<input type="text"/>'
146
+ @f.with_opts(:wrapper=>:div){@f.input(:text).to_s}.should == '<div><input type="text"/></div>'
147
+ @f.with_opts(:wrapper=>:div){@f.input(:text).to_s.should == '<div><input type="text"/></div>'}
148
+ @f.with_opts(:wrapper=>:div) do
149
+ @f.input(:text).to_s.should == '<div><input type="text"/></div>'
150
+ @f.with_opts(:wrapper=>:li){@f.input(:text).to_s.should == '<li><input type="text"/></li>'}
151
+ @f.input(:text).to_s.should == '<div><input type="text"/></div>'
152
+ end
153
+ @f.input(:text).to_s.should == '<input type="text"/>'
154
+ end
155
+
156
+ specify "should handle delayed formatting when overriding form inputs on a per-block basis" do
157
+ @f.form do
158
+ @f.input(:text)
159
+ @f.with_opts(:wrapper=>:div) do
160
+ @f.input(:text)
161
+ @f.with_opts(:wrapper=>:li){@f.input(:text)}
162
+ @f.input(:text)
163
+ end
164
+ @f.input(:text)
165
+ end.to_s.should == '<form><input type="text"/><div><input type="text"/></div><li><input type="text"/></li><div><input type="text"/></div><input type="text"/></form>'
166
+ end
167
+
168
+ specify "should support :obj method to with_opts for changing the obj inside the block" do
169
+ @f.form do
170
+ @f.with_opts(:obj=>[:a, :c]) do
171
+ @f.input(:first)
172
+ @f.with_opts(:obj=>[:b]){@f.input(:first)}
173
+ @f.input(:last)
174
+ end
175
+ end.to_s.should == '<form><input id="first" name="first" type="text" value="a"/><input id="first" name="first" type="text" value="b"/><input id="last" name="last" type="text" value="c"/></form>'
176
+ end
177
+
48
178
  specify "should allow arbitrary attributes using the :attr option" do
49
179
  @f.input(:text, :attr=>{:bar=>"foo"}).to_s.should == '<input bar="foo" type="text"/>'
50
180
  end
@@ -157,10 +287,95 @@ describe "Forme plain forms" do
157
287
  @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>'
158
288
  end
159
289
 
290
+ specify "should use Forme.default_add_blank_prompt value if :add_blank option is true" do
291
+ begin
292
+ Forme.default_add_blank_prompt = 'foo'
293
+ @f.input(:select, :options=>[[:b, 2], [:c, 3]], :add_blank=>true, :value=>2).to_s.should == '<select><option value="">foo</option><option selected="selected" value="2">b</option><option value="3">c</option></select>'
294
+ ensure
295
+ Forme.default_add_blank_prompt = nil
296
+ end
297
+ end
298
+
160
299
  specify "should use :add_blank option value as prompt if it is a String" do
161
300
  @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>'
162
301
  end
163
302
 
303
+ specify "should create set of radio buttons" do
304
+ @f.input(:radioset, :options=>[1, 2, 3], :selected=>2).to_s.should == '<label class="option"><input type="radio" value="1"/> 1</label><label class="option"><input checked="checked" type="radio" value="2"/> 2</label><label class="option"><input type="radio" value="3"/> 3</label>'
305
+ @f.input(:radioset, :options=>[1, 2, 3], :value=>2).to_s.should == '<label class="option"><input type="radio" value="1"/> 1</label><label class="option"><input checked="checked" type="radio" value="2"/> 2</label><label class="option"><input type="radio" value="3"/> 3</label>'
306
+ end
307
+
308
+ specify "should create set of radio buttons with options and values" do
309
+ @f.input(:radioset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :selected=>2).to_s.should == '<label class="option"><input type="radio" value="1"/> a</label><label class="option"><input checked="checked" type="radio" value="2"/> b</label><label class="option"><input type="radio" value="3"/> c</label>'
310
+ end
311
+
312
+ specify "should create set of radio buttons with options and values with hashes" do
313
+ @f.input(:radioset, :options=>[[:a, {:attr=>{:foo=>1}}], [:b, {:class=>'foo', :value=>2}], [:c, {:id=>:baz}]], :selected=>2).to_s.should == '<label class="option"><input foo="1" type="radio" value="a"/> a</label><label class="option"><input checked="checked" class="foo" type="radio" value="2"/> b</label><label class="option"><input id="baz" type="radio" value="c"/> c</label>'
314
+ end
315
+
316
+ specify "should create set of radio buttons with options and values using given method" do
317
+ @f.input(:radioset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :text_method=>:last, :selected=>2).to_s.should == '<label class="option"><input type="radio" value="1"/> 1</label><label class="option"><input checked="checked" type="radio" value="2"/> 2</label><label class="option"><input type="radio" value="3"/> 3</label>'
318
+ @f.input(:radioset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :text_method=>:last, :value_method=>:first, :selected=>:b).to_s.should == '<label class="option"><input type="radio" value="a"/> 1</label><label class="option"><input checked="checked" type="radio" value="b"/> 2</label><label class="option"><input type="radio" value="c"/> 3</label>'
319
+ end
320
+
321
+ specify "should support :add_blank option for radioset inputs" do
322
+ @f.input(:radioset, :options=>[[:b, 2], [:c, 3]], :add_blank=>true, :value=>2).to_s.should == '<label class="option"><input type="radio" value=""/> </label><label class="option"><input checked="checked" type="radio" value="2"/> b</label><label class="option"><input type="radio" value="3"/> c</label>'
323
+ end
324
+
325
+ specify "should use :add_blank option value as prompt if it is a String" do
326
+ @f.input(:radioset, :options=>[[:b, 2], [:c, 3]], :add_blank=>"Prompt Here", :value=>2).to_s.should == '<label class="option"><input type="radio" value=""/> Prompt Here</label><label class="option"><input checked="checked" type="radio" value="2"/> b</label><label class="option"><input type="radio" value="3"/> c</label>'
327
+ end
328
+
329
+ specify "should respect the :key option for radio sets" do
330
+ @f.input(:radioset, :options=>[1, 2, 3], :key=>:foo, :value=>2).to_s.should == '<label class="option"><input id="foo_1" name="foo" type="radio" value="1"/> 1</label><label class="option"><input checked="checked" id="foo_2" name="foo" type="radio" value="2"/> 2</label><label class="option"><input id="foo_3" name="foo" type="radio" value="3"/> 3</label>'
331
+ end
332
+
333
+ specify "should create set of checkbox buttons" do
334
+ @f.input(:checkboxset, :options=>[1, 2, 3], :selected=>2).to_s.should == '<label class="option"><input type="checkbox" value="1"/> 1</label><label class="option"><input checked="checked" type="checkbox" value="2"/> 2</label><label class="option"><input type="checkbox" value="3"/> 3</label>'
335
+ @f.input(:checkboxset, :options=>[1, 2, 3], :value=>2).to_s.should == '<label class="option"><input type="checkbox" value="1"/> 1</label><label class="option"><input checked="checked" type="checkbox" value="2"/> 2</label><label class="option"><input type="checkbox" value="3"/> 3</label>'
336
+ end
337
+
338
+ specify "should create set of checkbox buttons with options and values" do
339
+ @f.input(:checkboxset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :selected=>2).to_s.should == '<label class="option"><input type="checkbox" value="1"/> a</label><label class="option"><input checked="checked" type="checkbox" value="2"/> b</label><label class="option"><input type="checkbox" value="3"/> c</label>'
340
+ end
341
+
342
+ specify "should create set of checkbox buttons with options and values with hashes" do
343
+ @f.input(:checkboxset, :options=>[[:a, {:attr=>{:foo=>1}}], [:b, {:class=>'foo', :value=>2}], [:c, {:id=>:baz}]], :selected=>2).to_s.should == '<label class="option"><input foo="1" type="checkbox" value="a"/> a</label><label class="option"><input checked="checked" class="foo" type="checkbox" value="2"/> b</label><label class="option"><input id="baz" type="checkbox" value="c"/> c</label>'
344
+ end
345
+
346
+ specify "should create set of checkbox buttons with options and values using given method" do
347
+ @f.input(:checkboxset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :text_method=>:last, :selected=>2).to_s.should == '<label class="option"><input type="checkbox" value="1"/> 1</label><label class="option"><input checked="checked" type="checkbox" value="2"/> 2</label><label class="option"><input type="checkbox" value="3"/> 3</label>'
348
+ @f.input(:checkboxset, :options=>[[:a, 1], [:b, 2], [:c, 3]], :text_method=>:last, :value_method=>:first, :selected=>:b).to_s.should == '<label class="option"><input type="checkbox" value="a"/> 1</label><label class="option"><input checked="checked" type="checkbox" value="b"/> 2</label><label class="option"><input type="checkbox" value="c"/> 3</label>'
349
+ end
350
+
351
+ specify "should support :add_blank option for checkboxset inputs" do
352
+ @f.input(:checkboxset, :options=>[[:b, 2], [:c, 3]], :add_blank=>true, :value=>2).to_s.should == '<label class="option"><input type="checkbox" value=""/> </label><label class="option"><input checked="checked" type="checkbox" value="2"/> b</label><label class="option"><input type="checkbox" value="3"/> c</label>'
353
+ end
354
+
355
+ specify "should use :add_blank option value as prompt if it is a String" do
356
+ @f.input(:checkboxset, :options=>[[:b, 2], [:c, 3]], :add_blank=>"Prompt Here", :value=>2).to_s.should == '<label class="option"><input type="checkbox" value=""/> Prompt Here</label><label class="option"><input checked="checked" type="checkbox" value="2"/> b</label><label class="option"><input type="checkbox" value="3"/> c</label>'
357
+ end
358
+
359
+ specify "should respect the :key option for checkbox sets" do
360
+ @f.input(:checkboxset, :options=>[1, 2, 3], :key=>:foo, :value=>2).to_s.should == '<label class="option"><input id="foo_1" name="foo[]" type="checkbox" value="1"/> 1</label><label class="option"><input checked="checked" id="foo_2" name="foo[]" type="checkbox" value="2"/> 2</label><label class="option"><input id="foo_3" name="foo[]" type="checkbox" value="3"/> 3</label>'
361
+ end
362
+
363
+ specify "should prefer the :name option to :key option for checkbox sets" do
364
+ @f.input(:checkboxset, :options=>[1, 2, 3], :key=>:foo, :name=>'bar[]', :value=>2).to_s.should == '<label class="option"><input id="foo_1" name="bar[]" type="checkbox" value="1"/> 1</label><label class="option"><input checked="checked" id="foo_2" name="bar[]" type="checkbox" value="2"/> 2</label><label class="option"><input id="foo_3" name="bar[]" type="checkbox" value="3"/> 3</label>'
365
+ end
366
+
367
+ specify "should prefer the :name and :id option to :key option for checkbox sets" do
368
+ @f.input(:checkboxset, :options=>[1, 2, 3], :key=>:foo, :name=>'bar[]', :id=>:baz, :value=>2).to_s.should == '<label class="option"><input id="baz_1" name="bar[]" type="checkbox" value="1"/> 1</label><label class="option"><input checked="checked" id="baz_2" name="bar[]" type="checkbox" value="2"/> 2</label><label class="option"><input id="baz_3" name="bar[]" type="checkbox" value="3"/> 3</label>'
369
+ end
370
+
371
+ specify "should respect the :error option for checkbox sets" do
372
+ @f.input(:checkboxset, :options=>[1, 2, 3], :error=>'foo', :value=>2).to_s.should == '<label class="option"><input type="checkbox" value="1"/> 1</label><label class="option"><input checked="checked" type="checkbox" value="2"/> 2</label><label class="option"><input class="error" type="checkbox" value="3"/> 3</label><span class="error_message">foo</span>'
373
+ end
374
+
375
+ specify "should raise an Error for empty checkbox sets" do
376
+ @f.input(:checkboxset, :options=>[], :error=>'foo', :value=>2).to_s.should == '<span class="error_message">foo</span>'
377
+ end
378
+
164
379
  specify "radio and checkbox inputs should handle :checked option" do
165
380
  @f.input(:radio, :checked=>true).to_s.should == '<input checked="checked" type="radio"/>'
166
381
  @f.input(:radio, :checked=>false).to_s.should == '<input type="radio"/>'
@@ -168,6 +383,11 @@ describe "Forme plain forms" do
168
383
  @f.input(:checkbox, :checked=>false).to_s.should == '<input type="checkbox"/>'
169
384
  end
170
385
 
386
+ specify "inputs should handle :autofocus option" do
387
+ @f.input(:text, :autofocus=>true).to_s.should == '<input autofocus="autofocus" type="text"/>'
388
+ @f.input(:text, :autofocus=>false).to_s.should == '<input type="text"/>'
389
+ end
390
+
171
391
  specify "inputs should handle :required option" do
172
392
  @f.input(:text, :required=>true).to_s.should == '<input required="required" type="text"/>'
173
393
  @f.input(:text, :required=>false).to_s.should == '<input type="text"/>'
@@ -283,6 +503,14 @@ describe "Forme plain forms" do
283
503
  @f.inputs([[:textarea, {:name=>'foo'}], [:text, {:id=>'bar'}]]).to_s.should == '<fieldset class="inputs"><textarea name="foo"></textarea><input id="bar" type="text"/></fieldset>'
284
504
  end
285
505
 
506
+ specify "should have #inputs accept transformer options to modify the options inside the inputs" do
507
+ @f.inputs([:textarea, :text], :wrapper=>:div).to_s.should == '<fieldset class="inputs"><div><textarea></textarea></div><div><input type="text"/></div></fieldset>'
508
+ end
509
+
510
+ specify "should have #inputs accept :nested_inputs_wrapper options to modify the :input_wrapper option inside the inputs" do
511
+ @f.inputs(:nested_inputs_wrapper=>:div){@f.inputs([:textarea, :text])}.to_s.should == '<fieldset class="inputs"><div><textarea></textarea><input type="text"/></div></fieldset>'
512
+ end
513
+
286
514
  specify "should escape tag content" do
287
515
  @f.tag(:div, {}, ['<p></p>']).to_s.should == '<div>&lt;p&gt;&lt;/p&gt;</div>'
288
516
  end
@@ -376,8 +604,14 @@ describe "Forme plain forms" do
376
604
  f.input(:textarea, :name=>"foo").to_s.should == '<textarea cols="80" name="foo" rows="6"></textarea>'
377
605
  end
378
606
 
607
+ specify "should work with input_defaults with symbol keys using using inputs with symbol keys" do
608
+ f = Forme::Form.new(:input_defaults=>{:text=>{:size=>20}, 'text'=>{:size=>30}})
609
+ f.input(:text, :name=>"foo").to_s.should == '<input name="foo" size="20" type="text"/>'
610
+ f.input('text', :name=>"foo").to_s.should == '<input name="foo" size="30" type="text"/>'
611
+ end
612
+
379
613
  specify "invalid custom transformers should raise an Error" do
380
- proc{Forme::Form.new(:wrapper=>Object.new)}.should raise_error(Forme::Error)
614
+ proc{Forme::Form.new(:wrapper=>Object.new).input(:text).to_s}.should raise_error(Forme::Error)
381
615
  proc{@f.input(:textarea, :wrapper=>Object.new).to_s}.should raise_error(Forme::Error)
382
616
  proc{@f.input(:textarea, :formatter=>nil).to_s}.should raise_error(Forme::Error)
383
617
  end
@@ -415,11 +649,11 @@ end
415
649
 
416
650
  describe "Forme custom" do
417
651
  specify "formatters can be specified as a proc" do
418
- 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>'
652
+ Forme::Form.new(:formatter=>proc{|i| i.form._tag(:textarea, i.opts[:name]=>:name)}).input(:text, :name=>'foo').to_s.should == '<textarea foo="name"></textarea>'
419
653
  end
420
654
 
421
655
  specify "serializers can be specified as a proc" do
422
- Forme::Form.new(:serializer=>proc{|t| "#{t.type} = #{t.opts.inspect}"}).input(:textarea, :name=>'foo').to_s.should == 'textarea = {:name=>"foo"}'
656
+ Forme::Form.new(:serializer=>proc{|t| "#{t.type} = #{t.opts[:name]}"}).input(:textarea, :name=>'foo').to_s.should == 'textarea = foo'
423
657
  end
424
658
 
425
659
  specify "labelers can be specified as a proc" do
@@ -441,7 +675,7 @@ end
441
675
 
442
676
  describe "Forme built-in custom" do
443
677
  specify "transformers should raise if the there is no matching transformer" do
444
- proc{Forme::Form.new(:formatter=>:foo)}.should raise_error(Forme::Error)
678
+ proc{Forme::Form.new(:formatter=>:foo).input(:text).to_s}.should raise_error(Forme::Error)
445
679
  end
446
680
 
447
681
  specify "formatter: disabled disables all inputs unless :disabled=>false option" do
@@ -463,28 +697,42 @@ describe "Forme built-in custom" do
463
697
  Forme::Form.new(:labeler=>:explicit).input(:textarea, :id=>'foo', :label=>'bar').to_s.should == '<label for="foo">bar</label><textarea id="foo"></textarea>'
464
698
  end
465
699
 
700
+ specify "labeler: explicit handles the key option correctly" do
701
+ Forme::Form.new(:labeler=>:explicit, :namespace=>:baz).input(:textarea, :key=>'foo', :label=>'bar').to_s.should == '<label for="baz_foo">bar</label><textarea id="baz_foo" name="baz[foo]"></textarea>'
702
+ end
703
+
466
704
  specify "labeler: explicit should handle tags with errors" do
467
705
  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>'
468
706
  end
469
707
 
470
708
  specify "wrapper: li wraps tag in an li" do
471
709
  Forme::Form.new(:wrapper=>:li).input(:textarea, :id=>'foo').to_s.should == '<li><textarea id="foo"></textarea></li>'
710
+ Forme::Form.new(:wrapper=>:li).input(:textarea, :id=>'foo', :wrapper_attr=>{:id=>'bar'}).to_s.should == '<li id="bar"><textarea id="foo"></textarea></li>'
472
711
  end
473
712
 
474
713
  specify "wrapper: p wraps tag in an p" do
475
714
  Forme::Form.new(:wrapper=>:p).input(:textarea, :id=>'foo').to_s.should == '<p><textarea id="foo"></textarea></p>'
715
+ Forme::Form.new(:wrapper=>:p).input(:textarea, :id=>'foo', :wrapper_attr=>{:id=>'bar'}).to_s.should == '<p id="bar"><textarea id="foo"></textarea></p>'
476
716
  end
477
717
 
478
718
  specify "wrapper: div wraps tag in an div" do
479
719
  Forme::Form.new(:wrapper=>:div).input(:textarea, :id=>'foo').to_s.should == '<div><textarea id="foo"></textarea></div>'
720
+ Forme::Form.new(:wrapper=>:div).input(:textarea, :id=>'foo', :wrapper_attr=>{:id=>'bar'}).to_s.should == '<div id="bar"><textarea id="foo"></textarea></div>'
480
721
  end
481
722
 
482
723
  specify "wrapper: span wraps tag in an span" do
483
724
  Forme::Form.new(:wrapper=>:span).input(:textarea, :id=>'foo').to_s.should == '<span><textarea id="foo"></textarea></span>'
725
+ Forme::Form.new(:wrapper=>:span).input(:textarea, :id=>'foo', :wrapper_attr=>{:id=>'bar'}).to_s.should == '<span id="bar"><textarea id="foo"></textarea></span>'
726
+ end
727
+
728
+ specify "wrapper: td wraps tag in an td" do
729
+ Forme::Form.new(:wrapper=>:td).input(:textarea, :id=>'foo').to_s.should == '<td><textarea id="foo"></textarea></td>'
730
+ Forme::Form.new(:wrapper=>:td).input(:textarea, :id=>'foo', :wrapper_attr=>{:id=>'bar'}).to_s.should == '<td id="bar"><textarea id="foo"></textarea></td>'
484
731
  end
485
732
 
486
733
  specify "wrapper: trtd wraps tag in an tr/td" do
487
734
  Forme::Form.new(:wrapper=>:trtd).input(:textarea, :id=>'foo').to_s.should == '<tr><td><textarea id="foo"></textarea></td><td></td></tr>'
735
+ Forme::Form.new(:wrapper=>:trtd).input(:textarea, :id=>'foo', :wrapper_attr=>{:id=>'bar'}).to_s.should == '<tr id="bar"><td><textarea id="foo"></textarea></td><td></td></tr>'
488
736
  end
489
737
 
490
738
  specify "wrapper: trtd supports multiple tags in separate tds" do
@@ -499,12 +747,44 @@ describe "Forme built-in custom" do
499
747
  Forme::Form.new(:wrapper=>:trtd, :labeler=>:explicit).input(:checkbox, :id=>'foo', :name=>'foo', :label=>'Foo').to_s.should == '<tr><td><label for="foo">Foo</label></td><td><input id="foo_hidden" name="foo" type="hidden" value="0"/><input id="foo" name="foo" type="checkbox"/></td></tr>'
500
748
  end
501
749
 
750
+ specify "wrapper: tr should use a td wrapper and tr inputs_wrapper" do
751
+ Forme::Form.new(:wrapper=>:tr).inputs([:textarea]).to_s.should == '<tr><td><textarea></textarea></td></tr>'
752
+ f = Forme::Form.new
753
+ f.with_opts(:wrapper=>:tr){f.inputs([:textarea])}.to_s.should == '<tr><td><textarea></textarea></td></tr>'
754
+ end
755
+
756
+ specify "wrapper: table should use a trtd wrapper and table inputs_wrapper" do
757
+ Forme::Form.new(:wrapper=>:table).inputs([:textarea]).to_s.should == '<table><tr><td><textarea></textarea></td><td></td></tr></table>'
758
+ f = Forme::Form.new
759
+ f.with_opts(:wrapper=>:table){f.inputs([:textarea])}.to_s.should == '<table><tr><td><textarea></textarea></td><td></td></tr></table>'
760
+ end
761
+
762
+ specify "wrapper: ol should use an li wrapper and ol inputs_wrapper" do
763
+ Forme::Form.new(:wrapper=>:ol).inputs([:textarea]).to_s.should == '<ol><li><textarea></textarea></li></ol>'
764
+ f = Forme::Form.new
765
+ f.with_opts(:wrapper=>:ol){f.inputs([:textarea])}.to_s.should == '<ol><li><textarea></textarea></li></ol>'
766
+ end
767
+
768
+ specify "wrapper: fieldset_ol should use an li wrapper and fieldset_ol inputs_wrapper" do
769
+ Forme::Form.new(:wrapper=>:fieldset_ol).inputs([:textarea]).to_s.should == '<fieldset class="inputs"><ol><li><textarea></textarea></li></ol></fieldset>'
770
+ f = Forme::Form.new
771
+ f.with_opts(:wrapper=>:fieldset_ol){f.inputs([:textarea])}.to_s.should == '<fieldset class="inputs"><ol><li><textarea></textarea></li></ol></fieldset>'
772
+ end
773
+
774
+ specify "wrapper should not override inputs_wrapper if both given" do
775
+ Forme::Form.new(:wrapper=>:tr, :inputs_wrapper=>:div).inputs([:textarea]).to_s.should == '<div><td><textarea></textarea></td></div>'
776
+ f = Forme::Form.new
777
+ f.with_opts(:wrapper=>:tr, :inputs_wrapper=>:div){f.inputs([:textarea])}.to_s.should == '<div><td><textarea></textarea></td></div>'
778
+ end
779
+
502
780
  specify "inputs_wrapper: ol wraps tags in an ol" do
503
781
  Forme::Form.new(:inputs_wrapper=>:ol, :wrapper=>:li).inputs([:textarea]).to_s.should == '<ol><li><textarea></textarea></li></ol>'
782
+ Forme::Form.new(:inputs_wrapper=>:ol, :wrapper=>:li).inputs([:textarea], :attr=>{:foo=>1}).to_s.should == '<ol foo="1"><li><textarea></textarea></li></ol>'
504
783
  end
505
784
 
506
785
  specify "inputs_wrapper: fieldset_ol wraps tags in a fieldset and an ol" do
507
786
  Forme::Form.new(:inputs_wrapper=>:fieldset_ol, :wrapper=>:li).inputs([:textarea]).to_s.should == '<fieldset class="inputs"><ol><li><textarea></textarea></li></ol></fieldset>'
787
+ Forme::Form.new(:inputs_wrapper=>:fieldset_ol, :wrapper=>:li).inputs([:textarea], :attr=>{:foo=>1}).to_s.should == '<fieldset class="inputs" foo="1"><ol><li><textarea></textarea></li></ol></fieldset>'
508
788
  end
509
789
 
510
790
  specify "inputs_wrapper: fieldset_ol supports a :legend option" do
@@ -513,10 +793,33 @@ describe "Forme built-in custom" do
513
793
 
514
794
  specify "inputs_wrapper: div wraps tags in a div" do
515
795
  Forme::Form.new(:inputs_wrapper=>:div, :wrapper=>:span).inputs([:textarea]).to_s.should == '<div><span><textarea></textarea></span></div>'
796
+ Forme::Form.new(:inputs_wrapper=>:div, :wrapper=>:span).inputs([:textarea], :attr=>{:foo=>1}).to_s.should == '<div foo="1"><span><textarea></textarea></span></div>'
797
+ end
798
+
799
+ specify "inputs_wrapper: tr wraps tags in an tr" do
800
+ Forme::Form.new(:inputs_wrapper=>:tr, :wrapper=>:td).inputs([:textarea]).to_s.should == '<tr><td><textarea></textarea></td></tr>'
801
+ Forme::Form.new(:inputs_wrapper=>:tr, :wrapper=>:td).inputs([:textarea], :attr=>{:foo=>1}).to_s.should == '<tr foo="1"><td><textarea></textarea></td></tr>'
516
802
  end
517
803
 
518
804
  specify "inputs_wrapper: table wraps tags in an table" do
519
805
  Forme::Form.new(:inputs_wrapper=>:table, :wrapper=>:trtd).inputs([:textarea]).to_s.should == '<table><tr><td><textarea></textarea></td><td></td></tr></table>'
806
+ Forme::Form.new(:inputs_wrapper=>:table, :wrapper=>:trtd).inputs([:textarea], :attr=>{:foo=>1}).to_s.should == '<table foo="1"><tr><td><textarea></textarea></td><td></td></tr></table>'
807
+ end
808
+
809
+ specify "inputs_wrapper: table accepts a :legend option" do
810
+ Forme::Form.new(:inputs_wrapper=>:table, :wrapper=>:trtd).inputs([:textarea], :legend=>'Inputs').to_s.should == '<table><caption>Inputs</caption><tr><td><textarea></textarea></td><td></td></tr></table>'
811
+ end
812
+
813
+ specify "inputs_wrapper: table accepts a :legend_attr option" do
814
+ Forme::Form.new(:inputs_wrapper=>:table, :wrapper=>:trtd).inputs([:textarea], :legend=>'Inputs', :legend_attr=>{:class=>'foo'}).to_s.should == '<table><caption class="foo">Inputs</caption><tr><td><textarea></textarea></td><td></td></tr></table>'
815
+ end
816
+
817
+ specify "inputs_wrapper: table accepts a :labels option" do
818
+ Forme::Form.new(:inputs_wrapper=>:table).inputs(:labels=>%w'A B C').to_s.should == '<table><tr><th>A</th><th>B</th><th>C</th></tr></table>'
819
+ end
820
+
821
+ specify "inputs_wrapper: table doesn't add empty header row for :labels=>[]" do
822
+ Forme::Form.new(:inputs_wrapper=>:table).inputs(:labels=>[]).to_s.should == '<table></table>'
520
823
  end
521
824
 
522
825
  specify "serializer: html_usa formats dates and datetimes in American format without timezones" do
@@ -630,6 +933,14 @@ describe "Forme object forms" do
630
933
  Forme::Form.new([:foo]).input(:first, :attr=>{:x=>'bar'}).to_s.should == '<input id="first" name="first" type="text" value="foo" x="bar"/>'
631
934
  end
632
935
 
936
+ specify "should respect current namespace" do
937
+ Forme::Form.new([:foo], :namespace=>'a').input(:first).to_s.should == '<input id="a_first" name="a[first]" type="text" value="foo"/>'
938
+ end
939
+
940
+ specify "should get values for hashes using #[]" do
941
+ Forme::Form.new(:obj=>{:bar=>:foo}, :namespace=>'a').input(:bar).to_s.should == '<input id="a_bar" name="a[bar]" type="text" value="foo"/>'
942
+ end
943
+
633
944
  specify "should handle obj passed in via :obj hash key" do
634
945
  Forme::Form.new(:obj=>[:foo]).input(:first).to_s.should == '<input id="first" name="first" type="text" value="foo"/>'
635
946
  end