doodle 0.2.2 → 0.2.3

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.
Files changed (48) hide show
  1. data/History.txt +24 -0
  2. data/Manifest.txt +26 -1
  3. data/README.txt +9 -8
  4. data/lib/doodle.rb +43 -1496
  5. data/lib/doodle/app.rb +6 -0
  6. data/lib/doodle/attribute.rb +165 -0
  7. data/lib/doodle/base.rb +180 -0
  8. data/lib/doodle/collector-1.9.rb +72 -0
  9. data/lib/doodle/collector.rb +191 -0
  10. data/lib/doodle/comparable.rb +8 -0
  11. data/lib/doodle/conversion.rb +80 -0
  12. data/lib/doodle/core.rb +42 -0
  13. data/lib/doodle/datatype-holder.rb +39 -0
  14. data/lib/doodle/debug.rb +20 -0
  15. data/lib/doodle/deferred.rb +13 -0
  16. data/lib/doodle/equality.rb +21 -0
  17. data/lib/doodle/exceptions.rb +29 -0
  18. data/lib/doodle/factory.rb +91 -0
  19. data/lib/doodle/getter-setter.rb +154 -0
  20. data/lib/doodle/info.rb +298 -0
  21. data/lib/doodle/inherit.rb +40 -0
  22. data/lib/doodle/json.rb +38 -0
  23. data/lib/doodle/marshal.rb +16 -0
  24. data/lib/doodle/normalized_array.rb +512 -0
  25. data/lib/doodle/normalized_hash.rb +356 -0
  26. data/lib/doodle/ordered-hash.rb +8 -0
  27. data/lib/doodle/singleton.rb +23 -0
  28. data/lib/doodle/smoke-and-mirrors.rb +23 -0
  29. data/lib/doodle/to_hash.rb +17 -0
  30. data/lib/doodle/utils.rb +173 -11
  31. data/lib/doodle/validation.rb +122 -0
  32. data/lib/doodle/version.rb +1 -1
  33. data/lib/molic_orderedhash.rb +24 -10
  34. data/spec/assigned_spec.rb +45 -0
  35. data/spec/attributes_spec.rb +7 -7
  36. data/spec/collector_spec.rb +100 -13
  37. data/spec/doodle_context_spec.rb +5 -5
  38. data/spec/from_spec.rb +43 -3
  39. data/spec/json_spec.rb +232 -0
  40. data/spec/member_init_spec.rb +11 -11
  41. data/spec/modules_spec.rb +4 -4
  42. data/spec/multi_collector_spec.rb +91 -0
  43. data/spec/must_spec.rb +32 -0
  44. data/spec/spec_helper.rb +14 -4
  45. data/spec/specialized_attribute_class_spec.rb +2 -2
  46. data/spec/typed_collector_spec.rb +57 -0
  47. data/spec/xml_spec.rb +8 -8
  48. metadata +33 -3
@@ -67,6 +67,7 @@ class Doodle
67
67
  class Filename < Option
68
68
  doodle do
69
69
  boolean :existing, :default => false, :doc => "set to true if file must exist"
70
+ boolean :expand, :default => false, :doc => "set to true if you want to have the filename expanded"
70
71
  end
71
72
  end
72
73
 
@@ -176,6 +177,11 @@ class Doodle
176
177
  File.exist?(s)
177
178
  end
178
179
  end
180
+ if da.expand
181
+ from String do |s|
182
+ File.expand_path(s)
183
+ end
184
+ end
179
185
  end
180
186
  end
181
187
  # expect an on/off flag, e.g. -b
