diy 1.1 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,6 @@
1
+ == 1.1.1 / 2008-05-21
2
+ * Fixed bug that did not allow a method to be properly injected into an object
3
+
1
4
  == 1.1 / 2008-05-20
2
5
  * Added 'method' directive for building a bounded method from an object defined in diy
3
6
 
data/Manifest.txt CHANGED
@@ -36,11 +36,14 @@ test/files/donkey/foo.rb
36
36
  test/files/donkey/foo/bar/qux.rb
37
37
  test/files/fud/objects.yml
38
38
  test/files/fud/toy.rb
39
+ test/files/functions/attached_things_builder.rb
39
40
  test/files/functions/invalid_method.yml
41
+ test/files/functions/method_extractor.rb
40
42
  test/files/functions/nonsingleton_objects.yml
41
43
  test/files/functions/objects.yml
42
44
  test/files/functions/thing.rb
43
45
  test/files/functions/thing_builder.rb
46
+ test/files/functions/things_builder.rb
44
47
  test/files/gnu/objects.yml
45
48
  test/files/gnu/thinger.rb
46
49
  test/files/goat/base.rb
data/README.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  == DIY
2
2
 
3
- * http://rubyforge.org/projects/atomicobjectrb/
4
3
  * http://atomicobjectrb.rubyforge.org/diy
4
+ * http://rubyforge.org/projects/atomicobjectrb/
5
5
 
6
6
  == DESCRIPTION:
7
7
 
@@ -182,9 +182,26 @@ a per-object override (handled in the context YAML):
182
182
  engine:
183
183
  auto_require: false
184
184
 
185
+ === Factories
186
+
187
+ It is possible to create factories automatically with DIY:
188
+
189
+ ---
190
+ car_dealer:
191
+ compose: car_factory
192
+
193
+ car_factory:
194
+ builds: car
195
+
196
+ Then you can use the factory to easily build objects:
197
+
198
+ context = DIY::Context.from_file('context.yml')
199
+ context[:car_factory].create => <Car:0x81eb0>
200
+
185
201
  === Method Directive
186
- This introduces the concept of first class methods. An object can now be constructed with a method bound to
187
- a particular object in the diy context.
202
+
203
+ This introduces the concept of first class methods. An object can now be constructed with a method object
204
+ bound to a particular object in the diy context.
188
205
 
189
206
  ---
190
207
  trinket_builder:
data/Rakefile CHANGED
@@ -1,6 +1,9 @@
1
1
  require 'rubygems'
2
2
  require 'hoe'
3
- require './lib/diy.rb'
3
+
4
+
5
+ $: << "lib"
6
+ require 'diy.rb'
4
7
 
5
8
  task :default => [ :test ]
6
9
 
@@ -9,7 +12,7 @@ Hoe.new('diy', DIY::VERSION) do |p|
9
12
  p.author = 'Atomic Object'
10
13
  p.email = 'dev@atomicobject.com'
11
14
  p.summary = 'Constructor-based dependency injection container using YAML input.'
12
- p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
15
+ p.description = p.paragraphs_of('README.txt', 3).join("\n\n")
13
16
  p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
14
17
  p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
15
18
  p.test_globs = 'test/*_test.rb'
data/lib/diy.rb CHANGED
@@ -1,9 +1,10 @@
1
+ require 'diy/factory.rb'
1
2
  require 'yaml'
2
3
  require 'set'
3
4
 
4
5
  module DIY #:nodoc:#
5
- VERSION = '1.1'
6
- class Context
6
+ VERSION = '1.1.1'
7
+ class Context
7
8
 
8
9
  class << self
9
10
  # Enable / disable automatic requiring of libraries. Default: true
@@ -13,118 +14,140 @@ module DIY #:nodoc:#
13
14
 
14
15
  # Accepts a Hash defining the object context (usually loaded from objects.yml), and an additional
15
16
  # Hash containing objects to inject into the context.
