doodle 0.2.2 → 0.2.3

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