wirer 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,234 @@
1
+ # dependency :foo, Class, :features => [:feature, :feature], :optional => true
2
+ # dependency :foo, Class, :features => [:feature, :feature], :multiple => true
3
+ #
4
+ # dependency :foo, Class, :optional => true
5
+ # dependency :foo, :feature, :another_feature, :optional => true, :constructor => true
6
+
7
+ module Wirer
8
+
9
+ class Dependency
10
+ def self.new_from_args(*args)
11
+ new(normalise_args(*args))
12
+ end
13
+
14
+ def self.new_from_arg_or_args_list(arg_or_args_list)
15
+ new(normalise_arg_or_args_list(arg_or_args_list))
16
+ end
17
+
18
+ def self.normalise_arg_or_args_list(arg_or_args_list)
19
+ case arg_or_args_list
20
+ when Hash then arg_or_args_list
21
+ when Array then normalise_args(*arg_or_args_list)
22
+ else normalise_args(arg_or_args_list)
23
+ end
24
+ end
25
+
26
+ def self.normalise_args(*args)
27
+ options = args.last.is_a?(Hash) ? args.pop : {}
28
+ args.each do |requirement|
29
+ case requirement
30
+ when Module then options[:class] = requirement
31
+ else (options[:features] ||= []) << requirement
32
+ end
33
+ end
34
+ options
35
+ end
36
+
37
+ OPTION_NAMES = [:class, :module, :features, :prefer, :multiple, :optional, :factory]
38
+
39
+ # By default, dependencies will :prefer => :default.
40
+ # This means if you want to force one factory to be preferred over another
41
+ # in a given situation, you can just add (or wrap it with) a provided feature
42
+ # name of :default.
43
+ PREFER_DEFAULT = :default
44
+
45
+ def initialize(options = {})
46
+ required_class = options[:class] || options[:module]
47
+ case required_class
48
+ when Module
49
+ @required_class = required_class
50
+ when String
51
+ @required_class_name = required_class
52
+ when NilClass
53
+ @required_class = nil
54
+ else
55
+ raise ArgumentError, "required :class for a Dependency must be a Module or Class, or a String name of a Module or Class"
56
+ end
57
+ @required_features = options[:features] && [*options[:features]]
58
+ @multiple = options[:multiple] || false
59
+ @optional = options[:optional] || false
60
+ @factory = options[:factory] || false
61
+
62
+ if @multiple
63
+ raise ArgumentError, "preferred features don't make sense for a :multiple depedency" if options.has_key?(:prefer)
64
+ else
65
+ @preferred_features = [*options.fetch(:prefer, PREFER_DEFAULT) || []]
66
+ end
67
+ end
68
+
69
+ attr_reader :required_features, :preferred_features
70
+ def multiple?; @multiple; end
71
+ def optional?; @optional; end
72
+
73
+ # Specifying :factory => true on a dependency means that you won't be given an actual instance
74
+ # of the class you asked for, but rather you'll be given a simple factory wrapper which allows
75
+ # you to construct your own instance(s), with any dependencies pre-supplied.
76
+ # See Factory::CurriedDependencies
77
+ def factory?; @factory; end
78
+
79
+ # A string class name may be supplied as the :class arg to the constructor, in which case we only
80
+ # attempt to resolve the actual class from it the first time .required_class is requested.
81
+ #
82
+ # This helps avoid introducing undesired load order dependencies between classes using Wirer::Factory::ClassDSL.
83
+ def required_class
84
+ return @required_class if defined?(@required_class)
85
+ @required_class = @required_class_name.split("::").inject(Object, :const_get)
86
+ end
87
+
88
+ def requirements_to_s
89
+ [
90
+ begin
91
+ case required_class
92
+ when ::Class then "class #{@required_class}"
93
+ when ::Module then "module #{@required_class}"
94
+ end
95
+ rescue NameError
96
+ "class or module name #{@required_class_name} (not currently loaded!)"
97
+ end,
98
+ @required_features && "features #{@required_features.inspect}"
99
+ ].compact.join(" and ")
100
+ end
101
+
102
+ def inspect
103
+ description = [
104
+ @factory ? "factory for #{requirements_to_s}" : requirements_to_s,
105
+ ("optional" if @optional),
106
+ ("multiple" if @multiple),
107
+ ("preferring features #{@preferred_features.inspect}" if @preferred_features && !@preferred_features.empty?)
108
+ ].compact.join(', ')
109
+ "#<#{self.class} on #{description}>"
110
+ end
111
+
112
+ def match_factories(available_factories)
113
+ candidates = available_factories.select {|f| self === f}
114
+ if !@optional && candidates.length == 0
115
+ raise DependencyFindingError, "No available factories matching #{requirements_to_s}"
116
+ end
117
+ if @multiple
118
+ candidates.map! {|c| yield c} if block_given?; candidates
119
+ else
120
+ candidate = if candidates.length > 1
121
+ if @preferred_features.empty?
122
+ raise DependencyFindingError, "More than one factory available matching #{requirements_to_s}"
123
+ else
124
+ unique_preferred_factory(candidates)
125
+ end
126
+ else
127
+ candidates.first
128
+ end
129
+ if block_given?
130
+ yield candidate if candidate
131
+ else
132
+ candidate
133
+ end
134
+ end
135
+ end
136
+
137
+ def ===(factory)
138
+ factory.is_a?(Factory::Interface) && matches_required_class(factory) && matches_required_features(factory)
139
+ end
140
+
141
+ def check_argument(argument_name, argument, strict_type_checks=false)
142
+ if @multiple
143
+ raise ArgumentError, "expected Array for argument #{argument_name}" unless argument.is_a?(Array)
144
+ if argument.empty?
145
+ if @optional then return else
146
+ raise ArgumentError, "expected at least one value for argument #{argument_name}"
147
+ end
148
+ end
149
+ argument.each do |value|
150
+ if @factory
151
+ unless value.respond_to?(:new)
152
+ raise ArgumentError, "expected Array of factory-like objects which respond_to?(:new) for argument #{argument_name}"
153
+ end
154
+ elsif strict_type_checks && required_class && !value.is_a?(required_class)
155
+ raise ArgumentError, "expected Array of #{required_class} for argument #{argument_name}"
156
+ end
157
+ end
158
+ else
159
+ if argument.nil?
160
+ if @optional then return else
161
+ raise ArgumentError, "expected argument #{argument_name}"
162
+ end
163
+ end
164
+ if @factory
165
+ unless argument.respond_to?(:new)
166
+ raise ArgumentError, "expected factory-like object which respond_to?(:new) for argument #{argument_name}"
167
+ end
168
+ elsif strict_type_checks && required_class && !argument.is_a?(required_class)
169
+ raise ArgumentError, "expected #{required_class} for argument #{argument_name}"
170
+ end
171
+ end
172
+ end
173
+
174
+ # if the required_class can't be resolved (ie a class of that name doesn't even exist) then nothing will match.
175
+ def matches_required_class(factory)
176
+ begin
177
+ !required_class || factory.provides_class <= required_class
178
+ rescue NameError
179
+ false
180
+ end
181
+ end
182
+
183
+ def matches_required_features(factory)
184
+ !@required_features || @required_features.all? {|feature| factory.provides_features.include?(feature)}
185
+ end
186
+
187
+ def with_options(options)
188
+ new_options = {
189
+ :multiple => @multiple,
190
+ :optional => @optional,
191
+ :class => required_class,
192
+ :features => @required_features,
193
+ :prefer => @preferred_features
194
+ }
195
+ new_required_class = options[:class] and begin
196
+ if required_class && !(new_required_class <= required_class)
197
+ raise "Required class #{new_required_class} not compatible with existing requirement for #{required_class}"
198
+ end
199
+ new_options[:class] = new_required_class
200
+ end
201
+ new_required_features = options[:features] and begin
202
+ new_options[:features] ||= []
203
+ new_options[:features] |= [*new_required_features]
204
+ end
205
+ new_preferred_features = options[:prefer] and begin
206
+ new_options[:prefer] ||= []
207
+ new_options[:prefer] |= [*new_preferred_features]
208
+ end
209
+ self.class.new(new_options)
210
+ end
211
+
212
+ private
213
+
214
+ def unique_preferred_factory(candidates)
215
+ max_preferred_features_count = 0
216
+ winners = []
217
+ candidates.each do |candidate|
218
+ provided = candidate.provides_features
219
+ count = @preferred_features.count {|f| provided.include?(f)}
220
+ if count > max_preferred_features_count
221
+ max_preferred_features_count = count
222
+ winners = [candidate]
223
+ elsif count == max_preferred_features_count
224
+ winners << candidate
225
+ end
226
+ end
227
+ if winners.length > 1
228
+ raise DependencyFindingError,
229
+ "More than one factory available matching #{requirements_to_s}, and tie can't be resolved using preferred_features #{@preferred_features.inspect}"
230
+ end
231
+ winners.first
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,30 @@
1
+ module Wirer
2
+
3
+ # Thank you to http://rubyforge.org/projects/nestegg for the pattern
4
+ class Error < StandardError
5
+
6
+ attr_reader :cause
7
+ alias :wrapped_error :cause
8
+
9
+ def initialize(msg, cause=nil)
10
+ @cause = cause
11
+ super(msg)
12
+ end
13
+
14
+ def set_backtrace(bt)
15
+ if cause
16
+ bt << "cause: #{cause.class.name}: #{cause}"
17
+ bt.concat cause.backtrace
18
+ end
19
+ super(bt)
20
+ end
21
+
22
+ end
23
+
24
+ class DependencyFindingError < Error; end
25
+ class CyclicDependencyError < Error; end
26
+
27
+ # raised when a dependency could be found, but failed to be constructed.
28
+ class DependencyConstructionError < Error; end
29
+
30
+ end
@@ -0,0 +1,117 @@
1
+ module Wirer
2
+ # You can extend a Class instance directly with this if you want
3
+ # the class itself to be usable as a factory, exposing
4
+ # Wirer::Factory::Interface.
5
+ #
6
+ # By default, new_from_dependencies will call new on the class
7
+ # with the hash of dependencies as the last argument (or merged
8
+ # into the last argument where this is already a Hash). If you
9
+ # don't like this you may want to override the new_from_dependencies
10
+ # class method.
11
+ #
12
+ # You'll still probably want to override some of
13
+ # constructor_dependencies, provides_features, setter_dependencies;
14
+ # if you'd prefer to do this via a handy DSL, instead see
15
+ # See Wirer::Factory::ClassDSL.
16
+ module Factory::ClassMixin
17
+ include Factory::Interface
18
+
19
+ def provides_class; self; end
20
+
21
+ def new_from_dependencies(dependencies, *other_args, &block_arg)
22
+ if other_args.last.is_a?(Hash)
23
+ hash_arg = other_args.pop
24
+ other_args.push(hash_arg.merge(dependencies))
25
+ else
26
+ other_args.push(dependencies) unless dependencies.empty?
27
+ end
28
+ new(*other_args, &block_arg)
29
+ end
30
+ end
31
+
32
+ # A more convenient form of Wirer::Factory::ClassMixin, this additionally adds some
33
+ # private DSL methods which let you declare your constructor_dependencies,
34
+ # setter_dependencies and provides_features.
35
+ #
36
+ # The DSL works nicely with respect to subclassing, so you can add extra dependencies
37
+ # or features in a subclass.
38
+ module Factory::ClassDSL
39
+ include Factory::ClassMixin
40
+
41
+ def constructor_dependencies
42
+ @constructor_dependencies ||= (superclass.is_a?(Factory::Interface) ? superclass.constructor_dependencies.dup : {})
43
+ end
44
+
45
+ # the supplied implementation of setter_dependencies does not allow for them varying dependening on the
46
+ # instance passed; if you want to specify setter_dependencies on an instance-sensitive basis you'll need
47
+ # to override this yourself.
48
+ def setter_dependencies(instance=nil)
49
+ @setter_dependencies ||= (superclass.is_a?(Factory::Interface) ? superclass.setter_dependencies.dup : {})
50
+ end
51
+
52
+ def provides_features
53
+ @provides_features ||= (superclass.is_a?(Factory::Interface) ? superclass.provides_features.dup : [])
54
+ end
55
+
56
+ private
57
+
58
+ def provides_feature(*args)
59
+ provides_features.concat(args)
60
+ end
61
+
62
+ def add_dependency(type, arg_name, *dependency_args)
63
+ deps_hash = type == :setter ? setter_dependencies : constructor_dependencies
64
+ deps_hash[arg_name] = Dependency.new_from_args(*dependency_args)
65
+ end
66
+
67
+ # as a convenience, will additionally define an attr_reader for this dependency name
68
+ # unless you specify :getter => false. by default it will be private, but :getter => :public
69
+ # will make it public.
70
+ def constructor_dependency(name, *args)
71
+ getter = if args.last.is_a?(Hash) then args.last.delete(:getter) end
72
+ if getter != false
73
+ attr_reader(name)
74
+ getter == :public ? public(name) : private(name)
75
+ end
76
+ add_dependency(:constructor, name, *args)
77
+ end
78
+ alias :dependency :constructor_dependency
79
+
80
+ # sugar for dependency :foo, ..., :factory => true.
81
+ #
82
+ # for factory dependencies, you'll be given a factory instance responding to 'new'
83
+ # from which you can construct your own instances of the dependency -- as opposed
84
+ # to normal dependencies where the container will give you a pre-constructed instance.
85
+ def factory_dependency(name, *args)
86
+ args.push({}) unless args.last.is_a?(Hash)
87
+ args.last[:factory] = true
88
+ constructor_dependency(name, *args)
89
+ end
90
+
91
+ # will additionally define a attr_writer method of this name, unless :setter => false
92
+ # is specified. this is private by default but made public if you specify :setter => :public.
93
+ #
94
+ # and a corresponding public attr_reader too if :accessor => true if specified.
95
+ def setter_dependency(name, *args)
96
+ options = args.last.is_a?(Hash) ? args.last : {}
97
+ getter = options.delete(:getter)
98
+ setter = options.delete(:setter)
99
+ if setter != false
100
+ attr_writer(name)
101
+ setter == :public ? public(:"#{name}=") : private(:"#{name}=")
102
+ end
103
+ if getter != false
104
+ attr_reader(name)
105
+ getter == :public ? public(name) : private(name)
106
+ end
107
+
108
+ add_dependency(:setter, name, *args)
109
+ end
110
+ end
111
+ end
112
+
113
+ class Class
114
+ def wireable
115
+ extend Wirer::Factory::ClassDSL
116
+ end
117
+ end
@@ -0,0 +1,33 @@
1
+ module Wirer
2
+ # This doesn't implement the full Factory::Interface, rather it's a simple
3
+ # 'curried' wrapper around the new_from_dependencies method of a factory,
4
+ # where the dependency arguments are pre-supplied.
5
+ #
6
+ # You can use one of these pretty much the same as a class, in that it has a
7
+ # 'new' method, or it also implements a Proc-like interface ('call' and 'to_proc')
8
+ # so you can also treat it like a block which constructs things.
9
+ #
10
+ # Factory::Interface#curry_with_dependencies is used to make these, but you'd
11
+ # normally get one via a Wirer::Containerm by specifying a dependency with :factory => true;
12
+ # the container will then give you a curried factory from which you can construct
13
+ # your own instances, rather than supplying a single pre-constructed instance.
14
+ #
15
+ # Note: at present only constructor dependencies can be curried in this way.
16
+ class Factory::CurriedDependencies
17
+ def initialize(factory, dependencies)
18
+ @factory = factory
19
+ @dependencies = dependencies
20
+ end
21
+
22
+ def new(*args, &block_arg)
23
+ @factory.new_from_dependencies(@dependencies, *args, &block_arg)
24
+ end
25
+
26
+ alias :call :new
27
+
28
+ # this allows it to be implicitly converted into a block argument, eg: instances = args.map(&factory)
29
+ def to_proc
30
+ method(:new).to_proc
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,100 @@
1
+ module Wirer
2
+ # This is handy if you want to create a factory instance entirely from
3
+ # supplied arguments, in particular from a supplied constructor block.
4
+ #
5
+ # This saves you having to create your own Factory class in almost all
6
+ # cases.
7
+ #
8
+ # If you specify a :class, a default implementation will be supplied
9
+ # for constructing instances from it, whereby dependencies are passed
10
+ # as the last argument to the class's new method, after any other args.
11
+ # (if the last arg is already a Hash, the dependencies will be merged
12
+ # into it).
13
+ #
14
+ # Is also aliased for convenience from Factory.new.
15
+ #
16
+ # Factory.new(
17
+ # :class => Foo,
18
+ # :dependencies => {
19
+ # :logger => Logger,
20
+ # :bars => {:class => Bar, :multiple => true}
21
+ # }
22
+ # ) do |depedencies, *args, &block|
23
+ # Foo.new(*args, dependencies, &block)
24
+ # end
25
+ class Factory::FromArgs
26
+ include Factory::Interface
27
+
28
+ OPTION_NAMES = [:class, :module, :args, :features, :dependencies, :constructor_dependencies, :setter_dependencies].freeze
29
+
30
+ def initialize(options={}, &constructor_block)
31
+ @provides_class = options[:class] || options[:module]
32
+
33
+ @constructor_dependencies = {}
34
+ (options[:constructor_dependencies] || options[:dependencies] || {}).each do |name, args|
35
+ @constructor_dependencies[name] = Dependency.new_from_arg_or_args_list(args)
36
+ end
37
+ @setter_dependencies = {}
38
+ (options[:setter_dependencies] || {}).each do |name, args|
39
+ @setter_dependencies[name] = Dependency.new_from_arg_or_args_list(args)
40
+ end
41
+
42
+ @provides_features = options[:features] || []
43
+ @constructor_block = constructor_block if constructor_block
44
+
45
+ case @provides_class
46
+ when ::Class
47
+ @initial_args = options[:args] if options[:args]
48
+ when Module
49
+ unless @constructor_block
50
+ raise ArgumentError, "when a Module is specified you need to supply a constructor block"
51
+ end
52
+ when NilClass
53
+ @provides_class = Object
54
+ unless @constructor_block
55
+ raise ArgumentError, "expected a :class or a constructor block or both"
56
+ end
57
+ else
58
+ raise TypeError, ":class / :module options only accept a Class or Module"
59
+ end
60
+ end
61
+
62
+ attr_reader :constructor_dependencies, :provides_class,
63
+ :provides_features, :initial_args, :wrapped_class
64
+
65
+ # Factory::FromArgs doesn't allow you to do instance-sensitive setter-dependencies;
66
+ # subclass or make your own factory if you want these.
67
+ def setter_dependencies(instance=nil); @setter_dependencies; end
68
+
69
+ def new_from_dependencies(dependencies, *other_args, &block_arg)
70
+ if @constructor_block
71
+ @constructor_block.call(dependencies, *other_args, &block_arg)
72
+ else
73
+ # The only time it allows you not to specify a constructor_block
74
+ # is when an actual Class is supplied for provides_class.
75
+ #
76
+ # In this case we supply a default construction method whereby
77
+ # dependencies are merged into a last argument:
78
+ if other_args.last.is_a?(Hash)
79
+ hash_arg = other_args.pop
80
+ other_args.push(hash_arg.merge(dependencies))
81
+ else
82
+ other_args.push(dependencies) unless dependencies.empty?
83
+ end
84
+ case @initial_args
85
+ when NilClass # forgeddit
86
+ when Array then other_args.unshift(*@initial_args)
87
+ else other_args.unshift(@initial_args)
88
+ end
89
+ @provides_class.new(*other_args, &block_arg)
90
+ end
91
+ end
92
+ end
93
+
94
+ module Factory
95
+ # delegates to Factory::FromArgs.new
96
+ def self.new(options, &constructor_block)
97
+ FromArgs.new(options, &constructor_block)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,24 @@
1
+ module Wirer
2
+ # For when you have some pre-existing instance which you want to wrap as a singleton
3
+ # Factory.
4
+ # (You wouldn't normally need to do this yourself, but rather Container uses it
5
+ # under the hood if you add an existing instance to the container)
6
+ class Factory::FromInstance
7
+ include Factory::Interface
8
+
9
+ attr_reader :instance, :provides_features
10
+
11
+ def initialize(instance, *features)
12
+ @instance = instance
13
+ @provides_features = features
14
+ end
15
+
16
+ def provides_class
17
+ @instance.class
18
+ end
19
+
20
+ def new_from_dependencies(deps=nil)
21
+ @instance
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,114 @@
1
+ module Wirer
2
+ module Factory; end
3
+
4
+ # This is the basic Factory interface around which the whole framework is built.
5
+ #
6
+ # You won't normally need to implement this interface yourself, but it's useful
7
+ # in terms of understanding how Wirer works.
8
+ #
9
+ # A Factory is responsible for creating some kind of object, given some dependencies
10
+ # and possibly some additional arguments. so new_from_dependencies is the main
11
+ # method here.
12
+ #
13
+ # It also comes with an interface (constructor_dependencies) telling you what
14
+ # the required dependencies are; this is specified via a hash of symbol argument
15
+ # names to Dependency objects which specify the criterea that must be satisfied
16
+ # for the dependency argument of that name.
17
+ #
18
+ # Wirer::Container uses this metadata to find and pass in dependencies automatically
19
+ # when constructing things from Factories.
20
+ #
21
+ # == Two-phase initialisation
22
+ #
23
+ # Factory can also support cases where some dependencies need to be passed in
24
+ # after creation time, eg when you have cyclic dependencies.
25
+ #
26
+ # This is done via specifying setter_dependencies in the same way as
27
+ # constructor_dependencies; the expectation is that after constructing an instance
28
+ # with new_from_dependencies, you must additionally fetch any setter_dependencies
29
+ # and 'inject' them into the instance via calling inject_dependency on the factory
30
+ # with the instance, dependency name and the value for that dependency.
31
+ #
32
+ # (the default implementation of inject_dependency will just call a setter method
33
+ # on the instance, eg instance.logger = logger; this will work with a private setter
34
+ # if you prefer to make private these things which are only a part of initialization)
35
+ #
36
+ # After a whole set of objects have been created and their setter_dependencies injected, it
37
+ # can be handy for them to get a notification along the lines of "hey so your whole dependency
38
+ # graph is now all wired up any ready". Factory#post_initialize should send this notification
39
+ # to an object created from the factory, where it's supported by the objects you construct.
40
+ #
41
+ # (by default it will call a :post_initialize method on the instance, if this is present;
42
+ # again this can be a private method if you wish).
43
+ module Factory::Interface
44
+ # A Module or Class which all objects constructed by this factory are kind_of?.
45
+ # The more specific you are, the more use this will be when specifying requirements
46
+ # based on a Module or Class.
47
+ def provides_class
48
+ Object # not very informative by default :)
49
+ end
50
+
51
+ # List of arbitrary objects representing features provided by this factory,
52
+ # which may be compared against required features when looking for dependencies.
53
+ # Typically symbols are used.
54
+ def provides_features
55
+ []
56
+ end
57
+
58
+ # Hash of symbol argument names to Wirer::Dependency objects, representing
59
+ # dependencies that need to be passed as arguments to new_from_dependencies
60
+ def constructor_dependencies
61
+ {}
62
+ end
63
+
64
+ # Hash of symbol argument names to Wirer::Dependency objects, representing
65
+ # dependencies which need to be injected into instances *after* they have
66
+ # been constructed via new_from_dependencies
67
+ #
68
+ # if no instance is passed, should return a hash of any setter dependencies
69
+ # applying to all instances constructed from this factory. (which may be none)
70
+ #
71
+ # if an instance is passed, it may add to this hash any additional setter dependencies
72
+ # which are specific to this instance. This is useful when you have some extra set
73
+ # of dependencies which varies depending on the parameters to the constructor.
74
+ def setter_dependencies(instance=nil)
75
+ {}
76
+ end
77
+
78
+ # Will be passed a hash which has keys for all of the argument names specified
79
+ # in constructor_dependencies, together with values which are the constructed
80
+ # dependencies meeting the requirements of the corresponding Wirer::Depedency.
81
+ #
82
+ # May also be passed additional non-dependency arguments supplied directly
83
+ # to the factory.
84
+ #
85
+ # The following must hold:
86
+ # factory.new_from_dependencies(dependencies, ...).is_a?(factory.provides_class)
87
+ #
88
+ # however the following is not required to hold:
89
+ # factory.new_from_dependencies(dependencies, ...).instance_of?(factory.provides_class)
90
+ def new_from_dependencies(dependencies={}, *other_args, &block_arg)
91
+ raise NotImplementedError
92
+ end
93
+
94
+ # Supplies a wrapper around the factory with a set of pre-supplied dependencies.
95
+ # The wrapper can then be used to construct instances.
96
+ # See Factory::CurriedDependencies
97
+ def curry_with_dependencies(dependencies)
98
+ Factory::CurriedDependencies.new(self, dependencies)
99
+ end
100
+
101
+ def inject_dependency(instance, attr_name, dependency)
102
+ instance.send(:"#{attr_name}=", dependency)
103
+ end
104
+
105
+ def post_initialize(instance)
106
+ instance.send(:post_initialize) if instance.respond_to?(:post_initialize, true)
107
+ end
108
+
109
+ def wrapped_with(additional_options={}, &wrapped_constructor_block)
110
+ Factory::Wrapped.new(self, additional_options, &wrapped_constructor_block)
111
+ end
112
+ end
113
+
114
+ end