16
- def initialize(context_hash, extra_inputs={})
17
- raise "Nil context hash" unless context_hash
18
- raise "Need a hash" unless context_hash.kind_of?(Hash)
19
- [ "[]", "keys" ].each do |mname|
20
- unless extra_inputs.respond_to?(mname)
21
- raise "Extra inputs must respond to hash-like [] operator and methods #keys and #each"
22
- end
23
- end
24
-
25
- # store extra inputs
26
- if extra_inputs.kind_of?(Hash)
27
- @extra_inputs= {}
28
- extra_inputs.each { |k,v| @extra_inputs[k.to_s] = v } # smooth out the names
29
- else
30
- @extra_inputs = extra_inputs
31
- end
17
+ def initialize(context_hash, extra_inputs={})
18
+ raise "Nil context hash" unless context_hash
19
+ raise "Need a hash" unless context_hash.kind_of?(Hash)
20
+ [ "[]", "keys" ].each do |mname|
21
+ unless extra_inputs.respond_to?(mname)
22
+ raise "Extra inputs must respond to hash-like [] operator and methods #keys and #each"
23
+ end
24
+ end
25
+
26
+ # store extra inputs
27
+ if extra_inputs.kind_of?(Hash)
28
+ @extra_inputs= {}
29
+ extra_inputs.each { |k,v| @extra_inputs[k.to_s] = v } # smooth out the names
30
+ else
31
+ @extra_inputs = extra_inputs
32
+ end
32
33
 
33
34
  collect_object_and_subcontext_defs context_hash
34
35
 
35
- # init the cache
36
- @cache = {}
37
- @cache['this_context'] = self
38
- end
36
+ # init the cache
37
+ @cache = {}
38
+ @cache['this_context'] = self
39
+ end
39
40
 
40
41
 
41
42
  # Convenience: create a new DIY::Context by loading from a String (or open file handle.)
42
- def self.from_yaml(io_or_string, extra_inputs={})
43
- raise "nil input to YAML" unless io_or_string
44
- Context.new(YAML.load(io_or_string), extra_inputs)
45
- end
43
+ def self.from_yaml(io_or_string, extra_inputs={})
44
+ raise "nil input to YAML" unless io_or_string
45
+ Context.new(YAML.load(io_or_string), extra_inputs)
46
+ end
46
47
 
47
48
  # Convenience: create a new DIY::Context by loading from the named file.
48
- def self.from_file(fname, extra_inputs={})
49
- raise "nil file name" unless fname
50
- self.from_yaml(File.read(fname), extra_inputs)
51
- end
49
+ def self.from_file(fname, extra_inputs={})
50
+ raise "nil file name" unless fname
51
+ self.from_yaml(File.read(fname), extra_inputs)
52
+ end
52
53
 
53
54
  # Return a reference to the object named. If necessary, the object will
54
55
  # be instantiated on first use. If the object is non-singleton, a new
55
56
  # object will be produced each time.
56
- def get_object(obj_name)
57
- key = obj_name.to_s
58
- obj = @cache[key]
59
- unless obj
60
- if extra_inputs_has(key)
61
- obj = @extra_inputs[key]
62
- end
63
- end
64
- unless obj
65
- case @defs[key]
66
- when MethodDef
67
- @cache[key] = construct_method(key)
68
- else
69
- obj = construct_object(key)
70
- @cache[key] = obj if @defs[key].singleton?
71
- end
72
- end
73
- obj
74
- end
75
- alias :[] :get_object
57
+ def get_object(obj_name)
58
+ key = obj_name.to_s
59
+ obj = @cache[key]
60
+ unless obj
61
+ if extra_inputs_has(key)
62
+ obj = @extra_inputs[key]
63
+ else
64
+ case @defs[key]
65
+ when MethodDef
66
+ obj = construct_method(key)
67
+ when FactoryDef
68
+ obj = construct_factory(key)
69
+ @cache[key] = obj
70
+ else
71
+ obj = construct_object(key)
72
+ @cache[key] = obj if @defs[key].singleton?
73
+ end
74
+ end
75
+ end
76
+ obj
77
+ end
78
+ alias :[] :get_object
76
79
 
77
80
  # Inject a named object into the Context. This must be done before the Context has instantiated the
78
81
  # object in question.
