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.
Files changed (184) hide show
  1. data/doc/README +88 -0
  2. data/doc/manual-html/chapter-1.html +454 -0
  3. data/doc/manual-html/chapter-10.html +399 -0
  4. data/doc/manual-html/chapter-11.html +600 -0
  5. data/doc/manual-html/chapter-12.html +406 -0
  6. data/doc/manual-html/chapter-2.html +382 -0
  7. data/doc/manual-html/chapter-3.html +424 -0
  8. data/doc/manual-html/chapter-4.html +432 -0
  9. data/doc/manual-html/chapter-5.html +381 -0
  10. data/doc/manual-html/chapter-6.html +364 -0
  11. data/doc/manual-html/chapter-7.html +434 -0
  12. data/doc/manual-html/chapter-8.html +373 -0
  13. data/doc/manual-html/chapter-9.html +324 -0
  14. data/doc/manual-html/copland.png +0 -0
  15. data/doc/manual-html/index.html +331 -0
  16. data/doc/manual-html/manual.css +179 -0
  17. data/doc/manual-html/tutorial-1.html +407 -0
  18. data/doc/manual-html/tutorial-2.html +451 -0
  19. data/doc/manual-html/tutorial-3.html +484 -0
  20. data/doc/manual-html/tutorial-4.html +446 -0
  21. data/doc/manual-html/tutorial-5.html +520 -0
  22. data/doc/manual/chapter.erb +18 -0
  23. data/doc/manual/example.erb +18 -0
  24. data/doc/manual/img/copland.png +0 -0
  25. data/doc/manual/index.erb +30 -0
  26. data/doc/manual/manual.css +179 -0
  27. data/doc/manual/manual.rb +239 -0
  28. data/doc/manual/manual.yml +2643 -0
  29. data/doc/manual/page.erb +102 -0
  30. data/doc/manual/tutorial.erb +30 -0
  31. data/doc/packages/copland.html +764 -0
  32. data/doc/packages/copland.lib.html +439 -0
  33. data/doc/packages/copland.remote.html +2096 -0
  34. data/doc/packages/copland.webrick.html +925 -0
  35. data/doc/packages/index.html +49 -0
  36. data/doc/packages/packrat.css +125 -0
  37. data/examples/calc/calc.rb +47 -0
  38. data/examples/calc/package.yml +35 -0
  39. data/examples/calc/services.rb +74 -0
  40. data/examples/solitaire-cipher/README +11 -0
  41. data/examples/solitaire-cipher/Rakefile +57 -0
  42. data/examples/solitaire-cipher/bin/main.rb +14 -0
  43. data/examples/solitaire-cipher/lib/cipher.rb +230 -0
  44. data/examples/solitaire-cipher/lib/cli.rb +24 -0
  45. data/examples/solitaire-cipher/lib/package.yml +106 -0
  46. data/examples/solitaire-cipher/test/tc_deck.rb +30 -0
  47. data/examples/solitaire-cipher/test/tc_key-stream.rb +19 -0
  48. data/examples/solitaire-cipher/test/tc_keying-algorithms.rb +31 -0
  49. data/examples/solitaire-cipher/test/tc_solitaire-cipher.rb +66 -0
  50. data/examples/solitaire-cipher/test/tc_unkeyed-algorithm.rb +17 -0
  51. data/examples/solitaire-cipher/test/tests.rb +2 -0
  52. data/lib/copland.rb +56 -0
  53. data/lib/copland/class-factory.rb +95 -0
  54. data/lib/copland/configuration-point.rb +38 -0
  55. data/lib/copland/configuration-point/common.rb +203 -0
  56. data/lib/copland/configuration-point/errors.rb +44 -0
  57. data/lib/copland/configuration-point/list.rb +59 -0
  58. data/lib/copland/configuration-point/map.rb +59 -0
  59. data/lib/copland/configuration/errors.rb +43 -0
  60. data/lib/copland/configuration/loader.rb +113 -0
  61. data/lib/copland/configuration/yaml/configuration-point.rb +87 -0
  62. data/lib/copland/configuration/yaml/implementor.rb +134 -0
  63. data/lib/copland/configuration/yaml/interceptor.rb +63 -0
  64. data/lib/copland/configuration/yaml/listener.rb +56 -0
  65. data/lib/copland/configuration/yaml/loader.rb +122 -0
  66. data/lib/copland/configuration/yaml/package.rb +125 -0
  67. data/lib/copland/configuration/yaml/parser.rb +71 -0
  68. data/lib/copland/configuration/yaml/schema.rb +165 -0
  69. data/lib/copland/configuration/yaml/service-point.rb +116 -0
  70. data/lib/copland/configuration/yaml/utils.rb +82 -0
  71. data/lib/copland/default-schema-processor.rb +144 -0
  72. data/lib/copland/errors.rb +82 -0
  73. data/lib/copland/event-producer.rb +95 -0
  74. data/lib/copland/impl/builder-factory.rb +112 -0
  75. data/lib/copland/impl/copland-config.yml +1 -0
  76. data/lib/copland/impl/include-exclude.rb +140 -0
  77. data/lib/copland/impl/logging-interceptor.rb +106 -0
  78. data/lib/copland/impl/package.yml +217 -0
  79. data/lib/copland/impl/startup.rb +116 -0
  80. data/lib/copland/impl/symbol-source-manager.rb +131 -0
  81. data/lib/copland/impl/symbol-source.rb +63 -0
  82. data/lib/copland/instantiator.rb +38 -0
  83. data/lib/copland/instantiator/abstract.rb +91 -0
  84. data/lib/copland/instantiator/complex.rb +96 -0
  85. data/lib/copland/instantiator/identity.rb +58 -0
  86. data/lib/copland/instantiator/simple.rb +68 -0
  87. data/lib/copland/interceptor-chain.rb +166 -0
  88. data/lib/copland/interceptor.rb +139 -0
  89. data/lib/copland/log-factory.rb +206 -0
  90. data/lib/copland/models.rb +39 -0
  91. data/lib/copland/models/abstract.rb +78 -0
  92. data/lib/copland/models/prototype-deferred.rb +58 -0
  93. data/lib/copland/models/prototype.rb +58 -0
  94. data/lib/copland/models/proxy.rb +100 -0
  95. data/lib/copland/models/singleton-deferred.rb +59 -0
  96. data/lib/copland/models/singleton.rb +77 -0
  97. data/lib/copland/models/threaded.rb +65 -0
  98. data/lib/copland/ordering.rb +123 -0
  99. data/lib/copland/package.rb +246 -0
  100. data/lib/copland/registry.rb +368 -0
  101. data/lib/copland/schema.rb +206 -0
  102. data/lib/copland/service-point.rb +282 -0
  103. data/lib/copland/utils.rb +221 -0
  104. data/lib/copland/version.rb +47 -0
  105. data/test/conf-test/list-bad-key.yml +30 -0
  106. data/test/conf-test/list-bad-missing.yml +28 -0
  107. data/test/conf-test/list-bad-type.yml +28 -0
  108. data/test/conf-test/list-good.yml +29 -0
  109. data/test/conf-test/map-bad-key.yml +25 -0
  110. data/test/conf-test/map-bad-missing.yml +24 -0
  111. data/test/conf-test/map-bad-type.yml +23 -0
  112. data/test/conf-test/map-good.yml +25 -0
  113. data/test/configuration-point/package.yml +52 -0
  114. data/test/configuration/yaml/config/copland-config.yml +2 -0
  115. data/test/configuration/yaml/config/module.yml +2 -0
  116. data/test/configuration/yaml/config/subdir/copland-config.yml +2 -0
  117. data/test/configuration/yaml/config/subdir/package.yml +4 -0
  118. data/test/configuration/yaml/defaults/package.yml +5 -0
  119. data/test/configuration/yaml/defaults/subdir/package.yml +4 -0
  120. data/test/configuration/yaml/tc_config-loader.rb +86 -0
  121. data/test/configuration/yaml/tc_configuration-point-processor.rb +134 -0
  122. data/test/configuration/yaml/tc_implementor-processor.rb +104 -0
  123. data/test/configuration/yaml/tc_interceptor-processor.rb +85 -0
  124. data/test/configuration/yaml/tc_listener-processor.rb +69 -0
  125. data/test/configuration/yaml/tc_loader.rb +74 -0
  126. data/test/configuration/yaml/tc_package-processor.rb +120 -0
  127. data/test/configuration/yaml/tc_parser.rb +94 -0
  128. data/test/configuration/yaml/tc_schema-parser.rb +160 -0
  129. data/test/configuration/yaml/tc_service-point-processor.rb +104 -0
  130. data/test/configuration/yaml/tc_type-validator.rb +90 -0
  131. data/test/custom-logger.yml +3 -0
  132. data/test/impl/logging/package.yml +44 -0
  133. data/test/impl/logging/services.rb +84 -0
  134. data/test/impl/startup/package.yml +46 -0
  135. data/test/impl/startup/services.rb +47 -0
  136. data/test/impl/symbols/package.yml +24 -0
  137. data/test/impl/symbols/services.rb +38 -0
  138. data/test/impl/tc_builder-factory.rb +173 -0
  139. data/test/impl/tc_logging-interceptor.rb +148 -0
  140. data/test/impl/tc_startup.rb +59 -0
  141. data/test/impl/tc_symbol-sources.rb +61 -0
  142. data/test/logger.yml +6 -0
  143. data/test/mock.rb +201 -0
  144. data/test/schema/bad-package.yml +65 -0
  145. data/test/schema/package.yml +102 -0
  146. data/test/schema/services.rb +5 -0
  147. data/test/services/package.yml +79 -0
  148. data/test/services/simple.rb +87 -0
  149. data/test/tc_class-factory.rb +93 -0
  150. data/test/tc_complex-instantiator.rb +107 -0
  151. data/test/tc_configuration-point-contrib.rb +74 -0
  152. data/test/tc_configuration-point-schema.rb +122 -0
  153. data/test/tc_configuration-point.rb +91 -0
  154. data/test/tc_default-schema-processor.rb +297 -0
  155. data/test/tc_identity-instantiator.rb +61 -0
  156. data/test/tc_interceptors.rb +84 -0
  157. data/test/tc_logger.rb +131 -0
  158. data/test/tc_models.rb +176 -0
  159. data/test/tc_package.rb +165 -0
  160. data/test/tc_proxy.rb +65 -0
  161. data/test/tc_registry.rb +141 -0
  162. data/test/tc_schema.rb +78 -0
  163. data/test/tc_service-point.rb +178 -0
  164. data/test/tc_service.rb +70 -0
  165. data/test/tc_simple-instantiator.rb +61 -0
  166. data/test/tests.rb +93 -0
  167. data/tutorial/01/main.rb +7 -0
  168. data/tutorial/01/package.yml +8 -0
  169. data/tutorial/01/tutorial.rb +7 -0
  170. data/tutorial/02/main.rb +10 -0
  171. data/tutorial/02/package.yml +27 -0
  172. data/tutorial/02/tutorial.rb +46 -0
  173. data/tutorial/03/main.rb +24 -0
  174. data/tutorial/03/package.yml +29 -0
  175. data/tutorial/03/tutorial.rb +48 -0
  176. data/tutorial/04/main.rb +24 -0
  177. data/tutorial/04/package.yml +35 -0
  178. data/tutorial/04/tutorial.rb +48 -0
  179. data/tutorial/05/functions/package.yml +16 -0
  180. data/tutorial/05/functions/services.rb +15 -0
  181. data/tutorial/05/main.rb +10 -0
  182. data/tutorial/05/package.yml +35 -0
  183. data/tutorial/05/tutorial.rb +53 -0
  184. 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