@@ -0,0 +1,165 @@
1
+ class Doodle
2
+ # Attribute is itself a Doodle object that is created by #has and
3
+ # added to the #attributes collection in an object's DoodleInfo
4
+ #
5
+ # It is used to provide a context for defining #must and #from rules
6
+ #
7
+ class DoodleAttribute < Doodle
8
+ # note: using extend with a module causes an infinite loop in 1.9
9
+ # hence the inline
10
+
11
+ module ClassMethods
12
+ # rewrite rules for the argument list to #has
13
+ def params_from_args(owner, *args)
14
+ Doodle::Debug.d { [owner, args] }
15
+ key_values, positional_args = args.partition{ |x| x.kind_of?(Hash)}
16
+ params = { }
17
+ if positional_args.size > 0
18
+ name = positional_args.shift
19
+ case name
20
+ # has Person --> has :person, :kind => Person
21
+ when Class
22
+ params[:name] = Utils.snake_case(name.to_s.split(/::/).last)
23
+ params[:kind] = name
24
+ else
25
+ params[:name] = name.to_s.to_sym
26
+ end
27
+ end
28
+ params = key_values.inject(params){ |acc, item| acc.merge(item)}
29
+ #DBG: Doodle::Debug.d { [:has, self, self.class, params] }
30
+ if !params.key?(:name)
31
+ __doodle__.handle_error name, ArgumentError, "#{self.class} must have a name", Doodle::Utils.doodle_caller
32
+ params[:name] = :__ERROR_missing_name__
33
+ else
34
+ # ensure that :name is a symbol
35
+ params[:name] = params[:name].to_sym
36
+ end
37
+ name = params[:name]
38
+ __doodle__.handle_error name, ArgumentError, "#{self.class} has too many arguments", Doodle::Utils.doodle_caller if positional_args.size > 0
39
+
40
+ if collector = params.delete(:collect)
41
+ if !params.key?(:using)
42
+ if params.key?(:key)
43
+ params[:using] = KeyedAttribute
44
+ else
45
+ params[:using] = AppendableAttribute
46
+ end
47
+ end
48
+ # this in generic CollectorAttribute class
49
+ # collector from(Hash)
50
+
51
+ # TODO: rework this to allow multiple classes and mappings
52
+ #p [:collector, collector, params, params[:init].kind_of?(Class)]
53
+ # FIXME: collector
54
+ if collector.kind_of?(Hash)
55
+ collector_spec = collector
56
+ #collector_name, collector_class = collector.to_a[0]
57
+ elsif collector.kind_of?(Array) && collector.all? { |i| i.kind_of?(Hash) }
58
+ collector_spec = collector.inject(OrderedHash.new) { |hash, item|
59
+ item.keys.each do |key|
60
+ hash[key] = item[key]
61
+ end
62
+ hash
63
+ }
64
+ else
65
+ collectors = [collector].flatten
66
+ collector_spec = collectors.inject(OrderedHash.new) do |hash, klass|
67
+ collector_class = klass.to_s
68
+ #p [:collector_klass, collector_klass]
69
+ collector_name = Utils.snake_case(collector_class.split(/::/).last)
70
+ #p [:collector_name, collector_class, collector_name]
71
+ # FIXME: sanitize class name for 1.9 (make this a Utils function)
72
+ collector_class = collector_class.gsub(/#<Class:0x[a-fA-F0-9]+>::/, '')
73
+ # if Capitalized word given, treat as classname and create
74
+ # collector for specific class
75
+ if collector_class !~ /^[A-Z]/
76
+ collector_class = nil
77
+ end
78
+ hash[collector_name] = collector_class
79
+ hash
80
+ end
81
+ #!p [:collector_klass, collector_klass, params[:init]]
82
+ end
83
+ params[:collector_spec] = collector_spec
84
+ end
85
+ params[:doodle_owner] = owner
86
+ #p [:params, owner, params]
87
+ params
88
+ end
89
+ end
90
+ extend ClassMethods
91
+
92
+ # must define these methods before using them in #has below
93
+
94
+ # hack: bump off +validate!+ for Attributes - maybe better way of doing
95
+ # this however, without this, tries to validate Attribute to :kind
96
+ # specified, e.g. if you have
97
+ #
98
+ # has :date, :kind => Date
99
+ #
100
+ # it will fail because Attribute is not a kind of Date -
101
+ # obviously, I have to think about this some more :S
102
+ #
103
+ # at least, I could hand roll a custom validate! method for Attribute
104
+ #
105
+ def validate!(all = true)
106
+ end
107
+
108
+ # has default been defined?
109
+ def default_defined?
110
+ ivar_defined?(:default)
111
+ end
112
+
113
+ # has default been defined?
114
+ def init_defined?
115
+ ivar_defined?(:init)
116
+ end
117
+
118
+ # is this attribute optional? true if it has a default defined for it
119
+ def optional?
120
+ default_defined? or init_defined?
121
+ end
122
+
123
+ # an attribute is required if it has no default or initial value defined for it
124
+ def required?
125
+ # d { [:default?, self.class, self.name, instance_variable_defined?("@default"), @default] }
126
+ !optional?
127
+ end
128
+
129
+ # special case - not an attribute
130
+ define_getter_setter :doodle_owner
131
+
132
+ # temporarily fake existence of abstract attribute - later has
133
+ # :abstract overrides this
134
+ def abstract
135
+ @abstract = false
136
+ end
137
+
138
+ # temporarily fake existence of readonly attribute
139
+ def readonly
140
+ false
141
+ end
142
+
143
+ # name of attribute
144
+ has :name, :kind => Symbol do
145
+ from String do |s|
146
+ s.to_sym
147
+ end
148
+ end
149
+
150
+ # default value (can be a block)
151
+ has :default, :default => nil
152
+
153
+ # initial value
154
+ has :init, :default => nil
155
+
156
+ # documentation
157
+ has :doc, :default => ""
158
+
159
+ # don't try to initialize from this class
160
+ remove_method(:abstract) # because we faked it earlier - remove to avoid redefinition warning
161
+ has :abstract, :default => false
162
+ remove_method(:readonly) # because we faked it earlier - remove to avoid redefinition warning
163
+ has :readonly, :default => false
164
+ end
165
+ end
@@ -0,0 +1,180 @@
1
+ class Doodle
2
+ # the core module of Doodle - however, to get most facilities
3
+ # provided by Doodle without inheriting from Doodle, include
4
+ # Doodle::Core, not this module
5
+ module BaseMethods
6
+ include Singleton
7
+ include SmokeAndMirrors
8
+ include ToHash
9
+ include ModMarshal
10
+ include GetterSetter
11
+ include ValidationHelper
12
+ include ConversionHelper
13
+
14
+ # NOTE: can't do either of these
15
+
16
+ # include Equality
17
+ # include Comparable
18
+
19
+ # def self.included(other)
20
+ # other.module_eval {
21
+ # include Equality
22
+ # include Comparable
23
+ # }
24
+ # end
25
+
26
+ # this is the only way to get at internal values. Note: this is
27
+ # initialized on the fly rather than in #initialize because
28
+ # classes and singletons don't call #initialize
29
+ def __doodle__
30
+ @__doodle__ ||= DoodleInfo.new(self)
31
+ end
32
+ protected :__doodle__
33
+
34
+ # set up global datatypes
35
+ def datatypes(*mods)
36
+ mods.each do |mod|
37
+ DataTypeHolder.class_eval { include mod }
38
+ end
39
+ end
40
+
41
+ # vector through this method to get to doodle info or enable global
42
+ # datatypes and provide an interface that allows you to add your own
43
+ # datatypes to this declaration
44
+ def doodle(*mods, &block)
45
+ if mods.size == 0 && !block_given?
46
+ __doodle__
47
+ else
48
+ dh = Doodle::DataTypeHolder.new(self)
49
+ mods.each do |mod|
50
+ dh.extend(mod)
51
+ end
52
+ dh.instance_eval(&block)
53
+ end
54
+ end
55
+
56
+ # +doc+ add docs to doodle class or attribute
57
+ def doc(*args, &block)
58
+ if args.size > 0
59
+ @doc = *args
60
+ else
61
+ @doc
62
+ end
63
+ end
64
+ alias :doc= :doc
65
+
66
+ # +has+ is an extended +attr_accessor+
67
+ #
68
+ # simple usage - just like +attr_accessor+:
69
+ #
70
+ # class Event
71
+ # has :date
72
+ # end
73
+ #
74
+ # set default value:
75
+ #
76
+ # class Event
77
+ # has :date, :default => Date.today
78
+ # end
79
+ #
80
+ # set lazily evaluated default value:
81
+ #
82
+ # class Event
83
+ # has :date do
84
+ # default { Date.today }
85
+ # end
86
+ # end
87
+ #
88
+ def has(*args, &block)
89
+ Doodle::Debug.d { [:args, self, self.class, args] }
90
+ params = DoodleAttribute.params_from_args(self, *args)
91
+ Doodle::Debug.d { [:params, self, params] }
92
+ # get specialized attribute class or use default
93
+ attribute_class = params.delete(:using) || DoodleAttribute
94
+
95
+ # could this be handled in DoodleAttribute?
96
+ # define getter setter before setting up attribute
97
+ define_getter_setter params[:name], params, &block
98
+ #p [:attribute, attribute_class, params]
99
+ attr = __doodle__.local_attributes[params[:name]] = attribute_class.new(params, &block)
100
+
101
+ # FIXME: not sure this is really the right place for this (but
102
+ # right now the only place I can get it to work :)
103
+ if from_defined = params[:from]
104
+ from_defined.each do |k, v|
105
+ Doodle::Debug.d { [:defining, self, k, v]}
106
+ attr.instance_eval { from k, &v }
107
+ end
108
+ end
109
+
110
+ if must_defined = params[:must]
111
+ must_defined.each do |k, v|
112
+ attr.instance_eval { must k, &v }
113
+ end
114
+ end
115
+
116
+ attr
117
+ end
118
+
119
+ # define order for positional arguments
120
+ def arg_order(*args)
121
+ if args.size > 0
122
+ begin
123
+ args = args.uniq
124
+ args.each do |x|
125
+ __doodle__.handle_error :arg_order, ArgumentError, "#{x} not a Symbol", Doodle::Utils.doodle_caller if !(x.class <= Symbol)
126
+ __doodle__.handle_error :arg_order, NameError, "#{x} not an attribute name", Doodle::Utils.doodle_caller if !doodle.attributes.keys.include?(x)
127
+ end
128
+ __doodle__.arg_order = args
129
+ rescue Exception => e
130
+ __doodle__.handle_error :arg_order, InvalidOrderError, e.to_s, Doodle::Utils.doodle_caller
131
+ end
132
+ else
133
+ __doodle__.arg_order + (__doodle__.attributes.keys - __doodle__.arg_order)
134
+ end
135
+ end
136
+
137
+ # return true if instance variable +name+ defined
138
+ # FIXME: move
139
+ def ivar_defined?(name)
140
+ instance_variable_defined?("@#{name}")
141
+ end
142
+ private :ivar_defined?
143
+
144
+ # get an instance variable by symbolic name
145
+ def ivar_get(name)
146
+ instance_variable_get("@#{name}")
147
+ end
148
+
149
+ # return true if attribute has default defined and not yet been
150
+ # assigned to (i.e. still has default value)
151
+ def default?(name)
152
+ # FIXME: should this be in DoodleInfo or here?
153
+ __doodle__.attributes[name.to_sym].optional? && !ivar_defined?(name)
154
+ end
155
+
156
+ # return true if attribute has been assigned to
157
+ def assigned?(name)
158
+ ivar_defined?(name)
159
+ end
160
+
161
+ # object can be initialized from a mixture of positional arguments,
162
+ # hash of keyword value pairs and a block which is instance_eval'd
163
+ def initialize(*args, &block)
164
+ built_in = Doodle::BuiltIns::BUILTINS.select{ |x| self.kind_of?(x) }.first
165
+ if built_in
166
+ super
167
+ end
168
+ __doodle__.validation_on = true
169
+ #p [:doodle_parent, Doodle.parent, caller[-1]]
170
+ Doodle.context.push(self)
171
+ __doodle__.defer_validation do
172
+ __doodle__.update(*args, &block)
173
+ end
174
+ Doodle.context.pop
175
+ #p [:doodle, __doodle__.__inspect__]
176
+ #p [:doodle, __doodle__.attributes]
177
+ #p [:doodle_parent, __doodle__.parent]
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,72 @@
1
+ # 1.8.7+ versions
2
+ class Doodle
3
+ class AppendableAttribute
4
+ def define_collector
5
+ # FIXME: don't use eval in 1.9+
6
+ #name = self.name
7
+ #collector_name = self.collector_name
8
+ #collector_class = self.collector_class
9
+ this = self
10
+ if collector_class.nil?
11
+ doodle_owner.sc_eval do
12
+ define_method this.collector_name do |*args, &block|
13
+ collection = send(this.name)
14
+ #p [this.collector_name, 1, this.name, args]
15
+ # unshift the block onto args so not consumed by <<
16
+ #args.unshift(block) if block_given?
17
+ collection.<<(*args, &block)
18
+ end
19
+ end
20
+ else
21
+ doodle_owner.sc_eval do
22
+ define_method this.collector_name do |*args, &block|
23
+ collection = send(this.name)
24
+ #p [this.collector_name, 1, this.name, args]
25
+ #args.unshift(block) if block_given?
26
+ if args.size > 0 and args.all?{|x| x.kind_of?(this.collector_class)}
27
+ collection.<<(*args, &block)
28
+ else
29
+ collection << this.collector_class.new(*args, &block)
30
+ #collection.<<(*args)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ class KeyedAttribute
39
+ def define_collector
40
+ # save ref to self for use in closure
41
+ this = self
42
+ if this.collector_class.nil?
43
+ doodle_owner.sc_eval do
44
+ #p [:defining, this.collector_name]
45
+ define_method this.collector_name do |*args, &block|
46
+ #p [this.collector_name, 1, args]
47
+ collection = send(this.name)
48
+ args.each do |arg|
49
+ collection[arg.send(key)] = arg
50
+ end
51
+ end
52
+ end
53
+ else
54
+ doodle_owner.sc_eval do
55
+ #p [:defining, this.collector_name]
56
+ define_method this.collector_name do |*args, &block|
57
+ #p [this.collector_name, 2, args]
58
+ collection = send(this.name)
59
+ if args.size > 0 and args.all?{|x| x.kind_of?(this.collector_class)}
60
+ args.each do |arg|
61
+ collection[arg.send(this.key)] = arg
62
+ end
63
+ else
64
+ obj = this.collector_class.new(*args, &block)
65
+ collection[obj.send(this.key)] = obj
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,191 @@
1
+ class Doodle
2
+
3
+ def self.TypedArray(*klasses)
4
+ typed_class = Class.new(NormalizedArray) do
5
+ define_method :normalize_value do |v|
6
+ if !klasses.any?{ |klass| v.kind_of?(klass) }
7
+ raise TypeError, "#{self.class}: #{v.class}(#{v.inspect}) is not a kind of #{klasses.map{ |c| c.to_s }.join(', ')}", [caller[-1]]
8
+ end
9
+ v
10
+ end
11
+ end
12
+ #p [:creating_class, typed_class]
13
+ typed_class
14
+ end
15
+
16
+ # base class for attribute collector classes
17
+ class AttributeCollector < DoodleAttribute
18
+ # FIXME: collector
19
+ has :collector_spec, :init => { }
20
+
21
+ def create_collection
22
+ if self.init.kind_of?(Class)
23
+ #p [:create_collection, :class]
24
+ collection = self.init.new
25
+ else
26
+ #p [:create_collection, :clone]
27
+ collection = self.init.clone
28
+ end
29
+ #p [:create_collection, collection]
30
+ collection
31
+ end
32
+ private :create_collection
33
+
34
+ def resolve_collector_class
35
+ # FIXME: collector - perhaps don't allow non-class collectors - should be resolved by this point
36
+ # perhaps do this in init?
37
+ collector_spec.each do |k, v|
38
+ if !v.kind_of?(Class)
39
+ collector_spec[k] = Doodle::Utils.const_resolve(v)
40
+ end
41
+ end
42
+ end
43
+
44
+ def resolve_value(value)
45
+ klasses = collector_spec.values
46
+ # FIXME: collector - find applicable collector class
47
+ if klasses.any? { |x| value.kind_of?(x) }
48
+ # no change required
49
+ #p [:resolve_value, :value, value]
50
+ value
51
+ elsif collector_class = klasses.select { |klass| klass.__doodle__.conversions.key?(value.class) }.first
52
+ # if the collector_class has a specific conversion for this value class
53
+ #p [:resolve_value, :collector_class_from, value]
54
+ collector_class.from(value)
55
+ else
56
+ collector_class = klasses.first
57
+ # try to instantiate collector_class using raw value
58
+ #p [:resolve_value, :collector_class_new, value]
59
+ collector_class.new(value)
60
+ end
61
+ end
62
+
63
+ def initialize(*args, &block)
64
+ #p [self.class, :initialize]
65
+ super
66
+ define_collector
67
+ from Hash do |hash|
68
+ # FIXME: collector - my bogon detector just went off the scale - I forget why I have to do this here... :/
69
+ # oh yes - because I allow forward references using symbols or strings
70
+ resolve_collector_class
71
+ collection = create_collection
72
+ hash.inject(collection) do |h, (key, value)|
73
+ h[key] = resolve_value(value)
74
+ h
75
+ end
76
+ end
77
+ from Enumerable do |enum|
78
+ #p [:enum, Enumerable]
79
+ # FIXME: collector
80
+ resolve_collector_class
81
+ # this is not very elegant but String is a classified as an
82
+ # Enumerable in 1.8.x (but behaves differently)
83
+ if enum.kind_of?(String) && self.init.kind_of?(String)
84
+ post_process( resolve_value(enum) )
85
+ else
86
+ post_process( enum.map{ |value| resolve_value(value) } )
87
+ end
88
+ end
89
+ end
90
+
91
+ def post_process(results)
92
+ #p [:post_process, results]
93
+ collection = create_collection
94
+ collection.replace(results)
95
+ end
96
+ end
97
+
98
+ # define collector methods for hash-like attribute collectors
99
+ class KeyedAttribute < AttributeCollector
100
+ # has :init, :init => DoodleHash.new
101
+ has :init, :init => { }
102
+ #has :init, :init => OrderedHash.new
103
+ has :key
104
+
105
+ def post_process(results)
106
+ collection = create_collection
107
+ results.inject(collection) do |h, result|
108
+ h[result.send(key)] = result
109
+ h
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ if false
116
+ # not ready for primetime
117
+ #if RUBY_VERSION >= '1.8.7'
118
+ # load ruby 1.8.7+ version specific methods
119
+ require 'doodle/collector-1.9'
120
+ else
121
+ # version for ruby 1.8.6
122
+ class Doodle
123
+
124
+ # define collector methods for array-like attribute collectors
125
+ class AppendableAttribute < AttributeCollector
126
+ # has :init, :init => DoodleArray.new
127
+ has :init, :init => []
128
+
129
+ # define a collector for appendable collections
130
+ # - collection should provide a :<< method
131
+ def define_collector
132
+ collector_spec.each do |collector_name, collector_class|
133
+ # FIXME: don't use eval in 1.9+
134
+ if collector_class.nil?
135
+ doodle_owner.sc_eval("def #{collector_name}(*args, &block)
136
+ collection = self.#{name}
137
+ args.unshift(block) if block_given?
138
+ collection.<<(*args);
139
+ end", __FILE__, __LINE__)
140
+ else
141
+ doodle_owner.sc_eval("def #{collector_name}(*args, &block)
142
+ collection = self.send(:#{name})
143
+ if args.size > 0 and args.all?{|x| x.kind_of?(#{collector_class})}
144
+ collection.<<(*args)
145
+ else
146
+ # FIXME: this is a wierd one - need name here - can't use collection directly...?
147
+ #{name} << #{collector_class}.new(*args, &block)
148
+ # this is OK
149
+ #self.send(:#{name}) << #{collector_class}.new(*args, &block)
150
+ # but this isn't
151
+ #collection.<<(#{collector_class}.new(*args, &block))
152
+ end
153
+ end", __FILE__, __LINE__)
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ class KeyedAttribute
160
+
161
+ # define a collector for keyed collections
162
+ # - collection should provide :[], :clone and :replace methods
163
+ def define_collector
164
+ collector_spec.each do |collector_name, collector_class|
165
+ # need to use string eval because passing block
166
+ # FIXME: don't use eval in 1.9+
167
+ if collector_class.nil?
168
+ doodle_owner.sc_eval("def #{collector_name}(*args, &block)
169
+ collection = #{name}
170
+ args.each do |arg|
171
+ #{name}[arg.send(:#{key})] = arg
172
+ end
173
+ end", __FILE__, __LINE__)
174
+ else
175
+ doodle_owner.sc_eval("def #{collector_name}(*args, &block)
176
+ collection = #{name}
177
+ if args.size > 0 and args.all?{|x| x.kind_of?(#{collector_class})}
178
+ args.each do |arg|
179
+ #{name}[arg.send(:#{key})] = arg
180
+ end
181
+ else
182
+ obj = #{collector_class}.new(*args, &block)
183
+ #{name}[obj.send(:#{key})] = obj
184
+ end
185
+ end", __FILE__, __LINE__)
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end