79
- def set_object(obj_name,obj)
80
- key = obj_name.to_s
81
- raise "object '#{key}' already exists in context" if @cache.keys.include?(key)
82
- @cache[key] = obj
83
- end
84
- alias :[]= :set_object
82
+ def set_object(obj_name,obj)
83
+ key = obj_name.to_s
84
+ raise "object '#{key}' already exists in context" if @cache.keys.include?(key)
85
+ @cache[key] = obj
86
+ end
87
+ alias :[]= :set_object
85
88
 
86
89
  # Provide a listing of object names
87
- def keys
88
- (@defs.keys.to_set + @extra_inputs.keys.to_set).to_a
89
- end
90
+ def keys
91
+ (@defs.keys.to_set + @extra_inputs.keys.to_set).to_a
92
+ end
90
93
 
91
- # Instantiate and yield the named subcontext
92
- def within(sub_context_name)
93
- # Find the subcontext definitaion:
94
- context_def = @sub_context_defs[sub_context_name.to_s]
95
- raise "No sub-context named #{sub_context_name}" unless context_def
96
- # Instantiate a new context using self as parent:
97
- context = Context.new( context_def, self )
94
+ # Instantiate and yield the named subcontext
95
+ def within(sub_context_name)
96
+ # Find the subcontext definitaion:
97
+ context_def = @sub_context_defs[sub_context_name.to_s]
98
+ raise "No sub-context named #{sub_context_name}" unless context_def
99
+ # Instantiate a new context using self as parent:
100
+ context = Context.new( context_def, self )
98
101
 
99
- yield context
100
- end
102
+ yield context
103
+ end
101
104
 
102
105
  # Returns true if the context contains an object with the given name
103
- def contains_object(obj_name)
104
- key = obj_name.to_s
105
- @defs.keys.member?(key) or extra_inputs_has(key)
106
- end
106
+ def contains_object(obj_name)
107
+ key = obj_name.to_s
108
+ @defs.keys.member?(key) or extra_inputs_has(key)
109
+ end
107
110
 
108
111
  # Every top level object in the Context is instantiated. This is especially useful for
109
112
  # systems that have "floating observers"... objects that are never directly accessed, who
110
113
  # would thus never be instantiated by coincedence. This does not build any subcontexts
111
114
  # that may exist.
112
- def build_everything
113
- @defs.keys.each { |k| self[k] }
114
- end
115
- alias :build_all :build_everything
116
- alias :preinstantiate_singletons :build_everything
115
+ def build_everything
116
+ @defs.keys.each { |k| self[k] }
117
+ end
118
+ alias :build_all :build_everything
119
+ alias :preinstantiate_singletons :build_everything
117
120
 
118
- private
121
+ private
119
122
 
120
123
  def collect_object_and_subcontext_defs(context_hash)
121
- @defs = {}
122
- @sub_context_defs = {}
124
+ @defs = {}
125
+ @sub_context_defs = {}
123
126
  get_defs_from context_hash
124
127
  end
125
128
 
126
129
  def get_defs_from(hash, namespace=nil)
127
130
  hash.each do |name,info|
131
+ # we modify the info hash below so it's important to have a new
132
+ # instance to play with
133
+ info = info.dup if info
134
+
135
+ # see if we are building a factory
136
+ if info and info.has_key?('builds')
137
+ unless info.has_key?('auto_require')
138
+ info['auto_require'] = self.class.auto_require
139
+ end
140
+
141
+ if namespace
142
+ info['builds'] = namespace.build_classname(info['builds'])
143
+ end
144
+ @defs[name] = FactoryDef.new({:name => name,
145
+ :target => info['builds'],
146
+ :library => info['library'],
147
+ :auto_require => info['auto_require']})
148
+ next
149
+ end
150
+
128
151
  name = name.to_s
129
152
  case name
130
153
  when /^\+/
@@ -135,11 +158,12 @@ module DIY #:nodoc:#
135
158
  # namespace: use a module(s) prefix for the classname of contained object defs
136
159
  # NOTE: namespacing is NOT scope... it's just a convenient way to setup class names for a group of objects.
137
160
  get_defs_from info, parse_namespace(name)
138
- when /^method/
161
+ when /^method\s/
139
162
  key_name = name.gsub(/^method\s/, "")
