kiss 0.9.4 → 1.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/VERSION +1 -1
- data/lib/kiss.rb +545 -446
- data/lib/kiss/action.rb +34 -56
- data/lib/kiss/bench.rb +54 -0
- data/lib/kiss/controller_accessors.rb +42 -26
- data/lib/kiss/debug.rb +45 -0
- data/lib/kiss/exception_report.rb +49 -44
- data/lib/kiss/form.rb +105 -86
- data/lib/kiss/form/field.rb +212 -98
- data/lib/kiss/format.rb +175 -37
- data/lib/kiss/hacks.rb +133 -22
- data/lib/kiss/mailer.rb +4 -16
- data/lib/kiss/model.rb +60 -28
- data/lib/kiss/rack/bench.rb +2 -87
- data/lib/kiss/rack/log_exceptions.rb +2 -11
- data/lib/kiss/rack/show_debug.rb +3 -71
- data/lib/kiss/rack/show_exceptions.rb +2 -15
- data/lib/kiss/sequel_mysql.rb +4 -2
- data/lib/kiss/static_file.rb +31 -0
- data/lib/kiss/template_methods.rb +4 -11
- metadata +5 -3
- data/lib/kiss/rack/file.rb +0 -0
data/lib/kiss/form.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
module Digest; end
|
2
|
+
|
1
3
|
class Kiss
|
2
4
|
class Form
|
3
5
|
module AttributesSetter
|
@@ -77,53 +79,50 @@ class Kiss
|
|
77
79
|
@fields << :section_break
|
78
80
|
end
|
79
81
|
|
80
|
-
|
81
|
-
|
82
|
-
raise "added second field named '#{attrs[:name]}'; field names must be unique" if @fields_hash[attrs[:name]]
|
82
|
+
def create_field(attrs)
|
83
|
+
raise "form already has field named '#{attrs[:name]}'" if @fields_hash[attrs[:name]]
|
83
84
|
|
84
85
|
attrs[:type] ||= :text
|
85
86
|
type = attrs[:type].to_sym
|
86
|
-
unless @@field_types.has_key?(type)
|
87
|
-
type = attrs[:type] = :text
|
88
|
-
end
|
89
|
-
|
90
|
-
@has_required_fields ||= attrs[:required]
|
87
|
+
type = attrs[:type] = :text unless @@field_types.has_key?(type)
|
91
88
|
|
92
89
|
field_class = @@field_types[type]
|
93
|
-
field = field_class.new(attrs)
|
90
|
+
field = field_class.new(self,attrs)
|
94
91
|
|
95
92
|
field_name = field.name.to_s
|
96
93
|
field.form = self
|
97
94
|
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
while true
|
96
|
+
other_field = field.other_field
|
97
|
+
break unless other_field
|
98
|
+
other_field.form = self
|
99
|
+
@other_field = @form.create_field( { :name => @name + '.other' }.merge(@other) )
|
101
100
|
end
|
102
101
|
|
102
|
+
@fields_hash[field_name] = field
|
103
|
+
|
103
104
|
@enctype = 'multipart/form-data' if field.type == :file
|
104
105
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
106
|
+
field
|
107
|
+
end
|
108
|
+
|
109
|
+
# Creates and adds a field to the form, according to specified attributes.
|
110
|
+
def add_field(attrs)
|
111
|
+
# create field
|
112
|
+
field = create_field(attrs)
|
113
|
+
|
114
|
+
# used with mark_required to display required fields legend
|
115
|
+
@has_required_fields ||= attrs[:required]
|
110
116
|
|
111
|
-
|
112
|
-
|
113
|
-
field.value = value ? value.strftime("%m/%d/%Y") : ''
|
114
|
-
when :datetime:
|
115
|
-
field.value = value ? value.strftime("%m/%d/%Y %I:%M%p") : ''
|
116
|
-
else
|
117
|
-
field.value = value.to_s
|
118
|
-
end
|
119
|
-
end
|
117
|
+
# add field to form array/hash
|
118
|
+
@fields << field
|
120
119
|
|
121
120
|
field
|
122
121
|
end
|
123
122
|
|
124
123
|
# Creates and adds set of submit buttons to the form, per specified attributes.
|
125
124
|
def add_submit(attrs)
|
126
|
-
@submit =
|
125
|
+
@submit = create_field({
|
127
126
|
:type => :submit,
|
128
127
|
:name => 'submit',
|
129
128
|
:save => false
|
@@ -131,13 +130,13 @@ class Kiss
|
|
131
130
|
end
|
132
131
|
|
133
132
|
# Gets hash of form values ready to be saved to Sequel::Model object.
|
134
|
-
def sequel_values
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
end
|
133
|
+
# def sequel_values
|
134
|
+
# @sequel_values ||= begin
|
135
|
+
# hash = {}
|
136
|
+
# @fields.each {|field| hash[field.name] = field.sequel_value unless field == :section_break }
|
137
|
+
# hash
|
138
|
+
# end
|
139
|
+
# end
|
141
140
|
|
142
141
|
# Gets hash of form values.
|
143
142
|
def values
|
@@ -194,9 +193,13 @@ class Kiss
|
|
194
193
|
# don't save 'ignore' fields to the database
|
195
194
|
next if field.ignore || !field.save
|
196
195
|
|
197
|
-
object[field.name.to_sym]
|
196
|
+
value = (field.value != nil || object.class.db_schema[field.name.to_sym].allow_null) ?
|
197
|
+
field.value : (object.class.db_schema[field.name.to_sym][:default] ||= field.format.default)
|
198
|
+
|
199
|
+
object[field.name.to_sym] = field.save == true ? value :
|
200
|
+
(digest_class = Kiss.digest_class(field.save)) ? digest_class.hexdigest(value) : value
|
198
201
|
end
|
199
|
-
|
202
|
+
|
200
203
|
object.save
|
201
204
|
end
|
202
205
|
|
@@ -211,7 +214,7 @@ class Kiss
|
|
211
214
|
# and returns true. Otherwise, returns false.
|
212
215
|
def process(object = @object)
|
213
216
|
return false unless submitted
|
214
|
-
|
217
|
+
|
215
218
|
if accepted
|
216
219
|
validate
|
217
220
|
return false if has_errors
|
@@ -233,7 +236,7 @@ class Kiss
|
|
233
236
|
def errors_html
|
234
237
|
return nil unless has_errors
|
235
238
|
|
236
|
-
@errors << "Please
|
239
|
+
@errors << "Please correct the errors highlighted below." if @has_field_errors
|
237
240
|
|
238
241
|
if @errors.size == 1
|
239
242
|
content = @errors[0]
|
@@ -244,7 +247,7 @@ class Kiss
|
|
244
247
|
@errors.pop if @has_field_errors
|
245
248
|
|
246
249
|
plural = @errors.size > 1 || @has_field_errors ? 's' : nil
|
247
|
-
%Q(<
|
250
|
+
%Q(<div class="kiss_error">#{content}</div><br clear="all" />)
|
248
251
|
end
|
249
252
|
|
250
253
|
# Renders current action using form's HTML as action render content.
|
@@ -266,74 +269,71 @@ class Kiss
|
|
266
269
|
# style tag
|
267
270
|
styles = []
|
268
271
|
styles.push( <<-EOT
|
269
|
-
.kiss_form
|
272
|
+
table.kiss_form {
|
270
273
|
margin-bottom: 6px;
|
271
274
|
}
|
272
275
|
.kiss_form td {
|
273
276
|
padding: 2px 4px;
|
277
|
+
vertical-align: middle;
|
274
278
|
}
|
275
|
-
.kiss_form
|
279
|
+
.kiss_form .kiss_error {
|
276
280
|
background-color: #ff8;
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
padding: 2px 4px 3px 4px;
|
281
|
-
text-align: right;
|
282
|
-
background-color: #fc7;
|
281
|
+
padding: 2px 4px;
|
282
|
+
line-height: 135%;
|
283
|
+
float: left;
|
283
284
|
color: #900;
|
284
|
-
border: 1px solid #
|
285
|
-
|
286
|
-
}
|
287
|
-
.kiss_form tr.kiss_error td {
|
288
|
-
vertical-align: top;
|
289
|
-
padding: 2px 6px 3px 6px;
|
290
|
-
border: 1px solid #f96;
|
291
|
-
border-left: none;
|
292
|
-
color: #000;
|
285
|
+
border: 1px solid #ea4;
|
286
|
+
margin-bottom: 4px;
|
293
287
|
}
|
294
|
-
.kiss_form
|
288
|
+
.kiss_form .kiss_error ul {
|
295
289
|
padding-left: 16px;
|
296
290
|
margin: 0;
|
297
291
|
}
|
298
|
-
.kiss_form tr.kiss_error td.kiss_required {
|
299
|
-
padding: 0;
|
300
|
-
border-right: none;
|
301
|
-
}
|
302
292
|
.kiss_form .kiss_help {
|
303
|
-
padding: 0px 3px
|
293
|
+
padding: 0px 3px 0 6px;
|
304
294
|
font-size: 90%;
|
305
295
|
color: #555;
|
306
296
|
}
|
307
|
-
.
|
308
|
-
|
309
|
-
|
297
|
+
.kiss_required {
|
298
|
+
color: #a21;
|
299
|
+
font-size: 150%;
|
300
|
+
line-height: 50%;
|
301
|
+
position: relative;
|
302
|
+
top: 4px;
|
310
303
|
}
|
311
304
|
.kiss_form td.kiss_label {
|
312
305
|
text-align: right;
|
313
306
|
white-space: nowrap;
|
314
307
|
}
|
308
|
+
.kiss_form td.kiss_label.error {
|
309
|
+
color: #b10;
|
310
|
+
}
|
315
311
|
.kiss_form tr.kiss_prompt td {
|
316
|
-
padding: 8px 3px
|
312
|
+
padding: 8px 3px 0px 4px;
|
317
313
|
}
|
318
314
|
.kiss_form tr.kiss_submit td.kiss_submit {
|
319
315
|
padding: 6px 3px;
|
320
316
|
}
|
321
|
-
.kiss_form input[type="text"],.kiss_form textarea {
|
317
|
+
.kiss_form input[type="text"],.kiss_form input[type=password],.kiss_form textarea {
|
322
318
|
width: 250px;
|
319
|
+
margin-left: 0;
|
320
|
+
margin-right: 0;
|
323
321
|
}
|
324
322
|
EOT
|
325
323
|
)
|
326
324
|
styles.push( <<-EOT
|
327
325
|
.kiss_form_error_message {
|
328
|
-
padding: 1px 4px
|
329
|
-
border: 1px solid #
|
326
|
+
padding: 1px 4px;
|
327
|
+
border: 1px solid #edc;
|
328
|
+
border-top: 0;
|
329
|
+
border-bottom: 1px solid #db4;
|
330
330
|
background-color: #ff8;
|
331
331
|
color: #900;
|
332
|
-
font-size:
|
332
|
+
font-size: 90%;
|
333
|
+
line-height: 135%;
|
333
334
|
display: block;
|
334
335
|
float: left;
|
335
|
-
margin-
|
336
|
-
|
336
|
+
margin-bottom: 2px;
|
337
337
|
}
|
338
338
|
.kiss_form_error_message div {
|
339
339
|
color: #c00;
|
@@ -343,6 +343,21 @@ EOT
|
|
343
343
|
padding-left: 16px;
|
344
344
|
margin: 0;
|
345
345
|
}
|
346
|
+
tr.kiss_form_error_row td {
|
347
|
+
padding-top: 0;
|
348
|
+
}
|
349
|
+
tr.kiss_form_error_row .kiss_form_error_message {
|
350
|
+
position: relative;
|
351
|
+
top: -2px;
|
352
|
+
margin-bottom: 0;
|
353
|
+
}
|
354
|
+
.kiss_form table.kiss_field_columns {
|
355
|
+
border-spacing: 0;
|
356
|
+
border-collapse: collapse;
|
357
|
+
}
|
358
|
+
.kiss_form table.kiss_field_columns td {
|
359
|
+
padding: 0 0 2px 0;
|
360
|
+
}
|
346
361
|
EOT
|
347
362
|
) if @error_class == 'kiss_form_error_message'
|
348
363
|
style_tag = styles.size == 0 ? '' : "<style>" + styles.join('') + "</style>"
|
@@ -367,8 +382,8 @@ EOT
|
|
367
382
|
|
368
383
|
# Renders open of form table.
|
369
384
|
def table_html_open
|
370
|
-
%Q(<table border=0 cellspacing=0>) +
|
371
|
-
(@has_required_fields && @mark_required ? %Q( <tr><td></td><td class="
|
385
|
+
%Q(<table class="kiss_form" border=0 cellspacing=0>) +
|
386
|
+
(@has_required_fields && @mark_required ? %Q( <tr><td></td><td class="kiss_help">Required fields marked by <span class="kiss_required">*</span></td></tr> ) : '')
|
372
387
|
end
|
373
388
|
|
374
389
|
# Renders close of form table.
|
@@ -378,7 +393,7 @@ EOT
|
|
378
393
|
|
379
394
|
# Renders form submit buttons.
|
380
395
|
def submit_html
|
381
|
-
field_html(@submit)
|
396
|
+
@submit ? field_html(@submit) : ''
|
382
397
|
end
|
383
398
|
|
384
399
|
# Renders complete form HTML.
|
@@ -396,19 +411,23 @@ EOT
|
|
396
411
|
# Renders HTML for specified form field.
|
397
412
|
def field_html(field)
|
398
413
|
type = field.type
|
399
|
-
prompt = field.prompt
|
400
|
-
label = field.label
|
414
|
+
prompt = field.prompt
|
415
|
+
label = field.label
|
416
|
+
errors = field.errors_html
|
417
|
+
required = field.required ? %Q(<span class="kiss_required">#{@mark_required}</span> ) : ''
|
401
418
|
|
402
|
-
[
|
403
|
-
|
404
|
-
|
405
|
-
%Q(<tr class="kiss_#{type}"><td class="kiss_label">),
|
406
|
-
|
407
|
-
'</td><td class="kiss_required">',
|
408
|
-
field.required ? @mark_required : '',
|
419
|
+
([
|
420
|
+
prompt ? %Q(<tr class="kiss_prompt"><td class="kiss_label">#{required}</td><td>#{prompt.to_s}</td></tr>) : '',
|
421
|
+
|
422
|
+
%Q(<tr class="kiss_#{type}"><td class="kiss_label#{errors ? ' error' : ''}">),
|
423
|
+
!prompt ? (required + (label ? label.to_s + ':' : '' )) : '',
|
409
424
|
%Q(</td><td class="kiss_#{type}">),
|
410
|
-
field.
|
411
|
-
]
|
425
|
+
field.element_html, "</td></tr>"
|
426
|
+
] + (errors ? [
|
427
|
+
'<tr class="kiss_form_error_row"><td class="kiss_required"></td><td>',
|
428
|
+
errors,
|
429
|
+
'</td></tr>'
|
430
|
+
] : [])).join
|
412
431
|
end
|
413
432
|
end
|
414
433
|
end
|
data/lib/kiss/form/field.rb
CHANGED
@@ -1,20 +1,58 @@
|
|
1
1
|
class Kiss
|
2
2
|
class Form
|
3
3
|
class Field
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
attr_accessor :name,:type,:form,:currency,:label,:no_label,:prompt,:value,:ignore,:save,
|
6
|
+
:options,:options_value_key,:options_display_key,:required,:cancel,:columns,:style,:hidden_join,
|
7
|
+
:html,:other,:other_field,:object,:format,:display_format
|
6
8
|
|
7
9
|
include Kiss::Form::AttributesSetter
|
8
10
|
|
9
|
-
def initialize(attrs)
|
11
|
+
def initialize(form,attrs)
|
12
|
+
@form = form
|
10
13
|
@save = true
|
11
|
-
@
|
14
|
+
@currency = nil
|
15
|
+
|
16
|
+
set_attributes({
|
17
|
+
:type => :text,
|
18
|
+
:object => @form.object,
|
19
|
+
:save => true
|
20
|
+
}.merge(attrs))
|
21
|
+
|
22
|
+
@value = (@object[@name.to_sym] if @object) || @value
|
12
23
|
|
13
|
-
|
24
|
+
@format = Kiss::Format.lookup(@format)
|
25
|
+
@display_format = Kiss::Format.lookup(@display_format)
|
26
|
+
|
27
|
+
@currency = '$' if @currency == :dollars
|
14
28
|
|
15
29
|
@errors = []
|
16
30
|
end
|
17
31
|
|
32
|
+
def other_field_html
|
33
|
+
return '' unless @other
|
34
|
+
|
35
|
+
other_checked = @value && !option_pairs.any? {|v,d| v == @value }
|
36
|
+
|
37
|
+
(@columns ? '<br/>' : ' ') + [
|
38
|
+
input_tag_html(
|
39
|
+
{ :value => 'other', :html => { :id => @name+'.other' } },
|
40
|
+
other_checked ? 'checked' : ''
|
41
|
+
),
|
42
|
+
@other[:label] || 'Other',
|
43
|
+
': ',
|
44
|
+
@currency.to_s,
|
45
|
+
input_tag_html({
|
46
|
+
:type => :text,
|
47
|
+
:name => @name+'.other',
|
48
|
+
:value => other_checked ? value_to_s(@value) : nil,
|
49
|
+
:html => {
|
50
|
+
:onfocus => "document.getElementById('#{@name}.other').checked = true"
|
51
|
+
}
|
52
|
+
}.merge(@other))
|
53
|
+
].join
|
54
|
+
end
|
55
|
+
|
18
56
|
def column_layout(elements_html)
|
19
57
|
if @columns
|
20
58
|
# format elements into cells
|
@@ -28,50 +66,76 @@ class Kiss
|
|
28
66
|
return ['<table class="kiss_field_columns">',element_ranges.map do |range|
|
29
67
|
["<tr>", elements_html[range], "</tr>"]
|
30
68
|
end,'</table>'].flatten.join
|
31
|
-
join_with = ''
|
32
|
-
else
|
33
|
-
join_with = ' '
|
34
69
|
end
|
35
|
-
|
70
|
+
|
36
71
|
# else columns <= 1
|
37
|
-
elements_html.join(
|
72
|
+
elements_html.join(' ')
|
38
73
|
end
|
39
74
|
|
40
|
-
def sequel_value
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
75
|
+
# def sequel_value
|
76
|
+
# case @format
|
77
|
+
# when :date:
|
78
|
+
# return Kiss.mdy_to_ymd(value)
|
79
|
+
# when :datetime:
|
80
|
+
# datetime = value.sub(/([ap]m)\s*\Z/i,' \1')
|
81
|
+
# date, time, ampm = datetime.split(/\s+/)
|
82
|
+
#
|
83
|
+
# hours, minutes = time.split(/:/)
|
84
|
+
# hours = 0 if hours.to_i == 12 && ampm
|
85
|
+
# if (ampm.downcase == 'pm')
|
86
|
+
# hours = hours.to_i + 12
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# return Kiss.mdy_to_ymd(date) + " #{hours}:#{minutes}"
|
90
|
+
# when :month_year:
|
91
|
+
# month, year = value.sub(/\A\s*/,'').sub(/\s*\Z/,'').split(/\D+/)
|
92
|
+
# # convert two-digit years to four-digit years
|
93
|
+
# year = year.to_i
|
94
|
+
# if year < 100
|
95
|
+
# year += 1900
|
96
|
+
# year += 100 if year < Time.now.year - 95
|
97
|
+
# end
|
98
|
+
# return sprintf("%04d-%02d-01",year,month.to_i)
|
99
|
+
# else
|
100
|
+
# return value.is_a?(Array) ? value.map {|v| v.gsub(/,/,'\,')}.join(',') : value
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
|
104
|
+
def param
|
105
|
+
@param ||= @form.params[@name.to_s]
|
58
106
|
end
|
59
107
|
|
60
108
|
def value
|
61
|
-
@value
|
109
|
+
@value
|
62
110
|
end
|
63
111
|
|
64
112
|
def add_error(message)
|
65
113
|
@errors << message
|
66
114
|
@form.has_field_errors = true
|
67
115
|
end
|
68
|
-
|
69
|
-
def
|
70
|
-
|
71
|
-
|
116
|
+
|
117
|
+
def value_to_s(value)
|
118
|
+
value ? @format.value_to_s(value) : ''
|
119
|
+
end
|
120
|
+
|
121
|
+
def display_to_s(value)
|
122
|
+
value ? @display_format.value_to_s(value) : ''
|
123
|
+
end
|
124
|
+
|
125
|
+
def validate(enter = 'enter')
|
126
|
+
if @required && (param !~ /\S/)
|
127
|
+
# value required
|
128
|
+
add_error("Please #{enter} #{@label}")
|
129
|
+
return
|
130
|
+
end
|
131
|
+
|
132
|
+
begin
|
133
|
+
@value = @format.validate(param)
|
134
|
+
rescue Kiss::Format::ValidateError => e
|
135
|
+
add_error("#{e.message.capitalize}")
|
72
136
|
end
|
73
137
|
|
74
|
-
value
|
138
|
+
@value
|
75
139
|
end
|
76
140
|
|
77
141
|
def errors_html
|
@@ -87,33 +151,62 @@ class Kiss
|
|
87
151
|
end
|
88
152
|
|
89
153
|
def element_html
|
90
|
-
|
91
|
-
|
154
|
+
attrs = { :value => param || value_to_s(@value) }
|
155
|
+
if width = @format.input_width
|
156
|
+
attrs[:style] = "width: #{width}px"
|
92
157
|
end
|
93
|
-
|
158
|
+
|
159
|
+
@currency.to_s + input_tag_html( attrs ) +
|
160
|
+
((legend = @format.legend) ? " <small>(#{legend})</small>" : '')
|
94
161
|
end
|
95
162
|
|
96
163
|
def html
|
97
164
|
errors = errors_html
|
98
|
-
element_html + (errors ? %Q(
|
165
|
+
element_html + (errors ? (@columns ? '' : '<br/>') + %Q(#{errors}) : '')
|
99
166
|
end
|
100
|
-
|
101
|
-
def
|
167
|
+
|
168
|
+
def tag_start_html(tag_name, attrs = {}, extra_html = nil)
|
102
169
|
attrs = attrs.clone
|
103
170
|
attrs[:name] ||= @name
|
104
|
-
attrs[:
|
105
|
-
attrs[:style] ||= @style
|
171
|
+
attrs[:size] ||= @size if @size
|
172
|
+
attrs[:style] ||= @style if @style
|
106
173
|
|
107
|
-
|
108
|
-
|
109
|
-
|
174
|
+
html_parts = ["<#{tag_name}"]
|
175
|
+
|
176
|
+
html = attrs.delete(:html) || @html
|
177
|
+
attrs.each_pair {|k,v| html_parts.push %Q(#{k}="#{v}") }
|
178
|
+
|
179
|
+
if html
|
180
|
+
if html.is_a?(Hash)
|
181
|
+
html.each_pair {|k,v| html_parts.push %Q(#{k}="#{v}") }
|
182
|
+
else
|
183
|
+
html_parts.push(@html.to_s)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
html_parts.push(extra_html) if extra_html
|
188
|
+
html_parts.join(' ')
|
189
|
+
end
|
190
|
+
|
191
|
+
def tag_html(tag_name, attrs = {}, extra_html = nil)
|
192
|
+
tag_start_html(tag_name,attrs,extra_html) + ' />'
|
193
|
+
end
|
194
|
+
|
195
|
+
def content_tag_html(tag_name, content, attrs = {}, extra_html = nil)
|
196
|
+
tag_start_html(tag_name,attrs,extra_html) + ">#{content}</#{tag_name}>"
|
197
|
+
end
|
198
|
+
|
199
|
+
def input_tag_html(attrs = {}, extra_html = nil)
|
200
|
+
tag_html(
|
201
|
+
'input',
|
202
|
+
{:type => @type}.merge(attrs),
|
203
|
+
extra_html
|
204
|
+
)
|
110
205
|
end
|
111
206
|
end
|
112
207
|
|
113
|
-
class HiddenField < Field
|
114
|
-
end
|
115
|
-
class TextField < Field
|
116
|
-
end
|
208
|
+
class HiddenField < Field; end
|
209
|
+
class TextField < Field; end
|
117
210
|
|
118
211
|
class TextAreaField < Field
|
119
212
|
attr_accessor :rows,:cols
|
@@ -123,8 +216,16 @@ class Kiss
|
|
123
216
|
@cols = 20
|
124
217
|
super(*args)
|
125
218
|
end
|
219
|
+
|
126
220
|
def element_html
|
127
|
-
|
221
|
+
content_tag_html(
|
222
|
+
'textarea',
|
223
|
+
param || value_to_s(@value),
|
224
|
+
{
|
225
|
+
:rows => @rows ||= 1,
|
226
|
+
:cols => @cols ||= 1
|
227
|
+
}
|
228
|
+
)
|
128
229
|
end
|
129
230
|
end
|
130
231
|
|
@@ -136,7 +237,7 @@ class Kiss
|
|
136
237
|
|
137
238
|
class BooleanField < Field
|
138
239
|
def element_html
|
139
|
-
input_tag_html({ :value => 1 }, @value ? '
|
240
|
+
input_tag_html({ :value => 1 }, @value ? 'checked' : '')
|
140
241
|
end
|
141
242
|
end
|
142
243
|
|
@@ -164,14 +265,9 @@ class Kiss
|
|
164
265
|
elements_html.join(' ')
|
165
266
|
end
|
166
267
|
|
167
|
-
def validate
|
168
|
-
# do nothing for now
|
169
|
-
value
|
170
|
-
end
|
171
|
-
|
172
268
|
def elements_html
|
173
269
|
@options.map do |option|
|
174
|
-
input_tag_html({ :value => option })
|
270
|
+
input_tag_html({ :value => value_to_s(option) })
|
175
271
|
end
|
176
272
|
end
|
177
273
|
end
|
@@ -180,13 +276,12 @@ class Kiss
|
|
180
276
|
|
181
277
|
class MultiChoiceField < Field
|
182
278
|
def option_pairs
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
@options.map {|option| [ option, option ] }
|
279
|
+
(defined? @options_value_key) ?
|
280
|
+
@options.map {|option| [ option[@options_value_key], option[@options_display_key] ]} :
|
281
|
+
begin
|
282
|
+
@display_format = @format
|
283
|
+
@options.map {|option| [ option, option ] }
|
284
|
+
end
|
190
285
|
end
|
191
286
|
|
192
287
|
def matched_options(value)
|
@@ -197,12 +292,13 @@ class Kiss
|
|
197
292
|
end
|
198
293
|
|
199
294
|
def validate
|
200
|
-
@
|
295
|
+
if @other && param == 'other'
|
296
|
+
@param = @form.params[@name+'.other']
|
297
|
+
end
|
298
|
+
super('select')
|
201
299
|
|
202
|
-
if
|
203
|
-
add_error "
|
204
|
-
elsif @required
|
205
|
-
add_error "#{@label} required"
|
300
|
+
if @value =~ /\S/ && matched_options(@value).size == 0
|
301
|
+
add_error "Invalid selection"
|
206
302
|
end
|
207
303
|
end
|
208
304
|
end
|
@@ -215,21 +311,24 @@ class Kiss
|
|
215
311
|
|
216
312
|
options_html = option_pairs.map do |value,display|
|
217
313
|
selected = (@value.to_s == value.to_s) ? ' selected' : ''
|
218
|
-
%Q(<option value="#{value}"#{selected}>#{display}</option>)
|
219
|
-
end
|
220
|
-
|
221
|
-
|
314
|
+
%Q(<option value="#{value_to_s(value)}"#{selected}>#{display}</option>)
|
315
|
+
end.join
|
316
|
+
|
317
|
+
content_tag_html('select', placeholder_html + options_html) + other_field_html
|
222
318
|
end
|
223
319
|
end
|
224
320
|
|
225
321
|
class RadioField < MultiChoiceField
|
226
322
|
def element_html
|
227
|
-
column_layout(elements_html)
|
323
|
+
column_layout(elements_html) + other_field_html
|
228
324
|
end
|
229
325
|
|
230
326
|
def elements_html
|
231
|
-
|
232
|
-
input_tag_html(
|
327
|
+
option_pairs.map do |value,display|
|
328
|
+
input_tag_html(
|
329
|
+
{ :value => value_to_s(value) },
|
330
|
+
(@value.to_s == value.to_s) ? 'checked' : ''
|
331
|
+
) + @currency.to_s + display_to_s(display)
|
233
332
|
end
|
234
333
|
end
|
235
334
|
end
|
@@ -238,18 +337,27 @@ class Kiss
|
|
238
337
|
# ------ MultiValueField
|
239
338
|
|
240
339
|
class MultiValueField < MultiChoiceField
|
340
|
+
def param
|
341
|
+
@form.params[@name.to_s+'[]'] || []
|
342
|
+
end
|
343
|
+
|
241
344
|
def validate
|
242
|
-
|
243
|
-
|
345
|
+
begin
|
346
|
+
@value = param.map { |p| @format.validate(p) }
|
347
|
+
rescue Kiss::Format::ValidateError => e
|
348
|
+
add_error("#{e.message.capitalize}")
|
349
|
+
return
|
350
|
+
end
|
351
|
+
|
244
352
|
if @value.size > 0
|
245
|
-
@value.each do |
|
246
|
-
unless (matched_options(
|
247
|
-
add_error "Invalid selection
|
353
|
+
@value.each do |v|
|
354
|
+
unless (matched_options(v).size > 0)
|
355
|
+
add_error "#Invalid selection"
|
248
356
|
return
|
249
357
|
end
|
250
358
|
end
|
251
359
|
elsif @required
|
252
|
-
add_error "#{@label}
|
360
|
+
add_error "Please select at least one #{@label}"
|
253
361
|
end
|
254
362
|
end
|
255
363
|
|
@@ -260,21 +368,22 @@ class Kiss
|
|
260
368
|
|
261
369
|
class CheckboxField < MultiValueField
|
262
370
|
def element_html
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
hidden_options = ''
|
271
|
-
end
|
272
|
-
column_layout(elements_html) + hidden_options
|
371
|
+
hidden_options = @hidden_join ? input_tag_html(
|
372
|
+
:type => 'hidden',
|
373
|
+
:name => "#{@name}_options",
|
374
|
+
:value => option_pairs.map {|v,d| value_to_s(v) }.join(@hidden_join)
|
375
|
+
) : ''
|
376
|
+
|
377
|
+
column_layout(elements_html) + other_field_html + hidden_options
|
273
378
|
end
|
274
379
|
|
275
380
|
def elements_html
|
276
|
-
|
277
|
-
|
381
|
+
name = @name.to_s+'[]'
|
382
|
+
option_pairs.map do |value,display|
|
383
|
+
input_tag_html(
|
384
|
+
{ :name => name, :value => value_to_s(value) },
|
385
|
+
selected_option_values[value.to_s] ? 'checked' : ''
|
386
|
+
) + @currency.to_s + display_to_s(display)
|
278
387
|
end
|
279
388
|
end
|
280
389
|
end
|
@@ -283,10 +392,15 @@ class Kiss
|
|
283
392
|
def element_html
|
284
393
|
options_html = option_pairs.map do |value,display|
|
285
394
|
selected = selected_option_values[value] ? ' selected' : ''
|
286
|
-
%Q(<option value="#{value}"#{selected}>#{display}</option>)
|
287
|
-
end
|
288
|
-
|
289
|
-
|
395
|
+
%Q(<option value="#{value_to_s(value)}"#{selected}>#{display}</option>)
|
396
|
+
end.join
|
397
|
+
|
398
|
+
content_tag_html(
|
399
|
+
'select',
|
400
|
+
options_html,
|
401
|
+
{},
|
402
|
+
'multiple'
|
403
|
+
)
|
290
404
|
end
|
291
405
|
end
|
292
406
|
end
|