copland 0.8.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|