copland 0.8.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/manual-html/chapter-1.html +227 -36
- data/doc/manual-html/chapter-10.html +155 -82
- data/doc/manual-html/chapter-11.html +90 -267
- data/doc/manual-html/chapter-12.html +289 -71
- data/doc/manual-html/chapter-13.html +430 -0
- data/doc/manual-html/chapter-2.html +45 -21
- data/doc/manual-html/chapter-3.html +45 -21
- data/doc/manual-html/chapter-4.html +45 -21
- data/doc/manual-html/chapter-5.html +45 -21
- data/doc/manual-html/chapter-6.html +49 -21
- data/doc/manual-html/chapter-7.html +45 -21
- data/doc/manual-html/chapter-8.html +66 -26
- data/doc/manual-html/chapter-9.html +48 -24
- data/doc/manual-html/index.html +54 -22
- data/doc/manual-html/manual.css +12 -0
- data/doc/manual-html/tutorial-1.html +45 -21
- data/doc/manual-html/tutorial-2.html +45 -21
- data/doc/manual-html/tutorial-3.html +45 -21
- data/doc/manual-html/tutorial-4.html +45 -21
- data/doc/manual-html/tutorial-5.html +45 -21
- data/doc/manual/manual.css +12 -0
- data/doc/manual/manual.rb +1 -1
- data/doc/manual/manual.yml +426 -20
- data/doc/packages/copland.html +41 -9
- data/doc/packages/copland.lib.html +36 -8
- data/doc/packages/copland.remote.html +46 -10
- data/doc/packages/copland.webrick.html +16 -65
- data/doc/packages/index.html +1 -1
- data/doc/presentation/copland.mgp +1083 -0
- data/doc/presentation/to_html.rb +52 -0
- data/lib/copland/configuration-point/common.rb +32 -1
- data/lib/copland/configuration/yaml/service-point.rb +10 -1
- data/lib/copland/log-factory.rb +28 -12
- data/lib/copland/logger.rb +155 -0
- data/lib/copland/models/singleton.rb +8 -2
- data/lib/copland/package.rb +32 -14
- data/lib/copland/service-point.rb +7 -0
- data/lib/copland/thread.rb +104 -0
- data/lib/copland/utils.rb +10 -3
- data/lib/copland/version.rb +2 -2
- data/test/configuration/yaml/tc_service-point-processor.rb +8 -0
- data/test/custom-logger.yml +2 -1
- data/test/impl/tc_logging-interceptor.rb +12 -12
- data/test/logger.yml +1 -1
- data/test/mock.rb +2 -0
- data/test/tc_logger.rb +19 -6
- data/test/tc_package.rb +25 -0
- data/test/tc_queryable-mutex.rb +75 -0
- data/test/tc_registry.rb +8 -4
- metadata +9 -2
@@ -14,8 +14,8 @@
|
|
14
14
|
</div>
|
15
15
|
</td><td valign='middle' align='right'>
|
16
16
|
<div class="info">
|
17
|
-
Copland Version: <strong>0.
|
18
|
-
Manual Last Updated: <strong>2004-
|
17
|
+
Copland Version: <strong>1.0.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-10-12 02:22 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -39,13 +39,21 @@
|
|
39
39
|
|
40
40
|
<li><a href="chapter-1.html#s1">What is Copland?</a></li>
|
41
41
|
|
42
|
-
<li><a href="chapter-1.html#s2">
|
42
|
+
<li><a href="chapter-1.html#s2">What Can Copland Do For Me?</a></li>
|
43
43
|
|
44
|
-
<li><a href="chapter-1.html#s3">
|
44
|
+
<li><a href="chapter-1.html#s3">Copland <em>sans</em> Buzzwords</a></li>
|
45
45
|
|
46
|
-
<li><a href="chapter-1.html#s4">
|
46
|
+
<li><a href="chapter-1.html#s4">Copland <em>avec</em> Buzzwords</a></li>
|
47
47
|
|
48
|
-
<li><a href="chapter-1.html#s5">
|
48
|
+
<li><a href="chapter-1.html#s5">The Buzzwords Themselves</a></li>
|
49
|
+
|
50
|
+
<li><a href="chapter-1.html#s6">Features</a></li>
|
51
|
+
|
52
|
+
<li><a href="chapter-1.html#s7">Getting Copland</a></li>
|
53
|
+
|
54
|
+
<li><a href="chapter-1.html#s8">License Information</a></li>
|
55
|
+
|
56
|
+
<li><a href="chapter-1.html#s9">Support</a></li>
|
49
57
|
|
50
58
|
</ol>
|
51
59
|
</li>
|
@@ -149,7 +157,9 @@
|
|
149
157
|
|
150
158
|
<li><a href="chapter-8.html#s1">Descriptor Syntax</a></li>
|
151
159
|
|
152
|
-
<li><a href="chapter-8.html#s2">
|
160
|
+
<li><a href="chapter-8.html#s2">FactoryDefaults</a></li>
|
161
|
+
|
162
|
+
<li><a href="chapter-8.html#s3">ApplicationDefaults</a></li>
|
153
163
|
|
154
164
|
</ol>
|
155
165
|
</li>
|
@@ -166,54 +176,68 @@
|
|
166
176
|
|
167
177
|
<li>
|
168
178
|
<a href="chapter-10.html">
|
179
|
+
Logging
|
180
|
+
</a>
|
181
|
+
|
182
|
+
<ol type="1">
|
183
|
+
|
184
|
+
<li><a href="chapter-10.html#s1">Log Factory</a></li>
|
185
|
+
|
186
|
+
<li><a href="chapter-10.html#s2">Configuration</a></li>
|
187
|
+
|
188
|
+
</ol>
|
189
|
+
</li>
|
190
|
+
|
191
|
+
<li>
|
192
|
+
<a href="chapter-11.html">
|
169
193
|
Service Factories
|
170
194
|
</a>
|
171
195
|
|
172
196
|
<ol type="1">
|
173
197
|
|
174
|
-
<li><a href="chapter-
|
198
|
+
<li><a href="chapter-11.html#s1">Schemas</a></li>
|
175
199
|
|
176
|
-
<li><a href="chapter-
|
200
|
+
<li><a href="chapter-11.html#s2">How do they work?</a></li>
|
177
201
|
|
178
|
-
<li><a href="chapter-
|
202
|
+
<li><a href="chapter-11.html#s3">BuilderFactory</a></li>
|
179
203
|
|
180
204
|
</ol>
|
181
205
|
</li>
|
182
206
|
|
183
207
|
<li>
|
184
|
-
<a href="chapter-
|
208
|
+
<a href="chapter-12.html">
|
185
209
|
Schemas
|
186
210
|
</a>
|
187
211
|
|
188
212
|
<ol type="1">
|
189
213
|
|
190
|
-
<li><a href="chapter-
|
214
|
+
<li><a href="chapter-12.html#s1">Basic Format</a></li>
|
191
215
|
|
192
|
-
<li><a href="chapter-
|
216
|
+
<li><a href="chapter-12.html#s2">Subschemas</a></li>
|
193
217
|
|
194
|
-
<li><a href="chapter-
|
218
|
+
<li><a href="chapter-12.html#s3">Arrays</a></li>
|
195
219
|
|
196
|
-
<li><a href="chapter-
|
220
|
+
<li><a href="chapter-12.html#s4">Named vs. Anonymous Schemas</a></li>
|
197
221
|
|
198
|
-
<li><a href="chapter-
|
222
|
+
<li><a href="chapter-12.html#s5">Extending Schemas</a></li>
|
199
223
|
|
200
|
-
<li><a href="chapter-
|
224
|
+
<li><a href="chapter-12.html#s6">Limitations</a></li>
|
201
225
|
|
202
226
|
</ol>
|
203
227
|
</li>
|
204
228
|
|
205
229
|
<li>
|
206
|
-
<a href="chapter-
|
230
|
+
<a href="chapter-13.html">
|
207
231
|
Listeners and Event Producers
|
208
232
|
</a>
|
209
233
|
|
210
234
|
<ol type="1">
|
211
235
|
|
212
|
-
<li><a href="chapter-
|
236
|
+
<li><a href="chapter-13.html#s1">Event Producers</a></li>
|
213
237
|
|
214
|
-
<li><a href="chapter-
|
238
|
+
<li><a href="chapter-13.html#s2">Listeners</a></li>
|
215
239
|
|
216
|
-
<li><a href="chapter-
|
240
|
+
<li><a href="chapter-13.html#s3">The Registry as an Event Producer</a></li>
|
217
241
|
|
218
242
|
</ol>
|
219
243
|
</li>
|
@@ -14,8 +14,8 @@
|
|
14
14
|
</div>
|
15
15
|
</td><td valign='middle' align='right'>
|
16
16
|
<div class="info">
|
17
|
-
Copland Version: <strong>0.
|
18
|
-
Manual Last Updated: <strong>2004-
|
17
|
+
Copland Version: <strong>1.0.0</strong><br />
|
18
|
+
Manual Last Updated: <strong>2004-10-12 02:22 GMT</strong>
|
19
19
|
</div>
|
20
20
|
</td></tr>
|
21
21
|
</table>
|
@@ -39,13 +39,21 @@
|
|
39
39
|
|
40
40
|
<li><a href="chapter-1.html#s1">What is Copland?</a></li>
|
41
41
|
|
42
|
-
<li><a href="chapter-1.html#s2">
|
42
|
+
<li><a href="chapter-1.html#s2">What Can Copland Do For Me?</a></li>
|
43
43
|
|
44
|
-
<li><a href="chapter-1.html#s3">
|
44
|
+
<li><a href="chapter-1.html#s3">Copland <em>sans</em> Buzzwords</a></li>
|
45
45
|
|
46
|
-
<li><a href="chapter-1.html#s4">
|
46
|
+
<li><a href="chapter-1.html#s4">Copland <em>avec</em> Buzzwords</a></li>
|
47
47
|
|
48
|
-
<li><a href="chapter-1.html#s5">
|
48
|
+
<li><a href="chapter-1.html#s5">The Buzzwords Themselves</a></li>
|
49
|
+
|
50
|
+
<li><a href="chapter-1.html#s6">Features</a></li>
|
51
|
+
|
52
|
+
<li><a href="chapter-1.html#s7">Getting Copland</a></li>
|
53
|
+
|
54
|
+
<li><a href="chapter-1.html#s8">License Information</a></li>
|
55
|
+
|
56
|
+
<li><a href="chapter-1.html#s9">Support</a></li>
|
49
57
|
|
50
58
|
</ol>
|
51
59
|
</li>
|
@@ -149,7 +157,9 @@
|
|
149
157
|
|
150
158
|
<li><a href="chapter-8.html#s1">Descriptor Syntax</a></li>
|
151
159
|
|
152
|
-
<li><a href="chapter-8.html#s2">
|
160
|
+
<li><a href="chapter-8.html#s2">FactoryDefaults</a></li>
|
161
|
+
|
162
|
+
<li><a href="chapter-8.html#s3">ApplicationDefaults</a></li>
|
153
163
|
|
154
164
|
</ol>
|
155
165
|
</li>
|
@@ -166,54 +176,68 @@
|
|
166
176
|
|
167
177
|
<li>
|
168
178
|
<a href="chapter-10.html">
|
179
|
+
Logging
|
180
|
+
</a>
|
181
|
+
|
182
|
+
<ol type="1">
|
183
|
+
|
184
|
+
<li><a href="chapter-10.html#s1">Log Factory</a></li>
|
185
|
+
|
186
|
+
<li><a href="chapter-10.html#s2">Configuration</a></li>
|
187
|
+
|
188
|
+
</ol>
|
189
|
+
</li>
|
190
|
+
|
191
|
+
<li>
|
192
|
+
<a href="chapter-11.html">
|
169
193
|
Service Factories
|
170
194
|
</a>
|
171
195
|
|
172
196
|
<ol type="1">
|
173
197
|
|
174
|
-
<li><a href="chapter-
|
198
|
+
<li><a href="chapter-11.html#s1">Schemas</a></li>
|
175
199
|
|
176
|
-
<li><a href="chapter-
|
200
|
+
<li><a href="chapter-11.html#s2">How do they work?</a></li>
|
177
201
|
|
178
|
-
<li><a href="chapter-
|
202
|
+
<li><a href="chapter-11.html#s3">BuilderFactory</a></li>
|
179
203
|
|
180
204
|
</ol>
|
181
205
|
</li>
|
182
206
|
|
183
207
|
<li>
|
184
|
-
<a href="chapter-
|
208
|
+
<a href="chapter-12.html">
|
185
209
|
Schemas
|
186
210
|
</a>
|
187
211
|
|
188
212
|
<ol type="1">
|
189
213
|
|
190
|
-
<li><a href="chapter-
|
214
|
+
<li><a href="chapter-12.html#s1">Basic Format</a></li>
|
191
215
|
|
192
|
-
<li><a href="chapter-
|
216
|
+
<li><a href="chapter-12.html#s2">Subschemas</a></li>
|
193
217
|
|
194
|
-
<li><a href="chapter-
|
218
|
+
<li><a href="chapter-12.html#s3">Arrays</a></li>
|
195
219
|
|
196
|
-
<li><a href="chapter-
|
220
|
+
<li><a href="chapter-12.html#s4">Named vs. Anonymous Schemas</a></li>
|
197
221
|
|
198
|
-
<li><a href="chapter-
|
222
|
+
<li><a href="chapter-12.html#s5">Extending Schemas</a></li>
|
199
223
|
|
200
|
-
<li><a href="chapter-
|
224
|
+
<li><a href="chapter-12.html#s6">Limitations</a></li>
|
201
225
|
|
202
226
|
</ol>
|
203
227
|
</li>
|
204
228
|
|
205
229
|
<li>
|
206
|
-
<a href="chapter-
|
230
|
+
<a href="chapter-13.html">
|
207
231
|
Listeners and Event Producers
|
208
232
|
</a>
|
209
233
|
|
210
234
|
<ol type="1">
|
211
235
|
|
212
|
-
<li><a href="chapter-
|
236
|
+
<li><a href="chapter-13.html#s1">Event Producers</a></li>
|
213
237
|
|
214
|
-
<li><a href="chapter-
|
238
|
+
<li><a href="chapter-13.html#s2">Listeners</a></li>
|
215
239
|
|
216
|
-
<li><a href="chapter-
|
240
|
+
<li><a href="chapter-13.html#s3">The Registry as an Event Producer</a></li>
|
217
241
|
|
218
242
|
</ol>
|
219
243
|
</li>
|
data/doc/manual/manual.css
CHANGED
@@ -148,6 +148,18 @@ a:hover {
|
|
148
148
|
margin-bottom: 1em;
|
149
149
|
}
|
150
150
|
|
151
|
+
#content h4 {
|
152
|
+
background: #FFE;
|
153
|
+
color: #000;
|
154
|
+
font-size: normal;
|
155
|
+
font-weight: bold;
|
156
|
+
font-variant: small-caps;
|
157
|
+
padding: 0.25em;
|
158
|
+
padding-left: 0.5em;
|
159
|
+
border: 1px dotted #777;
|
160
|
+
margin-bottom: 1em;
|
161
|
+
}
|
162
|
+
|
151
163
|
#navigation h1 {
|
152
164
|
margin: 0px;
|
153
165
|
padding: 1em;
|
data/doc/manual/manual.rb
CHANGED
data/doc/manual/manual.yml
CHANGED
@@ -19,22 +19,248 @@ product: !^product
|
|
19
19
|
project: http://rubyforge.org/projects/copland
|
20
20
|
|
21
21
|
recent_updates:
|
22
|
-
-
|
22
|
+
- documented the new "visibility" attribute of service points
|
23
|
+
- added chapter about Logging configuration
|
24
|
+
- fixed misinformation about symbol sources
|
25
|
+
- added discussion about FactoryDefaults vs. ApplicationDefaults
|
26
|
+
- added (HiveMind-inspired) section in Introduction describing concrete benefits of Copland
|
23
27
|
|
24
28
|
chapters:
|
25
29
|
|
26
30
|
- Introduction:
|
27
31
|
- What is Copland?: >
|
28
32
|
Copland is an _Inversion of Control_ (IoC) container, written in
|
29
|
-
"Ruby":http://www.ruby-lang.org, for use in Ruby programs.
|
30
|
-
|
31
|
-
|
32
|
-
one after it ("Copland _avec_ Buzzwords").
|
33
|
+
"Ruby":http://www.ruby-lang.org, for use in Ruby programs. In a nutshell,
|
34
|
+
it allows you to simplify the instantiation and initialization of your
|
35
|
+
classes.
|
33
36
|
|
34
37
|
|
35
|
-
|
38
|
+
- What Can Copland Do For Me?: >
|
39
|
+
So, what can Copland do for you? Ultimately, it can reduce the amount of code
|
40
|
+
that you have to write, simplifying many common programming tasks for you. This
|
41
|
+
has the two-fold benefit of both decreasing application development time, and of
|
42
|
+
decreasing the effort needed to maintain your application.
|
36
43
|
|
37
44
|
|
45
|
+
But what, _specifically_, can Copland do for you?
|
46
|
+
|
47
|
+
|
48
|
+
Try these on for size:
|
49
|
+
|
50
|
+
|
51
|
+
* "Log Method Execution":#logmethods
|
52
|
+
|
53
|
+
* "Reference Another Service":#refsvc
|
54
|
+
|
55
|
+
* "Service Configuration":#svcconfig
|
56
|
+
|
57
|
+
* "Unit Testing":#unittest
|
58
|
+
|
59
|
+
* "Lifecycle Management":#lifecycle
|
60
|
+
|
61
|
+
|
62
|
+
(Thanks to Howard Lewis Ship for his "HiveMind":http://jakarta.apache.org/hivemind
|
63
|
+
documentation, from which most of the above bullet points were adapted.)
|
64
|
+
|
65
|
+
|
66
|
+
h3. A. Log Method Execution
|
67
|
+
<a name="logmethods"></a>
|
68
|
+
|
69
|
+
|
70
|
+
Copland has an integrated logging framework, and the ability to log
|
71
|
+
execution trace information _without modifying a single line of your code_. This
|
72
|
+
means that you can easily see what methods get called, with what arguments,
|
73
|
+
and what the return values are, all without having to add a single line of
|
74
|
+
logging code to your classes.
|
75
|
+
|
76
|
+
|
77
|
+
Consider the following code, demonstrating how this would be done _without_
|
78
|
+
Copland:
|
79
|
+
|
80
|
+
|
81
|
+
<pre>
|
82
|
+
def foo( arg1, arg2 )
|
83
|
+
@log.debug( "in foo with #{arg1} and #{arg2}" ) if @log.debug?
|
84
|
+
...
|
85
|
+
result = the_result_of_the_method
|
86
|
+
@log.debug( "finishing foo with #{result}" ) if @log.debug
|
87
|
+
return result
|
88
|
+
rescue Exception => e
|
89
|
+
@log.debug( "foo raised exception #{e.message} (#{e.class})" ) if @log.debug?
|
90
|
+
raise
|
91
|
+
end
|
92
|
+
</pre>
|
93
|
+
|
94
|
+
|
95
|
+
Now, multiply that by the number of methods in your class... the logging messages
|
96
|
+
quickly overpower the rest of the code, and detract from the flow of your program.
|
97
|
+
This makes your program harder to debug, test, and maintain.
|
98
|
+
|
99
|
+
|
100
|
+
Now, consider the same method using Copland's integrated logging framework...
|
101
|
+
|
102
|
+
|
103
|
+
<pre>
|
104
|
+
def foo( arg1, arg2 )
|
105
|
+
...
|
106
|
+
return the_result_of_the_method
|
107
|
+
end
|
108
|
+
</pre>
|
109
|
+
|
110
|
+
|
111
|
+
Uh, huh. That's right. _There's no explicit logging code in there._ Instead, you just
|
112
|
+
tell Copland that the methods of the class should be logged, and away it goes. This has
|
113
|
+
the added benefit of allowing your objects to be unit tested, without spewing log
|
114
|
+
messages everywhere. (Look for the tutorial about "Logging Interceptors" for an actual
|
115
|
+
demonstration of how to do this.)
|
116
|
+
|
117
|
+
|
118
|
+
h3. B. Reference Another Service
|
119
|
+
<a name="refsvc"></a>
|
120
|
+
|
121
|
+
|
122
|
+
Invariably in a large application services will reference other services. This is
|
123
|
+
typically accomplished through something like this:
|
124
|
+
|
125
|
+
|
126
|
+
<pre>
|
127
|
+
def foo( parms )
|
128
|
+
@service ||= lookup_service
|
129
|
+
@service.do_something( parms )
|
130
|
+
end
|
131
|
+
</pre>
|
132
|
+
|
133
|
+
|
134
|
+
Whether the lookup is done lazily, as shown above, or when the class is first
|
135
|
+
instantiated is irrelevant. The point is that you either have to implement a bunch
|
136
|
+
of code to look up a service based on some criteria, or you hard code the class of
|
137
|
+
the service (which creates tight coupling and makes things like unit testing harder).
|
138
|
+
|
139
|
+
|
140
|
+
With Copland, you just declare a setter for the service, and then tell Copland that
|
141
|
+
the class depends on the other service:
|
142
|
+
|
143
|
+
|
144
|
+
<pre>
|
145
|
+
attr_writer :service
|
146
|
+
|
147
|
+
def foo( parms )
|
148
|
+
@service.do_something( parms )
|
149
|
+
end
|
150
|
+
</pre>
|
151
|
+
|
152
|
+
|
153
|
+
Then, when your service is instantiated, Copland will automatically look for,
|
154
|
+
instantiate, and set the dependencies for you. This makes for cleaner code, and
|
155
|
+
looser coupling between services.
|
156
|
+
|
157
|
+
|
158
|
+
h3. C. Service Configuration
|
159
|
+
<a name="svcconfig"></a>
|
160
|
+
|
161
|
+
|
162
|
+
Often, a large application or library will need some way to allow different classes
|
163
|
+
to be configured at runtime. Whether you have a factory class that requires the
|
164
|
+
available producable classes to register themselves with it, or whether you just want
|
165
|
+
to allow third-parties to be able to extend your application, you wind up implementing
|
166
|
+
some form of decentralized configuration.
|
167
|
+
|
168
|
+
|
169
|
+
Copland allows you to define _configuration points_, which any package may then
|
170
|
+
contribute values to. Furthermore, just as services can reference other services,
|
171
|
+
services can reference configuration points, and Copland will manage them just like
|
172
|
+
any other dependency.
|
173
|
+
|
174
|
+
|
175
|
+
See the tutorial about "Configuration Points" for an example of this feature.
|
176
|
+
|
177
|
+
|
178
|
+
h3. D. Unit Testing
|
179
|
+
<a name="unittest"></a>
|
180
|
+
|
181
|
+
|
182
|
+
Large applications can prove troublesome to unit test exhaustively, especially if there
|
183
|
+
is any kind of tight coupling between components. Such coupling of components can make
|
184
|
+
it difficult to test them separately.
|
185
|
+
|
186
|
+
|
187
|
+
Copland, by its very nature, encourages loose coupling of components. Also, because
|
188
|
+
dependencies are never instantiated in code, but are instead accepted via setters or
|
189
|
+
constructor arguments, it is trivial to replace those dependencies with mock objects
|
190
|
+
at unit test time.
|
191
|
+
|
192
|
+
|
193
|
+
Consider this tightly coupled example:
|
194
|
+
|
195
|
+
|
196
|
+
<pre>
|
197
|
+
def foo( args )
|
198
|
+
@some_dependency ||= MyNewDependency.new
|
199
|
+
@some_dependency.do_something(args)
|
200
|
+
end
|
201
|
+
</pre>
|
202
|
+
|
203
|
+
|
204
|
+
It is impossible to test the method @#foo@ without also testing the MyNewDependency
|
205
|
+
class. However, if the @@some_dependency@ object is made a property that is set
|
206
|
+
externally, you can replace it at test time with a blank:
|
207
|
+
|
208
|
+
|
209
|
+
<pre>
|
210
|
+
attr_writer :some_dependency
|
211
|
+
|
212
|
+
def foo( args )
|
213
|
+
@some_dependency.do_something( args )
|
214
|
+
end
|
215
|
+
</pre>
|
216
|
+
|
217
|
+
|
218
|
+
The unit test would become something like this:
|
219
|
+
|
220
|
+
|
221
|
+
<pre>
|
222
|
+
def test_foo
|
223
|
+
@obj.some_dependecy = MyMockDependency.new
|
224
|
+
@obj.foo( args )
|
225
|
+
assert @obj.is_in_some_state
|
226
|
+
end
|
227
|
+
</pre>
|
228
|
+
|
229
|
+
|
230
|
+
h3. E. Lifecycle Management
|
231
|
+
<a name="lifecycle"></a>
|
232
|
+
|
233
|
+
|
234
|
+
Singleton objects are a fact of life in complex systems. The singleton design pattern
|
235
|
+
is powerful and useful. However, using the Singleton mixin, or declaring methods at the
|
236
|
+
class level, can make your code difficult to unit test since the state of such objects
|
237
|
+
cannot be easily reset.
|
238
|
+
|
239
|
+
|
240
|
+
Copland has a solution. You can tell Copland to treat a service as either a _prototype_
|
241
|
+
service (meaning it will be instantiated every time you ask for it, like calling @#new@),
|
242
|
+
or a _singleton_ service (meaning it will only be instantiated once, and the same instance
|
243
|
+
will be returned for subsequent requests).
|
244
|
+
|
245
|
+
|
246
|
+
Your object is still just a plain ol' ordinary Ruby object, but Copland has effectively
|
247
|
+
transformed it into a singleton. This means you can unit test it as if it were nothing
|
248
|
+
special, but when it is used in your application it will act like a singleton.
|
249
|
+
|
250
|
+
|
251
|
+
Lifecycle management also means that you can control _when_ a service is instantiated.
|
252
|
+
The _prototype_ and _singleton_ models will always be instantiated as soon as they are
|
253
|
+
requested. Sometimes, though, you don't want that--you'd like the instantiation to be
|
254
|
+
deferred as long as possible.
|
255
|
+
|
256
|
+
|
257
|
+
With Copland, you can indicate that a service should use deferred instantiation. This will
|
258
|
+
cause the service to not actually be instantiated until a method is actually invoked on it.
|
259
|
+
Using this model, you can have services depend on themselves, or other forms of cyclical
|
260
|
+
dependencies.
|
261
|
+
|
262
|
+
|
263
|
+
- Copland _sans_ Buzzwords: >
|
38
264
|
Imagine being able to take all the various pieces of your program and implement
|
39
265
|
them in a vacuum. You just assume that all dependencies will be satisfied at runtime,
|
40
266
|
by properties being set, or parameters being passed to each object's constructor.
|
@@ -71,9 +297,7 @@ chapters:
|
|
71
297
|
specify those relationships and dependencies as run-time configuration options.
|
72
298
|
|
73
299
|
|
74
|
-
|
75
|
-
|
76
|
-
|
300
|
+
- Copland _avec_ Buzzwords: >
|
77
301
|
If you are already familiar with IoC, then you either love it or hate it. In the first
|
78
302
|
case, you probably only want to know what features Copland has compared to other IoC
|
79
303
|
containers you've used. In the second case, there's probably not much I can say to
|
@@ -97,9 +321,7 @@ chapters:
|
|
97
321
|
you've never used YAML, you'll find it surprisingly easy to pick up.
|
98
322
|
|
99
323
|
|
100
|
-
|
101
|
-
|
102
|
-
|
324
|
+
- The Buzzwords Themselves: >
|
103
325
|
As "Martin Fowler":http://www.martinfowler.com
|
104
326
|
"points out":http://martinfowler.com/articles/injection.html, the term "Inversion of Control"
|
105
327
|
is a poorly-chosen one. Still, it is the one that most people know the concept by, so I'll
|
@@ -823,6 +1045,11 @@ chapters:
|
|
823
1045
|
here, to be notified when some service-specific event is triggered. See the chapter on
|
824
1046
|
"Listeners and Event Producers" for more information.|
|
825
1047
|
|
1048
|
+
|^. @visibility@|This must be either the value "public" or "private". It defaults to "public".
|
1049
|
+
If it is "private", then the service point is not visible outside of its containing package.
|
1050
|
+
it is often the case that a particular package will have a few public services that are
|
1051
|
+
facades for multiple private services. See the @copland.webrick@ package for an example.|
|
1052
|
+
|
826
1053
|
|
827
1054
|
Of these attributes, only @implementor@ is required.
|
828
1055
|
|
@@ -1027,8 +1254,8 @@ chapters:
|
|
1027
1254
|
|
1028
1255
|
Of these attributes, only @type@ is required.
|
1029
1256
|
|
1030
|
-
-
|
1031
|
-
One of the core pre-defined configuration points is the map @copland.
|
1257
|
+
- FactoryDefaults: >
|
1258
|
+
One of the core pre-defined configuration points is the map @copland.FactoryDefaults@.
|
1032
1259
|
Although you will rarely access this configuration point directly, it is integrated
|
1033
1260
|
tightly with Copland. Any value you add to it becomes available as a _substitution
|
1034
1261
|
symbol_.
|
@@ -1038,13 +1265,13 @@ chapters:
|
|
1038
1265
|
utilities like "ant":http://ant.apache.org and "maven":http://maven.apache.org make
|
1039
1266
|
heavy use of them. What they are, is this: any time Copland encounters a value that
|
1040
1267
|
contains a certain pattern, it replaces that pattern with the value of the same name
|
1041
|
-
from the
|
1268
|
+
from the FactoryDefaults. The pattern is a dollar sign, followed by curly braces
|
1042
1269
|
that contain the name of the symbol.
|
1043
1270
|
|
1044
1271
|
|
1045
1272
|
For example, suppose you want to allow the application to configure which class gets
|
1046
1273
|
used when a particular service point is instantiated. You can do this by pulling the
|
1047
|
-
name of the class from the
|
1274
|
+
name of the class from the FactoryDefaults as a substitution symbol:
|
1048
1275
|
|
1049
1276
|
|
1050
1277
|
<pre>
|
@@ -1054,9 +1281,21 @@ chapters:
|
|
1054
1281
|
|
1055
1282
|
|
1056
1283
|
Then, the application would contribute a value named @the.class.to.use@ to
|
1057
|
-
@copland.
|
1284
|
+
@copland.FactoryDefaults@, and when @SomeServicePoint@ is instantiated, it would
|
1058
1285
|
use the value thus provided.
|
1059
1286
|
|
1287
|
+
- ApplicationDefaults: >
|
1288
|
+
The @copland.ApplicationDefaults@ configuration point works together with the
|
1289
|
+
@FactoryDefaults@ configuration point to provide a flexible way to override
|
1290
|
+
default values of substitution symbols.
|
1291
|
+
|
1292
|
+
|
1293
|
+
When a substitution symbol is requested, the @ApplicationDefaults@ configuration
|
1294
|
+
point is searched first. If the requested symbol is not found there, the
|
1295
|
+
@FactoryDefaults@ configuration point is searched. This allows package developers
|
1296
|
+
to define default values (in @FactoryDefaults@), and allow clients of the packages
|
1297
|
+
to override those defaults as needed (in @ApplicationDefaults@).
|
1298
|
+
|
1060
1299
|
- Contributions:
|
1061
1300
|
- >
|
1062
1301
|
_Contributions_ are the means by which packages may add values to configuration points.
|
@@ -1070,20 +1309,187 @@ chapters:
|
|
1070
1309
|
you want to contribute.
|
1071
1310
|
|
1072
1311
|
|
1073
|
-
For example, to contribute a value to the
|
1312
|
+
For example, to contribute a value to the ApplicationDefaults configuration point:
|
1074
1313
|
|
1075
1314
|
|
1076
1315
|
<pre>
|
1077
1316
|
contributions:
|
1078
1317
|
|
1079
|
-
copland.
|
1318
|
+
copland.ApplicationDefaults:
|
1080
1319
|
user.name: fritz
|
1081
1320
|
</pre>
|
1082
1321
|
|
1083
1322
|
|
1084
|
-
That little snippet just added a value called @user.name@ to the @copland.
|
1323
|
+
That little snippet just added a value called @user.name@ to the @copland.ApplicationDefaults@
|
1085
1324
|
configuration point!
|
1086
1325
|
|
1326
|
+
- Logging:
|
1327
|
+
- >
|
1328
|
+
Copland has an integrated logging system based on a slight variation of Ruby's own
|
1329
|
+
"logger" library. In addition to the features of that library, Copland's logging system
|
1330
|
+
provides:
|
1331
|
+
|
1332
|
+
|
1333
|
+
* A logger factory for creating shared logger instances
|
1334
|
+
|
1335
|
+
* YAML-based configuration of the factory
|
1336
|
+
|
1337
|
+
* Customizable message formatting
|
1338
|
+
|
1339
|
+
- Log Factory: >
|
1340
|
+
Each registry instance has its own instance of @Copland::LogFactory@. This provides
|
1341
|
+
a way to map a name to a logger instance, and allows each service point (for example)
|
1342
|
+
to have its own logger instance. This allows various logger settings to be tweaked
|
1343
|
+
_per service-point_.
|
1344
|
+
|
1345
|
+
|
1346
|
+
For example, if I want debugging information to be logged for one service point, but
|
1347
|
+
not for another, I can specify the logging priority level for the one service point to
|
1348
|
+
include debug messages, and have such messages excluded for the other service point.
|
1349
|
+
|
1350
|
+
|
1351
|
+
The log factory allows default values to be set for the priority level, date format
|
1352
|
+
string, and message format string, which will be applied to any logger that does not
|
1353
|
+
have a specific value set for those values. The log factory also allows configuration
|
1354
|
+
of the IO device to log to, the filename to log to, and various other (Logger-specific)
|
1355
|
+
data items (see the API documentation for @Copland::LogFactory@ for more information).
|
1356
|
+
|
1357
|
+
|
1358
|
+
If you ever need to access the current log factory instance, you can get it from the
|
1359
|
+
registry as the @copland.LogFactory@ service.
|
1360
|
+
|
1361
|
+
- Configuration: >
|
1362
|
+
There are three ways to configure loggers. The first is programmatically, manually
|
1363
|
+
tweaking each log instance directly. The second is to pass configuration options to
|
1364
|
+
the registry itself when it is built, and the third is to use a configuration file.
|
1365
|
+
|
1366
|
+
|
1367
|
+
h3. Programmatic Configuration
|
1368
|
+
|
1369
|
+
|
1370
|
+
To programmatically configure a logger, just obtain a handle to a logger instance
|
1371
|
+
and then tweak it via the accessor methods of the logger:
|
1372
|
+
|
1373
|
+
|
1374
|
+
<pre>
|
1375
|
+
factory = registry.service( "copland.LogFactory" )
|
1376
|
+
log = factory.get( "some.log.name" )
|
1377
|
+
|
1378
|
+
# Standard Logger attributes
|
1379
|
+
log.level = Logger::INFO
|
1380
|
+
log.progname = "different.log.name"
|
1381
|
+
log.datetime_format = "%Y%m%d"
|
1382
|
+
|
1383
|
+
# Extended Logger attributes (provided by Copland)
|
1384
|
+
log.message_format = "[%-5p] %d %c: %m"
|
1385
|
+
</pre>
|
1386
|
+
|
1387
|
+
|
1388
|
+
_Note:_ programmatically changing the logger @progname@ does not change the name by
|
1389
|
+
which the logger is known to the factory!
|
1390
|
+
|
1391
|
+
|
1392
|
+
In general, programmatic tweaking of the logger attributes as demonstrated above is not
|
1393
|
+
going to be the best way to do it. First of all, it is tedious, and bug prone. Secondly,
|
1394
|
+
it is difficult to know where the best place to do such work is, since you need to set
|
1395
|
+
those attributes before any other service tries to access that log.
|
1396
|
+
|
1397
|
+
|
1398
|
+
h3. Configuration via @Registry.build@
|
1399
|
+
|
1400
|
+
|
1401
|
+
The second approach is to pass logger configuration options to @Registry.build@,
|
1402
|
+
when you create a registry instance:
|
1403
|
+
|
1404
|
+
|
1405
|
+
<pre>
|
1406
|
+
registry = Copland::Registry.build(
|
1407
|
+
:log_device => STDOUT,
|
1408
|
+
:log_default_level => Logger::INFO,
|
1409
|
+
...
|
1410
|
+
)
|
1411
|
+
</pre>
|
1412
|
+
|
1413
|
+
|
1414
|
+
This gives you much greater flexibility than the previous approach, since it allows
|
1415
|
+
you to specify a greater array of options. The allowed options are:
|
1416
|
+
|
1417
|
+
|
1418
|
+
table(list).
|
1419
|
+
|
1420
|
+
|^. @:log_config_file@|This is the name of the configuration file to use to
|
1421
|
+
configure the log factory. It defaults to "logger.yml". (See the next section for
|
1422
|
+
more information.)|
|
1423
|
+
|
1424
|
+
|^. @:log_device@|This is the IO (or pseudo-IO) object to write log messages to.
|
1425
|
+
If this option is specified, @:log_filename@ must not be specified (and vice versa).
|
1426
|
+
This allows you to log messages to arbitrary IO streams.|
|
1427
|
+
|
1428
|
+
|^. @:log_filename@|This is the name of the file to write log messages to. If this
|
1429
|
+
option is specified, @:log_device@ must not be specified (and vice versa). This
|
1430
|
+
defaults to "./copland.log".|
|
1431
|
+
|
1432
|
+
|^. @:log_roll_age@|This specifies the number of days before the log should be
|
1433
|
+
rolled. (This option is only useful when used with @:log_filename@.)|
|
1434
|
+
|
1435
|
+
|^. @:log_roll_frequency@|This should be either @nil@, "daily", "weekly", or
|
1436
|
+
"monthly", and specifies how frequently the log should be rolled. This option cannot
|
1437
|
+
be used with @:log_roll_age@.|
|
1438
|
+
|
1439
|
+
|^. @:log_roll_size@|This specifies the maximum size of a log file. Once the log
|
1440
|
+
file gets larger than this value, the log will be rolled. (This option is only useful
|
1441
|
+
when used with @:log_filename@.)|
|
1442
|
+
|
1443
|
+
|^. @:log_default_date_format@|This is the default date format string to use for the
|
1444
|
+
loggers that are created. It may be any string that is understood by @Time#strftime@.
|
1445
|
+
If @nil@ (the default), then the default Logger date format string will be used.|
|
1446
|
+
|
1447
|
+
|^. @:log_default_message_format@|This is the default message format string to use
|
1448
|
+
for the loggers that are created. It is a printf-styled string with special format
|
1449
|
+
specifiers--see the API documentation for @Copland::Logger#message_format=@ for the
|
1450
|
+
available specifiers.|
|
1451
|
+
|
1452
|
+
|^. @:log_default_level@|This is the default level for each logger. Messages that
|
1453
|
+
are logged below this level (also "priority" or "severity") will not be logged. By
|
1454
|
+
default, all messages are logged.|
|
1455
|
+
|
1456
|
+
|^. @:log_levels@|This is a hash. Each key should be a string containing a regular
|
1457
|
+
expression. For every logger whose name matches one of these patterns, the corresponding
|
1458
|
+
value will be used to define specific values for that logger. The value for each key
|
1459
|
+
must either be a string (in which case it is the name of the severity level to use), or
|
1460
|
+
a hash (containing any of the keys "level", "date-format", or "message-format").|
|
1461
|
+
|
1462
|
+
|
1463
|
+
h3. Configuration via YAML
|
1464
|
+
|
1465
|
+
|
1466
|
+
The third option (and the most flexible) is to use a YAML configuration file to
|
1467
|
+
define the parameters of the log factory and its loggers. By default, this file
|
1468
|
+
is called "logger.yml", but you can specify a different logger configuration file
|
1469
|
+
via the @:log_config_file@ option to @Copland::Registry.build@. By default, the
|
1470
|
+
configuration file must reside in current working directory, but you can specify
|
1471
|
+
an explicit path in the string you give to @:log_config_file@.
|
1472
|
+
|
1473
|
+
|
1474
|
+
The file has options that correspond to the configuration parameters described above:
|
1475
|
+
|
1476
|
+
|
1477
|
+
<pre>
|
1478
|
+
---
|
1479
|
+
filename: log/filename.log
|
1480
|
+
roll-age: 5
|
1481
|
+
default-date-format: %Y%m%d%H%M%S
|
1482
|
+
default-message-format: %p %d %t %C: %m (%l)
|
1483
|
+
default-level: DEBUG
|
1484
|
+
levels:
|
1485
|
+
copland.*: WARN
|
1486
|
+
project.*:
|
1487
|
+
level: INFO
|
1488
|
+
date-format: %Y%m%d::%H%M%S
|
1489
|
+
message-format: %p %d: %m
|
1490
|
+
</pre>
|
1491
|
+
|
1492
|
+
|
1087
1493
|
- Service Factories:
|
1088
1494
|
- >
|
1089
1495
|
Sometimes it requires a little more effort (or a lot more effort) to instantiate and
|