needle 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/doc/LICENSE-BSD +27 -0
  2. data/doc/LICENSE-GPL +280 -0
  3. data/doc/LICENSE-RUBY +56 -0
  4. data/doc/README +70 -0
  5. data/doc/manual/chapter.erb +18 -0
  6. data/doc/manual/index.erb +29 -0
  7. data/doc/manual/manual.css +192 -0
  8. data/doc/manual/manual.rb +240 -0
  9. data/doc/manual/manual.yml +48 -0
  10. data/doc/manual/page.erb +86 -0
  11. data/doc/manual/parts/01_license.txt +5 -0
  12. data/doc/manual/parts/01_support.txt +1 -0
  13. data/doc/manual/parts/01_use_cases.txt +141 -0
  14. data/doc/manual/parts/01_what_is_needle.txt +1 -0
  15. data/doc/manual/parts/02_creating.txt +9 -0
  16. data/doc/manual/parts/02_namespaces.txt +47 -0
  17. data/doc/manual/parts/02_overview.txt +3 -0
  18. data/doc/manual/parts/02_services.txt +44 -0
  19. data/doc/manual/tutorial.erb +30 -0
  20. data/doc/manual-html/chapter-1.html +354 -0
  21. data/doc/manual-html/chapter-2.html +310 -0
  22. data/doc/manual-html/chapter-3.html +154 -0
  23. data/doc/manual-html/chapter-4.html +154 -0
  24. data/doc/manual-html/chapter-5.html +154 -0
  25. data/doc/manual-html/chapter-6.html +154 -0
  26. data/doc/manual-html/chapter-7.html +154 -0
  27. data/doc/manual-html/index.html +177 -0
  28. data/doc/manual-html/manual.css +192 -0
  29. data/lib/needle/container.rb +318 -0
  30. data/lib/needle/errors.rb +32 -0
  31. data/lib/needle/include-exclude.rb +116 -0
  32. data/lib/needle/interceptor-chain.rb +162 -0
  33. data/lib/needle/interceptor.rb +189 -0
  34. data/lib/needle/log-factory.rb +207 -0
  35. data/lib/needle/logger.rb +161 -0
  36. data/lib/needle/logging-interceptor.rb +62 -0
  37. data/lib/needle/models/prototype-deferred.rb +41 -0
  38. data/lib/needle/models/prototype.rb +39 -0
  39. data/lib/needle/models/proxy.rb +84 -0
  40. data/lib/needle/models/singleton-deferred.rb +57 -0
  41. data/lib/needle/models/singleton.rb +56 -0
  42. data/lib/needle/models.rb +44 -0
  43. data/lib/needle/registry.rb +110 -0
  44. data/lib/needle/service-point.rb +109 -0
  45. data/lib/needle/version.rb +28 -0
  46. data/lib/needle.rb +54 -0
  47. data/test/ALL-TESTS.rb +21 -0
  48. data/test/models/tc_prototype.rb +53 -0
  49. data/test/models/tc_prototype_deferred.rb +54 -0
  50. data/test/models/tc_proxy.rb +51 -0
  51. data/test/models/tc_singleton.rb +53 -0
  52. data/test/models/tc_singleton_deferred.rb +54 -0
  53. data/test/tc_container.rb +246 -0
  54. data/test/tc_interceptor.rb +92 -0
  55. data/test/tc_interceptor_chain.rb +181 -0
  56. data/test/tc_logger.rb +181 -0
  57. data/test/tc_models.rb +44 -0
  58. data/test/tc_registry.rb +34 -0
  59. data/test/tc_service_point.rb +100 -0
  60. metadata +107 -0