140
163
  @defs[key_name] = MethodDef.new(:name => key_name,
141
164
  :object => info['object'],
142
- :method => info['method'])
165
+ :method => info['method'],
166
+ :attach => info['attach'])
143
167
  else
144
168
  # Normal object def
145
169
  info ||= {}
@@ -163,69 +187,83 @@ module DIY #:nodoc:#
163
187
  end
164
188
  end
165
189
 
166
-
167
190
  def construct_method(key)
168
191
  method_definition = @defs[key]
169
192
  object = get_object(method_definition.object)
170
- return object.method(method_definition.method)
193
+ method = object.method(method_definition.method)
194
+
195
+ unless method_definition.attach.nil?
196
+ instance_var_name = "@__diy_#{method_definition.object}"
197
+
198
+ method_definition.attach.each do |object_key|
199
+ get_object(object_key).instance_eval do
200
+ instance_variable_set(instance_var_name, object)
201
+ eval %|def #{key}(*args)
202
+ #{instance_var_name}.#{method_definition.method}(*args)
203
+ end|
204
+ end
205
+ end
206
+ end
207
+
208
+ return method
171
209
  rescue Exception => oops
172
210
  build_and_raise_construction_error(key, oops)
173
211
  end
174
212
 
175
- def construct_object(key)
176
- # Find the object definition
177
- obj_def = @defs[key]
178
- raise "No object definition for '#{key}'" unless obj_def
179
- # If object def mentions a library, load it
180
- require obj_def.library if obj_def.library
181
-
182
- # Resolve all components for the object
183
- arg_hash = {}
184
- obj_def.components.each do |name,value|
185
- case value
186
- when Lookup
187
- arg_hash[name.to_sym] = get_object(value.name)
188
- when StringValue
189
- arg_hash[name.to_sym] = value.literal_value
190
- else
191
- raise "Cannot cope with component definition '#{value.inspect}'"
192
- end
193
- end
194
- # Get a reference to the class for the object
195
- big_c = get_class_for_name_with_module_delimeters(obj_def.class_name)
196
- # Make and return the instance
213
+ def construct_object(key)
214
+ # Find the object definition
215
+ obj_def = @defs[key]
216
+ raise "No object definition for '#{key}'" unless obj_def
217
+ # If object def mentions a library, load it
218
+ require obj_def.library if obj_def.library
219
+
220
+ # Resolve all components for the object
221
+ arg_hash = {}
222
+ obj_def.components.each do |name,value|
223
+ case value
224
+ when Lookup
225
+ arg_hash[name.to_sym] = get_object(value.name)
226
+ when StringValue
227
+ arg_hash[name.to_sym] = value.literal_value
228
+ else
229
+ raise "Cannot cope with component definition '#{value.inspect}'"
230
+ end
231
+ end
232
+ # Get a reference to the class for the object
233
+ big_c = get_class_for_name_with_module_delimeters(obj_def.class_name)
234
+ # Make and return the instance
197
235
  if obj_def.use_class_directly?
198
236
  return big_c
199
237
  elsif arg_hash.keys.size > 0
200
- return big_c.new(arg_hash)
201
- else
202
- return big_c.new
203
- end
204
- rescue Exception => oops
205
- build_and_raise_construction_error(key, oops)
206
- end
207
-
208
- def build_and_raise_construction_error(key, oops)
209
- cerr = ConstructionError.new(key,oops)
210
- cerr.set_backtrace(oops.backtrace)
211
- raise cerr
212
- end
213
-
214
- def get_class_for_name_with_module_delimeters(class_name)
215
- class_name.split(/::/).inject(Object) do |mod,const_name| mod.const_get(const_name) end
216
- end
217
-
218
- def extra_inputs_has(key)
219
- if key.nil? or key.strip == ''
220
- raise ArgumentError.new("Cannot lookup objects with nil keys")
221
- end
222
- @extra_inputs.keys.member?(key) or @extra_inputs.keys.member?(key.to_sym)
223
- end
238
+ return big_c.new(arg_hash)
239
+ else
240
+ return big_c.new
241
+ end
242
+ rescue Exception => oops
243
+ build_and_raise_construction_error(key, oops)
244
+ end
245
+
246
+ def build_and_raise_construction_error(key, oops)
247
+ cerr = ConstructionError.new(key,oops)
248
+ cerr.set_backtrace(oops.backtrace)
249
+ raise cerr
250
+ end
251
+
252
+ def get_class_for_name_with_module_delimeters(class_name)
253
+ class_name.split(/::/).inject(Object) do |mod,const_name| mod.const_get(const_name) end
254
+ end
255
+
256
+ def extra_inputs_has(key)
257
+ if key.nil? or key.strip == ''
258
+ raise ArgumentError.new("Cannot lookup objects with nil keys")
259
+ end
260
+ @extra_inputs.keys.member?(key) or @extra_inputs.keys.member?(key.to_sym)
261
+ end
224
262
 
