needle 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/benchmarks/instantiability.rb +26 -0
  2. data/benchmarks/instantiation.rb +33 -0
  3. data/benchmarks/interceptors.rb +42 -0
  4. data/benchmarks/interceptors2.rb +70 -0
  5. data/doc/README +3 -1
  6. data/doc/di-in-ruby.rdoc +201 -0
  7. data/doc/images/di_classdiagram.jpg +0 -0
  8. data/doc/manual/manual.yml +4 -0
  9. data/doc/manual/parts/01_alternatives.txt +11 -0
  10. data/doc/manual/parts/02_creating.txt +20 -0
  11. data/doc/manual/parts/02_namespaces.txt +1 -1
  12. data/doc/manual/parts/02_services.txt +15 -3
  13. data/doc/manual-html/chapter-1.html +34 -7
  14. data/doc/manual-html/chapter-2.html +43 -9
  15. data/doc/manual-html/chapter-3.html +6 -4
  16. data/doc/manual-html/chapter-4.html +6 -4
  17. data/doc/manual-html/chapter-5.html +6 -4
  18. data/doc/manual-html/chapter-6.html +6 -4
  19. data/doc/manual-html/chapter-7.html +6 -4
  20. data/doc/manual-html/index.html +19 -4
  21. data/lib/needle/container.rb +104 -66
  22. data/{test/tc_models.rb → lib/needle/lifecycle/deferred.rb} +14 -20
  23. data/lib/needle/lifecycle/initialize.rb +49 -0
  24. data/lib/needle/{models → lifecycle}/proxy.rb +16 -8
  25. data/lib/needle/lifecycle/singleton.rb +63 -0
  26. data/lib/needle/lifecycle/threaded.rb +58 -0
  27. data/lib/needle/pipeline/collection.rb +133 -0
  28. data/lib/needle/pipeline/element.rb +85 -0
  29. data/lib/needle/pipeline/interceptor.rb +46 -0
  30. data/lib/needle/registry.rb +48 -8
  31. data/lib/needle/service-point.rb +36 -39
  32. data/lib/needle/thread.rb +87 -0
  33. data/lib/needle/version.rb +1 -1
  34. data/{lib/needle/models/prototype.rb → test/lifecycle/tc_deferred.rb} +15 -17
  35. data/test/lifecycle/tc_initialize.rb +62 -0
  36. data/test/{models → lifecycle}/tc_proxy.rb +5 -5
  37. data/test/lifecycle/tc_singleton.rb +32 -0
  38. data/{lib/needle/models/prototype-deferred.rb → test/lifecycle/tc_threaded.rb} +24 -18
  39. data/test/models/model_test.rb +131 -0
  40. data/test/models/tc_prototype.rb +9 -30
  41. data/test/models/tc_prototype_deferred.rb +9 -31
  42. data/test/models/tc_prototype_deferred_initialize.rb +32 -0
  43. data/test/models/tc_prototype_initialize.rb +32 -0
  44. data/test/models/tc_singleton.rb +8 -29
  45. data/test/models/tc_singleton_deferred.rb +8 -30
  46. data/test/models/tc_singleton_deferred_initialize.rb +32 -0
  47. data/test/models/tc_singleton_initialize.rb +32 -0
  48. data/test/models/tc_threaded.rb +32 -0
  49. data/test/models/tc_threaded_deferred.rb +32 -0
  50. data/test/models/tc_threaded_deferred_initialize.rb +32 -0
  51. data/test/models/tc_threaded_initialize.rb +32 -0
  52. data/test/pipeline/tc_collection.rb +116 -0
  53. data/test/pipeline/tc_element.rb +72 -0
  54. data/test/tc_container.rb +77 -36
  55. data/test/tc_logger.rb +5 -0
  56. data/test/tc_registry.rb +39 -1
  57. data/test/tc_service_point.rb +43 -7
  58. metadata +39 -12
  59. data/lib/needle/models/singleton-deferred.rb +0 -57
  60. data/lib/needle/models/singleton.rb +0 -56
  61. data/lib/needle/models.rb +0 -44
