copland 0.8.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/README +88 -0
- data/doc/manual-html/chapter-1.html +454 -0
- data/doc/manual-html/chapter-10.html +399 -0
- data/doc/manual-html/chapter-11.html +600 -0
- data/doc/manual-html/chapter-12.html +406 -0
- data/doc/manual-html/chapter-2.html +382 -0
- data/doc/manual-html/chapter-3.html +424 -0
- data/doc/manual-html/chapter-4.html +432 -0
- data/doc/manual-html/chapter-5.html +381 -0
- data/doc/manual-html/chapter-6.html +364 -0
- data/doc/manual-html/chapter-7.html +434 -0
- data/doc/manual-html/chapter-8.html +373 -0
- data/doc/manual-html/chapter-9.html +324 -0
- data/doc/manual-html/copland.png +0 -0
- data/doc/manual-html/index.html +331 -0
- data/doc/manual-html/manual.css +179 -0
- data/doc/manual-html/tutorial-1.html +407 -0
- data/doc/manual-html/tutorial-2.html +451 -0
- data/doc/manual-html/tutorial-3.html +484 -0
- data/doc/manual-html/tutorial-4.html +446 -0
- data/doc/manual-html/tutorial-5.html +520 -0
- data/doc/manual/chapter.erb +18 -0
- data/doc/manual/example.erb +18 -0
- data/doc/manual/img/copland.png +0 -0
- data/doc/manual/index.erb +30 -0
- data/doc/manual/manual.css +179 -0
- data/doc/manual/manual.rb +239 -0
- data/doc/manual/manual.yml +2643 -0
- data/doc/manual/page.erb +102 -0
- data/doc/manual/tutorial.erb +30 -0
- data/doc/packages/copland.html +764 -0
- data/doc/packages/copland.lib.html +439 -0
- data/doc/packages/copland.remote.html +2096 -0
- data/doc/packages/copland.webrick.html +925 -0
- data/doc/packages/index.html +49 -0
- data/doc/packages/packrat.css +125 -0
- data/examples/calc/calc.rb +47 -0
- data/examples/calc/package.yml +35 -0
- data/examples/calc/services.rb +74 -0
- data/examples/solitaire-cipher/README +11 -0
- data/examples/solitaire-cipher/Rakefile +57 -0
- data/examples/solitaire-cipher/bin/main.rb +14 -0
- data/examples/solitaire-cipher/lib/cipher.rb +230 -0
- data/examples/solitaire-cipher/lib/cli.rb +24 -0
- data/examples/solitaire-cipher/lib/package.yml +106 -0
- data/examples/solitaire-cipher/test/tc_deck.rb +30 -0
- data/examples/solitaire-cipher/test/tc_key-stream.rb +19 -0
- data/examples/solitaire-cipher/test/tc_keying-algorithms.rb +31 -0
- data/examples/solitaire-cipher/test/tc_solitaire-cipher.rb +66 -0
- data/examples/solitaire-cipher/test/tc_unkeyed-algorithm.rb +17 -0
- data/examples/solitaire-cipher/test/tests.rb +2 -0
- data/lib/copland.rb +56 -0
- data/lib/copland/class-factory.rb +95 -0
- data/lib/copland/configuration-point.rb +38 -0
- data/lib/copland/configuration-point/common.rb +203 -0
- data/lib/copland/configuration-point/errors.rb +44 -0
- data/lib/copland/configuration-point/list.rb +59 -0
- data/lib/copland/configuration-point/map.rb +59 -0
- data/lib/copland/configuration/errors.rb +43 -0
- data/lib/copland/configuration/loader.rb +113 -0
- data/lib/copland/configuration/yaml/configuration-point.rb +87 -0
- data/lib/copland/configuration/yaml/implementor.rb +134 -0
- data/lib/copland/configuration/yaml/interceptor.rb +63 -0
- data/lib/copland/configuration/yaml/listener.rb +56 -0
- data/lib/copland/configuration/yaml/loader.rb +122 -0
- data/lib/copland/configuration/yaml/package.rb +125 -0
- data/lib/copland/configuration/yaml/parser.rb +71 -0
- data/lib/copland/configuration/yaml/schema.rb +165 -0
- data/lib/copland/configuration/yaml/service-point.rb +116 -0
- data/lib/copland/configuration/yaml/utils.rb +82 -0
- data/lib/copland/default-schema-processor.rb +144 -0
- data/lib/copland/errors.rb +82 -0
- data/lib/copland/event-producer.rb +95 -0
- data/lib/copland/impl/builder-factory.rb +112 -0
- data/lib/copland/impl/copland-config.yml +1 -0
- data/lib/copland/impl/include-exclude.rb +140 -0
- data/lib/copland/impl/logging-interceptor.rb +106 -0
- data/lib/copland/impl/package.yml +217 -0
- data/lib/copland/impl/startup.rb +116 -0
- data/lib/copland/impl/symbol-source-manager.rb +131 -0
- data/lib/copland/impl/symbol-source.rb +63 -0
- data/lib/copland/instantiator.rb +38 -0
- data/lib/copland/instantiator/abstract.rb +91 -0
- data/lib/copland/instantiator/complex.rb +96 -0
- data/lib/copland/instantiator/identity.rb +58 -0
- data/lib/copland/instantiator/simple.rb +68 -0
- data/lib/copland/interceptor-chain.rb +166 -0
- data/lib/copland/interceptor.rb +139 -0
- data/lib/copland/log-factory.rb +206 -0
- data/lib/copland/models.rb +39 -0
- data/lib/copland/models/abstract.rb +78 -0
- data/lib/copland/models/prototype-deferred.rb +58 -0
- data/lib/copland/models/prototype.rb +58 -0
- data/lib/copland/models/proxy.rb +100 -0
- data/lib/copland/models/singleton-deferred.rb +59 -0
- data/lib/copland/models/singleton.rb +77 -0
- data/lib/copland/models/threaded.rb +65 -0
- data/lib/copland/ordering.rb +123 -0
- data/lib/copland/package.rb +246 -0
- data/lib/copland/registry.rb +368 -0
- data/lib/copland/schema.rb +206 -0
- data/lib/copland/service-point.rb +282 -0
- data/lib/copland/utils.rb +221 -0
- data/lib/copland/version.rb +47 -0
- data/test/conf-test/list-bad-key.yml +30 -0
- data/test/conf-test/list-bad-missing.yml +28 -0
- data/test/conf-test/list-bad-type.yml +28 -0
- data/test/conf-test/list-good.yml +29 -0
- data/test/conf-test/map-bad-key.yml +25 -0
- data/test/conf-test/map-bad-missing.yml +24 -0
- data/test/conf-test/map-bad-type.yml +23 -0
- data/test/conf-test/map-good.yml +25 -0
- data/test/configuration-point/package.yml +52 -0
- data/test/configuration/yaml/config/copland-config.yml +2 -0
- data/test/configuration/yaml/config/module.yml +2 -0
- data/test/configuration/yaml/config/subdir/copland-config.yml +2 -0
- data/test/configuration/yaml/config/subdir/package.yml +4 -0
- data/test/configuration/yaml/defaults/package.yml +5 -0
- data/test/configuration/yaml/defaults/subdir/package.yml +4 -0
- data/test/configuration/yaml/tc_config-loader.rb +86 -0
- data/test/configuration/yaml/tc_configuration-point-processor.rb +134 -0
- data/test/configuration/yaml/tc_implementor-processor.rb +104 -0
- data/test/configuration/yaml/tc_interceptor-processor.rb +85 -0
- data/test/configuration/yaml/tc_listener-processor.rb +69 -0
- data/test/configuration/yaml/tc_loader.rb +74 -0
- data/test/configuration/yaml/tc_package-processor.rb +120 -0
- data/test/configuration/yaml/tc_parser.rb +94 -0
- data/test/configuration/yaml/tc_schema-parser.rb +160 -0
- data/test/configuration/yaml/tc_service-point-processor.rb +104 -0
- data/test/configuration/yaml/tc_type-validator.rb +90 -0
- data/test/custom-logger.yml +3 -0
- data/test/impl/logging/package.yml +44 -0
- data/test/impl/logging/services.rb +84 -0
- data/test/impl/startup/package.yml +46 -0
- data/test/impl/startup/services.rb +47 -0
- data/test/impl/symbols/package.yml +24 -0
- data/test/impl/symbols/services.rb +38 -0
- data/test/impl/tc_builder-factory.rb +173 -0
- data/test/impl/tc_logging-interceptor.rb +148 -0
- data/test/impl/tc_startup.rb +59 -0
- data/test/impl/tc_symbol-sources.rb +61 -0
- data/test/logger.yml +6 -0
- data/test/mock.rb +201 -0
- data/test/schema/bad-package.yml +65 -0
- data/test/schema/package.yml +102 -0
- data/test/schema/services.rb +5 -0
- data/test/services/package.yml +79 -0
- data/test/services/simple.rb +87 -0
- data/test/tc_class-factory.rb +93 -0
- data/test/tc_complex-instantiator.rb +107 -0
- data/test/tc_configuration-point-contrib.rb +74 -0
- data/test/tc_configuration-point-schema.rb +122 -0
- data/test/tc_configuration-point.rb +91 -0
- data/test/tc_default-schema-processor.rb +297 -0
- data/test/tc_identity-instantiator.rb +61 -0
- data/test/tc_interceptors.rb +84 -0
- data/test/tc_logger.rb +131 -0
- data/test/tc_models.rb +176 -0
- data/test/tc_package.rb +165 -0
- data/test/tc_proxy.rb +65 -0
- data/test/tc_registry.rb +141 -0
- data/test/tc_schema.rb +78 -0
- data/test/tc_service-point.rb +178 -0
- data/test/tc_service.rb +70 -0
- data/test/tc_simple-instantiator.rb +61 -0
- data/test/tests.rb +93 -0
- data/tutorial/01/main.rb +7 -0
- data/tutorial/01/package.yml +8 -0
- data/tutorial/01/tutorial.rb +7 -0
- data/tutorial/02/main.rb +10 -0
- data/tutorial/02/package.yml +27 -0
- data/tutorial/02/tutorial.rb +46 -0
- data/tutorial/03/main.rb +24 -0
- data/tutorial/03/package.yml +29 -0
- data/tutorial/03/tutorial.rb +48 -0
- data/tutorial/04/main.rb +24 -0
- data/tutorial/04/package.yml +35 -0
- data/tutorial/04/tutorial.rb +48 -0
- data/tutorial/05/functions/package.yml +16 -0
- data/tutorial/05/functions/services.rb +15 -0
- data/tutorial/05/main.rb +10 -0
- data/tutorial/05/package.yml +35 -0
- data/tutorial/05/tutorial.rb +53 -0
- metadata +260 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
|
8
|
+
#
|
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
10
|
+
# this list of conditions and the following disclaimer.
|
|
11
|
+
#
|
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright
|
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
|
15
|
+
#
|
|
16
|
+
# * The names of its contributors may not be used to endorse or promote
|
|
17
|
+
# products derived from this software without specific prior written
|
|
18
|
+
# permission.
|
|
19
|
+
#
|
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
23
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
24
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
25
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
26
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
27
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
28
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
29
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
30
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
31
|
+
# =============================================================================
|
|
32
|
+
#++
|
|
33
|
+
|
|
34
|
+
require 'copland/instantiator/abstract'
|
|
35
|
+
|
|
36
|
+
module Copland
|
|
37
|
+
module Instantiator
|
|
38
|
+
|
|
39
|
+
# This trivial instantiator simply returns the definition that was given
|
|
40
|
+
# when it was created. Hence, it is a kind of "identity" operation. This
|
|
41
|
+
# kind of instantiator is only available programmatically--it cannot be
|
|
42
|
+
# specified in a descriptor file. It is used (for example) to back the
|
|
43
|
+
# "copland.Registry" service point by always returning the registry
|
|
44
|
+
# itself when the point is instantiated.
|
|
45
|
+
class Identity < Abstract
|
|
46
|
+
|
|
47
|
+
# Simply returns the definition data that was given when the instantiator
|
|
48
|
+
# was created.
|
|
49
|
+
def instantiate
|
|
50
|
+
definition
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
register_as "identity"
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
|
8
|
+
#
|
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
10
|
+
# this list of conditions and the following disclaimer.
|
|
11
|
+
#
|
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright
|
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
|
15
|
+
#
|
|
16
|
+
# * The names of its contributors may not be used to endorse or promote
|
|
17
|
+
# products derived from this software without specific prior written
|
|
18
|
+
# permission.
|
|
19
|
+
#
|
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
23
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
24
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
25
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
26
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
27
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
28
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
29
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
30
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
31
|
+
# =============================================================================
|
|
32
|
+
#++
|
|
33
|
+
|
|
34
|
+
require 'copland/utils'
|
|
35
|
+
require 'copland/instantiator/abstract'
|
|
36
|
+
require 'singleton'
|
|
37
|
+
|
|
38
|
+
module Copland
|
|
39
|
+
module Instantiator
|
|
40
|
+
|
|
41
|
+
# A simple instantiator is used for instantiating a class. It does no other
|
|
42
|
+
# work than that.
|
|
43
|
+
class Simple < Abstract
|
|
44
|
+
|
|
45
|
+
# Takes the definition data that was given when the instantiator was
|
|
46
|
+
# created and treats it as the path to and name of the class to
|
|
47
|
+
# instantiate. This class is then instantiated and returned. This
|
|
48
|
+
# requires that the class have a no-argument constructor.
|
|
49
|
+
#
|
|
50
|
+
# If the class includes the Singleton module, then the singleton instance
|
|
51
|
+
# of the class will be returned.
|
|
52
|
+
def instantiate
|
|
53
|
+
unless @klass
|
|
54
|
+
@klass = Copland::get_class( definition )
|
|
55
|
+
end
|
|
56
|
+
if @klass.include?( Singleton )
|
|
57
|
+
@klass.instance
|
|
58
|
+
else
|
|
59
|
+
@klass.new
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
register_as "simple"
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
|
8
|
+
#
|
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
10
|
+
# this list of conditions and the following disclaimer.
|
|
11
|
+
#
|
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright
|
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
|
15
|
+
#
|
|
16
|
+
# * The names of its contributors may not be used to endorse or promote
|
|
17
|
+
# products derived from this software without specific prior written
|
|
18
|
+
# permission.
|
|
19
|
+
#
|
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
23
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
24
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
25
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
26
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
27
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
28
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
29
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
30
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
31
|
+
# =============================================================================
|
|
32
|
+
#++
|
|
33
|
+
|
|
34
|
+
require 'copland/errors'
|
|
35
|
+
require 'copland/ordering'
|
|
36
|
+
|
|
37
|
+
module Copland
|
|
38
|
+
|
|
39
|
+
# This module encapsulates the functionality for building interceptor chains.
|
|
40
|
+
module InterceptorChainBuilder
|
|
41
|
+
|
|
42
|
+
# The context of a method invocation. This is used in an interceptor chain
|
|
43
|
+
# to encapsulate the elements of the current invocation.
|
|
44
|
+
# sym: the name of the method being invoked
|
|
45
|
+
# args: the argument list being passed to the method
|
|
46
|
+
# block: the reference to the block attached to the method invocation
|
|
47
|
+
InvocationContext = Struct.new( :sym, :args, :block )
|
|
48
|
+
|
|
49
|
+
# A single element in an interceptor chain. Each interceptor object is
|
|
50
|
+
# wrapped in an instance of one of these. Calling #process_next on a given
|
|
51
|
+
# chain element, invokes the #process method on the corresponding
|
|
52
|
+
# interceptor, with the next element in the chain being passed in.
|
|
53
|
+
class InterceptorChainElement
|
|
54
|
+
|
|
55
|
+
# Create a new InterceptorChainElement that wraps the given interceptor.
|
|
56
|
+
def initialize( interceptor )
|
|
57
|
+
@interceptor = interceptor
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Set the next element in the interceptor chain to the given object. This
|
|
61
|
+
# must be either an InterceptorChainElement instance of a
|
|
62
|
+
# ProxyObjectChainElement instance.
|
|
63
|
+
def next=( next_obj )
|
|
64
|
+
@next_obj = next_obj
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Invokes the #process method of the interceptor encapsulated by this
|
|
68
|
+
# object, with the _next_ element in the chain being passed to it.
|
|
69
|
+
def process_next( context )
|
|
70
|
+
if @next_obj.nil?
|
|
71
|
+
raise CoplandError,
|
|
72
|
+
"[BUG] interceptor chain should always terminate with proxy"
|
|
73
|
+
end
|
|
74
|
+
@interceptor.process( @next_obj, context )
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Encapsulates the end of an interceptor chain, which is the actual object
|
|
80
|
+
# being effected.
|
|
81
|
+
class ProxyObjectChainElement
|
|
82
|
+
|
|
83
|
+
# Create a new ProxyObjectChainElement that wraps the given object.
|
|
84
|
+
def initialize( obj )
|
|
85
|
+
@obj = obj
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Invoke the method represented by the context on the wrapped object.
|
|
89
|
+
def process_next( context )
|
|
90
|
+
@obj.__send__( context.sym, *context.args, &context.block )
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# This is just a trivial proxy class that is used to wrap a service
|
|
96
|
+
# before the interceptors are applied to it. This additional level of
|
|
97
|
+
# abstractions prevents the need for mangling the names of the service's
|
|
98
|
+
# methods, and also offers those applications that need it the ability
|
|
99
|
+
# to invoke methods of the service without going through the interceptors.
|
|
100
|
+
#
|
|
101
|
+
# The proxy will be decorated with dynamically appended methods by the
|
|
102
|
+
# InterceptorChainBuilder#build method.
|
|
103
|
+
class InterceptedServiceProxy
|
|
104
|
+
|
|
105
|
+
# Create a new InterceptedServiceProxy that wraps the given interceptor
|
|
106
|
+
# chain.
|
|
107
|
+
def initialize( chain )
|
|
108
|
+
@chain = chain
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# This will apply the given interceptors to the given service by first
|
|
114
|
+
# ordering the interceptors based on their before and after preferences,
|
|
115
|
+
# and then dynamically modifying the service's methods so that the chain
|
|
116
|
+
# of interceptors sits in front of each of them.
|
|
117
|
+
#
|
|
118
|
+
# The modified service is returned.
|
|
119
|
+
def build( service, interceptors )
|
|
120
|
+
return service if interceptors.empty?
|
|
121
|
+
|
|
122
|
+
ordered_list = Orderer.order( interceptors )
|
|
123
|
+
|
|
124
|
+
chain = ProxyObjectChainElement.new( service )
|
|
125
|
+
|
|
126
|
+
ordered_list.reverse.each do |interceptor|
|
|
127
|
+
element = InterceptorChainElement.new( interceptor.instantiate )
|
|
128
|
+
element.next = chain
|
|
129
|
+
chain = element
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# FIXME: should inherited methods of "Object" be interceptable?
|
|
133
|
+
methods_to_intercept = ( service.class.instance_methods( true ) -
|
|
134
|
+
Object.instance_methods +
|
|
135
|
+
service.class.instance_methods( false ) ).uniq
|
|
136
|
+
|
|
137
|
+
service = InterceptedServiceProxy.new( chain )
|
|
138
|
+
singleton = class << service; self; end
|
|
139
|
+
|
|
140
|
+
methods_to_intercept.each do |method|
|
|
141
|
+
next if method =~ /^__/
|
|
142
|
+
|
|
143
|
+
singleton.class_eval <<-EOF
|
|
144
|
+
def #{method}( *args, &block )
|
|
145
|
+
context = InvocationContext.new( :#{method}, args, block )
|
|
146
|
+
@chain.process_next( context )
|
|
147
|
+
end
|
|
148
|
+
EOF
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# allow the interceptor to intercept methods not explicitly
|
|
152
|
+
# declared on the reciever.
|
|
153
|
+
singleton.class_eval <<-EOF
|
|
154
|
+
def method_missing( sym, *args, &block )
|
|
155
|
+
context = InvocationContext.new( sym, args, block )
|
|
156
|
+
@chain.process_next( context )
|
|
157
|
+
end
|
|
158
|
+
EOF
|
|
159
|
+
|
|
160
|
+
return service
|
|
161
|
+
end
|
|
162
|
+
module_function :build
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
|
8
|
+
#
|
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
10
|
+
# this list of conditions and the following disclaimer.
|
|
11
|
+
#
|
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright
|
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
|
15
|
+
#
|
|
16
|
+
# * The names of its contributors may not be used to endorse or promote
|
|
17
|
+
# products derived from this software without specific prior written
|
|
18
|
+
# permission.
|
|
19
|
+
#
|
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
23
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
24
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
25
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
26
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
27
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
28
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
29
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
30
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
31
|
+
# =============================================================================
|
|
32
|
+
#++
|
|
33
|
+
|
|
34
|
+
require 'copland/errors'
|
|
35
|
+
|
|
36
|
+
module Copland
|
|
37
|
+
|
|
38
|
+
# Represents an interceptor associated with a <em>service point</em>, not a
|
|
39
|
+
# _service_. When the service point with which this interceptor is associated
|
|
40
|
+
# is instantiated, the #instantiate method of this object will be invoked to
|
|
41
|
+
# obtain the actual interceptor instance.
|
|
42
|
+
#
|
|
43
|
+
# This wrapper object encapsulates the "before" and "after" lists, as well,
|
|
44
|
+
# which are used to determine the order in which the interceptors on the
|
|
45
|
+
# associated service point are invoked for each intercepted method call.
|
|
46
|
+
class Interceptor
|
|
47
|
+
|
|
48
|
+
# The service point with which this interceptor is associated.
|
|
49
|
+
attr_reader :owner
|
|
50
|
+
|
|
51
|
+
# The service point of the factory that will return the interceptor
|
|
52
|
+
# instance on demand.
|
|
53
|
+
attr_reader :point
|
|
54
|
+
|
|
55
|
+
# The array of interceptor service point _names_ (not services) that
|
|
56
|
+
# this interceptor should come before.
|
|
57
|
+
attr_reader :before
|
|
58
|
+
|
|
59
|
+
# The array of interceptor service point _names_ (not services) that
|
|
60
|
+
# this interceptor should come after.
|
|
61
|
+
attr_reader :after
|
|
62
|
+
|
|
63
|
+
# A hash of the constructor parameters that should be sent to the factory
|
|
64
|
+
# when instantiating the interceptor service. This attribute is the value
|
|
65
|
+
# of the hash <em>prior to processing by any schema</em>.
|
|
66
|
+
attr_reader :construction_parms
|
|
67
|
+
|
|
68
|
+
# Create a new interceptor on the given +owner+ service point, with the
|
|
69
|
+
# associated +definition+ map. The map _must_ include a value named
|
|
70
|
+
# "service", which should be the name of the service point of the factory
|
|
71
|
+
# that will be used to instantiate the interceptor when needed. If
|
|
72
|
+
# "before" or "after" exist, they are interpreted to be the "before" and
|
|
73
|
+
# "after" attributes of this interceptor. (If either of them are strings,
|
|
74
|
+
# they will be converted into an array of one element; otherwise, they
|
|
75
|
+
# should be arrays.)
|
|
76
|
+
#
|
|
77
|
+
# Any other elements in the +definition+ map will be used as constructor
|
|
78
|
+
# parameters.
|
|
79
|
+
def initialize( owner, definition )
|
|
80
|
+
@owner = owner
|
|
81
|
+
|
|
82
|
+
unless definition[ "service" ]
|
|
83
|
+
raise MissingImplementationException,
|
|
84
|
+
"interceptor for #{owner.full_name} needs 'service' element"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
@point = owner.find_service_point( definition[ "service" ] )
|
|
88
|
+
|
|
89
|
+
@before = [ *( definition[ "before" ] || [] ).dup ]
|
|
90
|
+
@after = [ *( definition[ "after" ] || [] ).dup ]
|
|
91
|
+
|
|
92
|
+
definition = definition.dup
|
|
93
|
+
definition.delete "service"
|
|
94
|
+
definition.delete "before"
|
|
95
|
+
definition.delete "after"
|
|
96
|
+
|
|
97
|
+
@construction_parms = definition
|
|
98
|
+
|
|
99
|
+
schema = point.schema
|
|
100
|
+
if schema.respond_to?( :validate )
|
|
101
|
+
schema.validate @point, @owner, @construction_parms
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Return an instance of the interceptor service that is wrapped by this
|
|
106
|
+
# object. This is done by invoking the #create_instance method on the
|
|
107
|
+
# factory service. If the factory service point has a schema associated
|
|
108
|
+
# with it, it will be used to pre-process the parameters.
|
|
109
|
+
def instantiate
|
|
110
|
+
@factory = @point.instance unless @factory
|
|
111
|
+
|
|
112
|
+
parms = @construction_parms
|
|
113
|
+
schema = @point.schema
|
|
114
|
+
if schema.respond_to?( :process )
|
|
115
|
+
parms = schema.process( point, owner, parms )
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
@factory.create_instance( @owner, parms )
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Returns +true+ if +self+ should be ordered before +interceptor+.
|
|
122
|
+
def before?( interceptor )
|
|
123
|
+
a = before.include?( interceptor.point.full_name )
|
|
124
|
+
b = interceptor.after.include?( point.full_name )
|
|
125
|
+
|
|
126
|
+
return a || b
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Returns +true+ if +self+ should be ordered after +interceptor+.
|
|
130
|
+
def after?( interceptor )
|
|
131
|
+
a = after.include?( interceptor.point.full_name )
|
|
132
|
+
b = interceptor.before.include?( point.full_name )
|
|
133
|
+
|
|
134
|
+
return a || b
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
|
8
|
+
#
|
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
10
|
+
# this list of conditions and the following disclaimer.
|
|
11
|
+
#
|
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright
|
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
|
15
|
+
#
|
|
16
|
+
# * The names of its contributors may not be used to endorse or promote
|
|
17
|
+
# products derived from this software without specific prior written
|
|
18
|
+
# permission.
|
|
19
|
+
#
|
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
23
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
24
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
25
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
26
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
27
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
28
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
29
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
30
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
31
|
+
# =============================================================================
|
|
32
|
+
#++
|
|
33
|
+
|
|
34
|
+
require 'logger'
|
|
35
|
+
require 'thread'
|
|
36
|
+
require 'yaml'
|
|
37
|
+
|
|
38
|
+
module Copland
|
|
39
|
+
|
|
40
|
+
# A factory class that returns Logger instances. The LogFactory is not
|
|
41
|
+
# a singleton for the same reason that Copland::Registry is not a
|
|
42
|
+
# singleton--the allow multiple registries to exist simultaneously.
|
|
43
|
+
# Since each registry has its own logger factory, the logger factory
|
|
44
|
+
# must be separately instantiable.
|
|
45
|
+
class LogFactory
|
|
46
|
+
|
|
47
|
+
# The default name of the logger configuration file. This file will be used
|
|
48
|
+
# to read default properties for initializing logger objects.
|
|
49
|
+
DEFAULT_CONFIG_FILE = "./logger.yml"
|
|
50
|
+
|
|
51
|
+
# The default name of the log file to write to.
|
|
52
|
+
DEFAULT_LOG_FILENAME = "./copland.log"
|
|
53
|
+
|
|
54
|
+
# Translate names of levels to their actual values.
|
|
55
|
+
LEVEL_TRANSLATOR = {
|
|
56
|
+
"DEBUG" => Logger::DEBUG,
|
|
57
|
+
"INFO" => Logger::INFO,
|
|
58
|
+
"WARN" => Logger::WARN,
|
|
59
|
+
"ERROR" => Logger::ERROR,
|
|
60
|
+
"FATAL" => Logger::FATAL,
|
|
61
|
+
"UNKNOWN" => Logger::UNKNOWN
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# The default (date) format string to use when logging.
|
|
65
|
+
attr_reader :default_format
|
|
66
|
+
|
|
67
|
+
# The default log level to use for logs that are created.
|
|
68
|
+
attr_reader :default_level
|
|
69
|
+
|
|
70
|
+
# The device that logs will write to.
|
|
71
|
+
attr_reader :device
|
|
72
|
+
|
|
73
|
+
# Create a new LogFactory using the given initialization parameters.
|
|
74
|
+
# The valid options are:
|
|
75
|
+
#
|
|
76
|
+
# * <tt>:config_file</tt>: the configuration file from which to read
|
|
77
|
+
# logger configuration options.
|
|
78
|
+
# * <tt>:device</tt>: the device (pseudo-IO object) to write log
|
|
79
|
+
# messages to. Either this, or <tt>:filename</tt> must be specified.
|
|
80
|
+
# * <tt>:filename</tt>: the filename of the log to write to.
|
|
81
|
+
# * <tt>:roll_age</tt>: the number of days before the log should be
|
|
82
|
+
# rolled.
|
|
83
|
+
# * <tt>:roll_frequency</tt>: either 'daily', 'weekly', or 'monthly'.
|
|
84
|
+
# * <tt>:roll_size</tt>: the maximum size of a log file.
|
|
85
|
+
# * <tt>:default_format</tt>: the default date format string for the log.
|
|
86
|
+
# * <tt>:default_level</tt>: the default log level for the log.
|
|
87
|
+
# * <tt>:levels</tt>: a hash of patterns that map to a hash of 'level'
|
|
88
|
+
# and 'format' keys, specifying the log level and date format for any
|
|
89
|
+
# log whose name matches the key.
|
|
90
|
+
def initialize( opts={} )
|
|
91
|
+
opts[ :config_file ] ||= DEFAULT_CONFIG_FILE
|
|
92
|
+
read_config opts
|
|
93
|
+
|
|
94
|
+
if opts[ :device ]
|
|
95
|
+
@device = opts[ :device ]
|
|
96
|
+
else
|
|
97
|
+
filename = opts[ :filename ] || DEFAULT_LOG_FILENAME
|
|
98
|
+
roll_age = opts[ :roll_age ] || opts[ :roll_frequency ] || 0
|
|
99
|
+
roll_size = opts[ :roll_size ]
|
|
100
|
+
@device = Logger::LogDevice.new( filename,
|
|
101
|
+
:shift_age => roll_age,
|
|
102
|
+
:shift_size => roll_size )
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
@default_format = opts[ :default_format ]
|
|
106
|
+
@default_level = opts[ :default_level ]
|
|
107
|
+
|
|
108
|
+
@levels = Hash.new "level" => nil, "format" => nil
|
|
109
|
+
|
|
110
|
+
( opts[ :levels ] || Hash.new ).each_pair do |key, value|
|
|
111
|
+
key = Regexp.new( "^" + key.gsub( /\./, "\\." ).gsub( /\*/, ".*" ) )
|
|
112
|
+
value = { "level" => value } if value.is_a? String
|
|
113
|
+
@levels[ key ] = value
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
@loggers = Hash.new
|
|
117
|
+
|
|
118
|
+
@mutex = Mutex.new
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Read the configuration parameters from the logger configuration file.
|
|
122
|
+
# If the file does not exist or is not readable by the current user, this
|
|
123
|
+
# method does nothing.
|
|
124
|
+
def read_config( opts )
|
|
125
|
+
file = opts[ :config_file ]
|
|
126
|
+
if File.exist?( file ) && File.readable?( file )
|
|
127
|
+
config = YAML.load( File.read( file ) )
|
|
128
|
+
opts[ :default_format ] ||= config[ "default-format" ]
|
|
129
|
+
opts[ :default_level ] ||=
|
|
130
|
+
LEVEL_TRANSLATOR[ config[ "default-level" ] ] ||
|
|
131
|
+
config[ "default-level" ]
|
|
132
|
+
opts[ :filename ] ||= config[ "filename" ]
|
|
133
|
+
opts[ :roll_age ] ||= config[ "roll-age" ]
|
|
134
|
+
opts[ :roll_frequency ] ||= config[ "roll-frequency" ]
|
|
135
|
+
opts[ :roll_size ] ||= config[ "roll-size" ]
|
|
136
|
+
opts[ :levels ] ||= config[ "levels" ]
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
private :read_config
|
|
140
|
+
|
|
141
|
+
# Searches for a level definition hash that matches the given log
|
|
142
|
+
# name.
|
|
143
|
+
def find_definition( name )
|
|
144
|
+
best_match = { :length => 0, :value => Hash.new }
|
|
145
|
+
|
|
146
|
+
@levels.each_pair do |key, value|
|
|
147
|
+
length = key.to_s.length
|
|
148
|
+
if best_match[ :length ] < length && key === name
|
|
149
|
+
best_match[ :length ] = length
|
|
150
|
+
best_match[ :value ] = value
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
return best_match[ :value ]
|
|
155
|
+
end
|
|
156
|
+
private :find_definition
|
|
157
|
+
|
|
158
|
+
# Retrieves the logger with the given name. If no such log has been
|
|
159
|
+
# created, the log will be created and initialized. Otherwise, the
|
|
160
|
+
# log with the given name is returned.
|
|
161
|
+
def get( name )
|
|
162
|
+
# the common case first, outside the synchronize, for speed
|
|
163
|
+
return @loggers[ name ] if @loggers[ name ]
|
|
164
|
+
|
|
165
|
+
@mutex.synchronize do
|
|
166
|
+
# check again, inside the synchronize, to avoid race conditions
|
|
167
|
+
return @loggers[ name ] if @loggers[ name ]
|
|
168
|
+
|
|
169
|
+
definition = find_definition( name )
|
|
170
|
+
|
|
171
|
+
level = definition[ "level" ] || @default_level
|
|
172
|
+
format = definition[ "format" ] || @default_format
|
|
173
|
+
|
|
174
|
+
level = LEVEL_TRANSLATOR[ level ] || level
|
|
175
|
+
|
|
176
|
+
logger = Logger.new( @device )
|
|
177
|
+
logger.level = level if level
|
|
178
|
+
logger.datetime_format = format if format
|
|
179
|
+
logger.progname = name
|
|
180
|
+
|
|
181
|
+
@loggers[ name ] = logger
|
|
182
|
+
return logger
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Closes the logging device and makes this factory invalid for future
|
|
187
|
+
# accesses.
|
|
188
|
+
def close
|
|
189
|
+
@mutex.synchronize do
|
|
190
|
+
return if @closed
|
|
191
|
+
|
|
192
|
+
@device.close unless [ $stdout, $stderr ].include?( @device )
|
|
193
|
+
|
|
194
|
+
@loggers = nil
|
|
195
|
+
@closed = true
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Returns true if the factory has been closed.
|
|
200
|
+
def closed?
|
|
201
|
+
@closed
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
end
|