forme 0.9.2 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +56 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +646 -69
- data/Rakefile +5 -4
- data/lib/forme.rb +433 -328
- data/lib/forme/rails.rb +4 -4
- data/lib/forme/version.rb +1 -1
- data/lib/sequel/plugins/forme.rb +43 -94
- data/spec/forme_coverage.rb +13 -0
- data/spec/forme_spec.rb +315 -4
- data/spec/rails_integration_spec.rb +31 -1
- data/spec/sequel_plugin_spec.rb +28 -11
- data/spec/sinatra_integration_spec.rb +1 -1
- data/spec/spec_helper.rb +20 -0
- metadata +3 -2
data/Rakefile
CHANGED
@@ -25,7 +25,6 @@ end
|
|
25
25
|
RDOC_DEFAULT_OPTS = ["--line-numbers", "--inline-source", '--title', 'Forme']
|
26
26
|
|
27
27
|
begin
|
28
|
-
gem 'rdoc', '= 3.12.2'
|
29
28
|
gem 'hanna-nouveau'
|
30
29
|
RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
|
31
30
|
rescue Gem::LoadError
|
@@ -74,9 +73,11 @@ begin
|
|
74
73
|
|
75
74
|
spec_with_cov = lambda do |name, files, d|
|
76
75
|
spec.call(name, files, d)
|
77
|
-
|
78
|
-
|
79
|
-
|
76
|
+
desc "#{d} with coverage"
|
77
|
+
task "#{name}_cov" do
|
78
|
+
ENV['COVERAGE'] = '1'
|
79
|
+
Rake::Task[name].invoke
|
80
|
+
end
|
80
81
|
end
|
81
82
|
|
82
83
|
task :default => [:spec]
|
data/lib/forme.rb
CHANGED
@@ -2,82 +2,28 @@ require 'date'
|
|
2
2
|
require 'bigdecimal'
|
3
3
|
require 'forme/version'
|
4
4
|
|
5
|
-
# Forme is designed to make creating HTML forms easier. Flexibility and
|
6
|
-
# simplicity are primary objectives. The basic usage involves creating
|
7
|
-
# a <tt>Forme::Form</tt> instance, and calling +input+ and +tag+ methods
|
8
|
-
# to return html strings for widgets, but it could also be used for
|
9
|
-
# serializing to other formats, or even as a DSL for a GUI application.
|
10
|
-
#
|
11
|
-
# In order to be flexible, Forme stores tags in abstract form until
|
12
|
-
# output is requested. There are two separate abstract <i>forms</i> that Forme
|
13
|
-
# uses. One is <tt>Forme::Input</tt>, and the other is <tt>Forme::Tag</tt>.
|
14
|
-
# <tt>Forme::Input</tt> is a high level abstract form, while <tt>Forme::Tag</tt>
|
15
|
-
# is a low level abstract form.
|
16
|
-
#
|
17
|
-
# The difference between <tt>Forme::Input</tt> and <tt>Forme::Tag</tt>
|
18
|
-
# is that <tt>Forme::Tag</tt> directly represents the underlying html
|
19
|
-
# tag, containing a type, optional attributes, and children, while the
|
20
|
-
# <tt>Forme::Input</tt> is more abstract and attempts to be user friendly.
|
21
|
-
# For example, these both compile by default to the same select tag:
|
22
|
-
#
|
23
|
-
# f.input(:select, :options=>[['foo', 1]])
|
24
|
-
# # or
|
25
|
-
# f.tag(:select, {}, [f.tag(:option, {:value=>1}, ['foo'])])
|
26
|
-
#
|
27
|
-
# The processing of high level <tt>Forme::Input</tt>s into raw html
|
28
|
-
# data is broken down to the following steps (called transformers):
|
29
|
-
#
|
30
|
-
# 1. +Formatter+: converts a <tt>Forme::Input</tt> instance into a
|
31
|
-
# <tt>Forme::Tag</tt> instance (or array of them).
|
32
|
-
# 2. +ErrorHandler+: If the <tt>Forme::Input</tt> instance has a error,
|
33
|
-
# takes the formatted tag and marks it as having the error.
|
34
|
-
# 2. +Labeler+: If the <tt>Forme::Input</tt> instance has a label,
|
35
|
-
# takes the formatted output and labels it.
|
36
|
-
# 3. +Wrapper+: Takes the output of the labeler (or formatter if
|
37
|
-
# no label), and wraps it in another tag (or just returns it
|
38
|
-
# directly).
|
39
|
-
# 4. +Serializer+: converts a <tt>Forme::Tag</tt> instance into a
|
40
|
-
# string.
|
41
|
-
#
|
42
|
-
# Technically, only the +Serializer+ is necessary. The +input+
|
43
|
-
# and +tag+ methods return +Input+ and +Tag+ objects. These objects
|
44
|
-
# both have +to_s+ defined to call the appropriate +Serializer+ with
|
45
|
-
# themselves. The +Serializer+ calls the appropriate +Formatter+ if
|
46
|
-
# it encounters an +Input+ instance, and attempts to serialize the
|
47
|
-
# output of that (which is usually a +Tag+ instance). It is up to
|
48
|
-
# the +Formatter+ to call the +Labeler+ and/or +ErrorHandler+ (if
|
49
|
-
# necessary) and the +Wrapper+.
|
50
|
-
#
|
51
|
-
# There is also an +InputsWrapper+ transformer, that is called by
|
52
|
-
# <tt>Forme::Form#inputs</tt>. It's used to wrap up a group of
|
53
|
-
# related options (in a fieldset by default).
|
54
|
-
#
|
55
|
-
# The <tt>Forme::Form</tt> object takes the 6 transformers as options (:formatter,
|
56
|
-
# :labeler, :error_handler, :wrapper, :inputs_wrapper, and :serializer), all of which
|
57
|
-
# should be objects responding to +call+ (so you can use +Proc+s) or be symbols
|
58
|
-
# registered with the library using <tt>Forme.register_transformer</tt>:
|
59
|
-
#
|
60
|
-
# Forme.register_transformer(:wrapper, :p){|t| t.tag(:p, {}, t)}
|
61
|
-
#
|
62
|
-
# Most of the transformers can be overridden on a per instance basis by
|
63
|
-
# passing the appopriate option to +input+ or +inputs+:
|
64
|
-
#
|
65
|
-
# f.input(:name, :wrapper=>:p)
|
66
5
|
module Forme
|
67
6
|
# Exception class for exceptions raised by Forme.
|
68
7
|
class Error < StandardError
|
69
8
|
end
|
70
|
-
|
9
|
+
|
10
|
+
@default_add_blank_prompt = nil
|
71
11
|
@default_config = :default
|
72
12
|
class << self
|
73
13
|
# Set the default configuration to use if none is explicitly
|
74
14
|
# specified (default: :default).
|
75
15
|
attr_accessor :default_config
|
16
|
+
|
17
|
+
# The default prompt to use for the :add_blank option (default: nil).
|
18
|
+
attr_accessor :default_add_blank_prompt
|
76
19
|
end
|
77
20
|
|
78
21
|
# Array of all supported transformer types.
|
79
22
|
TRANSFORMER_TYPES = [:formatter, :serializer, :wrapper, :error_handler, :labeler, :inputs_wrapper]
|
80
23
|
|
24
|
+
# Transformer symbols shared by wrapper and inputs_wrapper
|
25
|
+
SHARED_WRAPPERS = [:tr, :table, :ol, :fieldset_ol]
|
26
|
+
|
81
27
|
# Hash storing all configurations. Configurations are groups of related transformers,
|
82
28
|
# so that you can specify a single :config option when creating a +Form+ and have
|
83
29
|
# all of the transformers set from that.
|
@@ -131,66 +77,77 @@ module Forme
|
|
131
77
|
classes.compact.join(' ')
|
132
78
|
end
|
133
79
|
|
80
|
+
# If there is a related transformer, call it with the given +args+ and +block+.
|
81
|
+
# Otherwise, attempt to return the initial input without modifying it.
|
82
|
+
def self.transform(type, trans_name, default_opts, *args, &block)
|
83
|
+
if trans = transformer(type, trans_name, default_opts)
|
84
|
+
trans.call(*args, &block)
|
85
|
+
else
|
86
|
+
case type
|
87
|
+
when :inputs_wrapper
|
88
|
+
yield
|
89
|
+
when :labeler, :error_handler, :wrapper
|
90
|
+
args.first
|
91
|
+
else
|
92
|
+
raise Error, "No matching #{type}: #{trans_name.inspect}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Get the related transformer for the given transformer type. Output depends on the type
|
98
|
+
# of +trans+:
|
99
|
+
# +Symbol+ :: Assume a request for a registered transformer, so look it up in the +TRANSFORRMERS+ hash.
|
100
|
+
# +Hash+ :: If +type+ is also a key in +trans+, return the related value from +trans+, unless the related
|
101
|
+
# value is +nil+, in which case, return +nil+. If +type+ is not a key in +trans+, use the
|
102
|
+
# default transformer for the receiver.
|
103
|
+
# +nil+ :: Assume the default transformer for this receiver.
|
104
|
+
# otherwise :: return +trans+ directly if it responds to +call+, and raise an +Error+ if not.
|
105
|
+
def self.transformer(type, trans, default_opts)
|
106
|
+
case trans
|
107
|
+
when Symbol
|
108
|
+
TRANSFORMERS[type][trans] || raise(Error, "invalid #{type}: #{trans.inspect} (valid #{type}s: #{TRANSFORMERS[type].keys.map{|k| k.inspect}.join(', ')})")
|
109
|
+
when Hash
|
110
|
+
if trans.has_key?(type)
|
111
|
+
if v = trans[type]
|
112
|
+
transformer(type, v, default_opts)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
transformer(type, nil, default_opts)
|
116
|
+
end
|
117
|
+
when nil
|
118
|
+
transformer(type, default_opts[type], nil) if default_opts
|
119
|
+
else
|
120
|
+
if trans.respond_to?(:call)
|
121
|
+
trans
|
122
|
+
else
|
123
|
+
raise Error, "#{type} #{trans.inspect} must respond to #call"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
134
128
|
# The +Form+ class is the main entry point to the library.
|
135
129
|
# Using the +form+, +input+, +tag+, and +inputs+ methods, one can easily build
|
136
130
|
# an abstract syntax tree of +Tag+ and +Input+ instances, which can be serialized
|
137
131
|
# to a string using +to_s+.
|
138
132
|
class Form
|
139
|
-
#
|
140
|
-
# obj, then calls to +input+ are assumed to be accessing fields of the object
|
141
|
-
# instead to directly representing input types.
|
142
|
-
attr_reader :obj
|
143
|
-
|
144
|
-
# A hash of options for the receiver. Currently, the following are recognized by
|
145
|
-
# default:
|
146
|
-
# :obj :: Sets the +obj+ attribute
|
147
|
-
# :error_handler :: Sets the +error_handler+ for the form
|
148
|
-
# :formatter :: Sets the +formatter+ for the form
|
149
|
-
# :hidden_tags :: Sets the hidden tags to automatically add to this form.
|
150
|
-
# :input_defaults :: Sets the default options for each input type
|
151
|
-
# :inputs_wrapper :: Sets the +inputs_wrapper+ for the form
|
152
|
-
# :labeler :: Sets the +labeler+ for the form
|
153
|
-
# :wrapper :: Sets the +wrapper+ for the form
|
154
|
-
# :serializer :: Sets the +serializer+ for the form
|
133
|
+
# A hash of options for the form.
|
155
134
|
attr_reader :opts
|
156
135
|
|
157
|
-
# The +formatter+ determines how the +Input+s created are transformed into
|
158
|
-
# +Tag+ objects. Must respond to +call+ or be a registered symbol.
|
159
|
-
attr_reader :formatter
|
160
|
-
|
161
|
-
# The +error_handler+ determines how to to mark tags as containing errors.
|
162
|
-
# Must respond to +call+ or be a registered symbol.
|
163
|
-
attr_reader :error_handler
|
164
|
-
|
165
|
-
# The +labeler+ determines how to label tags. Must respond to +call+ or be
|
166
|
-
# a registered symbol.
|
167
|
-
attr_reader :labeler
|
168
|
-
|
169
|
-
# The +wrapper+ determines how (potentially labeled) tags are wrapped. Must
|
170
|
-
# respond to +call+ or be a registered symbol.
|
171
|
-
attr_reader :wrapper
|
172
|
-
|
173
136
|
# Set the default options for inputs by type. This should be a hash with
|
174
|
-
# input type
|
137
|
+
# input type keys and values that are hashes of input options.
|
175
138
|
attr_reader :input_defaults
|
176
139
|
|
177
|
-
# The
|
178
|
-
|
179
|
-
|
140
|
+
# The hidden tags to automatically add to the form.
|
141
|
+
attr_reader :hidden_tags
|
142
|
+
|
143
|
+
# The namespaces if any for the receiver's inputs. This can be used to
|
144
|
+
# automatically setup namespaced class and id attributes.
|
145
|
+
attr_accessor :namespaces
|
180
146
|
|
181
147
|
# The +serializer+ determines how +Tag+ objects are transformed into strings.
|
182
148
|
# Must respond to +call+ or be a registered symbol.
|
183
149
|
attr_reader :serializer
|
184
150
|
|
185
|
-
# The hidden tags to automatically add to the form. If set, this should be an
|
186
|
-
# array, where elements are one of the following types:
|
187
|
-
# String, Array, Forme::Tag :: Added directly as a child of the form tag.
|
188
|
-
# Hash :: Adds a hidden tag for each entry, with keys as the name of the hidden
|
189
|
-
# tag and values as the value of the hidden tag.
|
190
|
-
# Proc :: Will be called with the form tag object, and should return an instance
|
191
|
-
# of one of the handled types (or nil to not add a tag).
|
192
|
-
attr_reader :hidden_tags
|
193
|
-
|
194
151
|
# Create a +Form+ instance and yield it to the block,
|
195
152
|
# injecting the opening form tag before yielding and
|
196
153
|
# the closing form tag after yielding.
|
@@ -233,72 +190,31 @@ module Forme
|
|
233
190
|
# Creates a +Form+ object. Arguments:
|
234
191
|
# obj :: Sets the obj for the form. If a hash, is merged with the +opts+ argument
|
235
192
|
# to set the opts.
|
236
|
-
# opts :: A hash of options for the form
|
237
|
-
# available options.
|
193
|
+
# opts :: A hash of options for the form
|
238
194
|
def initialize(obj=nil, opts={})
|
239
|
-
|
240
|
-
|
241
|
-
@obj = @opts.delete(:obj)
|
242
|
-
else
|
243
|
-
@obj = obj
|
244
|
-
@opts = opts
|
245
|
-
end
|
246
|
-
if @obj && @obj.respond_to?(:forme_config)
|
247
|
-
@obj.forme_config(self)
|
248
|
-
end
|
249
|
-
config = CONFIGURATIONS[@opts[:config]||Forme.default_config]
|
250
|
-
TRANSFORMER_TYPES.each{|k| instance_variable_set(:"@#{k}", transformer(k, @opts.fetch(k, config[k])))}
|
251
|
-
@input_defaults = @opts[:input_defaults] || {}
|
252
|
-
@hidden_tags = @opts[:hidden_tags]
|
253
|
-
@nesting = []
|
254
|
-
end
|
195
|
+
@opts = opts.merge(obj.is_a?(Hash) ? obj : {:obj=>obj})
|
196
|
+
@opts[:namespace] = Array(@opts[:namespace])
|
255
197
|
|
256
|
-
|
257
|
-
|
258
|
-
def transform(type, trans_name, *args, &block)
|
259
|
-
if trans = transformer(type, trans_name)
|
260
|
-
trans.call(*args, &block)
|
261
|
-
else
|
262
|
-
case type
|
263
|
-
when :inputs_wrapper
|
264
|
-
yield
|
265
|
-
when :labeler, :error_handler, :wrapper
|
266
|
-
args.first
|
267
|
-
else
|
268
|
-
raise Error, "No matching #{type}: #{trans_name.inspect}"
|
269
|
-
end
|
198
|
+
if obj && obj.respond_to?(:forme_config)
|
199
|
+
obj.forme_config(self)
|
270
200
|
end
|
271
|
-
end
|
272
201
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
case trans
|
283
|
-
when Symbol
|
284
|
-
TRANSFORMERS[type][trans] || raise(Error, "invalid #{type}: #{trans.inspect} (valid #{type}s: #{TRANSFORMERS[type].keys.map{|k| k.inspect}.join(', ')})")
|
285
|
-
when Hash
|
286
|
-
if trans.has_key?(type)
|
287
|
-
if v = trans[type]
|
288
|
-
transformer(type, v)
|
289
|
-
end
|
290
|
-
else
|
291
|
-
transformer(type, nil)
|
292
|
-
end
|
293
|
-
when nil
|
294
|
-
send(type)
|
295
|
-
else
|
296
|
-
if trans.respond_to?(:call)
|
297
|
-
trans
|
298
|
-
else
|
299
|
-
raise Error, "#{type} #{trans.inspect} must respond to #call"
|
202
|
+
config = CONFIGURATIONS[@opts[:config]||Forme.default_config]
|
203
|
+
copy_inputs_wrapper_from_wrapper(@opts)
|
204
|
+
|
205
|
+
TRANSFORMER_TYPES.each do |t|
|
206
|
+
case @opts[t]
|
207
|
+
when Symbol
|
208
|
+
@opts[t] = Forme.transformer(t, @opts[t], @opts)
|
209
|
+
when nil
|
210
|
+
@opts[t] = Forme.transformer(t, config, @opts)
|
300
211
|
end
|
301
212
|
end
|
213
|
+
|
214
|
+
@serializer = @opts[:serializer]
|
215
|
+
@input_defaults = @opts[:input_defaults] || {}
|
216
|
+
@hidden_tags = @opts[:hidden_tags]
|
217
|
+
@nesting = []
|
302
218
|
end
|
303
219
|
|
304
220
|
# Create a form tag with the given attributes.
|
@@ -306,11 +222,6 @@ module Forme
|
|
306
222
|
tag(:form, attr, method(:hidden_form_tags), &block)
|
307
223
|
end
|
308
224
|
|
309
|
-
# Formats the +input+ using the +formatter+.
|
310
|
-
def format(input)
|
311
|
-
transform(:formatter, input.opts, input)
|
312
|
-
end
|
313
|
-
|
314
225
|
# Empty method designed to ease integration with other libraries where
|
315
226
|
# Forme is used in template code and some output implicitly
|
316
227
|
# created by Forme needs to be injected into the template output.
|
@@ -342,15 +253,19 @@ module Forme
|
|
342
253
|
obj.forme_input(self, field, opts.dup)
|
343
254
|
else
|
344
255
|
opts = opts.dup
|
345
|
-
opts[:
|
346
|
-
|
347
|
-
|
256
|
+
opts[:key] = field unless opts.has_key?(:key)
|
257
|
+
unless opts.has_key?(:value)
|
258
|
+
opts[:value] = if obj.is_a?(Hash)
|
259
|
+
obj[field]
|
260
|
+
else
|
261
|
+
obj.send(field)
|
262
|
+
end
|
263
|
+
end
|
348
264
|
_input(:text, opts)
|
349
265
|
end
|
350
266
|
else
|
351
267
|
_input(field, opts)
|
352
268
|
end
|
353
|
-
use_serializer(input) if input.is_a?(Array)
|
354
269
|
self << input
|
355
270
|
input
|
356
271
|
end
|
@@ -362,7 +277,7 @@ module Forme
|
|
362
277
|
end
|
363
278
|
|
364
279
|
# Creates a tag using the +inputs_wrapper+ (a fieldset by default), calls
|
365
|
-
# input on each element of +inputs+, and yields
|
280
|
+
# input on each element of +inputs+, and yields if given a block.
|
366
281
|
# You can use array arguments if you want inputs to be created with specific
|
367
282
|
# options:
|
368
283
|
#
|
@@ -372,22 +287,47 @@ module Forme
|
|
372
287
|
# The given +opts+ are passed to the +inputs_wrapper+, and the default
|
373
288
|
# +inputs_wrapper+ supports a <tt>:legend</tt> option that is used to
|
374
289
|
# set the legend for the fieldset.
|
375
|
-
|
376
|
-
|
290
|
+
#
|
291
|
+
# +opts+ can also include transformer options itself (e.g. :wrapper), which
|
292
|
+
# override the form's current transformer options for the duration of the block.
|
293
|
+
# The exception is the :inputs_wrapper transformer option, which affects the
|
294
|
+
# wrapper to use for this inputs call. You can use the :nested_inputs_wrapper
|
295
|
+
# option to set the default :inputs_wrapper option for the duration of the block.
|
296
|
+
#
|
297
|
+
# This can also be called with a single hash argument to just use an options hash:
|
298
|
+
#
|
299
|
+
# inputs(:legend=>'Foo'){...}
|
300
|
+
#
|
301
|
+
# or even without any arguments:
|
302
|
+
#
|
303
|
+
# inputs{...}
|
304
|
+
def inputs(inputs=[], opts={}, &block)
|
305
|
+
_inputs(inputs, opts, &block)
|
377
306
|
end
|
378
307
|
|
379
308
|
# Internals of #inputs, should be used internally by the library, where #inputs
|
380
|
-
# is designed for external use.
|
381
|
-
def _inputs(inputs=[], opts={})
|
309
|
+
# is designed for external use.
|
310
|
+
def _inputs(inputs=[], opts={}) # :nodoc:
|
382
311
|
if inputs.is_a?(Hash)
|
383
312
|
opts = inputs.merge(opts)
|
384
313
|
inputs = []
|
385
314
|
end
|
386
|
-
|
387
|
-
|
388
|
-
|
315
|
+
|
316
|
+
form_opts = {}
|
317
|
+
form_opts[:inputs_wrapper] = opts[:nested_inputs_wrapper] if opts[:nested_inputs_wrapper]
|
318
|
+
TRANSFORMER_TYPES.each do |t|
|
319
|
+
if opts.has_key?(t) && t != :inputs_wrapper
|
320
|
+
form_opts[t] = opts[t]
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
Forme.transform(:inputs_wrapper, opts, @opts, self, opts) do
|
325
|
+
with_opts(form_opts) do
|
326
|
+
inputs.each do |i|
|
327
|
+
emit(input(*i))
|
328
|
+
end
|
329
|
+
yield if block_given?
|
389
330
|
end
|
390
|
-
yield if block_given?
|
391
331
|
end
|
392
332
|
end
|
393
333
|
|
@@ -409,6 +349,18 @@ module Forme
|
|
409
349
|
tag = Tag.new(self, *a, &block)
|
410
350
|
end
|
411
351
|
|
352
|
+
# The object associated with this form, if any. If the +Form+ has an associated
|
353
|
+
# obj, then calls to +input+ are assumed to be accessing fields of the object
|
354
|
+
# instead to directly representing input types.
|
355
|
+
def obj
|
356
|
+
@opts[:obj]
|
357
|
+
end
|
358
|
+
|
359
|
+
# The current namespaces for the form, if any.
|
360
|
+
def namespaces
|
361
|
+
@opts[:namespace]
|
362
|
+
end
|
363
|
+
|
412
364
|
# Creates a +Tag+ associated to the receiver with the given arguments.
|
413
365
|
# Add the tag to the the list of children for the currently open tag.
|
414
366
|
# If a block is given, make this tag the currently open tag while inside
|
@@ -420,7 +372,8 @@ module Forme
|
|
420
372
|
tag
|
421
373
|
end
|
422
374
|
|
423
|
-
|
375
|
+
# Aliased for tag. Workaround for issue with rails plugin.
|
376
|
+
def tag_(*a, &block) # :nodoc:
|
424
377
|
tag(*a, &block)
|
425
378
|
end
|
426
379
|
|
@@ -440,13 +393,46 @@ module Forme
|
|
440
393
|
end
|
441
394
|
end
|
442
395
|
|
443
|
-
#
|
444
|
-
|
445
|
-
|
396
|
+
# Calls the block for each object in objs, using with_obj with the given namespace
|
397
|
+
# and an index namespace (starting at 0).
|
398
|
+
def each_obj(objs, namespace=nil)
|
399
|
+
objs.each_with_index do |obj, i|
|
400
|
+
with_obj(obj, Array(namespace) + [i]) do
|
401
|
+
yield obj, i
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
# Temporarily override the given object and namespace for the form. Any given
|
407
|
+
# namespaces are appended to the form's current namespace.
|
408
|
+
def with_obj(obj, namespace=nil)
|
409
|
+
with_opts(:obj=>obj, :namespace=>@opts[:namespace]+Array(namespace)) do
|
410
|
+
yield obj
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
# Temporarily override the opts for the form for the duration of the block.
|
415
|
+
# This merges the given opts with the form's current opts, restoring
|
416
|
+
# the previous opts before returning.
|
417
|
+
def with_opts(opts)
|
418
|
+
orig_opts = @opts
|
419
|
+
@opts = orig_opts.merge(opts)
|
420
|
+
copy_inputs_wrapper_from_wrapper(opts, @opts)
|
421
|
+
yield
|
422
|
+
ensure
|
423
|
+
@opts = orig_opts if orig_opts
|
446
424
|
end
|
447
425
|
|
448
426
|
private
|
449
427
|
|
428
|
+
# Copy the :wrapper option to :inputs_wrapper in output_opts if only :wrapper
|
429
|
+
# is present in input_opts and the :wrapper option value is a shared wrapper.
|
430
|
+
def copy_inputs_wrapper_from_wrapper(input_opts, output_opts=input_opts)
|
431
|
+
if input_opts[:wrapper] && !input_opts[:inputs_wrapper] && SHARED_WRAPPERS.include?(input_opts[:wrapper])
|
432
|
+
output_opts[:inputs_wrapper] = output_opts[:wrapper]
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
450
436
|
# Return array of hidden tags to use for this form,
|
451
437
|
# or nil if the form does not have hidden tags added automatically.
|
452
438
|
def hidden_form_tags(form_tag)
|
@@ -476,15 +462,6 @@ module Forme
|
|
476
462
|
end
|
477
463
|
end
|
478
464
|
|
479
|
-
# Extend +obj+ with +Serialized+ and associate it with the receiver, such
|
480
|
-
# that calling +to_s+ on the object will use the receiver's serializer
|
481
|
-
# to generate the resulting string.
|
482
|
-
def use_serializer(obj)
|
483
|
-
obj.extend(Serialized)
|
484
|
-
obj._form = self
|
485
|
-
obj
|
486
|
-
end
|
487
|
-
|
488
465
|
# Make the given tag the currently open tag, and yield. After the
|
489
466
|
# block returns, make the previously open tag the currently open
|
490
467
|
# tag.
|
@@ -505,35 +482,18 @@ module Forme
|
|
505
482
|
# The type of input, should be a symbol (e.g. :submit, :text, :select).
|
506
483
|
attr_reader :type
|
507
484
|
|
508
|
-
# The options hash for the
|
509
|
-
# used by the built-in formatter transformers:
|
510
|
-
#
|
511
|
-
# :error :: Set an error message, invoking the error_handler
|
512
|
-
# :label :: Set a label, invoking the labeler
|
513
|
-
# :wrapper :: Set a custom wrapper, overriding the form's default
|
514
|
-
# :labeler :: Set a custom labeler, overriding the form's default
|
515
|
-
# :error_handler :: Set a custom error_handler, overriding the form's default
|
516
|
-
# :attr :: The attributes hash to use for the given tag, takes precedence over
|
517
|
-
# other options that set attributes.
|
518
|
-
# :data :: A hash of data-* attributes for the resulting tag. Keys in this hash
|
519
|
-
# will have attributes created with data- prepended to the attribute name.
|
520
|
-
# :name :: The name attribute to use
|
521
|
-
# :id :: The id attribute to use
|
522
|
-
# :placeholder :: The placeholder attribute to use
|
523
|
-
# :value :: The value attribute to use for input tags, the content of the textarea
|
524
|
-
# for textarea tags, or the selected option(s) for select tags.
|
525
|
-
# :class :: A class to use. Unlike other options, this is combined with the
|
526
|
-
# classes set in the :attr hash.
|
527
|
-
# :disabled :: Set the disabled attribute if true
|
528
|
-
# :required :: Set the required attribute if true
|
529
|
-
#
|
530
|
-
# For other supported options, see the private methods in +Formatter+.
|
485
|
+
# The options hash for the Input.
|
531
486
|
attr_reader :opts
|
532
487
|
|
488
|
+
# The options hash in use by the form at the time of the Input's instantiation.
|
489
|
+
attr_reader :form_opts
|
490
|
+
|
533
491
|
# Set the +form+, +type+, and +opts+.
|
534
492
|
def initialize(form, type, opts={})
|
535
493
|
@form, @type = form, type
|
536
|
-
|
494
|
+
defaults = form.input_defaults
|
495
|
+
@opts = (defaults.fetch(type){defaults[type.to_s]} || {}).merge(opts)
|
496
|
+
@form_opts = form.opts
|
537
497
|
end
|
538
498
|
|
539
499
|
# Replace the +opts+ by merging the given +hash+ into +opts+,
|
@@ -550,13 +510,13 @@ module Forme
|
|
550
510
|
|
551
511
|
# Return a string containing the serialized content of the receiver.
|
552
512
|
def to_s
|
553
|
-
|
513
|
+
Forme.transform(:serializer, @opts, @form_opts, self)
|
554
514
|
end
|
555
515
|
|
556
516
|
# Transform the receiver into a lower level +Tag+ form (or an array
|
557
517
|
# of them).
|
558
518
|
def format
|
559
|
-
|
519
|
+
Forme.transform(:formatter, @opts, @form_opts, self)
|
560
520
|
end
|
561
521
|
end
|
562
522
|
|
@@ -599,7 +559,7 @@ module Forme
|
|
599
559
|
|
600
560
|
# Return a string containing the serialized content of the receiver.
|
601
561
|
def to_s
|
602
|
-
form.
|
562
|
+
Forme.transform(:serializer, @opts, @form.opts, self)
|
603
563
|
end
|
604
564
|
|
605
565
|
private
|
@@ -619,19 +579,6 @@ module Forme
|
|
619
579
|
end
|
620
580
|
end
|
621
581
|
|
622
|
-
# Module that can extend objects associating them with a specific
|
623
|
-
# +Form+ instance. Calling +to_s+ on the object will then use the
|
624
|
-
# form's serializer to return a string.
|
625
|
-
module Serialized
|
626
|
-
# The +Form+ instance related to the receiver.
|
627
|
-
attr_accessor :_form
|
628
|
-
|
629
|
-
# Return a string containing the serialized content of the receiver.
|
630
|
-
def to_s
|
631
|
-
_form.serialize(self)
|
632
|
-
end
|
633
|
-
end
|
634
|
-
|
635
582
|
# Empty module for marking objects as "raw", where they will no longer
|
636
583
|
# html escaped by the default serializer.
|
637
584
|
module Raw
|
@@ -654,6 +601,11 @@ module Forme
|
|
654
601
|
# the :attr option version takes precedence.
|
655
602
|
ATTRIBUTE_OPTIONS = [:name, :id, :placeholder, :value, :style]
|
656
603
|
|
604
|
+
# Options copied from the options hash into the attributes hash,
|
605
|
+
# where a true value in the options hash sets the attribute
|
606
|
+
# value to the same name as the key.
|
607
|
+
ATTRIBUTE_BOOLEAN_OPTIONS = [:autofocus, :required, :disabled]
|
608
|
+
|
657
609
|
# Create a new instance and call it
|
658
610
|
def self.call(input)
|
659
611
|
new.call(input)
|
@@ -716,10 +668,7 @@ module Forme
|
|
716
668
|
# same name that comes before this checkbox. That way, if the checkbox
|
717
669
|
# is checked, the web app will generally see the value of the checkbox, and
|
718
670
|
# if it is not checked, the web app will generally see the value of the hidden
|
719
|
-
# input tag.
|
720
|
-
# :checked :: checkbox is set to checked if so.
|
721
|
-
# :hidden_value :: sets the value of the hidden input tag.
|
722
|
-
# :no_hidden :: don't create a hidden input tag
|
671
|
+
# input tag.
|
723
672
|
def format_checkbox
|
724
673
|
@attr[:type] = :checkbox
|
725
674
|
@attr[:checked] = :checked if @opts[:checked]
|
@@ -791,69 +740,76 @@ module Forme
|
|
791
740
|
end
|
792
741
|
|
793
742
|
# Takes a select input and turns it into a select tag with (possibly) option
|
794
|
-
# children tags.
|
795
|
-
# :options :: an array of options. Processes each entry. If that entry is
|
796
|
-
# an array, takes the first entry in the hash as the text child
|
797
|
-
# of the option, and the last entry as the value of the option.
|
798
|
-
# if not set, ignores the remaining options.
|
799
|
-
# :add_blank :: Add a blank option if true. If the value is a string,
|
800
|
-
# use it as the text content of the blank option. The value of
|
801
|
-
# the blank option is always the empty string.
|
802
|
-
# :text_method :: If set, each entry in the array has this option called on
|
803
|
-
# it to get the text of the object.
|
804
|
-
# :value_method :: If set (and :text_method is set), each entry in the array
|
805
|
-
# has this method called on it to get the value of the option.
|
806
|
-
# :selected :: The value that should be selected. Any options that are equal to
|
807
|
-
# this value (or included in this value if a multiple select box),
|
808
|
-
# are set to selected.
|
809
|
-
# :multiple :: Creates a multiple select box.
|
810
|
-
# :value :: Same as :selected, but has lower priority.
|
743
|
+
# children tags.
|
811
744
|
def format_select
|
812
|
-
if os = @opts[:options]
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
else
|
821
|
-
cmp = lambda{|v| v == sel}
|
822
|
-
end
|
823
|
-
os = os.map do |x|
|
824
|
-
attr = {}
|
825
|
-
if tm
|
826
|
-
text = x.send(tm)
|
827
|
-
if vm
|
828
|
-
val = x.send(vm)
|
829
|
-
attr[:value] = val
|
830
|
-
attr[:selected] = :selected if cmp.call(val)
|
831
|
-
else
|
832
|
-
attr[:selected] = :selected if cmp.call(text)
|
833
|
-
end
|
834
|
-
form._tag(:option, attr, [text])
|
835
|
-
elsif x.is_a?(Array)
|
836
|
-
val = x.last
|
837
|
-
if val.is_a?(Hash)
|
838
|
-
attr.merge!(val)
|
839
|
-
val = attr[:value]
|
840
|
-
else
|
841
|
-
attr[:value] = val
|
842
|
-
end
|
843
|
-
attr[:selected] = :selected if attr.has_key?(:value) && cmp.call(val)
|
844
|
-
tag(:option, attr, [x.first])
|
845
|
-
else
|
846
|
-
attr[:selected] = :selected if cmp.call(x)
|
847
|
-
tag(:option, attr, [x])
|
745
|
+
if os = process_select_options(@opts[:options])
|
746
|
+
@attr[:multiple] = :multiple if @opts[:multiple]
|
747
|
+
|
748
|
+
os = os.map do |label, value, sel, attrs|
|
749
|
+
if value || sel
|
750
|
+
attrs = attrs.dup
|
751
|
+
attrs[:value] = value if value
|
752
|
+
attrs[:selected] = :selected if sel
|
848
753
|
end
|
849
|
-
|
850
|
-
if prompt = @opts[:add_blank]
|
851
|
-
os.unshift(tag(:option, {:value=>''}, prompt.is_a?(String) ? [prompt] : []))
|
754
|
+
tag(:option, attrs, [label])
|
852
755
|
end
|
853
756
|
end
|
854
757
|
tag(:select, @attr, os)
|
855
758
|
end
|
856
759
|
|
760
|
+
def format_checkboxset
|
761
|
+
@opts[:multiple] = true unless @opts.has_key?(:multiple)
|
762
|
+
_format_set(:checkbox, :no_hidden=>true, :multiple=>true)
|
763
|
+
end
|
764
|
+
|
765
|
+
def format_radioset
|
766
|
+
_format_set(:radio)
|
767
|
+
end
|
768
|
+
|
769
|
+
def _format_set(type, tag_attrs={})
|
770
|
+
raise Error, "can't have radioset with no options" unless os = @opts[:options]
|
771
|
+
key = @opts[:key]
|
772
|
+
name = @opts[:name]
|
773
|
+
id = @opts[:id]
|
774
|
+
if @opts[:error]
|
775
|
+
@opts[:set_error] = @opts.delete(:error)
|
776
|
+
end
|
777
|
+
if @opts[:label]
|
778
|
+
@opts[:set_label] = @opts.delete(:label)
|
779
|
+
end
|
780
|
+
tag_wrapper = @opts.delete(:tag_wrapper) || :default
|
781
|
+
wrapper = Forme.transformer(:wrapper, @opts, @input.form_opts)
|
782
|
+
|
783
|
+
tags = process_select_options(os).map do |label, value, sel, attrs|
|
784
|
+
value ||= label
|
785
|
+
r_opts = attrs.merge(tag_attrs).merge(:label=>label||value, :label_attr=>{:class=>:option}, :wrapper=>tag_wrapper)
|
786
|
+
r_opts[:value] ||= value if value
|
787
|
+
r_opts[:checked] ||= :checked if sel
|
788
|
+
|
789
|
+
if name
|
790
|
+
r_opts[:name] ||= name
|
791
|
+
end
|
792
|
+
if id
|
793
|
+
r_opts[:id] ||= "#{id}_#{value}"
|
794
|
+
end
|
795
|
+
if key
|
796
|
+
r_opts[:key] ||= key
|
797
|
+
r_opts[:key_id] ||= value
|
798
|
+
end
|
799
|
+
|
800
|
+
form._input(type, r_opts)
|
801
|
+
end
|
802
|
+
|
803
|
+
if (last_input = tags.last) && last_input.is_a?(Input)
|
804
|
+
last_input.opts[:error] = @opts[:set_error]
|
805
|
+
else
|
806
|
+
tags << form._tag(:span, {:class=>'error_message'}, [@opts[:set_error]])
|
807
|
+
end
|
808
|
+
tags.unshift(form._tag(:span, {:class=>:label}, @opts[:set_label])) if @opts[:set_label]
|
809
|
+
wrapper.call(tags, form._input(type, opts)) if wrapper
|
810
|
+
tags
|
811
|
+
end
|
812
|
+
|
857
813
|
# Formats a textarea. Respects the following options:
|
858
814
|
# :value :: Sets value as the child of the textarea.
|
859
815
|
def format_textarea
|
@@ -865,19 +821,31 @@ module Forme
|
|
865
821
|
end
|
866
822
|
end
|
867
823
|
|
868
|
-
|
869
|
-
|
824
|
+
# Copy option values for given keys to the attributes unless the
|
825
|
+
# attributes already have a value for the key.
|
826
|
+
def copy_options_to_attributes(keys)
|
827
|
+
keys.each do |k|
|
870
828
|
if @opts.has_key?(k) && !@attr.has_key?(k)
|
871
829
|
@attr[k] = @opts[k]
|
872
830
|
end
|
873
831
|
end
|
874
832
|
end
|
875
833
|
|
876
|
-
#
|
877
|
-
#
|
878
|
-
|
834
|
+
# Set attribute values for given keys to be the same as the key
|
835
|
+
# unless the attributes already have a value for the key.
|
836
|
+
def copy_boolean_options_to_attributes(keys)
|
837
|
+
keys.each do |k|
|
838
|
+
if @opts[k] && !@attr.has_key?(k)
|
839
|
+
@attr[k] = k
|
840
|
+
end
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
# Normalize the options used for all input types.
|
879
845
|
def normalize_options
|
880
846
|
copy_options_to_attributes(ATTRIBUTE_OPTIONS)
|
847
|
+
copy_boolean_options_to_attributes(ATTRIBUTE_BOOLEAN_OPTIONS)
|
848
|
+
handle_key_option
|
881
849
|
|
882
850
|
Forme.attr_classes(@attr, @opts[:class]) if @opts.has_key?(:class)
|
883
851
|
Forme.attr_classes(@attr, 'error') if @opts[:error]
|
@@ -888,9 +856,110 @@ module Forme
|
|
888
856
|
@attr[sym] = v unless @attr.has_key?(sym)
|
889
857
|
end
|
890
858
|
end
|
859
|
+
end
|
891
860
|
|
892
|
-
|
893
|
-
|
861
|
+
# Have the :key option possibly set the name, id, and/or value attributes if not already set.
|
862
|
+
def handle_key_option
|
863
|
+
if key = @opts[:key]
|
864
|
+
unless @attr[:name] || @attr['name']
|
865
|
+
@attr[:name] = namespaced_name(key, @opts[:array] || @opts[:multiple])
|
866
|
+
if !@attr.has_key?(:value) && !@attr.has_key?('value') && (values = @form.opts[:values])
|
867
|
+
set_value_from_namespaced_values(namespaces, values, key)
|
868
|
+
end
|
869
|
+
end
|
870
|
+
unless @attr[:id] || @attr['id']
|
871
|
+
id = namespaced_id(key)
|
872
|
+
if suffix = @opts[:key_id]
|
873
|
+
id << '_' << suffix.to_s
|
874
|
+
end
|
875
|
+
@attr[:id] = id
|
876
|
+
end
|
877
|
+
end
|
878
|
+
end
|
879
|
+
|
880
|
+
# Array of namespaces to use for the input
|
881
|
+
def namespaces
|
882
|
+
input.form_opts[:namespace]
|
883
|
+
end
|
884
|
+
|
885
|
+
# Return a unique id attribute for the +field+, based on the current namespaces.
|
886
|
+
def namespaced_id(field)
|
887
|
+
"#{namespaces.join('_')}#{'_' unless namespaces.empty?}#{field}"
|
888
|
+
end
|
889
|
+
|
890
|
+
# Return a unique name attribute for the +field+, based on the current namespaces.
|
891
|
+
# If +multiple+ is true, end the name with [] so that param parsing will treat
|
892
|
+
# the name as part of an array.
|
893
|
+
def namespaced_name(field, multiple=false)
|
894
|
+
if namespaces.empty?
|
895
|
+
if multiple
|
896
|
+
"#{field}[]"
|
897
|
+
else
|
898
|
+
field
|
899
|
+
end
|
900
|
+
else
|
901
|
+
root, *nsps = namespaces
|
902
|
+
"#{root}#{nsps.map{|n| "[#{n}]"}.join}[#{field}]#{'[]' if multiple}"
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
906
|
+
# Set the values option based on the (possibly nested) values
|
907
|
+
# hash given, array of namespaces, and key.
|
908
|
+
def set_value_from_namespaced_values(namespaces, values, key)
|
909
|
+
namespaces.each do |ns|
|
910
|
+
v = values[ns] || values[ns.to_s]
|
911
|
+
return unless v
|
912
|
+
values = v
|
913
|
+
end
|
914
|
+
|
915
|
+
@attr[:value] = values.fetch(key){values.fetch(key.to_s){return}}
|
916
|
+
end
|
917
|
+
|
918
|
+
# Returns an array of arrays, where each array entry contains the label, value,
|
919
|
+
# currently selected flag, and attributes for that tag.
|
920
|
+
def process_select_options(os)
|
921
|
+
if os
|
922
|
+
vm = @opts[:value_method]
|
923
|
+
tm = @opts[:text_method]
|
924
|
+
sel = @opts[:selected] || @attr.delete(:value)
|
925
|
+
|
926
|
+
if @opts[:multiple]
|
927
|
+
sel = Array(sel)
|
928
|
+
cmp = lambda{|v| sel.include?(v)}
|
929
|
+
else
|
930
|
+
cmp = lambda{|v| v == sel}
|
931
|
+
end
|
932
|
+
|
933
|
+
os = os.map do |x|
|
934
|
+
attr = {}
|
935
|
+
if tm
|
936
|
+
text = x.send(tm)
|
937
|
+
val = x.send(vm) if vm
|
938
|
+
elsif x.is_a?(Array)
|
939
|
+
text = x.first
|
940
|
+
val = x.last
|
941
|
+
|
942
|
+
if val.is_a?(Hash)
|
943
|
+
value = val[:value]
|
944
|
+
attr.merge!(val)
|
945
|
+
val = value
|
946
|
+
end
|
947
|
+
else
|
948
|
+
text = x
|
949
|
+
end
|
950
|
+
|
951
|
+
[text, val, val ? cmp.call(val) : cmp.call(text), attr]
|
952
|
+
end
|
953
|
+
|
954
|
+
if prompt = @opts[:add_blank]
|
955
|
+
unless prompt.is_a?(String)
|
956
|
+
prompt = Forme.default_add_blank_prompt
|
957
|
+
end
|
958
|
+
os.unshift([prompt, '', false, {}])
|
959
|
+
end
|
960
|
+
|
961
|
+
os
|
962
|
+
end
|
894
963
|
end
|
895
964
|
|
896
965
|
# Create a +Tag+ instance related to the receiver's +form+ with the given
|
@@ -901,17 +970,17 @@ module Forme
|
|
901
970
|
|
902
971
|
# Wrap the tag with the form's +wrapper+.
|
903
972
|
def wrap_tag(tag)
|
904
|
-
|
973
|
+
Forme.transform(:wrapper, @opts, input.form_opts, tag, input)
|
905
974
|
end
|
906
975
|
|
907
976
|
# Wrap the tag with the form's +error_handler+.
|
908
977
|
def wrap_tag_with_error(tag)
|
909
|
-
|
978
|
+
Forme.transform(:error_handler, @opts, input.form_opts, tag, input)
|
910
979
|
end
|
911
980
|
|
912
981
|
# Wrap the tag with the form's +labeler+.
|
913
982
|
def wrap_tag_with_label(tag)
|
914
|
-
|
983
|
+
Forme.transform(:labeler, @opts, input.form_opts, tag, input)
|
915
984
|
end
|
916
985
|
end
|
917
986
|
|
@@ -1038,14 +1107,24 @@ module Forme
|
|
1038
1107
|
# :label_for option is used, the label created will not be
|
1039
1108
|
# associated with an input.
|
1040
1109
|
def call(tag, input)
|
1110
|
+
unless id = input.opts[:id]
|
1111
|
+
if key = input.opts[:key]
|
1112
|
+
namespaces = input.form_opts[:namespace]
|
1113
|
+
id = "#{namespaces.join('_')}#{'_' unless namespaces.empty?}#{key}"
|
1114
|
+
if key_id = input.opts[:key_id]
|
1115
|
+
id << "_#{key_id.to_s}"
|
1116
|
+
end
|
1117
|
+
end
|
1118
|
+
end
|
1041
1119
|
if [:radio, :checkbox].include?(input.type)
|
1042
|
-
t = [tag, input.tag(:label, {:for=>input.opts.fetch(:label_for,
|
1043
|
-
|
1120
|
+
t = [tag, input.tag(:label, {:for=>input.opts.fetch(:label_for, id)}.merge(input.opts[:label_attr]||{}), [input.opts[:label]])]
|
1121
|
+
pos = :before
|
1044
1122
|
else
|
1045
|
-
t = [input.tag(:label, {:for=>input.opts.fetch(:label_for,
|
1046
|
-
|
1123
|
+
t = [input.tag(:label, {:for=>input.opts.fetch(:label_for, id)}.merge(input.opts[:label_attr]||{}), [input.opts[:label]]), tag]
|
1124
|
+
pos = :after
|
1047
1125
|
end
|
1048
|
-
|
1126
|
+
|
1127
|
+
if input.opts[:label_position] == pos
|
1049
1128
|
t.reverse
|
1050
1129
|
else
|
1051
1130
|
t
|
@@ -1054,7 +1133,7 @@ module Forme
|
|
1054
1133
|
end
|
1055
1134
|
|
1056
1135
|
Forme.register_transformer(:wrapper, :default){|tag, input| tag}
|
1057
|
-
[:li, :p, :div, :span].each do |x|
|
1136
|
+
[:li, :p, :div, :span, :td].each do |x|
|
1058
1137
|
Forme.register_transformer(:wrapper, x){|tag, input| input.tag(x, input.opts[:wrapper_attr], Array(tag))}
|
1059
1138
|
end
|
1060
1139
|
Forme.register_transformer(:wrapper, :trtd) do |tag, input|
|
@@ -1071,15 +1150,18 @@ module Forme
|
|
1071
1150
|
end
|
1072
1151
|
input.tag(:tr, input.opts[:wrapper_attr], [input.tag(:td, {}, ltd), input.tag(:td, {}, rtd)])
|
1073
1152
|
end
|
1153
|
+
{:tr=>:td, :table=>:trtd, :ol=>:li, :fieldset_ol=>:li}.each do |k, v|
|
1154
|
+
Forme.register_transformer(:wrapper, k, TRANSFORMERS[:wrapper][v])
|
1155
|
+
end
|
1074
1156
|
|
1075
|
-
# Default inputs_wrapper used by the library, uses a fieldset
|
1157
|
+
# Default inputs_wrapper used by the library, uses a <fieldset>.
|
1076
1158
|
#
|
1077
1159
|
# Registered as :default.
|
1078
1160
|
class InputsWrapper
|
1079
1161
|
Forme.register_transformer(:inputs_wrapper, :default, new)
|
1080
1162
|
|
1081
|
-
# Wrap the inputs in a fieldset
|
1082
|
-
# option is given, add a
|
1163
|
+
# Wrap the inputs in a <fieldset>. If the :legend
|
1164
|
+
# option is given, add a <legend> tag as the first
|
1083
1165
|
# child of the fieldset.
|
1084
1166
|
def call(form, opts)
|
1085
1167
|
attr = opts[:attr] ? opts[:attr].dup : {}
|
@@ -1095,51 +1177,74 @@ module Forme
|
|
1095
1177
|
end
|
1096
1178
|
end
|
1097
1179
|
|
1098
|
-
# Use a fieldset and an ol tag to wrap the inputs.
|
1180
|
+
# Use a <fieldset> and an <ol> tag to wrap the inputs.
|
1099
1181
|
#
|
1100
1182
|
# Registered as :fieldset_ol.
|
1101
1183
|
class InputsWrapper::FieldSetOL < InputsWrapper
|
1102
1184
|
Forme.register_transformer(:inputs_wrapper, :fieldset_ol, new)
|
1103
1185
|
|
1104
|
-
# Wrap the inputs in
|
1186
|
+
# Wrap the inputs in a <fieldset> and a <ol> tag.
|
1105
1187
|
def call(form, opts)
|
1106
1188
|
super(form, opts){form.tag_(:ol){yield}}
|
1107
1189
|
end
|
1108
1190
|
end
|
1109
1191
|
|
1110
|
-
# Use an ol tag to wrap the inputs.
|
1192
|
+
# Use an <ol> tag to wrap the inputs.
|
1111
1193
|
#
|
1112
1194
|
# Registered as :ol.
|
1113
1195
|
class InputsWrapper::OL
|
1114
1196
|
Forme.register_transformer(:inputs_wrapper, :ol, new)
|
1115
1197
|
|
1116
|
-
# Wrap the inputs in an ol tag
|
1198
|
+
# Wrap the inputs in an <ol> tag
|
1117
1199
|
def call(form, opts, &block)
|
1118
|
-
form.tag(:ol, &block)
|
1200
|
+
form.tag(:ol, opts[:attr], &block)
|
1119
1201
|
end
|
1120
1202
|
end
|
1121
1203
|
|
1122
|
-
# Use a div tag to wrap the inputs.
|
1204
|
+
# Use a <div> tag to wrap the inputs.
|
1123
1205
|
#
|
1124
1206
|
# Registered as :div.
|
1125
1207
|
class InputsWrapper::Div
|
1126
1208
|
Forme.register_transformer(:inputs_wrapper, :div, new)
|
1127
1209
|
|
1128
|
-
# Wrap the inputs in an
|
1210
|
+
# Wrap the inputs in an <div> tag
|
1211
|
+
def call(form, opts, &block)
|
1212
|
+
form.tag(:div, opts[:attr], &block)
|
1213
|
+
end
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
# Use a <tr> tag to wrap the inputs.
|
1217
|
+
#
|
1218
|
+
# Registered as :tr.
|
1219
|
+
class InputsWrapper::TR
|
1220
|
+
Forme.register_transformer(:inputs_wrapper, :tr, new)
|
1221
|
+
|
1222
|
+
# Wrap the inputs in an <tr> tag
|
1129
1223
|
def call(form, opts, &block)
|
1130
|
-
form.tag(:
|
1224
|
+
form.tag(:tr, opts[:attr], &block)
|
1131
1225
|
end
|
1132
1226
|
end
|
1133
1227
|
|
1134
|
-
# Use a table tag to wrap the inputs.
|
1228
|
+
# Use a <table> tag to wrap the inputs.
|
1135
1229
|
#
|
1136
1230
|
# Registered as :table.
|
1137
1231
|
class InputsWrapper::Table
|
1138
1232
|
Forme.register_transformer(:inputs_wrapper, :table, new)
|
1139
1233
|
|
1140
|
-
# Wrap the inputs in a table tag.
|
1234
|
+
# Wrap the inputs in a <table> tag.
|
1141
1235
|
def call(form, opts, &block)
|
1142
|
-
|
1236
|
+
attr = opts[:attr] ? opts[:attr].dup : {}
|
1237
|
+
form.tag(:table, attr) do
|
1238
|
+
if legend = opts[:legend]
|
1239
|
+
form.emit(form.tag(:caption, opts[:legend_attr], legend))
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
if (labels = opts[:labels]) && !labels.empty?
|
1243
|
+
form.emit(form.tag(:tr, {}, labels.map{|l| form._tag(:th, {}, l)}))
|
1244
|
+
end
|
1245
|
+
|
1246
|
+
yield
|
1247
|
+
end
|
1143
1248
|
end
|
1144
1249
|
end
|
1145
1250
|
|