@@ -0,0 +1,26 @@
1
+ $:.unshift "../lib"
2
+
3
+ require 'benchmark'
4
+ require 'needle'
5
+
6
+ ITERATIONS = 100_000
7
+
8
+ registry = Needle::Registry.new
9
+ registry.register( :deferred, :model=>:singleton_deferred ) { Struct.new( :value ).new( 1 ) }
10
+ registry.register( :immediate, :model=>:singleton ) { Struct.new( :value ).new( 1 ) }
11
+
12
+ deferred = registry.deferred
13
+ immediate = registry.immediate
14
+
15
+ puts
16
+ puts "--------------------------------------------------------------------"
17
+ puts "Proxied method dispatch vs. direct method dispatch"
18
+ puts "#{ITERATIONS} iterations"
19
+ puts
20
+
21
+ Benchmark.bm(7) do |x|
22
+ x.report( "proxy:" ) { ITERATIONS.times { deferred.value } }
23
+ x.report( "direct:" ) { ITERATIONS.times { immediate.value } }
24
+ end
25
+
26
+ puts
@@ -0,0 +1,33 @@
1
+ $:.unshift "../lib"
2
+
3
+ require 'benchmark'
4
+ require 'needle'
5
+
6
+ ITERATIONS = 100_000
7
+
8
+ S = Struct.new( :value )
9
+
10
+ registry = Needle::Registry.new
11
+ registry.register( :immediate, :model=>:prototype ) { S.new }
12
+ registry.register( :deferred, :model=>:prototype_deferred ) { S.new }
13
+
14
+ puts
15
+ puts "--------------------------------------------------------------------"
16
+ puts "Direct vs. Immediate vs. Deferred instantiation (trivial)"
17
+ puts "#{ITERATIONS} iterations"
18
+ puts
19
+
20
+ Benchmark.bm(10) do |x|
21
+ GC.disable
22
+ x.report( "direct:" ) { ITERATIONS.times { S.new } }
23
+ GC.start
24
+ x.report( "immediate:" ) { ITERATIONS.times { registry.immediate } }
25
+ GC.start
26
+ x.report( "deferred:" ) { ITERATIONS.times { registry.deferred } }
27
+ GC.start
28
+ x.report( "deferred*:" ) { ITERATIONS.times { registry.deferred.value } }
29
+ GC.enable
30
+ end
31
+
32
+ puts "* this benchmark forced the proxy to instantiate its wrapped service"
33
+ puts
@@ -0,0 +1,42 @@
1
+ $:.unshift "../lib"
2
+
3
+ require 'benchmark'
4
+ require 'needle'
5
+
6
+ ITERATIONS = 100_000
7
+
8
+ class TrivialInterceptor
9
+ def initialize( point, parms )
10
+ end
11
+
12
+ def process( chain, ctx )
13
+ chain.process_next( ctx )
14
+ end
15
+ end
16
+
17
+ registry = Needle::Registry.new
18
+ registry.register( :interceptor ) { TrivialInterceptor }
19
+ registry.register( :direct ) { Struct.new( :value ).new }
20
+ registry.register( :intercepted_doing ) { Struct.new( :value ).new }
21
+ registry.register( :intercepted_with ) { Struct.new( :value ).new }
22
+
23
+ registry.intercept( :intercepted_doing ).doing { |chain,ctx| chain.process_next(ctx) }
24
+ registry.intercept( :intercepted_with ).with { registry.interceptor }
25
+
26
+ direct = registry.direct
27
+ intercepted_doing = registry.intercepted_doing
28
+ intercepted_with = registry.intercepted_with
29
+
30
+ puts
31
+ puts "--------------------------------------------------------------------"
32
+ puts "Direct method dispatch vs. intercepted method dispatch (trivial)"
33
+ puts "#{ITERATIONS} iterations"
34
+ puts
35
+
36
+ Benchmark.bm(20) do |x|
37
+ x.report( "direct:" ) { ITERATIONS.times { direct.value } }
38
+ x.report( "intercepted (doing):" ) { ITERATIONS.times { intercepted_doing.value } }
39
+ x.report( "intercepted (with):" ) { ITERATIONS.times { intercepted_with.value } }
40
+ end
41
+
42
+ puts
@@ -0,0 +1,70 @@
1
+ $:.unshift "../lib"
2
+
3
+ require 'benchmark'
4
+ require 'needle'
5
+
6
+ ITERATIONS = 10_000
7
+
8
+ class UnloggedBeast
9
+ def value( p1, p2 )
10
+ [ p1, p2 ]
11
+ end
12
+ end
13
+
14
+ class LoggedBeast < UnloggedBeast
15
+ attr_writer :log
16
+
17
+ def value( p1, p2 )
18
+ @log.debug( "value(#{p1.inspect}, #{p2.inspect})" ) if @log.debug?
19
+ result = super
20
+ @log.debug( "value(...) => #{result.inspect}" ) if @log.debug?
21
+ return result
22
+ rescue Exception => e
23
+ @log.debug( "value(...) raised #{e.message.inspect} (#{e.class})" ) if @log.debug?
24
+ raise
25
+ end
26
+ end
27
+
28
+ registry = Needle::Registry.new( :logs=> { :filename=>"/dev/null" } )
29
+ registry.register( :direct ) do
30
+ beast = LoggedBeast.new
31
+ beast.log = registry.logs.get( "direct" )
32
+ beast
33
+ end
34
+
35
+ registry.register( :intercepted_doing ) { UnloggedBeast.new }
36
+ registry.register( :intercepted_with ) { UnloggedBeast.new }
37
+
38
+ registry.intercept( :intercepted_doing ).
39
+ with_options( :log => registry.logs.get( "doing" ) ).
40
+ doing do |chain,ctx|
41
+ log = ctx.data[:options][:log]
42
+ begin
43
+ log.debug( "#{ctx.sym}(#{ctx.args.map{|i|i.inspect}.join(",")})" ) if log.debug?
44
+ result = chain.process_next(ctx)
45
+ log.debug( "#{ctx.sym}(...) => #{result.inspect}" ) if log.debug?
46
+ result
47
+ rescue Exception
48
+ log.debug( "value(...) raised #{e.message.inspect} (#{e.class})" ) if log.debug?
49
+ end
50
+ end
51
+
52
+ registry.intercept( :intercepted_with ).with { registry.logging_interceptor }
53
+
54
+ direct = registry.direct
55
+ intercepted_doing = registry.intercepted_doing
56
+ intercepted_with = registry.intercepted_with
57
+
58
+ puts
59
+ puts "--------------------------------------------------------------------"
60
+ puts "Direct method dispatch vs. intercepted method dispatch (non-trivial)"
61
+ puts "#{ITERATIONS} iterations"
62
+ puts
63
+
64
+ Benchmark.bm(20) do |x|
65
+ x.report( "direct:" ) { ITERATIONS.times { direct.value( :a, :b ) } }
66
+ x.report( "intercepted (doing):" ) { ITERATIONS.times { intercepted_doing.value( :a, :b ) } }
67
+ x.report( "intercepted (with):" ) { ITERATIONS.times { intercepted_with.value( :a, :b ) } }
68
+ end
69
+
70
+ puts
data/doc/README CHANGED
@@ -12,6 +12,8 @@ closures and +instance_eval+.
12
12
  * API Documentation: http://needle.rubyforge.org/api
