doodle 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|