formkeeper 0.0.4

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/lib/formkeeper.rb ADDED
@@ -0,0 +1,849 @@
1
+ #--
2
+ # Copyright (C) 2012 Lyo Kato, <lyo.kato _at_ gmail.com>.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require "formkeeper/version"
24
+ require 'hpricot'
25
+ require 'yaml'
26
+ require 'rack'
27
+ require 'uri'
28
+ require 'date'
29
+
30
+ module FormKeeper
31
+ module Filter
32
+ class Base
33
+ def process(value)
34
+ return value
35
+ end
36
+ end
37
+
38
+ class Custom < Base
39
+ def initialize(block)
40
+ @custom = block
41
+ end
42
+ def process(value)
43
+ @custom.call(value)
44
+ end
45
+ end
46
+
47
+ class UpCase < Base
48
+ def process(value)
49
+ return value.upcase
50
+ end
51
+ end
52
+
53
+ class DownCase < Base
54
+ def process(value)
55
+ return value.downcase
56
+ end
57
+ end
58
+
59
+ class Strip < Base
60
+ def process(value)
61
+ return value.strip
62
+ end
63
+ end
64
+ end
65
+
66
+ module Constraint
67
+ class Base
68
+ def validate(value, arg)
69
+ return true
70
+ end
71
+ end
72
+
73
+ class Custom < Base
74
+ def initialize(block)
75
+ @custom = block
76
+ end
77
+ def validate(value, arg)
78
+ @custom.call(value, arg)
79
+ end
80
+ end
81
+
82
+ class Ascii < Base
83
+ def validate(value, arg)
84
+ result = value =~ /^[\x21-\x7e]+$/
85
+ result = !result if !arg
86
+ result
87
+ end
88
+ end
89
+
90
+ class Regexp < Base
91
+ def validate(value, arg)
92
+ r = arg
93
+ r = Regexp.new(r) unless r.kind_of?(Regexp)
94
+ value =~ r
95
+ end
96
+ end
97
+
98
+ class Int < Base
99
+ def validate(value, arg)
100
+ result = value =~ /^\-?[[:digit:]]+$/
101
+ result = !result if !arg
102
+ result
103
+ end
104
+ end
105
+
106
+ class Uint < Base
107
+ def validate(value, arg)
108
+ result = value =~ /^[[:digit:]]+$/
109
+ result = !result if !arg
110
+ result
111
+ end
112
+ end
113
+
114
+ class Alpha < Base
115
+ def validate(value, arg)
116
+ result = value =~ /^[[:alpha:]]+$/
117
+ result = !result if !arg
118
+ result
119
+ end
120
+ end
121
+
122
+ class AlphaSpace < Base
123
+ def validate(value, arg)
124
+ result = value =~ /^[[:alpha:][:space:]]+$/
125
+ result = !result if !arg
126
+ result
127
+ end
128
+ end
129
+
130
+ class Alnum < Base
131
+ def validate(value, arg)
132
+ result = value =~ /^[[:alnum:]]+$/
133
+ result = !result if !arg
134
+ result
135
+ end
136
+ end
137
+
138
+ class AlnumSpace < Base
139
+ def validate(value, arg)
140
+ result = value =~ /^[[:alnum:][:space:]]+$/
141
+ result = !result if !arg
142
+ result
143
+ end
144
+ end
145
+
146
+ class URI < Base
147
+ def validate(value, arg)
148
+ u = URI.parse(value)
149
+ return false if u.nil?
150
+ arg = [arg] unless arg.kind_of?(Array)
151
+ arg.collect(&:to_s).include?(u.scheme)
152
+ end
153
+ end
154
+
155
+ class Length < Base
156
+ def validate(value, arg)
157
+ l = value.length
158
+ case arg
159
+ when Fixnum
160
+ return (l == arg)
161
+ when Range
162
+ return arg.include?(l)
163
+ else
164
+ raise ArgumentError.new('Invalid number of arguments')
165
+ end
166
+ end
167
+ end
168
+
169
+ class Characters < Base
170
+ def validate(value, arg)
171
+ l = value.split(//u).length
172
+ case arg
173
+ when Fixnum
174
+ return (l == args)
175
+ when Range
176
+ return arg.include?(l)
177
+ else
178
+ raise ArgumentError.new('Invalid number of arguments')
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+ module CombinationConstraint
185
+ class Base
186
+ def validate(values, arg)
187
+ return true
188
+ end
189
+ end
190
+
191
+ class Same < Base
192
+ def validate(values, arg)
193
+ return false unless values.size == 2
194
+ values[0] == values[1]
195
+ end
196
+ end
197
+
198
+ class Any < Base
199
+ def validate(values, arg)
200
+ values.any? { |v| not (v.nil? or v.empty?) }
201
+ end
202
+ end
203
+
204
+ class Date < Base
205
+ def validate(values, arg)
206
+ return false unless values.size == 3
207
+ # TODO handle range by args[:from] and args[:to]
208
+ ::Date.valid_date?(values[0], values[1], values[2])
209
+ end
210
+ end
211
+
212
+ class Time < Base
213
+ def validate(values, arg)
214
+ return false unless values.size == 3
215
+ # TODO handle range by args[:from] and args[:to]
216
+ (0..23).include?(values[0]) and (0..59).include(values[1]) and (0..59).include(values[2])
217
+ end
218
+ end
219
+
220
+ class DateTime < Base
221
+ def validate(values, arg)
222
+ return false unless values.size == 6
223
+ # TODO handle range by args[:from] and args[:to]
224
+ ::Date.valid_date?(values[0], values[1], values[2]) and (0..23).include?(values[3]) and (0..59).include(values[5]) and (0..59).include(values[5])
225
+ end
226
+ end
227
+ end
228
+
229
+ class Messages
230
+ DEFAULT_ACTION_NAME = 'DEFAULT'
231
+ DEFAULT_CONSTRAINT_NAME = 'DEFAULT'
232
+ def self.from_file(path)
233
+ data = YAML.load_file(path)
234
+ self.new(data)
235
+ end
236
+ def initialize(data)
237
+ @data = data
238
+ end
239
+ def get(action_name, target_name, constraint_name)
240
+ if @data.has_key?(action_name.to_s)
241
+ action = @data[action_name.to_s]
242
+ return search_from_action_part(action, target_name, constraint_name)
243
+ else
244
+ if @data.has_key?(DEFAULT_ACTION_NAME)
245
+ action = @data[DEFAULT_ACTION_NAME]
246
+ return search_from_action_part(action, target_name, constraint_name)
247
+ else
248
+ return handle_missing_message(target_name)
249
+ end
250
+ end
251
+ end
252
+
253
+ private
254
+ def search_from_action_part(action, target_name, constraint_name)
255
+ if action.has_key?(target_name.to_s)
256
+ field = action[target_name.to_s]
257
+ return search_from_field_part(target_name, field, constraint_name)
258
+ else
259
+ return handle_missing_message(target_name)
260
+ end
261
+ end
262
+
263
+ def search_from_field_part(target_name, field, constraint_name)
264
+ if field.has_key?(constraint_name.to_s)
265
+ return field[constraint_name.to_s]
266
+ else
267
+ if field.has_key?(DEFAULT_CONSTRAINT_NAME)
268
+ return field[DEFAULT_CONSTRAINT_NAME]
269
+ else
270
+ return handle_missing_message(target_name)
271
+ end
272
+ end
273
+ end
274
+
275
+ def handle_missing_message(target_name)
276
+ build_default_message(target_name)
277
+ end
278
+
279
+ def build_default_message(target_name)
280
+ "#{target_name.to_s} is invalid."
281
+ end
282
+ end
283
+
284
+ class Record
285
+ attr_reader :name, :failed_constraints
286
+ attr_accessor :value
287
+ def initialize(name)
288
+ @name = name
289
+ @value = nil
290
+ @failed_constraints = []
291
+ end
292
+ def fail(constraint)
293
+ @failed_constraints << constraint
294
+ end
295
+ def failed?
296
+ @failed_constraints.size > 0
297
+ end
298
+ def failed_by?(constraint)
299
+ @failed_constraints.include?(constraint.to_sym)
300
+ end
301
+ end
302
+
303
+ class Report
304
+ def initialize(messages=nil)
305
+ @failed_records = {}
306
+ @messages = messages || Messages.new({})
307
+ @valid_params = {}
308
+ end
309
+ def <<(record)
310
+ if record.failed?
311
+ @failed_records[record.name.to_sym] = record
312
+ else
313
+ @valid_params[record.name.to_sym] = record.value
314
+ end
315
+ end
316
+ def [](name)
317
+ @valid_params[name.to_sym]
318
+ end
319
+ def valid_fields
320
+ @valid_params.keys
321
+ end
322
+ def valid?(name)
323
+ @valid_params.has_key?(name.to_sym)
324
+ end
325
+ def failed?
326
+ !@failed_records.empty?
327
+ end
328
+ def failed_on?(name, constraint=nil)
329
+ return false unless @failed_records.has_key?(name.to_sym)
330
+ return true if constraint.nil?
331
+ record = @failed_records[name.to_sym]
332
+ record.failed_by?(constraint)
333
+ end
334
+ def failed_fields
335
+ @failed_records.keys
336
+ end
337
+ def failed_constraints(name)
338
+ return [] unless @failed_records.has_key?(name.to_sym)
339
+ record = @failed_records[name.to_sym]
340
+ record.failed_constraints
341
+ end
342
+ def messages(action, name=nil)
343
+ messages = []
344
+ if name.nil?
345
+ @failed_records.values.each do |record|
346
+ record.failed_constraints.each do |constraint|
347
+ messages << @messages.get(action, record.name, constraint)
348
+ end
349
+ end
350
+ else
351
+ if @failed_records.has_key?(name.to_sym)
352
+ record = @failed_records[name.to_sym]
353
+ record.failed_constraints.each do |constraint|
354
+ messages << @messages.get(action, record.name, constraint)
355
+ end
356
+ end
357
+ end
358
+ messages.select{ |m| !m.nil? and !m.empty? }.uniq
359
+ end
360
+ def message(action, name, constraint)
361
+ if @failed_records.has_key?(name.to_sym) and @failed_records[name.to_sym].failed_by?(constraint)
362
+ @messages.get(action, name, constraint)
363
+ else
364
+ nil
365
+ end
366
+ end
367
+ end
368
+
369
+ class Rule
370
+ module Criteria
371
+ class Field
372
+ attr_reader :default, :filters, :constraints
373
+ def initialize(criteria)
374
+ if criteria.has_key?(:default)
375
+ default = criteria.delete :default
376
+ @default = default.empty? ? nil : default
377
+ else
378
+ @default = nil
379
+ end
380
+ if criteria.has_key?(:filters)
381
+ filters = criteria.delete :filters
382
+ case filters
383
+ when Array
384
+ @filters = filters.collect(&:to_sym)
385
+ when String
386
+ @filters = [filters.to_sym]
387
+ when Symbol
388
+ @filters = [filters]
389
+ else
390
+ raise ArgumentError.new 'invalid :filters'
391
+ end
392
+ else
393
+ @filters = []
394
+ end
395
+
396
+ if criteria.has_key?(:present)
397
+ if not @default.nil?
398
+ raise ArgumentError.new "don't set both :default and :present at once"
399
+ end
400
+ present = criteria.delete :present
401
+ @present = !!present
402
+ else
403
+ @present = false
404
+ end
405
+ @constraints = criteria
406
+ end
407
+
408
+ def require_presence?
409
+ @present
410
+ end
411
+ end
412
+
413
+ class Checkbox
414
+ attr_reader :default, :filters, :count, :constraints
415
+ def initialize(criteria)
416
+ if criteria.has_key?(:default)
417
+ default = criteria.delete :default
418
+ case default
419
+ when Array
420
+ @default = default.collect(&:to_s)
421
+ else
422
+ @default = [default.to_s]
423
+ end
424
+ else
425
+ @default = []
426
+ end
427
+
428
+ if criteria.has_key?(:filters)
429
+ filters = criteria.delete :filters
430
+ case filters
431
+ when Array
432
+ @filters = filters.collect(&:to_sym)
433
+ when String
434
+ @filters = [filters.to_sym]
435
+ when Symbol
436
+ @filters = [filters]
437
+ else
438
+ raise ArgumentError.new 'invalid :filters'
439
+ end
440
+ else
441
+ @filters = []
442
+ end
443
+ if criteria.has_key?(:count)
444
+ count = criteria.delete :count
445
+ case count
446
+ when Fixnum
447
+ @count = Range.new(count, count)
448
+ when Range
449
+ @count = count
450
+ else
451
+ raise ArgumentError.new 'invalid :count'
452
+ end
453
+ else
454
+ @count = nil
455
+ end
456
+ @constraints = criteria
457
+ end
458
+ end
459
+
460
+ class Combination
461
+ attr_reader :fields, :filters, :constraint, :arg
462
+ def initialize(criteria)
463
+ if criteria.has_key?(:fields)
464
+ fields = criteria.delete(:fields)
465
+ if fields.kind_of?(Array) && fields.size >= 2
466
+ @fields = fields.collect(&:to_sym)
467
+ else
468
+ raise ArgumentError.new("combination rule requires :fields as array which include 2 fields at lease")
469
+ end
470
+ else
471
+ raise ArgumentError.new("combination rule requires :fields")
472
+ end
473
+ if criteria.has_key?(:filters)
474
+ filters = criteria.delete :filters
475
+ case filters
476
+ when Array
477
+ @filters = filters.collect(&:to_sym)
478
+ when String
479
+ @filters = [filters.to_sym]
480
+ when Symbol
481
+ @filters = [filters]
482
+ else
483
+ raise ArgumentError.new 'invalid :filters'
484
+ end
485
+ else
486
+ @filters = []
487
+ end
488
+ constraint = criteria.shift
489
+ if constraint.nil?
490
+ raise ArgumentError.new 'constraint not found'
491
+ else
492
+ @constraint = constraint[0]
493
+ @arg = constraint[1]
494
+ end
495
+ end
496
+ end
497
+ end
498
+
499
+ attr_reader :default_filters, :fields, :checkboxes, :combinations
500
+
501
+ def initialize
502
+ @default_filters = []
503
+ @fields = {}
504
+ @checkboxes = {}
505
+ @combinations = {}
506
+ end
507
+
508
+ def filters(*args)
509
+ @default_filters = args
510
+ end
511
+
512
+ def field(name, criteria)
513
+ raise ArgumentError.new unless criteria.kind_of?(Hash)
514
+ @fields[name.to_sym] = Criteria::Field.new(criteria)
515
+ end
516
+
517
+ def checkbox(name, criteria)
518
+ raise ArgumentError.new unless criteria.kind_of?(Hash)
519
+ @checkboxes[name.to_sym] = Criteria::Checkbox.new(criteria)
520
+ end
521
+
522
+ def combination(name, criteria)
523
+ raise ArgumentError.new unless criteria.kind_of?(Hash)
524
+ @combinations[name.to_sym] = Criteria::Combination.new(criteria)
525
+ end
526
+
527
+ def method_missing(name, *args)
528
+ rule = args[0]
529
+ criteria = {}
530
+ criteria[:fields] = args[1]
531
+ opts = args[2] || {}
532
+ if opts.has_key?(:filters)
533
+ criteria[:filters] = opts.delete(:filters)
534
+ end
535
+ if opts.empty?
536
+ criteria[name.to_sym] = true
537
+ else
538
+ criteria[name.to_sym] = opts
539
+ end
540
+ combination(rule, criteria)
541
+ end
542
+ end
543
+
544
+ class Validator
545
+
546
+ @@filter_store = {}
547
+ @@constraint_store = {}
548
+ @@combination_constraint_store = {}
549
+
550
+ def self.register_filter(name, filter)
551
+ @@filter_store[name] = filter
552
+ end
553
+
554
+ def self.register_constraint(name, constraint)
555
+ @@constraint_store[name] = constraint
556
+ end
557
+
558
+ def self.register_combination_constraint(name, constraint)
559
+ @@combination_constraint_store[name] = constraint
560
+ end
561
+
562
+ register_filter :strip, Filter::Strip.new
563
+ register_filter :downcase, Filter::DownCase.new
564
+ register_filter :upcase, Filter::UpCase.new
565
+
566
+ register_constraint :ascii, Constraint::Ascii.new
567
+ register_constraint :regexp, Constraint::Regexp.new
568
+ register_constraint :int, Constraint::Int.new
569
+ register_constraint :uint, Constraint::Uint.new
570
+ register_constraint :alpha, Constraint::Alpha.new
571
+ register_constraint :alpha_space, Constraint::AlphaSpace.new
572
+ register_constraint :alnum, Constraint::Alnum.new
573
+ register_constraint :alnum_space, Constraint::AlnumSpace.new
574
+ register_constraint :uri, Constraint::URI.new
575
+ register_constraint :length, Constraint::Length.new
576
+ register_constraint :characters, Constraint::Characters.new
577
+
578
+ register_combination_constraint :datetime, CombinationConstraint::DateTime.new
579
+ register_combination_constraint :date, CombinationConstraint::Date.new
580
+ register_combination_constraint :time, CombinationConstraint::Time.new
581
+ register_combination_constraint :same, CombinationConstraint::Same.new
582
+ register_combination_constraint :any, CombinationConstraint::Any.new
583
+
584
+ def initialize
585
+ end
586
+
587
+ def validate(params, rule, messages=nil)
588
+ report = Report.new(messages)
589
+ rule.fields.each do |name, criteria|
590
+ criteria.filters.concat(rule.default_filters)
591
+ report << validate_field(name, criteria, params)
592
+ end
593
+ rule.checkboxes.each do |name, criteria|
594
+ criteria.filters.concat(rule.default_filters)
595
+ report << validate_checkbox(name, criteria, params)
596
+ end
597
+ rule.combinations.each do |name, criteria|
598
+ criteria.filters.concat(rule.default_filters)
599
+ report << validate_combination(name, criteria, params)
600
+ end
601
+ return report
602
+ end
603
+
604
+ private
605
+ def validate_combination(name, criteria, params)
606
+ record = Record.new(name)
607
+ values = criteria.fields.collect { |name| params[name.to_s] }
608
+ values = filter_combination_values(values, criteria.filters)
609
+ constraint = find_combination_constraint(criteria.constraint)
610
+ result = constraint.validate(values, criteria.arg)
611
+ record.fail(name) unless result
612
+ record
613
+ end
614
+
615
+ def validate_checkbox(name, criteria, params)
616
+ record = Record.new(name)
617
+ if params.has_key?(name.to_s)
618
+ values = params[name.to_s]
619
+ if values.kind_of?(Array)
620
+ values = filter_checkbox_values(values, criteria.filters)
621
+ record.value = values
622
+ if criteria.count.nil?
623
+ if values.size == 0
624
+ handle_missing_checkbox(criteria, record)
625
+ else
626
+ values.each do |value|
627
+ validate_value(value, criteria, record)
628
+ end
629
+ end
630
+ else
631
+ if criteria.count.include?(values.size)
632
+ values.each do |value|
633
+ validate_value(value, criteria, record)
634
+ end
635
+ else
636
+ if values.size == 0
637
+ handle_missing_checkbox(criteria, record)
638
+ else
639
+ record.fail(:count)
640
+ end
641
+ end
642
+ end
643
+ else
644
+ handle_missing_checkbox(criteria, record)
645
+ end
646
+ else
647
+ handle_missing_checkbox(criteria, record)
648
+ end
649
+ record
650
+ end
651
+
652
+ def filter_combination_values(values, filters)
653
+ values = values.collect{ |v| filter_value(v, filters) }
654
+ values
655
+ end
656
+
657
+ def filter_checkbox_values(values, filters)
658
+ values = filter_combination_values(values, filters)
659
+ values.delete_if { |v| v.nil? or v.empty? }
660
+ values
661
+ end
662
+
663
+ def handle_missing_checkbox(criteria, record)
664
+ if criteria.default.empty?
665
+ record.fail(:count) unless criteria.count.nil?
666
+ else
667
+ record.value = criteria.default
668
+ end
669
+ end
670
+
671
+ def validate_field(name, criteria, params)
672
+ record = Record.new name
673
+ if params.has_key?(name.to_s)
674
+ value = params[name.to_s]
675
+ unless value.kind_of?(Array)
676
+ value = filter_field_value(value, criteria.filters)
677
+ record.value = value
678
+ if value.empty?
679
+ handle_missing_field(criteria, record)
680
+ else
681
+ validate_value(value, criteria, record)
682
+ end
683
+ else
684
+ handle_missing_field(criteria, record)
685
+ end
686
+ else
687
+ handle_missing_field(criteria, record)
688
+ end
689
+ record
690
+ end
691
+
692
+ def filter_field_value(value, filters)
693
+ filter_value(value, filters)
694
+ end
695
+
696
+ def handle_missing_field(criteria, record)
697
+ if criteria.default.nil?
698
+ record.fail(:present) if criteria.require_presence?
699
+ else
700
+ record.value = criteria.default
701
+ end
702
+ end
703
+
704
+ def find_filter(type)
705
+ raise ArgumentError.new("unknown filter type: %s" % type) unless @@filter_store.has_key?(type)
706
+ @@filter_store[type]
707
+ end
708
+
709
+ def find_constraint(type)
710
+ raise ArgumentError.new("unknown constraint type: %s" % type) unless @@constraint_store.has_key?(type)
711
+ @@constraint_store[type]
712
+ end
713
+
714
+ def find_combination_constraint(type)
715
+ raise ArgumentError.new("unknown combination constraint type: %s" % type) unless @@combination_constraint_store.has_key?(type)
716
+ @@combination_constraint_store[type]
717
+ end
718
+
719
+ def filter_value(value, filters)
720
+ filters.each { |f| value = find_filter(f).process(value) }
721
+ value
722
+ end
723
+
724
+ def validate_value(value, criteria, record)
725
+ criteria.constraints.each do |constraint, arg|
726
+ result = find_constraint(constraint).validate(value, arg)
727
+ record.fail(constraint) unless result
728
+ end
729
+ end
730
+ end
731
+
732
+ class Respondent
733
+
734
+ def initialize
735
+ replace_method = Proc.new { |elem, param| replase_value(elem, param) }
736
+ check_method = Proc.new { |elem, param| check_if_selected(elem, param) }
737
+ @input_elem_fill_methods = {}
738
+ @input_elem_fill_methods[:text] = replace_method
739
+ @input_elem_fill_methods[:password] = replace_method
740
+ @input_elem_fill_methods[:hidden] = replace_method
741
+ @input_elem_fill_methods[:search] = replace_method
742
+ @input_elem_fill_methods[:number] = replace_method
743
+ @input_elem_fill_methods[:range] = replace_method
744
+ @input_elem_fill_methods[:tel] = replace_method
745
+ @input_elem_fill_methods[:url] = replace_method
746
+ @input_elem_fill_methods[:email] = replace_method
747
+ @input_elem_fill_methods[:time] = replace_method
748
+ @input_elem_fill_methods[:date] = replace_method
749
+ @input_elem_fill_methods[:week] = replace_method
750
+ @input_elem_fill_methods[:color] = replace_method
751
+ @input_elem_fill_methods[:datetime] = replace_method
752
+ @input_elem_fill_methods[:"datetime-local"] = replace_method
753
+ @input_elem_fill_methods[:radio] = check_method
754
+ @input_elem_fill_methods[:checkbox] = check_method
755
+ end
756
+
757
+ def fill_up(str, params)
758
+ doc = Hpricot(str)
759
+ (doc/"input").each do |elem|
760
+ name = elem[:name]
761
+ next if name.nil?
762
+ name = name.sub(/\[\]$/, '');
763
+ next unless params.has_key?(name.to_s)
764
+ type = elem[:type]
765
+ next if type.nil?
766
+ next unless @input_elem_fill_methods.has_key?(type.to_sym)
767
+ @input_elem_fill_methods[type.to_sym].call(elem, params[name.to_s])
768
+ end
769
+ (doc/"select").each do |select_elem|
770
+ name = select_elem[:name]
771
+ next if name.nil?
772
+ name = name.sub(/\[\]$/, '');
773
+ next unless params.has_key?(name.to_s)
774
+ param = params[name.to_s]
775
+ multiple = select_elem.has_attribute?(:multiple)
776
+ can_select_more = true
777
+ (select_elem/"option").each do |option_elem|
778
+ value = option_elem[:value]
779
+ next if value.nil? or value.empty?
780
+ case param
781
+ when Array
782
+ if can_select_more and param.include?(value)
783
+ option_elem[:selected] = "selected"
784
+ can_select_more = false unless multiple
785
+ else
786
+ option_elem.remove_attribute(:selected)
787
+ end
788
+ when String
789
+ if can_select_more and param == value
790
+ option_elem[:selected] = "selected"
791
+ can_select_more = false unless multiple
792
+ else
793
+ option_elem.remove_attribute(:selected)
794
+ end
795
+ else
796
+ if can_select_more and param.to_s == value
797
+ option_elem[:selected] = "selected"
798
+ can_select_more = false unless multiple
799
+ else
800
+ option_elem.remove_attribute(:selected)
801
+ end
802
+ end
803
+ end
804
+ end
805
+ (doc/"textarea").each do |elem|
806
+ name = elem[:name]
807
+ next if name.nil?
808
+ next unless params.has_key?(name.to_s)
809
+ param = params[name.to_s]
810
+ if param.kind_of?(Array)
811
+ elem.innerHTML = Rack::Utils::escape_html(param[0])
812
+ else
813
+ elem.innerHTML = Rack::Utils::escape_html(param)
814
+ end
815
+ end
816
+ doc.to_html
817
+ end
818
+
819
+ private
820
+ def check_if_selected(elem, param)
821
+ value = elem[:value]
822
+ if value.nil?
823
+ value = ''
824
+ end
825
+ if param.kind_of?(Array)
826
+ if param.collect(&:to_s).include?(value)
827
+ elem[:checked] = 'checked'
828
+ else
829
+ elem.remove_attribute('checked')
830
+ end
831
+ else
832
+ if value == param.to_s
833
+ elem[:checked] = 'checked'
834
+ else
835
+ elem.remove_attribute('checked')
836
+ end
837
+ end
838
+ end
839
+
840
+ def replase_value(elem, param)
841
+ if param.kind_of?(Array)
842
+ elem[:value] = param[0]
843
+ else
844
+ elem[:value] = param
845
+ end
846
+ end
847
+ end
848
+
849
+ end