@@ -0,0 +1,240 @@
1
+ # --------------------------------------------------------------------------
2
+ # Portions of this code were taken from the "poignant.rb" script created
3
+ # by whytheluckystiff, which generates "Why's (Poignant) Guide to Ruby".
4
+ # The original script may be obtained from the poignant CVS repository,
5
+ # at http://poignant.rubyforge.org.
6
+ #
7
+ # This script is distributed under the by-sa/1.0 Creative Commons license.
8
+ # --------------------------------------------------------------------------
9
+
10
+ require 'erb'
11
+ require 'fileutils'
12
+ require 'yaml'
13
+ require 'redcloth'
14
+
15
+ module Needle
16
+ module Manual
17
+
18
+ class Manual
19
+ attr_accessor :product, :meta, :chapters, :tutorials, :examples, :recent_updates
20
+
21
+ def Manual.load( file_name )
22
+ File.open( file_name ) { |file| YAML.load( file ) }
23
+ end
24
+ end
25
+
26
+ class Meta
27
+ attr_accessor :copyright, :author, :email
28
+ end
29
+
30
+ class Product
31
+ attr_accessor :name, :tagline, :version, :logo, :urls, :project
32
+ end
33
+
34
+ class Sidebar
35
+ attr_accessor :title, :content
36
+ end
37
+
38
+ class Chapter
39
+ attr_accessor :index, :title, :sections
40
+
41
+ def initialize( index, title, sections )
42
+ @index = index
43
+ @title = title
44
+
45
+ section_index = 0
46
+ @sections = ( sections || [] ).collect do |section|
47
+ section_index += 1
48
+ if section.respond_to? :keys
49
+ Section.new( section_index, section.keys.first, section.values.first )
50
+ else
51
+ section_index -= 1
52
+ Section.new( section_index, nil, section )
53
+ end
54
+ end
55
+ end
56
+
57
+ def page_title
58
+ "Chapter #{index}: #{title}"
59
+ end
60
+ end
61
+
62
+ class Tutorial
63
+ attr_accessor :index, :title, :brief, :intro, :steps, :summary
64
+
65
+ def initialize( index, title, brief, intro, steps, summary )
66
+ @index = index
67
+ @title = title
68
+ @brief = RedCloth.new( brief )
69
+ @intro = RedCloth.new( intro ) if intro
70
+ @summary = RedCloth.new( summary ) if summary
71
+ @steps = steps.map { |step| RedCloth.new( step ) }
72
+ end
73
+
74
+ def page_title
75
+ "Tutorial #{index}: #{title}"
76
+ end
77
+ end
78
+
79
+ class Example
80
+ attr_accessor :index, :title, :brief, :intro, :design, :implementation, :summary
81
+
82
+ def initialize( index, title, brief, intro, design, implementation, summary )
83
+ @index = index
84
+ @title = title
85
+ @brief = RedCloth.new( brief )
86
+ @intro = RedCloth.new( intro )
87
+ @design = RedCloth.new( design )
88
+ @implementation = RedCloth.new( implementation )
89
+ @summary = RedCloth.new( summary )
90
+ end
91
+
92
+ def page_title
93
+ "Example #{index}: #{title}"
94
+ end
95
+ end
96
+
97
+ class Section
98
+ attr_accessor :index, :title, :content
99
+
100
+ def initialize( index, title, content )
101
+ @index = index
102
+ @title = RedCloth.new( title ).to_html.gsub( %r{</?p>}, "" ) if title
103
+ @content = RedCloth.new( content )
104
+ end
105
+ end
106
+
107
+ YAML.add_private_type( 'file' ) { |type_id, value| File.read( value ) }
108
+ YAML.add_private_type( 'eval' ) { |type_id, value| eval( value ) }
109
+
110
+ YAML.add_domain_type( 'jamisbuck.org,2004', 'manual' ) do |taguri, val|
111
+ index = 0
112
+
113
+ val['chapters'].collect! do |chapter|
114
+ index += 1
115
+ Chapter.new( index, chapter.keys.first, chapter.values.first )
116
+ end
117
+
118
+ index = 0
119
+ ( val['tutorials'] ||= [] ).collect! do |tutorial|
120
+ index += 1
121
+ content = tutorial.values.first
122
+ Tutorial.new( index, tutorial.keys.first, content['brief'], content['intro'],
123
+ content['steps'], content['summary'] )
124
+ end
125
+
126
+ index = 0
127
+ ( val['examples'] ||= [] ).collect! do |example|
128
+ index += 1
129
+ content = example.values.first
130
+ Example.new( index, example.keys.first, content['brief'], content['intro'], content['design'],
131
+ content['implementation'], content['summary'] )
132
+ end
133
+
134
+ YAML.object_maker( Manual, val )
135
+ end
136
+
137
+ YAML.add_domain_type( 'jamisbuck.org,2004', 'meta' ) do |taguri, val|
138
+ YAML.object_maker( Meta, val )
139
+ end
140
+
141
+ YAML.add_domain_type( 'jamisbuck.org,2004', 'product' ) do |taguri, val|
142
+ version = val["version"]
143
+ if version.respond_to?( :type_id )
144
+ if version.type_id == "version"
145
+ if version.value =~ %r{(.*)/(.*)}
146
+ require_file, constant = $1, $2
147
+ else
148
+ constant = version.value
149
+ end
150
+
151
+ require require_file if require_file
152
+ val["version"] = eval(constant)
153
+ else
154
+ raise "Unexpected type: #{val.type_id}"
155
+ end
156
+ end
157
+ YAML.object_maker( Product, val )
158
+ end
159
+
160
+ YAML.add_domain_type( 'jamisbuck.org,2004', 'sidebar' ) do |taguri, val|
161
+ YAML.object_maker( Sidebar,
162
+ 'title' => val.keys.first,
163
+ 'content'=> RedCloth.new( val.values.first ) )
164
+ end
165
+
166
+ end
167
+ end
168
+
169
+ if __FILE__ == $0
170
+
171
+ def log_action( action )
172
+ $stderr.puts action
173
+ end
174
+
175
+ unless ( output_path = ARGV[0] )
176
+ $stderr.puts "Usage: #{$0} [/path/to/save/html]"
177
+ exit
178
+ end
179
+
180
+ Dir.mkdir output_path rescue nil
181
+
182
+ log_action "Loading manual.yml..."
183
+ manual = Needle::Manual::Manual.load( 'manual.yml' )
184
+
185
+ # force these to be defined at the TOPLEVEL_BINDING
186
+ object = nil
187
+ guts = nil
188
+
189
+ page = File.open( "page.erb" ) { |file| ERB.new( file.read ) }
190
+ page.filename = "page.erb"
191
+
192
+ template = File.open( "index.erb" ) { |file| ERB.new( file.read ) }
193
+ template.filename = "index.erb"
194
+
195
+ File.open( File.join( output_path, "index.html" ), "w" ) do |file|
196
+ guts = template.result
197
+ file << page.result
198
+ end
199
+
200
+ template = File.open( "chapter.erb" ) { |file| ERB.new( file.read ) }
201
+ template.filename = "chapter.erb"
202
+
203
+ manual.chapters.each do |object|
204
+ log_action "Processing chapter ##{object.index}..."
205
+ File.open( File.join( output_path, "chapter-#{object.index}.html" ), "w" ) do |file|
206
+ guts = template.result
207
+ file << page.result
208
+ end
209
+ end
210
+
211
+ template = File.open( "tutorial.erb" ) { |file| ERB.new( file.read ) }
212
+ template.filename = "tutorial.erb"
213
+
214
+ manual.tutorials.each do |object|
215
+ log_action "Processing tutorial ##{object.index}..."
216
+ File.open( File.join( output_path, "tutorial-#{object.index}.html" ), "w" ) do |file|
217
+ guts = template.result
218
+ file << page.result
219
+ end
220
+ end
221
+
222
+ # template = File.open( "example.erb" ) { |file| ERB.new( file.read ) }
223
+ # template.filename = "example.erb"
224
+
225
+ # manual.examples.each do |object|
226
+ # log_action "Processing example ##{object.index}..."
227
+ # File.open( File.join( output_path, "example-#{object.index}.html" ), "w" ) do |file|
228
+ # guts = template.result
229
+ # file << page.result
230
+ # end
231
+ # end
232
+
233
+ log_action "Copying style sheets..."
234
+ FileUtils.cp Dir["*.css"], output_path
235
+
236
+ log_action "Copying images..."
237
+ FileUtils.cp Dir["img/*.jpg"], output_path
238
+
239
+ log_action "Done!"
240
+ end
@@ -0,0 +1,48 @@
1
+ --- !jamisbuck.org,2004/^manual
2
+
3
+ # This content is made available under the Attribution-ShareAlike 2.0
4
+ # license from the Create Commons:
5
+ #
6
+ # http://creativecommons.org/licenses/by-sa/2.0/
7
+
8
+ meta: !^meta
9
+ copyright: 2004
10
+ author: Jamis Buck
11
+ email: jgb3@email.byu.edu
12
+
13
+ product: !^product
14
+ name: Needle
15
+ tagline: to the point -->
16
+ version: !!eval require "../../lib/needle/version"; Needle::Version::STRING
17
+ #logo: needle.png
18
+ urls:
19
+ - Project Page: http://rubyforge.org/projects/needle
20
+ - User Manual: http://needle.rubyforge.org
21
+ - API Documentation: http://needle.rubyforge.org/api
22
+ - Needle Wiki: http://needle.rubyforge.org/wiki/wiki.pl
23
+
24
+ recent_updates:
25
+
26
+ chapters:
27
+
28
+ - Introduction:
29
+ - What is Needle?: !!file parts/01_what_is_needle.txt
30
+ - How Can It Help Me?: !!file parts/01_use_cases.txt
31
+ - License Information: !!file parts/01_license.txt
32
+ - Support: !!file parts/01_support.txt
33
+
34
+ - Registry:
35
+ - Overview: !!file parts/02_overview.txt
36
+ - Creating: !!file parts/02_creating.txt
37
+ - Services: !!file parts/02_services.txt
38
+ - Namespaces: !!file parts/02_namespaces.txt
39
+
40
+ - Dependency Injection:
41
+
42
+ - Interceptors:
43
+
44
+ - Service Models:
45
+
46
+ - Logging:
47
+
48
+ - Creating Libraries:
@@ -0,0 +1,86 @@
1
+ <html>
2
+ <head>
3
+ <title><%= manual.product.name %> Manual<% if object %> :: <%= object.page_title %><% end %></title>
4
+ <link type="text/css" rel="stylesheet" href="manual.css" />
5
+ </head>
6
+
7
+ <body>
8
+ <div id="banner">
9
+ <table border='0' cellpadding='0' cellspacing='0' width='100%'>
10
+ <tr><td valign='top' align='left'>
11
+ <div class="title">
12
+ <span class="product"><%= manual.product.name %>&mdash;</span><br />
13
+ <span class="tagline"><%= manual.product.tagline %></span>
14
+ </div>
15
+ </td><td valign='middle' align='right'>
16
+ <div class="info">
17
+ <%= manual.product.name %> Version: <strong><%= manual.product.version %></strong><br />
18
+ Manual Last Updated: <strong><%= Time.now.gmtime.strftime('%Y-%m-%d %H:%M %Z') %></strong>
19
+ </div>
20
+ </td></tr>
21
+ </table>
22
+ </div>
23
+
24
+ <table border='0' width='100%' cellpadding='0' cellspacing='0'>
25
+ <tr><td valign='top'>
26
+
27
+ <div id="navigation">
28
+ <h1><%= manual.product.name %> Manual</h1>
29
+
30
+ <h2>Chapters</h2>
31
+ <ol type="I">
32
+ <% manual.chapters.each do |c| %>
33
+ <li><%= "<strong>" if c == object %>
34
+ <a href="chapter-<%= c.index %>.html">
35
+ <%= c.title %>
36
+ </a>
37
+ <%= "</strong> <big>&larr;</big>" if c == object %>
38
+ <ol type="1">
39
+ <% c.sections.each do |s|
40
+ next unless s.title %>
41
+ <li><a href="chapter-<%= c.index %>.html#s<%= s.index %>"><%= s.title %></a></li>
42
+ <% end %>
43
+ </ol>
44
+ </li>
45
+ <% end %>
46
+ </ol>
47
+
48
+ <h2>API Reference</h2>
49
+
50
+ <ul>
51
+ <li><a href="http://needle.rubyforge.org/api/index.html">Needle API</a></li>
52
+ </ul>
53
+
54
+ <h2>Tutorials</h2>
55
+ <ol>
56
+ <% manual.tutorials.each do |t| %>
57
+ <li><%= "<strong>" if t == object %>
58
+ <a href="tutorial-<%= t.index %>.html">
59
+ <%= t.title %>
60
+ </a>
61
+ <%= "</strong> <big>&larr;</big>" if t == object %><br />
62
+ <%= t.brief.to_html %>
63
+ </li>
64
+ <% end %>
65
+ </ol>
66
+
67
+ <p align="center"><strong>More To Come...</strong></p>
68
+
69
+ <div class="license">
70
+ <a href="http://creativecommons.org/licenses/by-sa/2.0/"><img alt="Creative Commons License" border="0" src="http://creativecommons.org/images/public/somerights" /></a><br />
71
+ This manual is licensed under a <a href="http://creativecommons.org/licenses/by-sa/2.0/">Creative Commons License</a>.
72
+ </div>
73
+ </div>
74
+
75
+ </td><td valign='top' width="100%">
76
+
77
+ <div id="content">
78
+
79
+ <%= guts %>
80
+
81
+ </div>
82
+
83
+ </td></tr>
84
+ </table>
85
+ </body>
86
+ </html>
@@ -0,0 +1,5 @@
1
+ Needle is made available under either the BSD license, or the same license Ruby (which, by extension, also allows the GPL as a permissable license as well). You can view the full text of any of these licenses in the @doc@ subdirectory of the Needle distrubtion. The texts of the BSD and GPL licenses are also available online: "BSD":http://www.opensource.org/licenses/bsd-license.php and "GPL":http://www.opensource.org/licenses/gpl-license.php.
2
+
3
+ This manual (in any form, be it source or otherwise) and the scripts and templates used to generate it, are all distributed under the "Creative Commons":http://creativecommons.org "Attribution-ShareAlike":http://creativecommons.org/licenses/by-sa/2.0 license.
4
+
5
+ If you desire permission to use either Needle or the manual in a manner incompatible with these licenses, please contact the copyright holder ("Jamis Buck":mailto:jgb3@email.byu.edu) in order to negotiate a more compatible license.
@@ -0,0 +1 @@
1
+ Mailing lists, bug trackers, feature requests, and public forums are available (courtesy of "RubyForge":http://rubyforge.org) at Needle's RubyForge project page. Just direct your browser to "http://rubyforge.org/projects/needle":http://rubyforge.org/projects/needle.
@@ -0,0 +1,141 @@
1
+ So, what can Needle do for you? Ultimately, it can reduce the amount of code that you have to write, simplifying many common programming tasks for you. This has the two-fold benefit of both decreasing application development time, and of decreasing the effort needed to maintain your application.
2
+
3
+ But what, _specifically_, can Needle do for you?
4
+
5
+ Try these on for size:
6
+
7
+ * "Log Method Execution":#logexec
8
+ * "Reference Another Service":#refsvc
9
+ * "Unit Testing":#unittest
10
+ * "Lifecycle Management":#lifecycle
11
+
12
+ (Thanks to Howard Lewis Ship for his "HiveMind":http://jakarta.apache.org/hivemind documentation, from which some of the above bullet points were adapted.)
13
+
14
+
15
+ h3. Log Method Execution <a name="#logexec"></a>
16
+
17
+ Needle has an integrated logging framework, and the ability to log execution trace information without modifying a single line of code in your classes. This means that you can easily see what methods get called, with what arguments, and what the return values are, all without having to physically modify any of your classes.
18
+
19
+ Consider the following code, demonstrating how this would be done without Needle:
20
+
21
+ <pre>
22
+ def foo( arg1, arg2 )
23
+ @log.debug( "in foo with #{arg1} and #{arg2}" ) if @log.debug?
24
+ ...
25
+ result = the_result_of_the_method
26
+ @log.debug( "finishing foo with #{result}" ) if @log.debug
27
+ return result
28
+ rescue Exception => e
29
+ @log.debug( "foo raised exception #{e.message} (#{e.class})" ) if @log.debug?
30
+ raise
31
+ end
32
+ </pre>
33
+
34
+ Now, multiply that by the number of methods in your class... the logging messages quickly overpower the rest of the code, and detract from the flow of your program. This makes your program harder to debug, test, and maintain.
35
+
36
+ Now, consider the same method using Needle's integrated logging framework...
37
+
38
+ <pre>
39
+ def foo( arg1, arg2 )
40
+ ...
41
+ return the_result_of_the_method
42
+ end
43
+ </pre>
44
+
45
+ Then, when you define the service that you want to add the logging to:
46
+
47
+ <pre>
48
+ registry.register( :service_name_here ) { |reg| ... }
49
+ registry.intercept( :service_name_here ).with! { logging_interceptor }
50
+ </pre>
51
+
52
+ That's right. There's no explicit logging code in there. Instead, you just tell Needle that the methods of the class should be logged, and away it goes. This has the added benefit of allowing your objects to be unit tested, without spewing log messages everywhere.
53
+
54
+ h3. Reference Another Service <a name="#refsvc"></a>
55
+
56
+ Invariably in a large application services will reference other services. This is typically accomplished through something like this:
57
+
58
+ <pre>
59
+ class Component
60
+ ...
61
+ def foo( parms )
62
+ @service ||= lookup_service
63
+ @service.do_something( parms )
64
+ end
65
+
66
+ def lookup_service
67
+ ...
68
+ end
69
+ ...
70
+ end
71
+ </pre>
72
+
73
+ Whether the lookup is done lazily, as shown above, or when the class is first instantiated is irrelevant. The point is that you either have to implement a bunch of code to look up a service based on some criteria, or you hard code the class of the service (which creates tight coupling and makes things like unit testing harder).
74
+
75
+ With Needle, you just declare a setter for the service, and then tell Needle that the class depends on the other service:
76
+
77
+ <pre>
78
+ class Component
79
+ attr_writer :service
80
+ ...
81
+ def foo( parms )
82
+ @service.do_something( parms )
83
+ end
84
+ ...
85
+ end
86
+
87
+ registry.register( :component ) do |reg|
88
+ c = Component.new
89
+ c.service = reg.some_other_service
90
+ c
91
+ end
92
+ </pre>
93
+
94
+ Then, when your service is instantiated, Needle will automatically look for and instantiate the dependencies for you. This makes for cleaner code, and looser coupling between services.
95
+
96
+ h3. Unit Testing <a name="#unittest"></a>
97
+
98
+ Large applications can prove troublesome to unit test exhaustively, especially if there is any kind of tight coupling between components. Such coupling of components can make it difficult to test them separately.
99
+
100
+ Needle, by its very nature, encourages loose coupling of components. Also, because dependencies are never instantiated in code, but are instead accepted via setters or constructor arguments, it is trivial to replace those dependencies with mock objects at unit test time.
101
+
102
+ Consider this tightly coupled example:
103
+
104
+ <pre>
105
+ def foo( args )
106
+ @some_dependency ||= MyNewDependency.new
107
+ @some_dependency.do_something(args)
108
+ end
109
+ </pre>
110
+
111
+ It is impossible to test the method @#foo@ without also testing the MyNewDependency class. However, if the @@some_dependency@ object is made a property that is set externally, you can replace it at test time with a blank:
112
+
113
+ <pre>
114
+ attr_writer :some_dependency
115
+
116
+ def foo( args )
117
+ @some_dependency.do_something( args )
118
+ end
119
+ </pre>
120
+
121
+ The unit test would become something like this:
122
+
123
+ <pre>
124
+ def test_foo
125
+ @obj.some_dependecy = MyMockDependency.new
126
+ @obj.foo( args )
127
+ assert @obj.is_in_some_state
128
+ end
129
+ </pre>
130
+
131
+ h3. Lifecycle Management <a name="#lifecycle"></a>
132
+
133
+ Singleton objects are a fact of life in complex systems. The singleton design pattern is powerful and useful. However, using the Singleton mixin, or declaring methods at the class level, can make your code difficult to unit test since the state of such objects cannot be easily reset.
134
+
135
+ Needle has a solution. You can tell Needle to treat a service as either a _prototype_ service (meaning it will be instantiated every time you ask for it, like calling @#new@), or a _singleton_ service (meaning it will only be instantiated once, and the same instance will be returned for subsequent requests).
136
+
137
+ Your object is still just a plain ol' ordinary Ruby object, but Needle has effectively transformed it into a singleton. This means you can unit test it as if it were nothing special, but when it is used in your application it will act like a singleton.
138
+
139
+ Lifecycle management also means that you can control _when_ a service is instantiated. The _prototype_ and _singleton_ models will always be instantiated as soon as they are requested. Sometimes, though, you don't want that--you'd like the instantiation to be deferred as late as possible.
140
+
141
+ With Needle, you can indicate that a service should use deferred instantiation. This will cause the service to not actually be instantiated until a method is actually invoked on it. Using this model, you can have services depend on themselves, or other forms of cyclical dependencies.
@@ -0,0 +1 @@
1
+ Needle is a dependency injection (also, inversion of control) container for "Ruby":http://www.ruby-lang.org.
@@ -0,0 +1,9 @@
1
+ Creating a registry is as simple as calling @Needle::Registry.new@. This will give you a new registry object, bootstrapped to contain a few general services.
2
+
3
+ <pre>
4
+ require 'needle'
5
+
6
+ registry = Needle::Registry.new
7
+ </pre>
8
+
9
+ Once you have the reference to the registry, you can register services with it, create new namespaces in it, and so forth.
@@ -0,0 +1,47 @@
1
+ Namespaces allow you to organize your services. The feature has many different applications, including:
2
+
3
+ # Third-parties may distribute Needle-enabled libraries without worrying about their choice of service names conflicting with the service names of their clients.
4
+ # Developers may organize complex applications into modules, and the service definitions may be stored in the registry to reflect that organization.
5
+ # Services deeper in the hierarchy may override services higher up.
6
+
7
+ Creating a namespace is as easy as invoking the @#namespace@ method of the registry (or of another namespace):
8
+
9
+ <pre>
10
+ registry.namespace :stuff
11
+ </pre>
12
+
13
+ This would create a new namespace in the registry called @:stuff@. The application may then proceed to register services inside that namespace:
14
+
15
+ <pre>
16
+ registry.stuff.register( :foo ) { Bar.new }
17
+ ...
18
+ svc = registry.stuff.foo
19
+ </pre>
20
+
21
+ Here's a tip: _namespaces are just a special kind of service._ This means that you can access namespaces in the same ways that you can access services:
22
+
23
+ <pre>
24
+ svc = registry[:stuff][:foo]
25
+ </pre>
26
+
27
+ h3. Convenience Methods
28
+
29
+ Because it is often the case that you will be creating a namespace and then immediately registering services on it, you can pass a block to @namespace@. The block will receive a reference to the new namespace:
30
+
31
+ <pre>
32
+ registry.namespace :stuff do |spc|
33
+ spc.register( :foo ) { Bar.new }
34
+ ...
35
+ end
36
+ </pre>
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.
39
+
40
+ <pre>
41
+ registry.namespace! :stuff do
42
+ foo { Bar.new }
43
+ ...
44
+ end
45
+ </pre>
46
+
47
+ The above code would create a new namespace called @:stuff@ in the registry, and would then proceed to register a service called @:foo@ in the new namespace.
@@ -0,0 +1,3 @@
1
+ The registry is at the heart of any dependency-injected application or library. All services are registered with the registry, so that when an application needs an instance of a particular service, it may obtain that service reference by querying the registry.
2
+
3
+ In order to use Needle, you only really _need_ to understand how to create and manipulate registry objects.
@@ -0,0 +1,44 @@
1
+ Registering services with a Needle registry is very straightforward. The simplest way to do it is:
2
+
3
+ <pre>
4
+ registry.register( :foo ) { Bar.new }
5
+ </pre>
6
+
7
+ The above will register a new service with the registry, naming it @:foo@. When @:foo@ is requested from the registry, a new instance of @Bar@ will be instantiated and returned.
8
+
9
+ You get services from the registry in either of two ways:
10
+
11
+ <pre>
12
+ # Treating the registry as a Hash
13
+ svc = registry[:foo]
14
+
15
+ # Treating the service as a property of the registry
16
+ svc = registry.foo
17
+ </pre>
18
+
19
+ h3. Convenience Methods
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:
22
+
23
+ <pre>
24
+ registry.register! do
25
+ foo { Bar.new }
26
+ bar { Foo.new }
27
+ ...
28
+ end
29
+ </pre>
30
+
31
+ The above will register two new services with the registry, @:foo@ and @:bar@.
32
+
33
+ h3. Default Lifecycle
34
+
35
+ By default, a service is only instantiated once per registry. This means that (using the above example) if the service @:foo@ were queried twice, the registry would return the same object for both queries:
36
+
37
+ <pre>
38
+ svc1 = registry.foo
39
+ svc2 = registry.foo
40
+
41
+ p svc1.object_id == svc2.object_id #=> true
42
+ </pre>
43
+
44
+ You can change this behavior, with _service models_. See the chapter on Service Models for more information.
@@ -0,0 +1,30 @@
1
+ <h1>Tutorial #<%= object.index %>. <%= object.title %></h1>
2
+
3
+ <p>The sources for this tutorial may be found in the <tt>tutorial/<%= "%02d" % object.index %></tt>
4
+ directory of the Copland distribution.</p>
5
+
6
+ <% if object.intro %>
7
+
8
+ <h2>Introduction</h2>
9
+
10
+ <%= object.intro.to_html %>
11
+
12
+ <% end %>
13
+
14
+ <h2>Steps</h2>
15
+
16
+ <ol>
17
+ <% object.steps.each do |step| %>
18
+
19
+ <li><%= step.to_html %></li>
20
+
21
+ <% end %>
22
+ </ol>
23
+
24
+ <% if object.summary %>
25
+
26
+ <h2>Summary</h2>
27
+
28
+ <%= object.summary.to_html %>
29
+
30
+ <% end %>