doodle 0.1.3 → 0.1.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/COPYING +18 -0
- data/History.txt +9 -0
- data/Manifest.txt +16 -12
- data/PostInstall.txt +1 -0
- data/lib/doodle.rb +166 -86
- data/lib/doodle/datatypes.rb +19 -6
- data/lib/doodle/rfc822.rb +22 -15
- data/lib/doodle/version.rb +1 -1
- data/spec/arg_order_spec.rb +11 -11
- data/spec/attributes_spec.rb +8 -8
- data/spec/bugs_spec.rb +12 -12
- data/spec/class_spec.rb +21 -21
- data/spec/collector_spec.rb +87 -11
- data/spec/defaults_spec.rb +21 -21
- data/spec/doodle_context_spec.rb +2 -2
- data/spec/doodle_spec.rb +86 -70
- data/spec/factory_spec.rb +8 -8
- data/spec/inheritance_spec.rb +74 -0
- data/spec/new_doodle_spec.rb +3 -3
- data/spec/singleton_spec.rb +16 -16
- data/spec/spec_helper.rb +10 -0
- data/spec/specialized_attribute_class_spec.rb +111 -0
- data/spec/superclass_spec.rb +4 -4
- data/spec/validation_spec.rb +8 -8
- data/tasks/deployment.rake +5 -5
- metadata +21 -14
- data/tasks/website.rake +0 -17
data/COPYING
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2008 Sean O'Halpin <monkeymind.textdriven.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/History.txt
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
== 0.1.4 / 2008-05-06
|
2
|
+
- Features:
|
3
|
+
- keyed collections - see http://doodle.rubyforge.org/#keyed_collections for details
|
4
|
+
- specialized Attribute classes in #has - see
|
5
|
+
spec/specialized_attribute_class_spec.rb for example
|
6
|
+
|
7
|
+
- Bug fixes:
|
8
|
+
- fixed bug when using a collect generated method with a type but no arguments
|
9
|
+
|
1
10
|
== 0.1.3 / 2008-05-01
|
2
11
|
- really avoid wierd interaction with ActiveRecord this time
|
3
12
|
- also, managed to get rid of crufty reliance on inspect and found
|
data/Manifest.txt
CHANGED
@@ -1,31 +1,33 @@
|
|
1
|
+
COPYING
|
2
|
+
CREDITS
|
1
3
|
History.txt
|
2
4
|
License.txt
|
3
5
|
Manifest.txt
|
6
|
+
PostInstall.txt
|
4
7
|
README.txt
|
5
|
-
CREDITS
|
6
8
|
Rakefile
|
7
9
|
config/hoe.rb
|
8
10
|
config/requirements.rb
|
9
|
-
examples/smtp_tls.rb
|
10
|
-
examples/example-01.rdoc
|
11
|
-
examples/profile-options.rb
|
12
11
|
examples/doodle-errors.rb
|
13
12
|
examples/event-location.rb
|
14
|
-
examples/
|
13
|
+
examples/example-01.rb
|
14
|
+
examples/example-01.rdoc
|
15
15
|
examples/example-02.rb
|
16
16
|
examples/example-02.rdoc
|
17
|
-
examples/
|
17
|
+
examples/mail-datatypes.rb
|
18
18
|
examples/mail.rb
|
19
|
-
examples/
|
20
|
-
examples/
|
19
|
+
examples/parent.rb
|
20
|
+
examples/profile-options.rb
|
21
|
+
examples/smtp_tls.rb
|
21
22
|
examples/test-datatypes.rb
|
22
|
-
examples/
|
23
|
+
examples/yaml-example.rb
|
24
|
+
examples/yaml-example2.rb
|
23
25
|
lib/doodle.rb
|
24
|
-
lib/molic_orderedhash.rb
|
25
26
|
lib/doodle/datatypes.rb
|
26
27
|
lib/doodle/rfc822.rb
|
27
28
|
lib/doodle/utils.rb
|
28
29
|
lib/doodle/version.rb
|
30
|
+
lib/molic_orderedhash.rb
|
29
31
|
log/debug.log
|
30
32
|
script/console
|
31
33
|
script/destroy
|
@@ -36,6 +38,7 @@ spec/arg_order_spec.rb
|
|
36
38
|
spec/attributes_spec.rb
|
37
39
|
spec/bugs_spec.rb
|
38
40
|
spec/class_spec.rb
|
41
|
+
spec/class_validation_spec.rb
|
39
42
|
spec/class_var_spec.rb
|
40
43
|
spec/collector_spec.rb
|
41
44
|
spec/conversion_spec.rb
|
@@ -45,6 +48,7 @@ spec/doodle_spec.rb
|
|
45
48
|
spec/extra_args_spec.rb
|
46
49
|
spec/factory_spec.rb
|
47
50
|
spec/flatten_first_level_spec.rb
|
51
|
+
spec/inheritance_spec.rb
|
48
52
|
spec/init_spec.rb
|
49
53
|
spec/new_doodle_spec.rb
|
50
54
|
spec/required_spec.rb
|
@@ -52,10 +56,10 @@ spec/serialization_spec.rb
|
|
52
56
|
spec/singleton_spec.rb
|
53
57
|
spec/spec.opts
|
54
58
|
spec/spec_helper.rb
|
59
|
+
spec/specialized_attribute_class_spec.rb
|
55
60
|
spec/superclass_spec.rb
|
56
|
-
spec/validation_spec.rb
|
57
61
|
spec/validation2_spec.rb
|
62
|
+
spec/validation_spec.rb
|
58
63
|
tasks/deployment.rake
|
59
64
|
tasks/environment.rake
|
60
65
|
tasks/rspec.rake
|
61
|
-
tasks/website.rake
|
data/PostInstall.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
For more information on doodle, see http://doodle.rubyforge.org
|
data/lib/doodle.rb
CHANGED
@@ -6,7 +6,6 @@ $:.unshift(File.dirname(__FILE__)) unless
|
|
6
6
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
7
7
|
|
8
8
|
require 'molic_orderedhash' # todo[replace this with own (required functions only) version]
|
9
|
-
#require 'bleak_house' if ENV['BLEAK_HOUSE']
|
10
9
|
|
11
10
|
# require Ruby 1.8.6 or higher
|
12
11
|
if RUBY_VERSION < '1.8.6'
|
@@ -57,19 +56,6 @@ class Doodle
|
|
57
56
|
def snake_case(camel_cased_word)
|
58
57
|
camel_cased_word.gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
|
59
58
|
end
|
60
|
-
# what kind of object are we dealing with?
|
61
|
-
def doodle_category(obj)
|
62
|
-
return :nil if obj.class == NilClass
|
63
|
-
if obj.kind_of?(Module)
|
64
|
-
if obj.ancestors.include?(obj)
|
65
|
-
:class
|
66
|
-
else
|
67
|
-
:singleton_class
|
68
|
-
end
|
69
|
-
else
|
70
|
-
:instance
|
71
|
-
end
|
72
|
-
end
|
73
59
|
end
|
74
60
|
end
|
75
61
|
|
@@ -125,22 +111,17 @@ class Doodle
|
|
125
111
|
|
126
112
|
# parents returns the set of parent classes of an object
|
127
113
|
def parents
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
until klass.nil? || klass == klass.superclass
|
140
|
-
klasses << klass
|
141
|
-
klass = klass.superclass
|
142
|
-
end
|
143
|
-
klasses
|
114
|
+
anc = if respond_to?(:ancestors)
|
115
|
+
if ancestors.include?(self)
|
116
|
+
ancestors[1..-1]
|
117
|
+
else
|
118
|
+
# singletons have no parents (they're orphans)
|
119
|
+
[]
|
120
|
+
end
|
121
|
+
else
|
122
|
+
self.class.ancestors
|
123
|
+
end
|
124
|
+
anc.select{|x| x.kind_of?(Class)}
|
144
125
|
end
|
145
126
|
|
146
127
|
# need concepts of
|
@@ -164,6 +145,7 @@ class Doodle
|
|
164
145
|
private :collect_inherited
|
165
146
|
end
|
166
147
|
|
148
|
+
# = embrace
|
167
149
|
# the intent of embrace is to provide a way to create directives
|
168
150
|
# that affect all members of a class 'family' without having to
|
169
151
|
# modify Module, Class or Object - in some ways, it's similar to Ara
|
@@ -286,6 +268,8 @@ class Doodle
|
|
286
268
|
end
|
287
269
|
end
|
288
270
|
|
271
|
+
#### ERRORS COLLECTION
|
272
|
+
|
289
273
|
# where should I put this?
|
290
274
|
def errors
|
291
275
|
__doodle__.errors
|
@@ -308,6 +292,8 @@ class Doodle
|
|
308
292
|
end
|
309
293
|
end
|
310
294
|
|
295
|
+
#### ERRORS COLLECTION
|
296
|
+
|
311
297
|
def _handle_inherited_hash(tf, method)
|
312
298
|
if tf
|
313
299
|
collect_inherited(method).inject(OrderedHash.new){ |hash, item|
|
@@ -330,6 +316,7 @@ class Doodle
|
|
330
316
|
# - if tf == false, returns only those attributes defined in the current object/class
|
331
317
|
def attributes(tf = true)
|
332
318
|
results = _handle_inherited_hash(tf, :local_attributes)
|
319
|
+
# if an instance, include the singleton_class attributes
|
333
320
|
if !kind_of?(Class) && singleton_class.respond_to?(:attributes)
|
334
321
|
results = results.merge(singleton_class.attributes)
|
335
322
|
end
|
@@ -402,8 +389,10 @@ class Doodle
|
|
402
389
|
def getter_setter(name, *args, &block)
|
403
390
|
name = name.to_sym
|
404
391
|
if block_given? || args.size > 0
|
392
|
+
#!p [:getter_setter, :setter, name, *args]
|
405
393
|
_setter(name, *args, &block)
|
406
394
|
else
|
395
|
+
#!p [:getter_setter, :getter, name]
|
407
396
|
_getter(name)
|
408
397
|
end
|
409
398
|
end
|
@@ -413,6 +402,7 @@ class Doodle
|
|
413
402
|
def _getter(name, &block)
|
414
403
|
ivar = "@#{name}"
|
415
404
|
if instance_variable_defined?(ivar)
|
405
|
+
#!p [:_getter, name, ivar, instance_variable_get(ivar)]
|
416
406
|
instance_variable_get(ivar)
|
417
407
|
else
|
418
408
|
# handle default
|
@@ -421,6 +411,7 @@ class Doodle
|
|
421
411
|
att = lookup_attribute(name)
|
422
412
|
# special case for class/singleton :init
|
423
413
|
if att.init_defined?
|
414
|
+
#!p [:_setter, att.init]
|
424
415
|
_setter(name, att.init)
|
425
416
|
elsif att.default_defined?
|
426
417
|
case att.default
|
@@ -441,17 +432,20 @@ class Doodle
|
|
441
432
|
|
442
433
|
# set an attribute by name - apply validation if defined
|
443
434
|
def _setter(name, *args, &block)
|
444
|
-
|
435
|
+
##DBG: Doodle::Debug.d { [:_setter, name, args] }
|
445
436
|
ivar = "@#{name}"
|
446
437
|
if block_given?
|
447
438
|
args.unshift(DeferredBlock.new(block))
|
448
439
|
end
|
449
440
|
if att = lookup_attribute(name)
|
450
|
-
|
441
|
+
##DBG: Doodle::Debug.d { [:_setter, name, args] }
|
442
|
+
#!p [:_setter, :got_att, name, *args]
|
451
443
|
v = instance_variable_set(ivar, att.validate(self, *args))
|
444
|
+
#!p [:_setter, :got_att, name, :value, v]
|
452
445
|
#v = instance_variable_set(ivar, *args)
|
453
446
|
else
|
454
|
-
|
447
|
+
#!p [:_setter, :no_att, name, *args]
|
448
|
+
##DBG: Doodle::Debug.d { [:_setter, "no attribute"] }
|
455
449
|
v = instance_variable_set(ivar, *args)
|
456
450
|
end
|
457
451
|
validate!(false)
|
@@ -491,37 +485,43 @@ class Doodle
|
|
491
485
|
# convert a value according to conversion rules
|
492
486
|
def convert(owner, *args)
|
493
487
|
begin
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
488
|
+
args = args.map do |value|
|
489
|
+
if (converter = conversions[value.class])
|
490
|
+
#p [:convert, :using, value.class]
|
491
|
+
value = converter[*args]
|
492
|
+
else
|
493
|
+
# try to find nearest ancestor
|
494
|
+
ancestors = value.class.ancestors
|
495
|
+
matches = ancestors & conversions.keys
|
496
|
+
indexed_matches = matches.map{ |x| ancestors.index(x)}
|
497
|
+
if indexed_matches.size > 0
|
498
|
+
converter_class = ancestors[indexed_matches.min]
|
499
|
+
if converter = conversions[converter_class]
|
500
|
+
#p [:convert, :using, converter_class]
|
501
|
+
value = converter[*args]
|
502
|
+
end
|
506
503
|
end
|
507
504
|
end
|
505
|
+
value
|
508
506
|
end
|
509
507
|
rescue Exception => e
|
510
508
|
owner.handle_error name, ConversionError, e.to_s, [caller[-1]]
|
511
509
|
end
|
512
|
-
|
510
|
+
if args.size > 1
|
511
|
+
args
|
512
|
+
else
|
513
|
+
args.first
|
514
|
+
end
|
513
515
|
end
|
514
516
|
|
515
517
|
# validate that args meet rules defined with +must+
|
516
518
|
def validate(owner, *args)
|
517
|
-
|
518
|
-
|
519
|
-
# so what is going on?
|
520
|
-
#return args.first
|
519
|
+
##DBG: Doodle::Debug.d { [:validate, self, :owner, owner, :args, args ] }
|
520
|
+
#!p [:validate, :before_conversion, args]
|
521
521
|
value = convert(owner, *args)
|
522
|
-
|
522
|
+
#!p [:validate, :after_conversion, args, :becomes, value]
|
523
523
|
validations.each do |v|
|
524
|
-
|
524
|
+
##DBG: Doodle::Debug.d { [:validate, self, v, args, value] }
|
525
525
|
if !v.block[value]
|
526
526
|
owner.handle_error name, ValidationError, "#{ name } must #{ v.message } - got #{ value.class }(#{ value.inspect })", [caller[-1]]
|
527
527
|
end
|
@@ -547,24 +547,50 @@ class Doodle
|
|
547
547
|
end
|
548
548
|
private :define_getter_setter
|
549
549
|
|
550
|
-
# define a collector
|
550
|
+
# define a collector for appendable collections
|
551
551
|
# - collection should provide a :<< method
|
552
|
-
def
|
552
|
+
def define_appendable_collector(collection, name, klass = nil, &block)
|
553
553
|
# need to use string eval because passing block
|
554
554
|
if klass.nil?
|
555
555
|
sc_eval("def #{name}(*args, &block); args.unshift(block) if block_given?; #{collection}.<<(*args); end", __FILE__, __LINE__)
|
556
556
|
else
|
557
|
-
sc_eval("def #{name}(*args, &block)
|
558
|
-
if args.all?{|x| x.kind_of?(#{klass})}
|
557
|
+
sc_eval("def #{name}(*args, &block)
|
558
|
+
if args.size > 0 and args.all?{|x| x.kind_of?(#{klass})}
|
559
559
|
#{collection}.<<(*args)
|
560
560
|
else
|
561
|
-
#{collection} << #{klass}.new(*args, &block)
|
561
|
+
#{collection} << #{klass}.new(*args, &block)
|
562
562
|
end
|
563
563
|
end", __FILE__, __LINE__)
|
564
564
|
end
|
565
565
|
end
|
566
|
-
private :
|
566
|
+
private :define_appendable_collector
|
567
567
|
|
568
|
+
# define a collector for keyed collections
|
569
|
+
# - collection should provide a :[] method
|
570
|
+
def define_keyed_collector(collection, name, key_method, klass = nil, &block)
|
571
|
+
#DBG: Doodle::Debug.d { [:define_keyed_collector, collection, name, key_method, klass]}
|
572
|
+
# need to use string eval because passing block
|
573
|
+
if klass.nil?
|
574
|
+
sc_eval("def #{name}(*args, &block)
|
575
|
+
args.each do |arg|
|
576
|
+
#{collection}[arg.send(:#{key_method})] = arg
|
577
|
+
end
|
578
|
+
end", __FILE__, __LINE__)
|
579
|
+
else
|
580
|
+
sc_eval("def #{name}(*args, &block)
|
581
|
+
if args.size > 0 and args.all?{|x| x.kind_of?(#{klass})}
|
582
|
+
args.each do |arg|
|
583
|
+
#{collection}[arg.send(:#{key_method})] = arg
|
584
|
+
end
|
585
|
+
else
|
586
|
+
obj = #{klass}.new(*args, &block)
|
587
|
+
#{collection}[obj.send(:#{key_method})] = obj
|
588
|
+
end
|
589
|
+
end", __FILE__, __LINE__)
|
590
|
+
end
|
591
|
+
end
|
592
|
+
private :define_keyed_collector
|
593
|
+
|
568
594
|
# +has+ is an extended +attr_accessor+
|
569
595
|
#
|
570
596
|
# simple usage - just like +attr_accessor+:
|
@@ -588,19 +614,37 @@ class Doodle
|
|
588
614
|
# end
|
589
615
|
#
|
590
616
|
def has(*args, &block)
|
591
|
-
Doodle::Debug.d { [:has, self, self.class, args] }
|
592
|
-
name = args.shift.to_sym
|
617
|
+
#DBG: Doodle::Debug.d { [:has, self, self.class, args] }
|
593
618
|
# d { [:has2, name, args] }
|
594
619
|
key_values, positional_args = args.partition{ |x| x.kind_of?(Hash)}
|
595
|
-
|
596
|
-
|
620
|
+
if positional_args.size > 0
|
621
|
+
name = positional_args.shift.to_sym
|
622
|
+
params = { :name => name }
|
623
|
+
else
|
624
|
+
params = { }
|
625
|
+
end
|
597
626
|
params = key_values.inject(params){ |acc, item| acc.merge(item)}
|
627
|
+
#DBG: Doodle::Debug.d { [:has, self, self.class, params] }
|
628
|
+
if !params.key?(:name)
|
629
|
+
handle_error name, ArgumentError, "Must have a name"
|
630
|
+
else
|
631
|
+
name = params[:name].to_sym
|
632
|
+
end
|
633
|
+
handle_error name, ArgumentError, "Too many arguments" if positional_args.size > 0
|
598
634
|
|
599
635
|
# don't pass collector params through to Attribute
|
600
636
|
collector_klass = nil
|
637
|
+
# fixme: this should be in specialized attribute class
|
638
|
+
# (and also distinction between appendable and keyed collections
|
601
639
|
if collector = params.delete(:collect)
|
602
640
|
if !params.key?(:init)
|
603
|
-
params
|
641
|
+
if params.key?(:key)
|
642
|
+
#!p [:defining, :hash, name]
|
643
|
+
params[:init] = { }
|
644
|
+
else
|
645
|
+
#!p [:defining, :array, name]
|
646
|
+
params[:init] = []
|
647
|
+
end
|
604
648
|
end
|
605
649
|
if collector.kind_of?(Hash)
|
606
650
|
collector_name, collector_klass = collector.to_a[0]
|
@@ -612,23 +656,40 @@ class Doodle
|
|
612
656
|
if collector_klass !~ /^[A-Z]/
|
613
657
|
collector_klass = nil
|
614
658
|
end
|
659
|
+
#!p [:collector_klass, collector_klass, params[:init]]
|
660
|
+
end
|
661
|
+
if key = params.delete(:key)
|
662
|
+
define_keyed_collector name, collector_name, key, collector_klass
|
663
|
+
else
|
664
|
+
define_appendable_collector name, collector_name, collector_klass
|
615
665
|
end
|
616
|
-
define_collector name, collector_name, collector_klass
|
617
666
|
end
|
667
|
+
|
668
|
+
# get specialized attribute class or use default
|
669
|
+
attribute_class = params.delete(:using) || Attribute
|
618
670
|
|
619
671
|
# define getter setter before setting up attribute
|
620
672
|
define_getter_setter name, *args, &block
|
621
|
-
local_attributes[name] = attribute =
|
673
|
+
local_attributes[name] = attribute = attribute_class.new(params, &block)
|
674
|
+
|
622
675
|
# if a collector has been defined and has a specific class, then you can pass in an array of hashes
|
623
676
|
if collector_klass
|
677
|
+
# fixme: this should be in specialized attribute class
|
678
|
+
#!p [:collector_klass2, collector_klass, params[:init]]
|
624
679
|
attribute.instance_eval {
|
680
|
+
# applying map to hashes returns an array, so avoid that by simply passing it through
|
681
|
+
from Hash do |hash|
|
682
|
+
hash
|
683
|
+
end
|
625
684
|
from Enumerable do |enum|
|
626
685
|
if !collector_klass.kind_of?(Class)
|
627
686
|
tmp_klass = self.class.const_get(collector_klass)
|
628
687
|
else
|
629
688
|
tmp_klass = collector_klass
|
630
689
|
end
|
631
|
-
enum
|
690
|
+
#!p [:enumerating, name, enum]
|
691
|
+
results = enum.map{|x|
|
692
|
+
#!p [:enumerating, x]
|
632
693
|
if x.kind_of?(tmp_klass)
|
633
694
|
x
|
634
695
|
elsif tmp_klass.conversions.key?(x.class)
|
@@ -637,6 +698,16 @@ class Doodle
|
|
637
698
|
tmp_klass.new(x)
|
638
699
|
end
|
639
700
|
}
|
701
|
+
#!p [:enumerating, :results, results]
|
702
|
+
# fixme: this is all getting a bit too specific to arrays and hashes
|
703
|
+
# figure out a way to do this with specialized Doodle::Attribute?
|
704
|
+
if !key.nil?
|
705
|
+
results = results.inject({ }) do |hash, result|
|
706
|
+
hash[result.send(key)] = result
|
707
|
+
hash
|
708
|
+
end
|
709
|
+
end
|
710
|
+
results
|
640
711
|
end
|
641
712
|
}
|
642
713
|
end
|
@@ -647,7 +718,7 @@ class Doodle
|
|
647
718
|
def arg_order(*args)
|
648
719
|
if args.size > 0
|
649
720
|
begin
|
650
|
-
args.uniq
|
721
|
+
args = args.uniq
|
651
722
|
args.each do |x|
|
652
723
|
handle_error :arg_order, ArgumentError, "#{x} not a Symbol" if !(x.class <= Symbol)
|
653
724
|
handle_error :arg_order, NameError, "#{x} not an attribute name" if !attributes.keys.include?(x)
|
@@ -662,7 +733,8 @@ class Doodle
|
|
662
733
|
end
|
663
734
|
|
664
735
|
def get_init_values(tf = true)
|
665
|
-
attributes(tf).select{|n, a| a.init_defined? }.inject({}) {|hash, (n, a)|
|
736
|
+
attributes(tf).select{|n, a| a.init_defined? }.inject({}) {|hash, (n, a)|
|
737
|
+
#!p [:get_init_values, a.init]
|
666
738
|
hash[n] = begin
|
667
739
|
case a.init
|
668
740
|
when NilClass, TrueClass, FalseClass, Fixnum
|
@@ -675,7 +747,8 @@ class Doodle
|
|
675
747
|
rescue Exception => e
|
676
748
|
a.init
|
677
749
|
end
|
678
|
-
|
750
|
+
hash
|
751
|
+
}
|
679
752
|
end
|
680
753
|
private :get_init_values
|
681
754
|
|
@@ -688,22 +761,22 @@ class Doodle
|
|
688
761
|
# validate this object by applying all validations in sequence
|
689
762
|
# - if all == true, validate all attributes, e.g. when loaded from YAML, else validate at object level only
|
690
763
|
def validate!(all = true)
|
691
|
-
|
764
|
+
##DBG: Doodle::Debug.d { [:validate!, all, caller] }
|
692
765
|
if all
|
693
766
|
clear_errors
|
694
767
|
end
|
695
768
|
if __doodle__.validation_on
|
696
769
|
if self.class == Class
|
697
770
|
attribs = class_attributes
|
698
|
-
|
771
|
+
##DBG: Doodle::Debug.d { [:validate!, "using class_attributes", class_attributes] }
|
699
772
|
else
|
700
773
|
attribs = attributes
|
701
|
-
|
774
|
+
##DBG: Doodle::Debug.d { [:validate!, "using instance_attributes", attributes] }
|
702
775
|
end
|
703
776
|
attribs.each do |name, att|
|
704
777
|
# treat default as special case
|
705
778
|
if att.default_defined?
|
706
|
-
|
779
|
+
##DBG: Doodle::Debug.d { [:validate!, "default_defined - breaking" ]}
|
707
780
|
break
|
708
781
|
end
|
709
782
|
ivar_name = "@#{att.name}"
|
@@ -712,7 +785,7 @@ class Doodle
|
|
712
785
|
# validations are applied to raw instance variables
|
713
786
|
# e.g. when loaded from YAML
|
714
787
|
if all
|
715
|
-
|
788
|
+
##DBG: Doodle::Debug.d { [:validate!, :sending, att.name, instance_variable_get(ivar_name) ] }
|
716
789
|
__send__("#{att.name}=", instance_variable_get(ivar_name))
|
717
790
|
end
|
718
791
|
elsif self.class != Class
|
@@ -721,9 +794,9 @@ class Doodle
|
|
721
794
|
end
|
722
795
|
# now apply instance level validations
|
723
796
|
|
724
|
-
|
797
|
+
##DBG: Doodle::Debug.d { [:validate!, "validations", validations ]}
|
725
798
|
validations.each do |v|
|
726
|
-
|
799
|
+
##DBG: Doodle::Debug.d { [:validate!, self, v ] }
|
727
800
|
begin
|
728
801
|
if !instance_eval(&v.block)
|
729
802
|
handle_error self, ValidationError, "#{ self.class } must #{ v.message }", [caller[-1]]
|
@@ -756,31 +829,38 @@ class Doodle
|
|
756
829
|
# after initialization (validate! is called if this method is
|
757
830
|
# called after initialization)
|
758
831
|
def initialize_from_hash(*args)
|
832
|
+
#!p [:initialize_from_hash, :args, *args]
|
759
833
|
defer_validation do
|
760
834
|
# hash initializer
|
761
835
|
# separate into array of hashes of form [{:k1 => v1}, {:k2 => v2}] and positional args
|
762
836
|
key_values, args = args.partition{ |x| x.kind_of?(Hash)}
|
763
|
-
Doodle::Debug.d { [self.class, :initialize_from_hash, :key_values, key_values, :args, args] }
|
764
|
-
|
765
|
-
# match up positional args with attribute names (from arg_order) using idiom to create hash from array of assocs
|
766
|
-
arg_keywords = Hash[*(Utils.flatten_first_level(self.class.arg_order[0...args.size].zip(args)))]
|
837
|
+
#DBG: Doodle::Debug.d { [self.class, :initialize_from_hash, :key_values, key_values, :args, args] }
|
838
|
+
#!p [self.class, :initialize_from_hash, :key_values, key_values, :args, args]
|
767
839
|
|
768
840
|
# set up initial values with ~clones~ of specified values (so not shared between instances)
|
769
841
|
init_values = get_init_values
|
770
|
-
|
771
|
-
|
772
|
-
|
842
|
+
#p [:init_values, init_values]
|
843
|
+
|
844
|
+
# match up positional args with attribute names (from arg_order) using idiom to create hash from array of assocs
|
845
|
+
arg_keywords = init_values.merge(Hash[*(Utils.flatten_first_level(self.class.arg_order[0...args.size].zip(args)))])
|
846
|
+
#p [self.class, :initialize_from_hash, :arg_keywords, arg_keywords]
|
773
847
|
|
774
848
|
# merge all hash args into one
|
775
|
-
key_values = key_values.inject(arg_keywords) { |hash, item|
|
849
|
+
key_values = key_values.inject(arg_keywords) { |hash, item|
|
850
|
+
#p [self.class, :initialize_from_hash, :merge, hash, item]
|
851
|
+
hash.merge(item)
|
852
|
+
}
|
853
|
+
#p [self.class, :initialize_from_hash, :key_values2, key_values]
|
776
854
|
|
777
855
|
# convert key names to symbols
|
778
856
|
key_values = key_values.inject({}) {|h, (k, v)| h[k.to_sym] = v; h}
|
779
|
-
Doodle::Debug.d { [self.class, :initialize_from_hash, :key_values2, key_values, :args2, args] }
|
857
|
+
#DBG: Doodle::Debug.d { [self.class, :initialize_from_hash, :key_values2, key_values, :args2, args] }
|
858
|
+
#p [self.class, :initialize_from_hash, :key_values3, key_values]
|
780
859
|
|
781
860
|
# create attributes
|
782
861
|
key_values.keys.each do |key|
|
783
|
-
Doodle::Debug.d { [self.class, :initialize_from_hash, :setting, key, key_values[key]] }
|
862
|
+
#DBG: Doodle::Debug.d { [self.class, :initialize_from_hash, :setting, key, key_values[key]] }
|
863
|
+
#p [self.class, :initialize_from_hash, :setting, key, key_values[key]]
|
784
864
|
if respond_to?(key)
|
785
865
|
__send__(key, key_values[key])
|
786
866
|
else
|