225
263
  def parse_namespace(str)
226
264
  Namespace.new(str)
227
265
  end
228
- end
266
+ end
229
267
 
230
268
  class Namespace #:nodoc:#
231
269
  def initialize(str)
@@ -248,69 +286,69 @@ module DIY #:nodoc:#
248
286
  end
249
287
  end
250
288
 
251
- class Lookup #:nodoc:
252
- attr_reader :name
253
- def initialize(obj_name)
254
- @name = obj_name
255
- end
256
- end
289
+ class Lookup #:nodoc:
290
+ attr_reader :name
291
+ def initialize(obj_name)
292
+ @name = obj_name
293
+ end
294
+ end
257
295
 
258
- class MethodDef
259
- attr_accessor :name, :object, :method
296
+ class MethodDef #:nodoc:
297
+ attr_accessor :name, :object, :method, :attach
260
298
 
261
299
  def initialize(opts)
262
- @name, @object, @method = opts[:name], opts[:object], opts[:method]
300
+ @name, @object, @method, @attach = opts[:name], opts[:object], opts[:method], opts[:attach]
263
301
  end
264
302
  end
265
303
 
266
- class ObjectDef #:nodoc:
267
- attr_accessor :name, :class_name, :library, :components
268
- def initialize(opts)
304
+ class ObjectDef #:nodoc:
305
+ attr_accessor :name, :class_name, :library, :components
306
+ def initialize(opts)
269
307
  name = opts[:name]
270
308
  raise "Can't make an ObjectDef without a name" if name.nil?
271
309
 
272
310
  info = opts[:info] || {}
273
311
  info = info.clone
274
312
 
275
- @components = {}
313
+ @components = {}
276
314
 
277
- # Object name
278
- @name = name
315
+ # Object name
316
+ @name = name
279
317
 
280
- # Class name
281
- @class_name = info.delete 'class'
282
- @class_name ||= info.delete 'type'
283
- @class_name ||= Infl.camelize(@name)
318
+ # Class name
319
+ @class_name = info.delete 'class'
320
+ @class_name ||= info.delete 'type'
321
+ @class_name ||= Infl.camelize(@name)
284
322
 
285
323
  # Auto Require
286
324
  @auto_require = info.delete 'auto_require'
287
325
 
288
- # Library
289
- @library = info.delete 'library'
290
- @library ||= info.delete 'lib'
291
- @library ||= Infl.underscore(@class_name) if @auto_require
292
-
293
- # Use Class Directly
294
- @use_class_directly = info.delete 'use_class_directly'
295
-
296
- # Auto-compose
297
- compose = info.delete 'compose'
298
- if compose
299
- case compose
300
- when Array
301
- auto_names = compose.map { |x| x.to_s }
302
- when String
303
- auto_names = compose.split(',').map { |x| x.to_s.strip }
304
- when Symbol
305
- auto_names = [ compose.to_s ]
306
- else
307
- raise "Cannot auto compose object #{@name}, bad 'compose' format: #{compose.inspect}"
308
- end
309
- end
310
- auto_names ||= []
311
- auto_names.each do |cname|
312
- @components[cname] = Lookup.new(cname)
313
- end
326
+ # Library
327
+ @library = info.delete 'library'
328
+ @library ||= info.delete 'lib'
329
+ @library ||= Infl.underscore(@class_name) if @auto_require
330
+
331
+ # Use Class Directly
332
+ @use_class_directly = info.delete 'use_class_directly'
333
+
334
+ # Auto-compose
335
+ compose = info.delete 'compose'
336
+ if compose
337
+ case compose
338
+ when Array
339
+ auto_names = compose.map { |x| x.to_s }
340
+ when String
341
+ auto_names = compose.split(',').map { |x| x.to_s.strip }
342
+ when Symbol
343
+ auto_names = [ compose.to_s ]
344
+ else
345
+ raise "Cannot auto compose object #{@name}, bad 'compose' format: #{compose.inspect}"
346
+ end
347
+ end
348
+ auto_names ||= []
349
+ auto_names.each do |cname|
350
+ @components[cname] = Lookup.new(cname)
351
+ end
314
352
 
