campo 0.3.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/{CHANGES → CHANGES.markdown} +62 -0
- data/Gemfile +15 -0
- data/README.markdown +305 -199
- data/Rakefile +11 -0
- data/campo.gemspec +5 -4
- data/lib/campo.rb +7 -524
- data/lib/campo/campo.rb +740 -0
- data/lib/campo/plugins.rb +41 -0
- data/lib/campo/plugins/aria.rb +92 -0
- data/lib/campo/plugins/jqueryvalidation.rb +155 -0
- data/lib/campo/plugins/partial.rb +49 -0
- data/lib/campo/version.rb +1 -1
- data/spec/aria_spec.rb +67 -0
- data/spec/campo_spec.rb +475 -243
- data/spec/jqueryvalidation_spec.rb +174 -0
- data/spec/partial_spec.rb +77 -0
- data/spec/plugins_spec.rb +66 -0
- data/spec/support/matchers/items.rb +8 -0
- metadata +64 -54
- data.tar.gz.sig +0 -2
- metadata.gz.sig +0 -0
data/lib/campo/campo.rb
ADDED
@@ -0,0 +1,740 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Campo
|
4
|
+
|
5
|
+
# Deals with adding children and tracking parents.
|
6
|
+
module Childish
|
7
|
+
|
8
|
+
# Push something onto the end of the fields array.
|
9
|
+
# @param [Object] child The object to push.
|
10
|
+
# @return [Object] self
|
11
|
+
def push=( child )
|
12
|
+
@fields << child
|
13
|
+
child.parent = self
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
# @see #push=
|
18
|
+
alias :<< :push=
|
19
|
+
|
20
|
+
attr_accessor :parent
|
21
|
+
end # Childish
|
22
|
+
|
23
|
+
# Helpers for id'ing fields.
|
24
|
+
module Iding
|
25
|
+
|
26
|
+
# Helps to create a unique id tag.
|
27
|
+
# @param [String,nil] val
|
28
|
+
def id_tag( val )
|
29
|
+
val.nil? ? "" : "_#{val}"
|
30
|
+
end
|
31
|
+
end # Iding
|
32
|
+
|
33
|
+
|
34
|
+
def self.constantize(camel_cased_word)
|
35
|
+
names = camel_cased_word.split('::')
|
36
|
+
names.shift if names.empty? || names.first.empty?
|
37
|
+
|
38
|
+
constant = Object
|
39
|
+
names.each do |name|
|
40
|
+
constant = constant.const_defined?(name) ? constant.const_get(name,false) : constant.const_missing(name)
|
41
|
+
end
|
42
|
+
constant
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# keeps track of the current plugins
|
47
|
+
def self.plugins
|
48
|
+
@plugins ||= {}
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def self.plugin( name, options={} )
|
53
|
+
unless plugins.include? name
|
54
|
+
modname = (str = name.to_s) && (str[0,1].upcase + str[1..-1])
|
55
|
+
plugins[name] = constantize("Campo::Plugins::#{modname}").new options
|
56
|
+
plugins[name].plugged_in
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Here to make life a bit easier and cut down on RSI.
|
61
|
+
module Convenience
|
62
|
+
|
63
|
+
# @param [optional, Hash] attributes Any attributes you wish to add to the haml element.
|
64
|
+
# @example Fieldset as a block is easiest to read
|
65
|
+
# form.fieldset("Your details") do |f|
|
66
|
+
# f.text( "full_name", size: 60 )
|
67
|
+
# f.text( "dob", "Date of birth: ", size: 8 )
|
68
|
+
# end
|
69
|
+
def fieldset( text, attributes={}, &block )
|
70
|
+
self << Fieldset.new( text, attributes, &block )
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# @example Add a bit of code to the markup
|
75
|
+
# form.bit_of_ruby( "= 5 + 1" ) }
|
76
|
+
def bit_of_ruby( *args, &block )
|
77
|
+
tag = Campo::Haml_Ruby_Insert.new( *args, &block )
|
78
|
+
self << tag
|
79
|
+
tag
|
80
|
+
end
|
81
|
+
|
82
|
+
alias :haml_ruby_insert :bit_of_ruby
|
83
|
+
|
84
|
+
|
85
|
+
# @example Output a literal string
|
86
|
+
# form.literal %Q!%p= "This is a paragraph "!
|
87
|
+
# @see Campo::Literal#initialize
|
88
|
+
def literal( *args, &block )
|
89
|
+
tag = Campo::Literal.new( *args, &block )
|
90
|
+
self << tag
|
91
|
+
tag
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# @example
|
96
|
+
# # Select with a block of options
|
97
|
+
# f.select("teas") do |s|
|
98
|
+
# s.with_default
|
99
|
+
# s.option("ceylon")
|
100
|
+
# s.option("breakfast")
|
101
|
+
# s.option("earl grey")
|
102
|
+
# s.option("oolong")
|
103
|
+
# s.option("sencha")
|
104
|
+
# end.labelled("Favourite tea:")
|
105
|
+
#
|
106
|
+
# # Select using chain of options
|
107
|
+
# form.select("bands").option("Suede").option("Blur").option("Oasis").option("Echobelly").option("Pulp").option("Supergrass").with_default.labelled("Favourite band:")
|
108
|
+
#
|
109
|
+
# @see Select#initialize
|
110
|
+
def select( *args, &block )
|
111
|
+
select = Campo::Select.new( *args, &block )
|
112
|
+
self << select
|
113
|
+
select
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# Add an input with type of text
|
118
|
+
# @param [String] name The name html attribute.
|
119
|
+
# @param [optional, String, nil] label Give the label a name. Defaults to a capitalised name with _ replaced by spaces.
|
120
|
+
# @param [optional, Hash] attributes Any attributes you wish to add to the haml element.
|
121
|
+
# @example
|
122
|
+
# f.text "full_name", size: 60
|
123
|
+
# f.text "dob", "Date of birth: ", size: 8
|
124
|
+
# @return [Input]
|
125
|
+
# With the attribute `type=text`
|
126
|
+
def text( name, label=nil, attributes={} )
|
127
|
+
input( name, :text, label, attributes )
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
def hidden( name, attributes={} )
|
132
|
+
self << Campo::Input.new( name, :hidden, attributes )
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
# @param (see #text)
|
137
|
+
def password( name, label=nil, attributes={} )
|
138
|
+
input( name, :password, label, attributes )
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
# @param (see #text)
|
143
|
+
def radio( name, label=nil, attributes={} )
|
144
|
+
input( name, :radio, label, attributes )
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
# @param (see #text)
|
149
|
+
def checkbox( name, label=nil, attributes={} )
|
150
|
+
input( name, :checkbox, label, attributes )
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
# @param (see #text)
|
155
|
+
# @param [:symbol] type The type html attribute.
|
156
|
+
def input( name, type, label=nil, attributes={} )
|
157
|
+
if label.kind_of? Hash
|
158
|
+
attributes = label
|
159
|
+
label = nil
|
160
|
+
end
|
161
|
+
|
162
|
+
field = Campo::Input.new( name, type, attributes ).labelled( label )
|
163
|
+
self << field
|
164
|
+
field
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
# @param [optional,String] name
|
169
|
+
# @param [optional, Hash] attributes Any attributes you wish to add to the haml element.
|
170
|
+
def submit( name="Submit", attributes={} )
|
171
|
+
submit = Campo::Input.new( name, :submit, {value: name}.merge(attributes) )
|
172
|
+
self << submit
|
173
|
+
submit
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
# There is no easy way to give this convenience method in the same convention as the other methods, so this uses a hash argument for the label
|
178
|
+
# @example
|
179
|
+
# textarea "name", "The text wrapped inside", label: "Example textarea"
|
180
|
+
def textarea( name, inner=nil, attributes={}, &block )
|
181
|
+
if inner.kind_of? Hash
|
182
|
+
attributes = inner
|
183
|
+
inner = nil
|
184
|
+
end
|
185
|
+
label = attributes.delete(:label) || attributes.delete(:labelled)
|
186
|
+
textarea = Campo::Textarea.new( name, inner, attributes ).labelled( label )
|
187
|
+
self << textarea
|
188
|
+
textarea
|
189
|
+
end
|
190
|
+
end # Convenience
|
191
|
+
|
192
|
+
|
193
|
+
module Helpers
|
194
|
+
|
195
|
+
# [ [id, lookup, selected || false], ... ]
|
196
|
+
def self.options_builder( name, opts )
|
197
|
+
return [] if opts.nil? || opts.empty?
|
198
|
+
|
199
|
+
if opts.respond_to? :each_pair
|
200
|
+
opts.map do |id, (inner, selected, atts)|
|
201
|
+
Campo::Option.new( name, id, inner, selected, atts )
|
202
|
+
end
|
203
|
+
else
|
204
|
+
opts.map do |id, inner, selected, atts|
|
205
|
+
Campo::Option.new( name, id, inner, selected, atts )
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end # def
|
209
|
+
|
210
|
+
|
211
|
+
def self.options_outputter( opts=[] )
|
212
|
+
return "" if opts.nil? || opts.empty?
|
213
|
+
opts.map{|o| "#{o.output}\n" }.reduce(:+)
|
214
|
+
end
|
215
|
+
end # Helpers
|
216
|
+
|
217
|
+
@atts = {}
|
218
|
+
|
219
|
+
|
220
|
+
class << self
|
221
|
+
attr_accessor :atts
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
# Almost every Campo class inherits from this.
|
226
|
+
# @abstract Not entirely abstract, but should always be subclassed.
|
227
|
+
class Base
|
228
|
+
include Childish
|
229
|
+
include Iding
|
230
|
+
include Enumerable
|
231
|
+
alias_method :enumerable_select, :select
|
232
|
+
include Convenience
|
233
|
+
|
234
|
+
# Default attributes.
|
235
|
+
DEFAULT = { tabindex: nil }
|
236
|
+
|
237
|
+
# @!attribute [r] attributes The element's html attributes.
|
238
|
+
# @return [Hash]
|
239
|
+
|
240
|
+
# @!attribute [r] fields The element's child elements.
|
241
|
+
# @return [Array<Base>]
|
242
|
+
|
243
|
+
attr_accessor :attributes, :fields
|
244
|
+
|
245
|
+
# @param [String] name The value of the element's name attribute.
|
246
|
+
# @param [Hash,optional] attributes Any attributes for the element. Defaults to a generated tabindex (dependent on the order of form elements).
|
247
|
+
# @yield Any fields defined in the passed block become children of this element.
|
248
|
+
def initialize( name, attributes={}, &block )
|
249
|
+
@attributes = DEFAULT.merge( {id: name}.merge(attributes.merge({name: name})) ).reject{|k,v| v.nil? }
|
250
|
+
@fields = []
|
251
|
+
|
252
|
+
instance_eval( &block ) if block
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
# Iterates over the fields array.
|
257
|
+
def each(&block)
|
258
|
+
block.call self if block
|
259
|
+
if respond_to?(:fields) &! fields.empty?
|
260
|
+
fields.each{|field| field.each &block }
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
# Takes a block that handles the rendering.
|
266
|
+
def on_output( &block )
|
267
|
+
@output_listener = block
|
268
|
+
end
|
269
|
+
|
270
|
+
|
271
|
+
# Render to Haml
|
272
|
+
# @param [Integer] n
|
273
|
+
# @param [Integer] tab
|
274
|
+
def output( n=0, tab=2 )
|
275
|
+
n ||= 0
|
276
|
+
tab ||= 2
|
277
|
+
@output_listener.call n, tab
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
# Bit of a convenience method for adding a label around any element.
|
282
|
+
# @param [String] inner The text for the label.
|
283
|
+
# @return [Base]
|
284
|
+
def labelled( inner=nil )
|
285
|
+
inner ||= self.attributes[:name].gsub(/\[\]/, "").gsub("_"," ").capitalize
|
286
|
+
parent = self.parent
|
287
|
+
label = Label.new( %Q!#{@attributes[:id]}!, inner ) << self
|
288
|
+
retval = if parent.nil?
|
289
|
+
label
|
290
|
+
else
|
291
|
+
parent.fields.delete self
|
292
|
+
parent << label
|
293
|
+
label
|
294
|
+
end
|
295
|
+
|
296
|
+
retval
|
297
|
+
end # labelled
|
298
|
+
|
299
|
+
|
300
|
+
# Takes a hash and transforms the key value pairs into a stringified version that Haml can consume.
|
301
|
+
# @param [Hash] hash The hash to stringify.
|
302
|
+
# @param [Array<#to_s>] skips Keys to skip.
|
303
|
+
# @todo Make an Attributes class < Hash that deals with this.
|
304
|
+
# @api private
|
305
|
+
def self.unhash( hash, skips=nil )
|
306
|
+
skips = skips.nil? ? [] : skips.map(&:to_sym) # all keys are symbols
|
307
|
+
hash.reject{|k,v| v.nil? }.reject{|k,v| skips.include? k.to_sym }.reduce(""){|mem, (k,v)| mem + %Q!#{k.to_s.include?("-") ? ":\"#{k}\" =>" : "#{k}:"} #{Base.quotable(v)}, !}
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
# @api private
|
312
|
+
# if the string provided begins with a double quote but does not end in one, make it an unquoted string on output
|
313
|
+
# else, wrap it in quotes
|
314
|
+
# @param [String] s
|
315
|
+
def self.quotable( s )
|
316
|
+
retval = if s.respond_to?(:start_with?) && s.start_with?( %Q!"! ) &! s.end_with?( %Q!"! )
|
317
|
+
s[1.. -1] # chop the first character
|
318
|
+
else
|
319
|
+
%Q!"#{s}"! # wrap
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
# Where the magic of output happens.
|
325
|
+
# @param [Base] top
|
326
|
+
# @param [String] so_far
|
327
|
+
# @param [Integer] depth
|
328
|
+
# @param [Integer] tab Number of spaces for a tab.
|
329
|
+
def self.output( top, so_far="", depth=0, tab=2)
|
330
|
+
so_far << "#{top.output( depth, tab )}\n"
|
331
|
+
depth += 1
|
332
|
+
if top.respond_to?( :fields ) && top.fields.length >= 1
|
333
|
+
top.fields.each do |field|
|
334
|
+
so_far = Base.output( field, so_far, depth, tab )
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
so_far
|
339
|
+
end
|
340
|
+
|
341
|
+
alias :render :output
|
342
|
+
|
343
|
+
end # Base
|
344
|
+
|
345
|
+
|
346
|
+
# @see Convenience#literal
|
347
|
+
def self.literal( *args, &block )
|
348
|
+
Campo::Literal.new( *args, &block )
|
349
|
+
end
|
350
|
+
|
351
|
+
|
352
|
+
# Pass anything but the form for the first argument to *not* have the local variable defaults added to the top
|
353
|
+
# @example
|
354
|
+
# Campo.output form # would add the default locals
|
355
|
+
# # these won't
|
356
|
+
# Campo.output :partial, input_field
|
357
|
+
# Campo.output false, label
|
358
|
+
# Campo.output true, fieldset
|
359
|
+
def self.output( fields, options={} )
|
360
|
+
Outputter.new( options.delete(:tab) ).run( fields, options )
|
361
|
+
end # self.output
|
362
|
+
|
363
|
+
# end Campo methods
|
364
|
+
|
365
|
+
|
366
|
+
class Outputter
|
367
|
+
|
368
|
+
def before_output( &block )
|
369
|
+
befores << block
|
370
|
+
end
|
371
|
+
|
372
|
+
|
373
|
+
def after_output( &block )
|
374
|
+
afters << block
|
375
|
+
end
|
376
|
+
|
377
|
+
|
378
|
+
def befores
|
379
|
+
@befores ||= check_for_plugins :befores
|
380
|
+
end
|
381
|
+
|
382
|
+
|
383
|
+
def afters
|
384
|
+
@afters ||= check_for_plugins :afters
|
385
|
+
end
|
386
|
+
|
387
|
+
|
388
|
+
def check_for_plugins( type )
|
389
|
+
Campo.plugins.reduce [] do |mem, (_,plugin)|
|
390
|
+
mem + plugin.send(:"#{type}" )
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
|
395
|
+
def initialize( tab=nil, &block )
|
396
|
+
options[:tab] = tab unless tab.nil?
|
397
|
+
instance_eval( &block ) if block
|
398
|
+
end
|
399
|
+
|
400
|
+
attr_accessor :output
|
401
|
+
|
402
|
+
DEFAULT_OPTIONS={n: 0, tab: 2}
|
403
|
+
|
404
|
+
|
405
|
+
def options
|
406
|
+
@options ||= DEFAULT_OPTIONS
|
407
|
+
end
|
408
|
+
|
409
|
+
|
410
|
+
def run( fields, opts={} )
|
411
|
+
opts = options.merge opts
|
412
|
+
tab = opts.delete(:tab) || @tab
|
413
|
+
|
414
|
+
output = ""
|
415
|
+
befores.each{|f| instance_exec( fields, options, &f ) }
|
416
|
+
output = Base.output( fields, output, options[:n], tab )
|
417
|
+
output = afters.reduce(output){|mem,obj| instance_exec mem, opts, &obj }
|
418
|
+
output
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
|
423
|
+
class Form < Base
|
424
|
+
DEFAULT = { method: "POST" }
|
425
|
+
|
426
|
+
# @param [String] name The form's name (html) attribute.
|
427
|
+
# @param [optional, Hash] attributes Html attributes. They can be anything you like. Defaults follow:
|
428
|
+
# @option attributes [String] :method ("POST")
|
429
|
+
# @example
|
430
|
+
# form = Campo::Form.new "example", "/path/to/post/to/" do |form|
|
431
|
+
# form.text "first_field"
|
432
|
+
# #... more fields follow
|
433
|
+
# end
|
434
|
+
def initialize(name, attributes={} )
|
435
|
+
super( name, DEFAULT.merge( attributes ) )
|
436
|
+
self.on_output do |n=0, tab=2|
|
437
|
+
%Q!#{" " * n * tab}%form{ atts[:#{name.gsub(/\W/, "_").downcase}], #{Base.unhash( @attributes )} }!
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
end # Form
|
442
|
+
|
443
|
+
|
444
|
+
# Probably, the first method you'll call.
|
445
|
+
# @example
|
446
|
+
# # Form with a block
|
447
|
+
# form = Campo.form "form1", action: "/go/for/it/" do |f|
|
448
|
+
# f.text "Hello"
|
449
|
+
# #... more fields follow
|
450
|
+
# end
|
451
|
+
#
|
452
|
+
# @param [String] name The form's name (html) attribute.
|
453
|
+
# @param [optional, Hash] attributes Html attributes. They can be anything you like. Defaults follow:
|
454
|
+
# @option attributes [String] :method ("POST") The method attribute for the form.
|
455
|
+
# @see Form#initialize
|
456
|
+
def self.form( name, attributes={}, &block )
|
457
|
+
Form.new( name, attributes, &block )
|
458
|
+
end
|
459
|
+
|
460
|
+
|
461
|
+
class Haml_Ruby_Insert < Base
|
462
|
+
def initialize( s )
|
463
|
+
raise ArgumentError, "you may only pass a string to Haml_Ruby_Insert/bit_of_ruby" unless s.kind_of?( String )
|
464
|
+
super( nil ) # no name needed
|
465
|
+
|
466
|
+
# @todo Don't enforce the equals sign, as a hyphen is also valid for adding a bit of ruby. Raise an exception
|
467
|
+
@s = s.start_with?( '=' ) ? s : "= " + s.to_s
|
468
|
+
|
469
|
+
self.on_output do |n=0, tab=2|
|
470
|
+
(" " * n * tab) + @s
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end # Haml_Ruby_Insert
|
474
|
+
|
475
|
+
|
476
|
+
# Add whatever you need to with a literal.
|
477
|
+
class Literal < Base
|
478
|
+
|
479
|
+
# @param [String] s The literal string.
|
480
|
+
# @param [Hash,optional] attributes Any html attributes you wish the literal to have.
|
481
|
+
def initialize( s, attributes={} )
|
482
|
+
super( nil, attributes ) # no name needed
|
483
|
+
@s = s
|
484
|
+
|
485
|
+
self.on_output do |n=0, tab=2|
|
486
|
+
left,right = if @attributes.empty?
|
487
|
+
['','']
|
488
|
+
else
|
489
|
+
['{ ', '}']
|
490
|
+
end
|
491
|
+
%Q!#{" " * n * tab}#{@s}! + left + Base.unhash( @attributes ) + right
|
492
|
+
end
|
493
|
+
self
|
494
|
+
end
|
495
|
+
end # Literal
|
496
|
+
|
497
|
+
|
498
|
+
class Select < Base
|
499
|
+
def initialize( name, params={} )
|
500
|
+
opts = params[:opts] || []
|
501
|
+
attributes = params[:attributes] || {}
|
502
|
+
haml_insert = params[:haml_insert] || nil
|
503
|
+
|
504
|
+
super( name, { tabindex: %q!#{@campo_tabindex += 1}! }.merge(attributes) )
|
505
|
+
|
506
|
+
self.on_output do |n=0, tab=2|
|
507
|
+
%Q!#{" " * n * tab}%select{ atts[:#{name.gsub(/\W/, "_").downcase}], #{Base.unhash( @attributes )} }!
|
508
|
+
end
|
509
|
+
|
510
|
+
self.fields += Helpers.options_builder( name, opts ) unless opts.nil? || opts.empty?
|
511
|
+
|
512
|
+
self.fields << Haml_Ruby_Insert.new( haml_insert ) unless haml_insert.nil?
|
513
|
+
|
514
|
+
self
|
515
|
+
end # initialize
|
516
|
+
|
517
|
+
|
518
|
+
# @example (see Convenience#select)
|
519
|
+
def option( *args )
|
520
|
+
value = args.shift
|
521
|
+
inner = args.shift
|
522
|
+
selected, attributes = *args
|
523
|
+
inner = value.capitalize if inner.nil?
|
524
|
+
self << Campo::Option.new( @attributes[:name], value, inner, selected, attributes )
|
525
|
+
self
|
526
|
+
end
|
527
|
+
|
528
|
+
|
529
|
+
# Adds a default selection to a select list. By default it is disabled.
|
530
|
+
# @param [String,nil] The display string for the option. Default is "Choose one:".
|
531
|
+
# @param [Hash,nil] attributes Attributes for the option. Defaults to {disabled: "disabled"}, pass in an empty hash to override (or a filled one), or nil for the default.
|
532
|
+
# @example
|
533
|
+
# As a default:
|
534
|
+
# form.select("teas").with_default.option("ceylon")
|
535
|
+
# # output:
|
536
|
+
# %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", name: "teas", }
|
537
|
+
# %option{ value: "", disabled: "disabled", name: "teas", }Choose one:
|
538
|
+
# %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
|
539
|
+
#
|
540
|
+
# form.select("teas").with_default("My fave tea is:").option("ceylon")
|
541
|
+
# # output:
|
542
|
+
# %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", name: "teas", }
|
543
|
+
# %option{ value: "", disabled: "disabled", name: "teas", }My fave tea is:
|
544
|
+
# %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
|
545
|
+
def with_default( inner="Choose one:", attributes={disabled: "disabled"} )
|
546
|
+
unless inner.nil? || inner.kind_of?( String )
|
547
|
+
attributes = inner
|
548
|
+
inner = nil
|
549
|
+
end
|
550
|
+
|
551
|
+
inner ||="Choose one:"
|
552
|
+
attributes ||= {disabled: "disabled"}
|
553
|
+
attributes = {id: "#{@attributes[:name]}_default" }.merge! attributes
|
554
|
+
self.fields.unshift Campo::Option.new( @attributes[:name], "", inner , nil, attributes )
|
555
|
+
self
|
556
|
+
end
|
557
|
+
|
558
|
+
end # Select
|
559
|
+
|
560
|
+
|
561
|
+
# Options for your Selectas!
|
562
|
+
class Option < Base
|
563
|
+
|
564
|
+
# @param [String] name
|
565
|
+
# @param [String] value
|
566
|
+
# @param [String] inner
|
567
|
+
# @param [true,false,nil] selected
|
568
|
+
# @param [Hash] attributes
|
569
|
+
def initialize( name, value, inner=nil, selected=nil, attributes={} )
|
570
|
+
unless inner.nil? || inner.kind_of?( String )
|
571
|
+
attributes = selected
|
572
|
+
selected = inner
|
573
|
+
inner = nil
|
574
|
+
end
|
575
|
+
|
576
|
+
unless selected.nil? || selected.kind_of?( TrueClass )
|
577
|
+
if selected.respond_to? :each_pair
|
578
|
+
attributes = selected
|
579
|
+
selected = nil
|
580
|
+
else
|
581
|
+
selected = true
|
582
|
+
@selected = true
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
attributes ||= {}
|
587
|
+
|
588
|
+
@inner = (inner || value.gsub("_"," ").capitalize)
|
589
|
+
|
590
|
+
attributes = { id: "#{(name.gsub(/\W/, "_") + id_tag(value).gsub(/\W/, "_")).downcase}" }.merge(attributes) unless value.nil? || value.to_s.empty?
|
591
|
+
|
592
|
+
super( name, {
|
593
|
+
value: value,
|
594
|
+
selected: (selected ? "selected" : nil)
|
595
|
+
}.merge( attributes ) )
|
596
|
+
|
597
|
+
atts_string = "atts[:#{@attributes[:id]}]," unless @attributes[:id].nil?
|
598
|
+
|
599
|
+
self.on_output do |n=0, tab=2|
|
600
|
+
%Q!#{" " * n * tab}%option{ #{atts_string} #{Base.unhash( @attributes )} }#{@inner}!
|
601
|
+
end
|
602
|
+
|
603
|
+
end #initialize
|
604
|
+
end # Option
|
605
|
+
|
606
|
+
|
607
|
+
# form << Campo::Input.new( "submit", :submit )
|
608
|
+
class Input < Base
|
609
|
+
|
610
|
+
# @param [String] name
|
611
|
+
# @param [Symbol] type
|
612
|
+
# @param [Hash] attributes
|
613
|
+
# @example
|
614
|
+
# Campo::Input.new( "abc", :text, maxlength: 50 )
|
615
|
+
# # => %input{ atts[:abc], tabindex: "#{@campo_tabindex += 1}", id: "abc", type: "text", maxlength: "50", name: "abc", }
|
616
|
+
def initialize( name, type=:text, attributes={} )
|
617
|
+
id_tag = id_tag(
|
618
|
+
[:text,:hidden,:submit,:password].include?(type) ?
|
619
|
+
nil :
|
620
|
+
attributes[:value]
|
621
|
+
).gsub(/\W/, "_")
|
622
|
+
|
623
|
+
name2 = name.gsub(/\[\]/, "") # remove any [] that may have been used for an array like / grouped object
|
624
|
+
|
625
|
+
atts_name = "#{name2.gsub(/\W/, "_")}#{id_tag}"
|
626
|
+
|
627
|
+
super( name,
|
628
|
+
{ type: type.to_s,
|
629
|
+
id: "#{name2}#{id_tag}",
|
630
|
+
tabindex: %q!#{@campo_tabindex += 1}!,
|
631
|
+
}.merge( attributes ) )
|
632
|
+
|
633
|
+
|
634
|
+
@attributes.delete(:name) if type == :submit
|
635
|
+
@attributes.delete(:tabindex) if type == :hidden
|
636
|
+
|
637
|
+
self.on_output do |n=0, tab=2|
|
638
|
+
%Q!#{" " * n * tab}%input{ atts[:#{atts_name}], #{Base.unhash( @attributes )} }!
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
|
644
|
+
class Fieldset < Base
|
645
|
+
|
646
|
+
# @param [String,nil] text Text for the legend tag
|
647
|
+
# @param [Hash] attributes Hash of html attributes
|
648
|
+
def initialize( text=nil, attributes={} )
|
649
|
+
if text.kind_of? Hash
|
650
|
+
attributes = text
|
651
|
+
text = nil
|
652
|
+
end
|
653
|
+
super( nil, attributes )
|
654
|
+
@attributes.delete(:name)
|
655
|
+
|
656
|
+
self.on_output do |n=0, tab=2|
|
657
|
+
%Q!#{" " * n * tab}%fieldset{ #{Base.unhash( @attributes )} }!
|
658
|
+
end
|
659
|
+
@fields.unshift Legend.new( text ) unless text.nil?
|
660
|
+
end # initialize
|
661
|
+
end # Fieldset
|
662
|
+
|
663
|
+
|
664
|
+
class Legend < Base
|
665
|
+
|
666
|
+
def initialize( inner, attributes={} )
|
667
|
+
super( nil, attributes )
|
668
|
+
@attributes.delete(:name)
|
669
|
+
@inner = inner
|
670
|
+
|
671
|
+
self.on_output do |n=0, tab=2|
|
672
|
+
%Q!#{" " * n * tab}%legend{ #{Base.unhash( @attributes )} }#{@inner}!
|
673
|
+
end
|
674
|
+
end # initialize
|
675
|
+
end # Fieldset
|
676
|
+
|
677
|
+
|
678
|
+
class Label < Base
|
679
|
+
|
680
|
+
DEFAULT = { for: nil }
|
681
|
+
|
682
|
+
attr_reader :attributes, :fields
|
683
|
+
|
684
|
+
def initialize( for_element, inner=nil, attributes={} )
|
685
|
+
if inner.kind_of? Hash
|
686
|
+
attributes = inner
|
687
|
+
inner = nil
|
688
|
+
end
|
689
|
+
super( nil, attributes.merge(for: for_element) )
|
690
|
+
|
691
|
+
@inner = inner
|
692
|
+
|
693
|
+
self.on_output do |n=0, tab=2|
|
694
|
+
%Q!#{" " * n * tab}%label{ #{Base.unhash( @attributes )} }\n#{" " * (n + 1) * tab}#{@inner}!
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
end # Label
|
699
|
+
|
700
|
+
|
701
|
+
class Textarea < Base
|
702
|
+
DEFAULT = { cols: 40, rows: 10, tabindex: %q!#{@campo_tabindex += 1}! }
|
703
|
+
|
704
|
+
def initialize( name, inner=nil, attributes={} )
|
705
|
+
if inner.kind_of? Hash
|
706
|
+
attributes = inner
|
707
|
+
inner = nil
|
708
|
+
end
|
709
|
+
super( name, DEFAULT.merge( attributes ) )
|
710
|
+
@inner = inner
|
711
|
+
self.on_output do |n=0, tab=2|
|
712
|
+
%Q!#{" " * n * tab}%textarea{ atts[:#{name.gsub(/\W/, "_")}], #{Base.unhash( @attributes )} }= inners[:#{name.gsub(/\W/, "_")}] !
|
713
|
+
end
|
714
|
+
end
|
715
|
+
end # Textarea
|
716
|
+
|
717
|
+
# add whatever you need to with a literal
|
718
|
+
class Span < Base
|
719
|
+
|
720
|
+
def initialize( id, inner, attributes={} )
|
721
|
+
if inner.kind_of? Hash
|
722
|
+
attributes = inner
|
723
|
+
inner = nil
|
724
|
+
end
|
725
|
+
super( id, attributes )
|
726
|
+
@attributes.delete(:name) # only id for this element
|
727
|
+
@inner = inner
|
728
|
+
|
729
|
+
unless @inner.nil? or @inner.empty?
|
730
|
+
self.fields.push Campo.literal(@inner)
|
731
|
+
end
|
732
|
+
|
733
|
+
self.on_output do |n=0, tab=2|
|
734
|
+
%Q!#{" " * n * tab}%span{#{Base.unhash( @attributes )}}!
|
735
|
+
end
|
736
|
+
self
|
737
|
+
end
|
738
|
+
end # Literal
|
739
|
+
|
740
|
+
end
|