diy 1.1 → 1.1.1

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/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