315
353
  # Singleton status
316
354
  if info['singleton'].nil?
@@ -320,12 +358,12 @@ module DIY #:nodoc:#
320
358
  end
321
359
  info.delete 'singleton'
322
360
 
323
- # Remaining keys
324
- info.each do |key,val|
325
- @components[key.to_s] = Lookup.new(val.to_s)
326
- end
361
+ # Remaining keys
362
+ info.each do |key,val|
363
+ @components[key.to_s] = Lookup.new(val.to_s)
364
+ end
327
365
 
328
- end
366
+ end
329
367
 
330
368
  def singleton?
331
369
  @singleton
@@ -335,19 +373,19 @@ module DIY #:nodoc:#
335
373
  @use_class_directly == true
336
374
  end
337
375
 
338
- end
339
-
340
- class ConstructionError < RuntimeError #:nodoc:#
341
- def initialize(object_name, cause=nil)
342
- object_name = object_name
343
- cause = cause
344
- m = "Failed to construct '#{object_name}'"
345
- if cause
346
- m << "\n ...caused by:\n >>> #{cause}"
347
- end
348
- super m
349
- end
350
- end
376
+ end
377
+
378
+ class ConstructionError < RuntimeError #:nodoc:#
379
+ def initialize(object_name, cause=nil)
380
+ object_name = object_name
381
+ cause = cause
382
+ m = "Failed to construct '#{object_name}'"
383
+ if cause
384
+ m << "\n ...caused by:\n >>> #{cause}"
385
+ end
386
+ super m
387
+ end
388
+ end
351
389
 
352
390
  class NamespaceError < RuntimeError #:nodoc:#
353
391
  end
data/test/diy_test.rb CHANGED
@@ -540,7 +540,7 @@ class DIYTest < Test::Unit::TestCase
540
540
 
541
541
  assert_not_nil build_thing, "should not be nil"
542
542
  assert_kind_of(Method, build_thing)
543
- assert_same(build_thing, @diy['build_thing'])
543
+ assert_equal(build_thing, @diy['build_thing'])
544
544
  end
545
545
 
546
546
  def test_bounded_method_can_be_used
@@ -564,6 +564,12 @@ class DIYTest < Test::Unit::TestCase
564
564
  assert_not_equal(@diy['build_thing'], @diy['thing_builder'].method(:build))
565
565
  end
566
566
 
567
+ def test_composing_bounded_methods_into_other_objects
568
+ load_context "functions/objects.yml"
569
+ @diy.build_everything
570
+ assert_equal(@diy['build_thing'], @diy['things_builder'].build_thing)
571
+ end
572
+
567
573
  def test_raises_construction_error_if_invalid_method_specified
568
574
  load_context "functions/invalid_method.yml"
569
575
  assert_raises DIY::ConstructionError do
@@ -571,22 +577,26 @@ class DIYTest < Test::Unit::TestCase
571
577
  end
572
578
  end
573
579
 
580
+ def test_can_optionally_attach_method_to_other_objects_in_context
581
+ load_context "functions/objects.yml"
582
+ @diy.build_everything
583
+
584
+ thing = @diy['attached_things_builder'].build_thing("the name", "flying")
585
+ assert_kind_of(Thing, thing)
586
+ assert_equal("the name", thing.name)
587
+ assert_equal("flying", thing.ability)
588
+
589
+ ["attached_things_builder", "things_builder"].each do |key|
590
+ thing = @diy[key].build_default_thing
591
+ assert_kind_of(Thing, thing)
592
+ assert_equal("Thing", thing.name)
593
+ assert_equal("nothing", thing.ability)
594
+ end
595
+ end
596
+
574
597
  #
