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 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.
@@ -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
@@ -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/parent.rb
13
+ examples/example-01.rb
14
+ examples/example-01.rdoc
15
15
  examples/example-02.rb
16
16
  examples/example-02.rdoc
17
- examples/example-01.rb
17
+ examples/mail-datatypes.rb
18
18
  examples/mail.rb
19
- examples/yaml-example2.rb
20
- examples/yaml-example.rb
19
+ examples/parent.rb
20
+ examples/profile-options.rb
21
+ examples/smtp_tls.rb
21
22
  examples/test-datatypes.rb
22
- examples/mail-datatypes.rb
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
@@ -0,0 +1 @@
1
+ For more information on doodle, see http://doodle.rubyforge.org
@@ -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
- # if singleton class (e.g. class << [Ff]oo; self; end) then it has
129
- # no parents
130
- case Doodle::Utils.doodle_category(self)
131
- when :instance
132
- klass = self.class
133
- when :class
134
- klass = superclass
135
- when :singleton_class
136
- klass = nil
137
- end
138
- klasses = []
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
- #Doodle::Debug.d { [:_setter, name, args] }
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
- #Doodle::Debug.d { [:_setter, name, args] }
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
- #Doodle::Debug.d { [:_setter, "no attribute"] }
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
- value = args.first
495
- if (converter = conversions[value.class])
496
- value = converter[*args]
497
- else
498
- # try to find nearest ancestor
499
- ancestors = value.class.ancestors
500
- matches = ancestors & conversions.keys
501
- indexed_matches = matches.map{ |x| ancestors.index(x)}
502
- if indexed_matches.size > 0
503
- converter_class = ancestors[indexed_matches.min]
504
- if converter = conversions[converter_class]
505
- value = converter[*args]
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
- value
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
- #Doodle::Debug.d { [:validate, self, :owner, owner, :args, args ] }
518
- # if I bypass convert here, the AR inspect wierdness stops
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
- #return args.first
522
+ #!p [:validate, :after_conversion, args, :becomes, value]
523
523
  validations.each do |v|
524
- #Doodle::Debug.d { [:validate, self, v, args, value] }
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 define_collector(collection, name, klass = nil, &block)
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 :define_collector
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
- handle_error name, ArgumentError, "Too many arguments" if positional_args.size > 0
596
- params = { :name => name }
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[:init] = []
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 = Attribute.new(params, &block)
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.map{|x|
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
- ; hash }
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
- #Doodle::Debug.d { [:validate!, all, caller] }
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
- #Doodle::Debug.d { [:validate!, "using class_attributes", class_attributes] }
771
+ ##DBG: Doodle::Debug.d { [:validate!, "using class_attributes", class_attributes] }
699
772
  else
700
773
  attribs = attributes
701
- #Doodle::Debug.d { [:validate!, "using instance_attributes", attributes] }
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
- #Doodle::Debug.d { [:validate!, "default_defined - breaking" ]}
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
- #Doodle::Debug.d { [:validate!, :sending, att.name, instance_variable_get(ivar_name) ] }
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
- #Doodle::Debug.d { [:validate!, "validations", validations ]}
797
+ ##DBG: Doodle::Debug.d { [:validate!, "validations", validations ]}
725
798
  validations.each do |v|
726
- #Doodle::Debug.d { [:validate!, self, v ] }
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
- # add to start of key_values array (so can be overridden by params)
772
- key_values.unshift(init_values)
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| hash.merge(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