forme 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +24 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +37 -18
- data/Rakefile +6 -5
- data/lib/forme.rb +6 -1348
- data/lib/forme/form.rb +387 -0
- data/lib/forme/input.rb +48 -0
- data/lib/forme/raw.rb +12 -0
- data/lib/forme/tag.rb +60 -0
- data/lib/forme/transformers/error_handler.rb +18 -0
- data/lib/forme/transformers/formatter.rb +511 -0
- data/lib/forme/transformers/helper.rb +17 -0
- data/lib/forme/transformers/inputs_wrapper.rb +95 -0
- data/lib/forme/transformers/labeler.rb +78 -0
- data/lib/forme/transformers/serializer.rb +171 -0
- data/lib/forme/transformers/wrapper.rb +52 -0
- data/lib/forme/version.rb +1 -1
- data/lib/sequel/plugins/forme.rb +7 -4
- data/spec/forme_spec.rb +65 -5
- data/spec/sequel_plugin_spec.rb +18 -3
- metadata +15 -4
@@ -0,0 +1,18 @@
|
|
1
|
+
module Forme
|
2
|
+
# Default error handler used by the library, using an "error" class
|
3
|
+
# for the input field and a span tag with an "error_message" class
|
4
|
+
# for the error message.
|
5
|
+
#
|
6
|
+
# Registered as :default.
|
7
|
+
class ErrorHandler
|
8
|
+
Forme.register_transformer(:error_handler, :default, new)
|
9
|
+
|
10
|
+
# Return tag with error message span tag after it.
|
11
|
+
def call(tag, input)
|
12
|
+
attr = input.opts[:error_attr]
|
13
|
+
attr = attr ? attr.dup : {}
|
14
|
+
Forme.attr_classes(attr, 'error_message')
|
15
|
+
[tag, input.tag(:span, attr, input.opts[:error])]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,511 @@
|
|
1
|
+
module Forme
|
2
|
+
# The default formatter used by the library. Any custom formatters should
|
3
|
+
# probably inherit from this formatter unless they have very special needs.
|
4
|
+
#
|
5
|
+
# Unlike most other transformers which are registered as instances and use
|
6
|
+
# a functional style, this class is registered as a class due to the large
|
7
|
+
# amount of state it uses.
|
8
|
+
#
|
9
|
+
# Registered as :default.
|
10
|
+
class Formatter
|
11
|
+
Forme.register_transformer(:formatter, :default, self)
|
12
|
+
|
13
|
+
# These options are copied directly from the options hash to the the
|
14
|
+
# attributes hash, so they don't need to be specified in the :attr
|
15
|
+
# option. However, they can be specified in both places, and if so,
|
16
|
+
# the :attr option version takes precedence.
|
17
|
+
ATTRIBUTE_OPTIONS = [:name, :id, :placeholder, :value, :style]
|
18
|
+
|
19
|
+
# Options copied from the options hash into the attributes hash,
|
20
|
+
# where a true value in the options hash sets the attribute
|
21
|
+
# value to the same name as the key.
|
22
|
+
ATTRIBUTE_BOOLEAN_OPTIONS = [:autofocus, :required, :disabled]
|
23
|
+
|
24
|
+
# Create a new instance and call it
|
25
|
+
def self.call(input)
|
26
|
+
new.call(input)
|
27
|
+
end
|
28
|
+
|
29
|
+
# The +Form+ instance for the receiver, taken from the +input+.
|
30
|
+
attr_reader :form
|
31
|
+
|
32
|
+
# The +Input+ instance for the receiver. This is what the receiver
|
33
|
+
# converts to the lower level +Tag+ form (or an array of them).
|
34
|
+
attr_reader :input
|
35
|
+
|
36
|
+
# The attributes to to set on the lower level +Tag+ form returned.
|
37
|
+
# This are derived from the +input+'s +opts+, but some processing is done on
|
38
|
+
# them.
|
39
|
+
attr_reader :attr
|
40
|
+
|
41
|
+
# The +opts+ hash of the +input+.
|
42
|
+
attr_reader :opts
|
43
|
+
|
44
|
+
# Used to specify the value of the hidden input created for checkboxes.
|
45
|
+
# Since the default for an unspecified checkbox value is 1, the default is
|
46
|
+
# 0. If the checkbox value is 't', the hidden value is 'f', since that is
|
47
|
+
# common usage for boolean values.
|
48
|
+
CHECKBOX_MAP = Hash.new(0)
|
49
|
+
CHECKBOX_MAP['t'] = 'f'
|
50
|
+
|
51
|
+
# Transform the +input+ into a +Tag+ instance (or an array of them),
|
52
|
+
# wrapping it with the +form+'s wrapper, and the form's +error_handler+
|
53
|
+
# and +labeler+ if the +input+ has an <tt>:error</tt> or <tt>:label</tt>
|
54
|
+
# options.
|
55
|
+
def call(input)
|
56
|
+
@input = input
|
57
|
+
@form = input.form
|
58
|
+
attr = input.opts[:attr]
|
59
|
+
@attr = attr ? attr.dup : {}
|
60
|
+
@opts = input.opts
|
61
|
+
normalize_options
|
62
|
+
|
63
|
+
tag = convert_to_tag(input.type)
|
64
|
+
tag = wrap_tag_with_label(tag) if input.opts[:label]
|
65
|
+
tag = wrap_tag_with_error(tag) if input.opts[:error]
|
66
|
+
tag = wrap(:helper, tag) if input.opts[:help]
|
67
|
+
wrap_tag(tag)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Dispatch to a format_<i>type</i> method if there is one that matches the
|
73
|
+
# type, otherwise, call +_format_input+ with the given +type+.
|
74
|
+
def convert_to_tag(type)
|
75
|
+
meth = :"format_#{type}"
|
76
|
+
if respond_to?(meth, true)
|
77
|
+
send(meth)
|
78
|
+
else
|
79
|
+
_format_input(type)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# If the checkbox has a name, will create a hidden input tag with the
|
84
|
+
# same name that comes before this checkbox. That way, if the checkbox
|
85
|
+
# is checked, the web app will generally see the value of the checkbox, and
|
86
|
+
# if it is not checked, the web app will generally see the value of the hidden
|
87
|
+
# input tag.
|
88
|
+
def format_checkbox
|
89
|
+
@attr[:type] = :checkbox
|
90
|
+
@attr[:checked] = :checked if @opts[:checked]
|
91
|
+
if @attr[:name] && !@opts[:no_hidden]
|
92
|
+
attr = {:type=>:hidden}
|
93
|
+
unless attr[:value] = @opts[:hidden_value]
|
94
|
+
attr[:value] = CHECKBOX_MAP[@attr[:value]]
|
95
|
+
end
|
96
|
+
attr[:id] = "#{@attr[:id]}_hidden" if @attr[:id]
|
97
|
+
attr[:name] = @attr[:name]
|
98
|
+
[tag(:input, attr), tag(:input)]
|
99
|
+
else
|
100
|
+
tag(:input)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# For radio buttons, recognizes the :checked option and sets the :checked
|
105
|
+
# attribute in the tag appropriately.
|
106
|
+
def format_radio
|
107
|
+
@attr[:checked] = :checked if @opts[:checked]
|
108
|
+
@attr[:type] = :radio
|
109
|
+
tag(:input)
|
110
|
+
end
|
111
|
+
|
112
|
+
DEFAULT_DATE_ORDER = [:year, '-'.freeze, :month, '-'.freeze, :day].freeze
|
113
|
+
# Use a date input by default. If the :as=>:select option is given,
|
114
|
+
# use a multiple select box for the options.
|
115
|
+
def format_date
|
116
|
+
if @opts[:as] == :select
|
117
|
+
values = {}
|
118
|
+
if v = @attr[:value]
|
119
|
+
v = Date.parse(v) unless v.is_a?(Date)
|
120
|
+
values[:year], values[:month], values[:day] = v.year, v.month, v.day
|
121
|
+
end
|
122
|
+
_format_date_select(values, @opts[:order] || DEFAULT_DATE_ORDER)
|
123
|
+
else
|
124
|
+
_format_input(:date)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
DEFAULT_DATETIME_ORDER = [:year, '-'.freeze, :month, '-'.freeze, :day, ' '.freeze, :hour, ':'.freeze, :minute, ':'.freeze, :second].freeze
|
129
|
+
# Use a datetime input by default. If the :as=>:select option is given,
|
130
|
+
# use a multiple select box for the options.
|
131
|
+
def format_datetime
|
132
|
+
if @opts[:as] == :select
|
133
|
+
values = {}
|
134
|
+
if v = @attr[:value]
|
135
|
+
v = DateTime.parse(v) unless v.is_a?(Time) || v.is_a?(DateTime)
|
136
|
+
values[:year], values[:month], values[:day], values[:hour], values[:minute], values[:second] = v.year, v.month, v.day, v.hour, v.min, v.sec
|
137
|
+
end
|
138
|
+
_format_date_select(values, @opts[:order] || DEFAULT_DATETIME_ORDER)
|
139
|
+
else
|
140
|
+
_format_input('datetime-local')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
DEFAULT_DATE_SELECT_OPS = {:year=>1900..2050, :month=>1..12, :day=>1..31, :hour=>0..23, :minute=>0..59, :second=>0..59}.freeze
|
145
|
+
DATE_SELECT_FORMAT = '%02i'.freeze
|
146
|
+
# Shared code for formatting dates/times as select boxes
|
147
|
+
def _format_date_select(values, order)
|
148
|
+
name = @attr[:name]
|
149
|
+
id = @attr[:id]
|
150
|
+
ops = DEFAULT_DATE_SELECT_OPS
|
151
|
+
ops = ops.merge(@opts[:select_options]) if @opts[:select_options]
|
152
|
+
first_input = true
|
153
|
+
format = DATE_SELECT_FORMAT
|
154
|
+
order.map do |x|
|
155
|
+
next x if x.is_a?(String)
|
156
|
+
opts = @opts.merge(:label=>nil, :wrapper=>nil, :error=>nil, :name=>"#{name}[#{x}]", :value=>values[x], :options=>ops[x].map{|y| [sprintf(format, y), y]})
|
157
|
+
opts[:id] = if first_input
|
158
|
+
first_input = false
|
159
|
+
id
|
160
|
+
else
|
161
|
+
"#{id}_#{x}"
|
162
|
+
end
|
163
|
+
form._input(:select, opts).format
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# The default fallback method for handling inputs. Assumes an input tag
|
168
|
+
# with the type attribute set to input.
|
169
|
+
def _format_input(type)
|
170
|
+
@attr[:type] = type
|
171
|
+
copy_options_to_attributes([:size, :maxlength])
|
172
|
+
tag(:input)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Takes a select input and turns it into a select tag with (possibly) option
|
176
|
+
# children tags.
|
177
|
+
def format_select
|
178
|
+
@attr[:multiple] = :multiple if @opts[:multiple]
|
179
|
+
copy_options_to_attributes([:size])
|
180
|
+
|
181
|
+
os = process_select_optgroups(:_format_select_optgroup) do |label, value, sel, attrs|
|
182
|
+
if value || sel
|
183
|
+
attrs = attrs.dup
|
184
|
+
attrs[:value] = value if value
|
185
|
+
attrs[:selected] = :selected if sel
|
186
|
+
end
|
187
|
+
tag(:option, attrs, [label])
|
188
|
+
end
|
189
|
+
tag(:select, @attr, os)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Use an optgroup around related options in a select tag.
|
193
|
+
def _format_select_optgroup(group, options)
|
194
|
+
group = {:label=>group} unless group.is_a?(Hash)
|
195
|
+
tag(:optgroup, group, options)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Use a fieldset/legend around related options in a checkbox or radio button set.
|
199
|
+
def _format_set_optgroup(group, options)
|
200
|
+
tag(:fieldset, {}, [tag(:legend, {}, [group])] + options)
|
201
|
+
end
|
202
|
+
|
203
|
+
def format_checkboxset
|
204
|
+
@opts[:multiple] = true unless @opts.has_key?(:multiple)
|
205
|
+
_format_set(:checkbox, :no_hidden=>true, :multiple=>true)
|
206
|
+
end
|
207
|
+
|
208
|
+
def format_radioset
|
209
|
+
_format_set(:radio)
|
210
|
+
end
|
211
|
+
|
212
|
+
def _format_set(type, tag_attrs={})
|
213
|
+
raise Error, "can't have radioset with no options" unless @opts[:optgroups] || @opts[:options]
|
214
|
+
key = @opts[:key]
|
215
|
+
name = @opts[:name]
|
216
|
+
id = @opts[:id]
|
217
|
+
if @opts[:error]
|
218
|
+
@opts[:set_error] = @opts.delete(:error)
|
219
|
+
end
|
220
|
+
if @opts[:label]
|
221
|
+
@opts[:set_label] = @opts.delete(:label)
|
222
|
+
end
|
223
|
+
tag_wrapper = @opts.delete(:tag_wrapper) || :default
|
224
|
+
wrapper = Forme.transformer(:wrapper, @opts, @input.form_opts)
|
225
|
+
|
226
|
+
tags = process_select_optgroups(:_format_set_optgroup) do |label, value, sel, attrs|
|
227
|
+
value ||= label
|
228
|
+
r_opts = attrs.merge(tag_attrs).merge(:label=>label||value, :label_attr=>{:class=>:option}, :wrapper=>tag_wrapper)
|
229
|
+
r_opts[:value] ||= value if value
|
230
|
+
r_opts[:checked] ||= :checked if sel
|
231
|
+
|
232
|
+
if name
|
233
|
+
r_opts[:name] ||= name
|
234
|
+
end
|
235
|
+
if id
|
236
|
+
r_opts[:id] ||= "#{id}_#{value}"
|
237
|
+
end
|
238
|
+
if key
|
239
|
+
r_opts[:key] ||= key
|
240
|
+
r_opts[:key_id] ||= value
|
241
|
+
end
|
242
|
+
|
243
|
+
form._input(type, r_opts)
|
244
|
+
end
|
245
|
+
|
246
|
+
if @opts[:set_error]
|
247
|
+
if (last_input = tags.last) && last_input.is_a?(Input)
|
248
|
+
last_input.opts[:error] = @opts[:set_error]
|
249
|
+
else
|
250
|
+
tags << form._tag(:span, {:class=>'error_message'}, [@opts[:set_error]])
|
251
|
+
end
|
252
|
+
end
|
253
|
+
tags.unshift(form._tag(:span, {:class=>:label}, @opts[:set_label])) if @opts[:set_label]
|
254
|
+
wrapper.call(tags, form._input(type, opts)) if wrapper
|
255
|
+
tags
|
256
|
+
end
|
257
|
+
|
258
|
+
# Formats a textarea. Respects the following options:
|
259
|
+
# :value :: Sets value as the child of the textarea.
|
260
|
+
def format_textarea
|
261
|
+
copy_options_to_attributes([:cols, :rows])
|
262
|
+
if val = @attr.delete(:value)
|
263
|
+
tag(:textarea, @attr, [val])
|
264
|
+
else
|
265
|
+
tag(:textarea)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Copy option values for given keys to the attributes unless the
|
270
|
+
# attributes already have a value for the key.
|
271
|
+
def copy_options_to_attributes(keys)
|
272
|
+
keys.each do |k|
|
273
|
+
if @opts.has_key?(k) && !@attr.has_key?(k)
|
274
|
+
@attr[k] = @opts[k]
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Set attribute values for given keys to be the same as the key
|
280
|
+
# unless the attributes already have a value for the key.
|
281
|
+
def copy_boolean_options_to_attributes(keys)
|
282
|
+
keys.each do |k|
|
283
|
+
if @opts[k] && !@attr.has_key?(k)
|
284
|
+
@attr[k] = k
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Normalize the options used for all input types.
|
290
|
+
def normalize_options
|
291
|
+
copy_options_to_attributes(ATTRIBUTE_OPTIONS)
|
292
|
+
copy_boolean_options_to_attributes(ATTRIBUTE_BOOLEAN_OPTIONS)
|
293
|
+
handle_key_option
|
294
|
+
|
295
|
+
Forme.attr_classes(@attr, @opts[:class]) if @opts.has_key?(:class)
|
296
|
+
Forme.attr_classes(@attr, 'error') if @opts[:error]
|
297
|
+
|
298
|
+
if data = opts[:data]
|
299
|
+
data.each do |k, v|
|
300
|
+
sym = :"data-#{k}"
|
301
|
+
@attr[sym] = v unless @attr.has_key?(sym)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Have the :key option possibly set the name, id, and/or value attributes if not already set.
|
307
|
+
def handle_key_option
|
308
|
+
if key = @opts[:key]
|
309
|
+
unless @attr[:name] || @attr['name']
|
310
|
+
@attr[:name] = namespaced_name(key, @opts[:array] || @opts[:multiple])
|
311
|
+
if !@attr.has_key?(:value) && !@attr.has_key?('value') && (values = @form.opts[:values])
|
312
|
+
set_value_from_namespaced_values(namespaces, values, key)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
unless @attr[:id] || @attr['id']
|
316
|
+
id = namespaced_id(key)
|
317
|
+
if suffix = @opts[:key_id]
|
318
|
+
id << '_' << suffix.to_s
|
319
|
+
end
|
320
|
+
@attr[:id] = id
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Array of namespaces to use for the input
|
326
|
+
def namespaces
|
327
|
+
input.form_opts[:namespace]
|
328
|
+
end
|
329
|
+
|
330
|
+
# Return a unique id attribute for the +field+, based on the current namespaces.
|
331
|
+
def namespaced_id(field)
|
332
|
+
"#{namespaces.join('_')}#{'_' unless namespaces.empty?}#{field}"
|
333
|
+
end
|
334
|
+
|
335
|
+
# Return a unique name attribute for the +field+, based on the current namespaces.
|
336
|
+
# If +multiple+ is true, end the name with [] so that param parsing will treat
|
337
|
+
# the name as part of an array.
|
338
|
+
def namespaced_name(field, multiple=false)
|
339
|
+
if namespaces.empty?
|
340
|
+
if multiple
|
341
|
+
"#{field}[]"
|
342
|
+
else
|
343
|
+
field
|
344
|
+
end
|
345
|
+
else
|
346
|
+
root, *nsps = namespaces
|
347
|
+
"#{root}#{nsps.map{|n| "[#{n}]"}.join}[#{field}]#{'[]' if multiple}"
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# Set the values option based on the (possibly nested) values
|
352
|
+
# hash given, array of namespaces, and key.
|
353
|
+
def set_value_from_namespaced_values(namespaces, values, key)
|
354
|
+
namespaces.each do |ns|
|
355
|
+
v = values[ns] || values[ns.to_s]
|
356
|
+
return unless v
|
357
|
+
values = v
|
358
|
+
end
|
359
|
+
|
360
|
+
@attr[:value] = values.fetch(key){values.fetch(key.to_s){return}}
|
361
|
+
end
|
362
|
+
|
363
|
+
# If :optgroups option is present, iterate over each of the groups
|
364
|
+
# inside of it and create options for each group. Otherwise, if
|
365
|
+
# :options option present, iterate over it and create options.
|
366
|
+
def process_select_optgroups(grouper, &block)
|
367
|
+
os = if groups = @opts[:optgroups]
|
368
|
+
groups.map do |group, options|
|
369
|
+
send(grouper, group, process_select_options(options, &block))
|
370
|
+
end
|
371
|
+
else
|
372
|
+
return unless @opts[:options]
|
373
|
+
process_select_options(@opts[:options], &block)
|
374
|
+
end
|
375
|
+
|
376
|
+
if prompt = @opts[:add_blank]
|
377
|
+
unless prompt.is_a?(String)
|
378
|
+
prompt = Forme.default_add_blank_prompt
|
379
|
+
end
|
380
|
+
blank_attr = @opts[:blank_attr] || {}
|
381
|
+
os.send(@opts[:blank_position] == :after ? :push : :unshift, yield([prompt, '', false, blank_attr]))
|
382
|
+
end
|
383
|
+
|
384
|
+
os
|
385
|
+
end
|
386
|
+
|
387
|
+
# Iterate over the given options, yielding the option text, value, whether it is selected, and any attributes.
|
388
|
+
# The block should return an appropriate tag object.
|
389
|
+
def process_select_options(os)
|
390
|
+
if os
|
391
|
+
vm = @opts[:value_method]
|
392
|
+
tm = @opts[:text_method]
|
393
|
+
sel = @opts[:selected] || @attr.delete(:value)
|
394
|
+
|
395
|
+
if @opts[:multiple]
|
396
|
+
sel = Array(sel)
|
397
|
+
cmp = lambda{|v| sel.include?(v)}
|
398
|
+
else
|
399
|
+
cmp = lambda{|v| v == sel}
|
400
|
+
end
|
401
|
+
|
402
|
+
os.map do |x|
|
403
|
+
attr = {}
|
404
|
+
if tm
|
405
|
+
text = x.send(tm)
|
406
|
+
val = x.send(vm) if vm
|
407
|
+
elsif x.is_a?(Array)
|
408
|
+
text = x.first
|
409
|
+
val = x.last
|
410
|
+
|
411
|
+
if val.is_a?(Hash)
|
412
|
+
value = val[:value]
|
413
|
+
attr.merge!(val)
|
414
|
+
val = value
|
415
|
+
end
|
416
|
+
else
|
417
|
+
text = x
|
418
|
+
end
|
419
|
+
|
420
|
+
yield [text, val, val ? cmp.call(val) : cmp.call(text), attr]
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
# Create a +Tag+ instance related to the receiver's +form+ with the given
|
426
|
+
# arguments.
|
427
|
+
def tag(type, attr=@attr, children=nil)
|
428
|
+
form._tag(type, attr, children)
|
429
|
+
end
|
430
|
+
|
431
|
+
# Wrap the tag for the given transformer type.
|
432
|
+
def wrap(type, tag)
|
433
|
+
Forme.transform(type, @opts, input.form_opts, tag, input)
|
434
|
+
end
|
435
|
+
|
436
|
+
# Wrap the tag with the form's +wrapper+.
|
437
|
+
def wrap_tag(tag)
|
438
|
+
wrap(:wrapper, tag)
|
439
|
+
end
|
440
|
+
|
441
|
+
# Wrap the tag with the form's +error_handler+.
|
442
|
+
def wrap_tag_with_error(tag)
|
443
|
+
wrap(:error_handler, tag)
|
444
|
+
end
|
445
|
+
|
446
|
+
# Wrap the tag with the form's +labeler+.
|
447
|
+
def wrap_tag_with_label(tag)
|
448
|
+
wrap(:labeler, tag)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
# Formatter that disables all input fields,
|
453
|
+
#
|
454
|
+
# Registered as :disabled.
|
455
|
+
class Formatter::Disabled < Formatter
|
456
|
+
Forme.register_transformer(:formatter, :disabled, self)
|
457
|
+
|
458
|
+
private
|
459
|
+
|
460
|
+
# Unless the :disabled option is specifically set
|
461
|
+
# to +false+, set the :disabled attribute on the
|
462
|
+
# resulting tag.
|
463
|
+
def normalize_options
|
464
|
+
if @opts[:disabled] == false
|
465
|
+
super
|
466
|
+
else
|
467
|
+
super
|
468
|
+
@attr[:disabled] = :disabled
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# Formatter that uses span tags with text for most input types,
|
474
|
+
# and disables radio/checkbox inputs.
|
475
|
+
#
|
476
|
+
# Registered as :readonly.
|
477
|
+
class Formatter::ReadOnly < Formatter
|
478
|
+
Forme.register_transformer(:formatter, :readonly, self)
|
479
|
+
|
480
|
+
private
|
481
|
+
|
482
|
+
# Disabled checkbox inputs.
|
483
|
+
def format_checkbox
|
484
|
+
@attr[:disabled] = :disabled
|
485
|
+
super
|
486
|
+
end
|
487
|
+
|
488
|
+
# Use a span with text instead of an input field.
|
489
|
+
def _format_input(type)
|
490
|
+
tag(:span, {}, @attr[:value])
|
491
|
+
end
|
492
|
+
|
493
|
+
# Disabled radio button inputs.
|
494
|
+
def format_radio
|
495
|
+
@attr[:disabled] = :disabled
|
496
|
+
super
|
497
|
+
end
|
498
|
+
|
499
|
+
# Use a span with text of the selected values instead of a select box.
|
500
|
+
def format_select
|
501
|
+
t = super
|
502
|
+
children = [t.children.select{|o| o.attr[:selected]}.map(&:children).join(', ')] if t.children
|
503
|
+
tag(:span, {}, children)
|
504
|
+
end
|
505
|
+
|
506
|
+
# Use a span with text instead of a text area.
|
507
|
+
def format_textarea
|
508
|
+
tag(:span, {}, @attr[:value])
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|