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