575
598
  # HELPERS
576
599
  #
577
- def path_to_test_file(fname)
578
- path_to("/files/#{fname}")
579
- end
580
-
581
- def load_context(file_name)
582
- hash = YAML.load(File.read(path_to_test_file(file_name)))
583
- load_hash(hash)
584
- end
585
-
586
- def load_hash(hash)
587
- @diy = DIY::Context.new(hash)
588
- end
589
-
590
600
  def check_dog_objects(context)
591
601
  assert_not_nil context, "nil context"
592
602
  names = %w|dog_presenter dog_model dog_view file_resolver|
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + "/test_helper"
2
+ require 'diy'
3
+ require 'fileutils'
4
+ include FileUtils
5
+
6
+ class FactoryTest < Test::Unit::TestCase
7
+
8
+ def setup
9
+ # Add load paths:
10
+ %w|factory|.each do |p|
11
+ libdir = path_to_test_file(p)
12
+ $: << libdir unless $:.member?(libdir)
13
+ end
14
+ DIY::Context.auto_require = true # Restore default
15
+ end
16
+
17
+
18
+ #
19
+ # TESTS
20
+ #
21
+
22
+ def test_creates_factory
23
+ load_context "factory/factory.yml"
24
+
25
+ cat_factory = @diy.get_object(:cat_factory)
26
+ assert_not_nil cat_factory
27
+
28
+ cat = cat_factory.create('a', 'b')
29
+
30
+ assert cat.is_a?(Kitten)
31
+ assert_equal "meow", cat.meow
32
+ assert_equal 'a', cat.a
33
+ assert_equal 'b', cat.b
34
+ end
35
+
36
+ def test_creates_factory_with_autorequire
37
+ load_context "factory/factory.yml"
38
+
39
+ dog_factory = @diy.get_object(:dog_factory)
40
+ assert_not_nil dog_factory
41
+
42
+ dog = dog_factory.create
43
+
44
+ assert dog.is_a?(Dog)
45
+ assert_equal "woof", dog.woof
46
+ end
47
+
48
+ def test_creates_factory_with_subcontext
49
+ load_context "factory/factory.yml"
50
+
51
+ @diy.within :inny do |context|
52
+ bull_factory = context.get_object(:bull_factory)
53
+ beef = bull_factory.create
54
+ end
55
+ end
56
+
57
+ def test_creates_factory_with_subcontext_and_namespace
58
+ load_context "factory/factory.yml"
59
+
60
+ @diy.within :congress do |context|
61
+ politician = context.get_object(:politician)
62
+ pork = politician.create
63
+ assert pork.is_a?(Farm::Pork)
64
+ assert_equal "money!", pork.oink
65
+ end
66
+ end
67
+
68
+ def test_creates_factory_with_namespace
69
+ load_context "factory/factory.yml"
70
+
71
+ llama_factory = @diy.get_object(:llama_factory)
72
+ assert_not_nil llama_factory
73
+
74
+ llama = llama_factory.create
75
+
76
+ assert llama.is_a?(Farm::Llama)
77
+ assert_equal "?", llama.make_llama_noise
78
+ end
79
+ end
@@ -0,0 +1,2 @@
1
+ class AttachedThingsBuilder
2
+ end
@@ -0,0 +1,3 @@
1
+ class MethodExtractor
2
+
3
+ end
@@ -1,5 +1,22 @@
1
1
  thing_builder:
2
2
 
3
+ method_extractor:
4
+
5
+ attached_things_builder:
6
+
3
7
  method build_thing:
4
8
  object: thing_builder
5
- method: build
9
+ method: build
10
+ attach:
11
+ - attached_things_builder
12
+
13
+ method build_default_thing:
14
+ object: thing_builder
15
+ method: build_default
16
+ attach:
17
+ - attached_things_builder
18
+ - things_builder
19
+
20
+ things_builder:
21
+ compose:
22
+ - build_thing
@@ -17,5 +17,9 @@ class ThingBuilder
17
17
 