13
13
  * Needle Wiki: http://needle.rubyforge.org/wiki/wiki.pl
14
14
 
15
+ For an excellent overview of dependency injection, have a look at "Dependency Injection in Ruby" (link:files/doc/di-in-ruby_rdoc.html).
16
+
15
17
  == Downloading
16
18
 
17
19
  You may download Needle from Needle's RubyForge project, at http://rubyforge.org/projects/needle.
@@ -61,7 +63,7 @@ Thanks go to:
61
63
 
62
64
  == License
63
65
 
64
- Needle is copyright (c) 2004 Jamis Buck. It is free software, and may be redistributed
66
+ Needle is copyright (c) 2004 Jamis Buck. It is open-source software, and may be redistributed
65
67
  under the terms of the BSD or Ruby licenses. The texts of these licences are included in the
66
68
  Needle distribution, under the +doc+ subdirectory.
67
69
 
@@ -0,0 +1,201 @@
1
+ = Dependency Injection in Ruby
2
+ By Jim Weirich, slightly adapted for Needle (original article at http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc)
3
+
4
+ <em>(The API described in the original version of this article inspired the creation of the Needle framework. This article has been modified slightly so that the DI examples use Needle, instead of the framework Jim originally presented. This has proven to be a very minor modification, since the two syntaxes are nearly identical. -- Jamis Buck (jgb3@email.byu.edu))</em>
5
+
6
+ <em>This article is modified and distributed under the terms of the Creative Commons Attribution-NonCommercial 1.0 license (http://creativecommons.org/licenses/by-nc/1.0).</em>
7
+
8
+ == What is Dependency Injection?
9
+
10
+ Consider the problem of putting together a moderately complex OO program. Typical OO programs create a bunch of objects, wire them together in interesting ways and then let the objects run. It is the first two steps, creating and wiring, that are addressed by Dependency Injection (DI).
11
+
12
+ By the way, another term for dependency injection is Inversion of Control (IOC). Unfortunately, so many things in computer science are called inversion of control that the phrase does not evoke the right connotations with me, so I tend to avoid it. But Inversion of Control is the older term for this pattern so you will see it in many places.
13
+
14
+ == A Moderately Complex Example
15
+
16
+ One of the problems with explaining Dependency Injection is that DI only becomes really useful in larger projects. Using a simple example to explain DI leaves the listener thinking "But I can do that easily by (<em>fill in the blank</em>)". So my example is going to be a bit more complex, but hopefully not so large that the reader is unable to understand it.
17
+
18
+ Imagine you have a webapp that tracks the prices of stocks over time. The application is nicely partitioned into different modules that each handle a portion of the job. A +StockQuotes+ module talks to a remote web service to pull down the current values of the stocks you are tracking. A +Database+ module records the stock values over time. Because this data is highly competitive, you require a login to use the system and thus have an +Authentication+ module to handle validation of user names and password. In addition to these "main" modules, there are a number of additional utility modules used by multiple modules: +ErrorHandler+ to standardize the handling and reporting of error messages and +Logger+ to provide a standard way of logging messsages.
19
+
20
+ A fully wired system might look something like this:
21
+
22
+ link:files/doc/images/di_classdiagram.jpg
23
+
24
+ == Building it Old Style!
25
+
26
+ In the bad, old days, we would just put the logic of building the web app directly into its initialize method. It might look something like this...
27
+
28
+ class WebApp
29
+ def initialize
30
+ @quotes = StockQuotes.new
31
+ @authenticator = Authenticator.new
32
+ @database = Database.new
33
+ @logger = Logger.new
34
+ @error_handler = ErrorHandler.new
35
+ end
36
+ # ...
37
+ end
38
+
39
+ That handles building the WebApp well enough, but what about the subordinate modules. How does the +StockQuotes+ module find out about the logger and error handler, or how does the +Authenticator+ find the database and logger?
40
+
41
+ We could rewrite <tt>WebApp#initialize</tt> to create everything in the right order and then pass the logger and error handler to +StockQuotes+. But that makes the web app rather dependent on details of the +StockQuotes+ module. Currently the database module is created after the quote module, but suppose a change in +StockQuotes+ causes it to need the database. That would require the WebApp to be aware of the change, rearrange the order of creation so that the database is created before the stock quotes module and finally make the database available to the quote service. Yuck!
42
+
43
+ Even worse, the WebApp knows the _concrete_ name of every module it uses. If I wanted to create an instance of the WebApp for testing, I might want to provide a mock quote service so that I can control the quotes used in testing. Or I might want a mock database for testing. All of these choices are difficult because WebApp knows the class name of all its subordinates.
44
+
45
+ == Enter the Service Locator
46
+
47
+ We would like to remove the explicit reference to class names in WebApp, but still allow it to locate the services it needs. The <em>Service Locator</em> pattern was designed to address this problem.
48
+
49
+ With Service Locator, we place references to services in one container and then pass that container to the modules that need to locate those services.
50
+
51
+ def create_application
52
+ locator = {}
53
+ locator[:logger] = Logger.new
54
+ locator[:error_handler] = ErrorHandler.new(locator)
55
+ locator[:quotes] = StockQuotes.new(locator)
56
+ locator[:database] = Database.new(locator)
57
+ locator[:authenticator] = Authenticator.new(locator)
58
+ locator[:webapp] = WebApp.new(locator)
59
+ end
60
+
61
+ The initialize function for a service just uses the locator to find the services. Here is how +StockQuotes+ might look...
62
+
63
+ class StockQuotes
64
+ def initialize(locator)
65
+ @error_handler = locator[:error_handler]
66
+ @logger = locator[:logger]
67
+ end
68
+ # ...
69
+ end
70
+
71
+ Not bad. Now no service is aware of the exact class used for the other services. We can reconfigure the system easily by editted the +create_application+ method.
72
+
73
+ We use the Service Locator pattern (and variations) at work in our Java system.
74
+
75
+ == External Configuration
76
+
77
+ Although we built the service locator in Ruby code, it would not be difficult to specify the locator as a configuration file. A simple Ruby method could read the file, instantiate the objects and populate a hash table. This might allow non-programmers to tweak a configuration to their liking.
78
+
79
+ == More Goodness
80
+
81
+ Another neat thing about the locator is that we can use it to configure data as well as modules. Suppose we wanted to specify the file to be used as the log file. We might modify the +create_application+ method to include the following:
82
+
83
+ locator[:log_file_name] = "webapp.log"
84
+ locator[:logger] = Logger.new(locator)
85
+
86
+ And +Logger+ would have to know that the log file was identified by <tt>:log_file_name</tt> in the locator. The +Database+ module is another likely candidate for locator based information (e.g. DB user name and password, DB host name).
87
+
88
+ == But ...
89
+
90
+ As good as the Service Locator pattern is, there are still some negatives. Every class that uses the locator needs to be written expecting a locator as an argument to +initialize+ method. This is not a natural idiom for Ruby programmer. In the absence of Service Locators, I would expect that the +Logger+ class would be written like this ...
91
+
92
+ class Logger
93
+ def initialize(log_filename)
94
+ # ...
95
+ end
96
+ # ...
97
+ end
98
+
99
+ which would make it unusable in a system that depended upon service locators.
100
+
101
+ Another downside is that all modules that use the locator must agree on the names of the services. For example, if MyLogger expects its file name to be under <tt>:log_filename</tt> and +YourLogger+ expects to find its filename under <tt>:log_file</tt> then the two loggers are not plug replaceable.
102
+
103
+ Also, suppose both +StackQuotes+ and +Database+ found their loggers using <tt>:logger</tt>, but we want to give them separate logger instances for some reason. The explicit dependence on the name of the logger service makes this a bit difficult.
104
+
105
+ And finally, the service locator did not solve the problem of creation order. The database is still created after the stock quotes module, causing problems if the stocks quotes module were modified to use the database.
106
+
107
+ None of the problems are show stoppers and there are workarounds for each, but it does make us wonder if there is a more general solution.
108
+
109
+ == Finally, Dependency Injection
110
+
111
+ Dependency Injection is much like using service locators in that we identify the services by name. The big difference is that dependency injectors also take the responsibility of creating the service objects and making sure the dependent services are provided as needed.
112
+
113
+ This means that the services can be written in complete ignorance of dependency injection framework. All they need to do is make sure that they can be told about the services they need, either through parameters to a constructor, or through some kind of setter.
114
+
115
+ It also means that dependency injectors are a bit more complicated than service locators, since they also handle the creation of the services as well.
116
+
117
+ == Dependency Injection in Action
118
+
119
+ How does dependency injection work? Generally, you create a DI container that is configured to know how to create the various services. Then you just ask for a service by name, and the container will create the serice (if needed) and give it to you.
120
+
121
+ For example, configuring a logger service is as easy as ...
122
+
123
+ registry = Needle::Registry.new
124
+ registry.register(:logger) { Logger.new )
125
+
126
+ This says that the logger service is named <tt>:logger</tt>. The first time a logger service is requested, the block supplied to register will be called and a logger object will be created. Subsequent requests for a logger will return the already created logger.
127
+
128
+ To get a logger service, all you need to do is ask:
129
+
130
+ logger = registry.logger
131
+
132
+ *Note*:: In my examples, Service Locators were hash based, so using [] to access the services seems like a natural choice. For dependency injection containers, I chose to use a message-like syntax to access services (e.g. registry.logger). Either notation can be used for either service locators or dependency injection containers. In fact, the Needle dependency injection framework supports both selecter messages and hash-like indexing (registry[:logger]).
133
+
134
+ If a logger requires a parameter, then you can easily handle that in the registration block.
135
+
136
+ registry.register(:logger) { Logger.new("logfile.log") }
137
+
138
+ If you would rather have the logger get its log filename from the container, you can do this ...
139
+
140
+ registry.register(:logger) { |c| Logger.new(c.log_filename) }
141
+
142
+ And then somewhere else you can specify the log name ...
143
+
144
+ registry.register(:log_filename) { "logfile.log" }
145
+
146
+ == Configuring the WebApp with Dependency Injection
147
+
148
+ Now that we've seen some DI in action, let's try it on our web app ...
149
+
150
+ def create_application
151
+ registry = Needle::Registry.new
152
+ registry.register(:logfilename) { "logfile.log" }
153
+ registry.register(:db_user) { "jim" }
154
+ registry.register(:db_password) { "secret" }
155
+ registry.register(:dbi_string) { "DBI:Pg:example_data" }
156
+
157
+ registry.register(:app) do |c|
158
+ app = WebApp.new(c.quotes, c.authenticator, c.database)
159
+ app.logger = c.logger
160
+ app.set_error_handler c.error_handler
161
+ app
162
+ end
163
+
164
+ registry.register(:quotes) { |c|
165
+ StockQuotes.new(c.error_handler, c.logger)
166
+ }
167
+
168
+ registry.register(:authenticator) { |c|
169
+ Authenticator.new(c.database, c.logger, c.error_handler)
170
+ }
171
+
172
+ registry.register(:database) { |c|
173
+ DBI.connect(c.dbi_string, c.db_user, c.db_password)
174
+ }
175
+
176
+ registry.register(:logger) { |c| Logger.new(c.logfilename) }
177
+ registry.register(:error_handler) do |c|
178
+ errh = ErrorHandler.new
179
+ errh.logger = c.logger
180
+ errh
181
+ end
182
+
183
+ registry.app
184
+ end
185
+
186
+ As you can see, it is a bit more complicated than the service locator. The main reason for the complexity is that we have moved the creation logic out of the services and into the DI container. What we have gained is the ability to inject dependencies into any object without having to make special code changes to support it.
187
+
188
+ Just a few closing notes:
189
+
190
+ * Both constructor injection (+StockQuotes+) and setter injection (+ErrorHandler+) or a combination of both (+WebApp+) can be supported with this framework.
191
+ * We can even handle cases where the creation method is not named "+new+" (DBI).
192
+ * If a poorly written service didn't provide a way to inject the services it depends upon, we _could_ use +instance_variable_set+ to force a dependent service into place. Obviously, this would be less than desireable.
193
+ * The order of the registration doesn't matter, since no service is created until everything is registered. If the +StockQuotes+ module suddenly starts needing a database connection, no problem. We just add a reference to a database service in the creation code for +StockQuotes+ and we are done. The DI framework worries about making sure the database is _created_ before anything that needs it.
194
+ * The container doesn't have to be configured in one place. For example, we could move the first four register calls to a separate file that would allow the log file and database information to be modified independently of the rest.
195
+ * There still needs to be agreement about service names, but now only the container knows about them. The individual services don't care.
196
+ * Since the DI container is responsible for all the service names and service creation, it is easy to intercept a service and wrap an AOP-like wrapper around a it.
197
+ * Just like the service locator, a DI container could be configured through a configuration file. The configuration would be more complex (because the DI container is more complex), but still quite doable. Another idea is to use Ruby as Domain Specific Language for DI container configuration.
198
+
199
+ == Summary
200
+
201
+ Both the Service Locator and Dependency Injection patterns are quite useful, but each has different tradeoffs between flexibility and complexity. Understand the differences and you will have all you need to choose the proper idiom for any given circumstance.
Binary file
@@ -22,12 +22,16 @@ product: !^product
22
22
  - Needle Wiki: http://needle.rubyforge.org/wiki/wiki.pl
23
23
 
24
24
  recent_updates:
25
+ - added Container#define
26
+ - renamed Container#register! to Container#define!
27
+ - added documentation of Container#new!
25
28
 
26
29
  chapters:
27
30
 
28
31
  - Introduction:
29
32
  - What is Needle?: !!file parts/01_what_is_needle.txt
30
33
  - How Can It Help Me?: !!file parts/01_use_cases.txt
34
+ - Alternatives: !!file parts/01_alternatives.txt
31
35
  - License Information: !!file parts/01_license.txt
32
36
  - Support: !!file parts/01_support.txt
33
37
 
@@ -0,0 +1,11 @@
1
+ Needle is not the only fish in the dependency-injection pond, even when it comes to Ruby. Other containers at your disposal include:
2
+
3
+ * "Copland":http://copland.rubyforge.org. Copland aims to be an "application framework", taking something of a heavy-weight approach to DI. In so doing, it provides functionality that Needle does not, but at the cost of performance. It also uses external (YAML) configuration files. It is inspired by a Java framework ("HiveMind":http://jakarta.apache.org/hivemind), and so has a vaguely Java-ish flavor to it.
4
+ * "Rico":http://www.picocontainer.org/Rico. Rico is another project inspired by a Java project ("PicoContainer":http://www.picocontainer.org). It is very lean, and appears to be experimental.
5
+ * "Tudura":http://sourceforge.jp/projects/nihohi/. I do not have any information on this project, as the information is all in Japanese. If someone with more information about Tudura would like to step forward, I'd be happy to post a summary here.
6
+
7
+ There is, at the time of this writing, at least one other project on RubyForge devoted to DI, although it has no public releases yet.
8
+
9
+ So, which one should you choose? It comes down to an issue of personal preference, mostly, but also one of what you are wanting to accomplish. Needle excels at providing an unobtrusive, light-weight container for managing your dependencies. The cost of it being light-weight is that there is functionality it does not provide, which other containers may. If you really need that missing functionality, you are required to either implement it yourself, or select a different container.
10
+
11
+ For most tasks, I think you'll find Needle more than sufficient.
@@ -7,3 +7,23 @@ Creating a registry is as simple as calling @Needle::Registry.new@. This will gi
7
7
  </pre>
8
8
 
9
9
  Once you have the reference to the registry, you can register services with it, create new namespaces in it, and so forth.
10
+
11
+ Alternatively, you can pass a block to @#new@:
12
+
13
+ <pre>
14
+ registry = Needle::Registry.new do |r|
15
+ ...
16
+ end
17
+ </pre>
18
+
19
+ The parameter to the block will be a reference to the registry. This allows you to register services with the registry as soon as it is created.
20
+
21
+ One other convenience method is @#new!@:
22
+
23
+ <pre>
24
+ registry = Needle::Registry.new! do
25
+ ...
26
+ end
27
+ </pre>
28
+
29
+ This block accepts no parameters, and evaluates the block as if it were passed to @Registry#define!@ (see below).
@@ -35,7 +35,7 @@ Because it is often the case that you will be creating a namespace and then imme
35
35
  end
36
36
  </pre>
37
37
 
38
- And, to mirror the @register!@ method, there is also a @namespace!@ method. This method creates a new namespace and then does a @register!@ call on that namespace.
38
+ And, to mirror the @namespace@ method, there is also a @namespace!@ method. This method creates a new namespace and then does a @define!@ call on that namespace.
39
39
 
40
40
  <pre>
41
41
  registry.namespace! :stuff do
@@ -18,17 +18,29 @@ You get services from the registry in either of two ways:
18
18
 
19
19
  h3. Convenience Methods
20
20
 
21
- Because you will often need to register many services with a registry at once, a convenience method has been provided to make this use case lean and mean. Just call @registry!@, passing a block that accepts no parameters. This block will be evaluated in a new context, with any unrecognized method call being interpreted as a new service registration of that name:
21
+ Because you will often need to register many services with a registry at once, two convenience methods have been provided to make this use case lean and mean.
22
+
23
+ The first is @define@. Just pass a block to define that accepts one parameter. This parameter will be a "builder" object that allows you to define services just by sending them as messages to the builder:
24
+
25
+ <pre>
26
+ registry.define do |b|
27
+ b.foo { Bar.new }
28
+ b.bar { Foo.new }
29
+ ...
30
+ end
31
+ </pre>
32
+
33
+ Alternative, you can call @define!@, passing a block that accepts no parameters. This block will be evaluated in the "builder" object's context, with any unrecognized method call being interpreted as a new service registration of that name:
22
34
 
23
35
  <pre>
24
- registry.register! do
36
+ registry.define! do
25
37
  foo { Bar.new }
26
38
  bar { Foo.new }
27
39
  ...
28
40
  end
29
41
  </pre>
30
42
 
31
- The above will register two new services with the registry, @:foo@ and @:bar@.
43
+ Both of the above will register two new services with the registry, @:foo@ and @:bar@.
32
44
 
33
45
  h3. Default Lifecycle
34
46
 
@@ -14,8 +14,8 @@
14
14
  </div>
15
15
  </td><td valign='middle' align='right'>
16
16
  <div class="info">
17
- Needle Version: <strong>0.5.0</strong><br />
18
- Manual Last Updated: <strong>2004-10-15 03:41 GMT</strong>
17
+ Needle Version: <strong>0.6.0</strong><br />
18
+ Manual Last Updated: <strong>2004-10-21 16:17 GMT</strong>
19
19
  </div>
20
20
  </td></tr>
21
21
  </table>
@@ -41,9 +41,11 @@
41
41
 
42
42
  <li><a href="chapter-1.html#s2">How Can It Help Me?</a></li>
43
43
 
44
- <li><a href="chapter-1.html#s3">License Information</a></li>
44
+ <li><a href="chapter-1.html#s3">Alternatives</a></li>
45
45
 
46
- <li><a href="chapter-1.html#s4">Support</a></li>
46
+ <li><a href="chapter-1.html#s4">License Information</a></li>
47
+
48
+ <li><a href="chapter-1.html#s5">Support</a></li>
47
49
 
48
50
  </ol>
49
51
  </li>
@@ -315,7 +317,32 @@
315
317
 
316
318
  <h2>
317
319
  <a name="s3"></a>
318
- 1.3. License Information
320
+ 1.3. Alternatives
321
+ </h2>
322
+
323
+
324
+
325
+ <div class="section">
326
+ <p>Needle is not the only fish in the dependency-injection pond, even when it comes to Ruby. Other containers at your disposal include:</p>
327
+
328
+ <ul>
329
+ <li><a href="http://copland.rubyforge.org">Copland</a>. Copland aims to be an &#8220;application framework&#8221;, taking something of a heavy-weight approach to DI. In so doing, it provides functionality that Needle does not, but at the cost of performance. It also uses external (YAML) configuration files. It is inspired by a Java framework (<a href="http://jakarta.apache.org/hivemind">HiveMind</a>), and so has a vaguely Java-ish flavor to it.</li>
330
+ <li><a href="http://www.picocontainer.org/Rico">Rico</a>. Rico is another project inspired by a Java project (<a href="http://www.picocontainer.org">PicoContainer</a>). It is very lean, and appears to be experimental.</li>
331
+ <li><a href="http://sourceforge.jp/projects/nihohi/">Tudura</a>. I do not have any information on this project, as the information is all in Japanese. If someone with more information about Tudura would like to step forward, I&#8217;d be happy to post a summary here.</li>
332
+ </ul>
333
+
334
+ <p>There is, at the time of this writing, at least one other project on RubyForge devoted to DI, although it has no public releases yet.</p>
335
+
336
+ <p>So, which one should you choose? It comes down to an issue of personal preference, mostly, but also one of what you are wanting to accomplish. Needle excels at providing an unobtrusive, light-weight container for managing your dependencies. The cost of it being light-weight is that there is functionality it does not provide, which other containers may. If you really need that missing functionality, you are required to either implement it yourself, or select a different container.</p>
337
+
338
+ <p>For most tasks, I think you&#8217;ll find Needle more than sufficient.</p>
339
+ </div>
340
+
341
+
342
+
343
+ <h2>
344
+ <a name="s4"></a>
345
+ 1.4. License Information
319
346
  </h2>
320
347
 
321
348
 
@@ -332,8 +359,8 @@
332
359
 
333
360
 
334
361
  <h2>
335
- <a name="s4"></a>
336
- 1.4. Support
362
+ <a name="s5"></a>
363
+ 1.5. Support
337
364
  </h2>
338
365
 
339
366