18
18
  def build(name, ability)
19
19
  Thing.new(:name => name, :ability => ability)
20
+ end
21
+
22
+ def build_default
23
+ Thing.new(:name => "Thing", :ability => "nothing")
20
24
  end
21
25
  end
@@ -0,0 +1,3 @@
1
+ class ThingsBuilder
2
+ constructor :build_thing, :accessors => true
3
+ end
data/test/test_helper.rb CHANGED
@@ -36,4 +36,20 @@ class Test::Unit::TestCase
36
36
  @_tracked_tests[msym] = true
37
37
  end
38
38
  end
39
+
40
+ #
41
+ # HELPERS
42
+ #
43
+ def path_to_test_file(fname)
44
+ path_to("/files/#{fname}")
45
+ end
46
+
47
+ def load_context(file_name)
48
+ hash = YAML.load(File.read(path_to_test_file(file_name)))
49
+ load_hash(hash)
50
+ end
51
+
52
+ def load_hash(hash)
53
+ @diy = DIY::Context.new(hash)
54
+ end
39
55
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diy
3
3
  version: !ruby/object:Gem::Version
4
- version: "1.1"
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Atomic Object
@@ -9,11 +9,12 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-05-20 00:00:00 -04:00
12
+ date: 2009-11-01 01:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: constructor
17
+ type: :runtime
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
@@ -23,14 +24,17 @@ dependencies:
23
24
  version:
24
25
  - !ruby/object:Gem::Dependency
25
26
  name: hoe
27
+ type: :development
26
28
  version_requirement:
27
29
  version_requirements: !ruby/object:Gem::Requirement
28
30
  requirements:
29
31
  - - ">="
30
32
  - !ruby/object:Gem::Version
31
- version: 1.5.1
33
+ version: 2.3.3
32
34
  version:
33
- description: "== DESCRIPTION: DIY (Dependency Injection in YAML) is a simple dependency injection library which focuses on declarative composition of objects through constructor injection. == INSTALL: * gem install diy"
35
+ description: |-
36
+ DIY (Dependency Injection in YAML) is a simple dependency injection library
37
+ which focuses on declarative composition of objects through constructor injection.
34
38
  email: dev@atomicobject.com
35
39
  executables: []
36
40
 
@@ -42,7 +46,6 @@ extra_rdoc_files:
42
46
  - README.txt
43
47
  - TODO.txt
44
48
  - homepage/Notes.txt
45
- - test/files/namespace/hello.txt
46
49
  files:
47
50
  - History.txt
48
51
  - Manifest.txt
@@ -82,11 +85,14 @@ files:
82
85
  - test/files/donkey/foo/bar/qux.rb
83
86
  - test/files/fud/objects.yml
84
87
  - test/files/fud/toy.rb
88
+ - test/files/functions/attached_things_builder.rb
85
89
  - test/files/functions/invalid_method.yml
90
+ - test/files/functions/method_extractor.rb
86
91
  - test/files/functions/nonsingleton_objects.yml
87
92
  - test/files/functions/objects.yml
88
93
  - test/files/functions/thing.rb
89
94
  - test/files/functions/thing_builder.rb
95
+ - test/files/functions/things_builder.rb
90
96
  - test/files/gnu/objects.yml
91
97
  - test/files/gnu/thinger.rb
92
98
  - test/files/goat/base.rb
@@ -132,6 +138,8 @@ files:
132
138
  - test/test_helper.rb
133
139
  has_rdoc: true
134
140
  homepage:
141
+ licenses: []
142
+
135
143
  post_install_message:
136
144
  rdoc_options:
137
145
  - --main
@@ -153,9 +161,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
161
  requirements: []
154
162
 
155
163
  rubyforge_project: atomicobjectrb
156
- rubygems_version: 1.1.1
164
+ rubygems_version: 1.3.5
157
165
  signing_key:
158
- specification_version: 2
166
+ specification_version: 3
159
167
  summary: Constructor-based dependency injection container using YAML input.
160
168
  test_files:
161
169
  - test/diy_test.rb
170
+ - test/factory_test.rb