copland 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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,65 @@
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/models/abstract'
35
+ require 'copland/models/proxy'
36
+
37
+ module Copland
38
+ module ServiceModel
39
+
40
+ # The threaded service model is much like the singleton service model,
41
+ # except it has one instance of the service point <em>per thread</em>.
42
+ class ThreadedServiceModel < AbstractServiceModel
43
+
44
+ # Return the current thread's instance of the service point. If no
45
+ # such instance exists, instantiate one.
46
+ def instance( &init )
47
+ Thread.current[ :threaded_services ] ||= Hash.new
48
+ service =
49
+ Thread.current[ :threaded_services ][ @service_point.full_name ]
50
+
51
+ unless service
52
+ service = Proxy.new( @service_point, &init )
53
+ Thread.current[ :threaded_services ][ @service_point.full_name ] =
54
+ service
55
+ end
56
+
57
+ return service
58
+ end
59
+
60
+ register_as "threaded"
61
+
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,123 @@
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
+ # This is module that implements ordering algorithms. Currently
39
+ # only one such algorithm is implemented, and it is used for ordering
40
+ # interceptors and AutoLoad-ed services.
41
+ module Orderer
42
+
43
+ # Orders the given list of elements. Each element MUST support the
44
+ # following interface:
45
+ #
46
+ # # +point+: return a service-point or configuration-point with
47
+ # which the element is associated. This is used for error reporting,
48
+ # when the ordering fails.
49
+ #
50
+ # # +before?(e): return true iff the element must occur before the
51
+ # element 'e' in the list. 'e' must be an element instance.
52
+ #
53
+ # # +after?(e): return true iff the element must occur after the
54
+ # element 'e' in the list. 'e' must be an element instance.
55
+ def order( elements )
56
+ list = []
57
+
58
+ return list if elements.empty?
59
+
60
+ elements = elements.dup
61
+
62
+ # 1: shift first element of 'elements' onto 'list'
63
+ list.push elements.shift
64
+
65
+ while !elements.empty?
66
+ # 2: for each element 'i' of 'elements', compare it with each
67
+ # element 'j' of 'list'. If position of i is constrained relative
68
+ # to j, shift i onto list relative to j. If conflicting constraints
69
+ # are discovered for i relative to multiple elements of list, raise
70
+ # an exception. If i is unconstrained relative to any j, leave it
71
+ # (for now) in 'elements'.
72
+
73
+ index = 0
74
+ insertions_made = 0
75
+
76
+ while index < elements.length
77
+ i = elements[ index ]
78
+ must_be_at = -1
79
+
80
+ list.each_index do |position|
81
+ j = list[ position ]
82
+
83
+ if must_be_at < 0 && i.before?( j )
84
+ must_be_at = position
85
+ end
86
+
87
+ if must_be_at >= 0 && i.after?( j )
88
+ raise OrderingException,
89
+ "#{i.point.full_name} < #{list[must_be_at].point.full_name} && " +
90
+ "#{i.point.full_name} > #{j.point.full_name}"
91
+ end
92
+ end
93
+
94
+ if must_be_at >= 0
95
+ elements.delete_at index
96
+ list.insert must_be_at, i
97
+ insertions_made += 1
98
+ else
99
+ index += 1
100
+ end
101
+ end
102
+
103
+ # 3: if no new elements were shifted onto 'list', start over at step 1.
104
+ # otherwise repeat from step 2. Continue until 'elements' list is
105
+ # empty.
106
+
107
+ if !elements.empty? && insertions_made < 1
108
+ # this is safe because, at this point, we know that the elements in 'list'
109
+ # and the remaining elements in 'elements' are independent of each other;
110
+ # thus, we can just push any value from 'elements' onto the end 'list',
111
+ # without violating any constraints.
112
+ list.push elements.shift
113
+ end
114
+
115
+ end
116
+
117
+ return list
118
+ end
119
+ module_function :order
120
+
121
+ end
122
+
123
+ end
@@ -0,0 +1,246 @@
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/utils'
36
+
37
+ module Copland
38
+
39
+ # This represents a single package in a Repository.
40
+ class Package
41
+
42
+ # The Registry instance that contains this package.
43
+ attr_reader :registry
44
+
45
+ # The name of this package.
46
+ attr_reader :name
47
+
48
+ # The description of the package.
49
+ attr_accessor :description
50
+
51
+ # Create a new package associated with the given registry. The package will
52
+ # have the given name. Note: this will _not_ add the package to the
53
+ # registry!
54
+ def initialize( registry, name )
55
+ @registry = registry
56
+ @name = name
57
+ @service_points = Hash.new
58
+ @configuration_points = Hash.new
59
+ @schemas = Hash.new
60
+ end
61
+
62
+ # Add the given service point to the package.
63
+ def add_service_point( service_point )
64
+ @service_points[ service_point.name ] = service_point
65
+ end
66
+
67
+ # Add the given configuration point to the package.
68
+ def add_configuration_point( configuration_point )
69
+ @configuration_points[ configuration_point.name ] = configuration_point
70
+ end
71
+
72
+ # Add the given schema to the package. Note that this requires that the
73
+ # schema have a name. This will also set the schema's +owner+ attribute
74
+ # to the package.
75
+ def add_schema( schema )
76
+ schema.owner = self
77
+ @schemas[ schema.name ] = schema
78
+ end
79
+
80
+ # Returns the service point with the given name. If no such service point
81
+ # exists, this returns +nil+.
82
+ def service_point( name )
83
+ @service_points[ name ]
84
+ end
85
+
86
+ # This returns the names of all service points in the package.
87
+ def service_points
88
+ @service_points.keys.freeze
89
+ end
90
+
91
+ # This instantiates the service point with the given name and returns the
92
+ # resulting service. If the service point does not exist, this will raise
93
+ # a ServicePointNotFound exception.
94
+ #
95
+ # If a block is given, it will be used to initialize the service (but only
96
+ # when the service is created--if the service is a singleton service and
97
+ # it was created previously, the init block will be ignored).
98
+ def service( name, &init )
99
+ point = service_point( name ) or raise ServicePointNotFound, name
100
+ point.instance( &init )
101
+ end
102
+
103
+ # Returns +true+ if the named service exists in this package, and +false+
104
+ # otherwise.
105
+ def service_exist?( name )
106
+ return !service_point( name ).nil?
107
+ end
108
+
109
+ # This is a convenience method for returning a service with the given name,
110
+ # giving preference when a package is not specified to the service points
111
+ # within the current package.
112
+ def find_service( name, &block )
113
+ Copland::get_possibly_local_service( registry, self, name, &block )
114
+ end
115
+
116
+ # This is a convenience method for returning a schema with the given name,
117
+ # giving preference when a package is not specified to the schemas
118
+ # within the current package.
119
+ def find_schema( name )
120
+ find_service( name ) do |pkg, id|
121
+ if pkg.nil?
122
+ raise PackageNotFound, name
123
+ else
124
+ schema = pkg.schema( id )
125
+ raise SchemaNotFound, name unless schema
126
+ return schema
127
+ end
128
+ end
129
+ end
130
+
131
+ # Returns the configuration point with the given name, or +nil+ if no such
132
+ # configuration point exists.
133
+ def configuration_point( name )
134
+ @configuration_points[ name ]
135
+ end
136
+
137
+ # Returns the names of all configuration points in the package.
138
+ def configuration_points
139
+ @configuration_points.keys.freeze
140
+ end
141
+
142
+ # Returns the schema with the given name, or +nil+ if no such schema
143
+ # exists.
144
+ def schema( name )
145
+ @schemas[ name ]
146
+ end
147
+
148
+ # Adds a "pending" contribution to the package. When the package is
149
+ # fixated (see #fixate!), the given value will be contributed to the
150
+ # configuration point with the given name. Once the package is
151
+ # fixated, this method will be illegal to invoke.
152
+ def add_pending_contribution( name, value )
153
+ ( @pending_contributions ||= [] ).push :name => name,
154
+ :value => value
155
+ end
156
+
157
+ # Iterates over each service point in the package.
158
+ def each_service_point( &block )
159
+ @service_points.each_value( &block )
160
+ self
161
+ end
162
+
163
+ # Iterates over each configuration point in the package.
164
+ def each_configuration_point( &block )
165
+ @configuration_points.each_value( &block )
166
+ end
167
+
168
+ # Iterates over each schema in the package.
169
+ def each_schema( &block )
170
+ @schemas.each_value( &block )
171
+ end
172
+
173
+ # Returns the number of service points in the package.
174
+ def service_point_count
175
+ @service_points.size
176
+ end
177
+
178
+ # Returns the number of configuration points in the package.
179
+ def configuration_point_count
180
+ @configuration_points.size
181
+ end
182
+
183
+ # Returns the number of schemas in the package.
184
+ def schema_count
185
+ @schemas.size
186
+ end
187
+
188
+ # Fixates the package. This will, in turn, fixate each configuration and
189
+ # service point in the package. Also, all pending contributions will be
190
+ # contributed to the configuration points they were intended for.
191
+ def fixate!
192
+ extend Fixated
193
+
194
+ @schemas.each_value { |schema| schema.fixate! }
195
+ @service_points.each_value { |point| point.fixate! }
196
+ @configuration_points.each_value { |point| point.fixate! }
197
+
198
+ if @pending_contributions
199
+ @pending_contributions.each do |value|
200
+ name = value[ :name ]
201
+ contribution = value[ :value ]
202
+
203
+ configuration_point = find_service( name ) do |pkg, id|
204
+ raise PackageNotFound, name unless pkg
205
+ pkg.configuration_point( id )
206
+ end
207
+
208
+ raise ConfigurationPointNotFound, name unless configuration_point
209
+
210
+ contribution.instance_variable_set( :@contributor, self )
211
+ configuration_point.contribute contribution
212
+ end
213
+
214
+ remove_instance_variable :@pending_contributions
215
+ end
216
+ end
217
+
218
+ # Returns +false+, although when the package is fixated this will be
219
+ # overridden with a method that will return +true+.
220
+ def fixated?
221
+ false
222
+ end
223
+
224
+ # This module will be included by the package when it is fixated.
225
+ module Fixated
226
+
227
+ # Raises DisallowedOperationException.
228
+ def add_pending_contribution( *args )
229
+ raise DisallowedOperationException,
230
+ "cannot add pending contributions to fixated package"
231
+ end
232
+
233
+ # Does nothing.
234
+ def fixate!
235
+ # does nothing
236
+ end
237
+
238
+ # Returns +true+.
239
+ def fixated?
240
+ true
241
+ end
242
+ end
243
+
244
+ end
245
+
246
+ end
@@ -0,0 +1,368 @@
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/class-factory'
35
+ require 'copland/errors'
36
+ require 'copland/event-producer'
37
+ require 'copland/instantiator'
38
+ require 'copland/log-factory'
39
+ require 'copland/models'
40
+ require 'copland/package'
41
+ require 'copland/service-point'
42
+ require 'copland/utils'
43
+
44
+ require 'copland/configuration/loader'
45
+ require 'copland/configuration/yaml/loader'
46
+
47
+ module Copland
48
+
49
+ # The Registry is the primary interface exported by Copland. Very rarely
50
+ # should client applications need to access any other interface directly.
51
+ #
52
+ # The Registry can be constructed in two ways. The first (and most common) is
53
+ # to call the #build method, which instantiates, initializes, and returns the
54
+ # new Registry instance in one step.
55
+ #
56
+ # registry = Registry.build
57
+ #
58
+ # The second is the instantiate an empty Registry and initialize it yourself.
59
+ # This is a more arduous approach, but may be necessary for some more complex
60
+ # Registry requirements.
61
+ class Registry
62
+ include EventProducer
63
+
64
+ # Instantiate, initialize, and return a new Registry instance. The accepted
65
+ # parameters are:
66
+ #
67
+ # * if the first parameter is a String, it is interpreted to be the
68
+ # "default" search path. (This defaults to ".".)
69
+ # * the other parameter (or the first parameter, if it is not a String)
70
+ # must be either absent, or it must be a Hash, in which case it specifies
71
+ # options that will be used to construct and initialize the Registry.
72
+ #
73
+ # Valid options are:
74
+ #
75
+ # * <tt>:default_service_model</tt>: the default service model to use when
76
+ # creating service points, when an explicit service model has not been
77
+ # given.
78
+ # * <tt>:search_paths</tt>: an array of paths that should be searched for
79
+ # package descriptors.
80
+ #
81
+ # Also, any option that LogFactory accepts is also valid, prefixed with
82
+ # "log_". (I.e., <tt>:log_device</tt> instead of <tt>:device</tt>.)
83
+ def self.build( *args )
84
+ if args.length > 2
85
+ raise ArgumentError, "expected [0..2] arguments, got #{args.length}"
86
+ end
87
+
88
+ default_search_path = "."
89
+ options = {}
90
+
91
+ default_search_path = args.shift if args.first.is_a? String
92
+ options = args.shift if args.first.is_a? Hash
93
+
94
+ unless args.empty?
95
+ raise ArgumentError, "unexpected argument type #{args.first.class}"
96
+ end
97
+
98
+ ( options[:search_paths] ||= [] ).unshift( default_search_path ).uniq!
99
+ options[:search_paths].compact!
100
+
101
+ search_paths = options[:search_paths]
102
+ options.delete :search_paths
103
+
104
+ registry = new( options )
105
+ loader = Copland::Configuration::Loader.new( search_paths )
106
+ loader.add_loader Copland::Configuration::YAML::Loader.new( registry,
107
+ loader )
108
+ loader.load( options )
109
+
110
+ startup = registry.service( "copland.Startup" )
111
+ startup.start
112
+
113
+ return registry
114
+ end
115
+
116
+ # The LogFactory instance that will be employed by this registry for
117
+ # creating logger instances.
118
+ attr_reader :logs
119
+
120
+ # Create a new, empty registry. The +options+ hash accepts any value that
121
+ # LogFactory's constructor accepts, with "log_" prefixed. (I.e.,
122
+ # <tt>:log_device</tt> instead of <tt>:device</tt>.)
123
+ def initialize( options={} )
124
+ @packages = Hash.new
125
+
126
+ log_options = Hash.new
127
+ options.each do |k,v|
128
+ log_options[$1.intern] = v if k.to_s =~ /^log_(.*)/
129
+ end
130
+
131
+ @logs = LogFactory.new( log_options )
132
+ end
133
+
134
+ # Fixates the registry. This performs one-time operations on the registry,
135
+ # and then makes it illegal to perform those operations subsequently on the
136
+ # same registry instance.
137
+ #
138
+ # This will create the "copland.Registry" service point, fixate each of the
139
+ # registry's packages, and then register a finalizer in ObjectSpace that
140
+ # will cause the registry to always be gracefully shutdown.
141
+ def fixate!
142
+ define_self_as_service
143
+ define_log_factory_as_service
144
+ @packages.each { |id, pkg| pkg.fixate! }
145
+ ObjectSpace.define_finalizer( self, proc { shutdown } )
146
+ extend Fixated
147
+ end
148
+
149
+ # Returns +false+. After the registry has been fixated (see #fixate!), this
150
+ # will be overridden with a method that returns +true+.
151
+ def fixated?
152
+ false
153
+ end
154
+
155
+ # Shuts down the registry by notifying any interested listeners (using the
156
+ # <tt>:registry_shutdown</tt> event), closing the logs, and then
157
+ # extending the Shutdown module.
158
+ def shutdown
159
+ fire_event( :registry_shutdown )
160
+ @logs.close
161
+ extend Shutdown
162
+ end
163
+
164
+ # Returns +false+. After the registry has been #shutdown, this will be be
165
+ # overridden with a method that returns +true+.
166
+ def shutdown?
167
+ false
168
+ end
169
+
170
+ # This creates a new service point called "copland.Registry" and makes it
171
+ # so that it always returns the active registry instance when instantiated.
172
+ def define_self_as_service
173
+ description = "This service is intended to allow services to " +
174
+ "obtain a reference to the registry which created them. In general, " +
175
+ "however, services should never need to reference the registry " +
176
+ "directly; instead, they should rely on their descriptors to " +
177
+ "specify their dependencies. There are a few exceptions to this " +
178
+ "rule, though, and it is for those exceptions that this service is " +
179
+ "made available."
180
+
181
+ add_identity_service "copland",
182
+ "Registry",
183
+ self,
184
+ description
185
+ end
186
+ private :define_self_as_service
187
+
188
+ # This creates a new service point called "copland.LogFactory" and makes it
189
+ # so that it always returns the log factory instance owned by the active
190
+ # registry.
191
+ def define_log_factory_as_service
192
+ description = "The LogFactory service provides log instances " +
193
+ "on demand. It is always an identity for the log factory instance " +
194
+ "owned by the registry."
195
+
196
+ add_identity_service "copland",
197
+ "LogFactory",
198
+ logs,
199
+ description
200
+ end
201
+ private :define_self_as_service
202
+
203
+ # A convenience method for clients that wish to add a specific object to
204
+ # the registry as an "identity" service (that is, a singleton service point
205
+ # that always instantiates as the given object).
206
+ def add_identity_service( package_name,
207
+ service_point_name,
208
+ object,
209
+ description=nil )
210
+ # begin
211
+ owner = package( package_name )
212
+ if owner.nil?
213
+ owner = Package.new( self, package_name )
214
+ add_package owner
215
+ end
216
+
217
+ service_point = ServicePoint.new( owner, service_point_name )
218
+ service_point.use_service_model "singleton"
219
+
220
+ service_point.description = description
221
+
222
+ service_point.instantiator = ClassFactory.instance.get(
223
+ Instantiator::POOL_NAME, "identity", service_point, object )
224
+ owner.add_service_point( service_point )
225
+
226
+ service_point
227
+ end
228
+
229
+ # Returns the package with the given name. If there is no such package,
230
+ # this returns +nil+.
231
+ def package( name )
232
+ @packages[ name ]
233
+ end
234
+
235
+ # Returns the service with the given name. If no such service point can
236
+ # be found, this raises the ServicePointNotFound exception. (See
237
+ # Package#service.)
238
+ #
239
+ # If a block is given, it is passed to Package#service and is used to
240
+ # initialize the service.
241
+ def service( name, &init )
242
+ Copland::get_possibly_local_service( self, nil, name ) do |pkg, id|
243
+ unless pkg
244
+ raise ServicePointNotFound,
245
+ "A fully-qualified service name must be given to Registry#service"
246
+ else
247
+ return pkg.service( id, &init )
248
+ end
249
+ end
250
+ end
251
+
252
+ # Returns the service point with the given name. If no such service point
253
+ # can be found, this +nil+.
254
+ def service_point( name )
255
+ Copland::get_possibly_local_service( self, nil, name ) do |pkg, id|
256
+ unless pkg
257
+ raise ServicePointNotFound,
258
+ "A fully-qualified service name must be given to Registry#service_point"
259
+ else
260
+ return pkg.service_point( id )
261
+ end
262
+ end
263
+ end
264
+
265
+ # Returns +true+ if the named service exists in this registry, and +false+
266
+ # otherwise.
267
+ def service_exist?( name )
268
+ Copland::get_possibly_local_service( self, nil, name ) do |pkg, id|
269
+ unless pkg
270
+ return false
271
+ else
272
+ return pkg.service_exist?( id )
273
+ end
274
+ end
275
+ end
276
+
277
+ # This returns the configuration point with the given name. If the
278
+ # package exists but no configuration point by that name can be
279
+ # found in it, this returns +nil+. If the package itself does not
280
+ # exist, then a ConfigurationPointNotFound exception is raised.
281
+ def configuration_point( name )
282
+ Copland::get_possibly_local_service( self, nil, name ) do |pkg, id|
283
+ unless pkg
284
+ raise ConfigurationPointNotFound,
285
+ "A fully-qualified configuration point name must be given " +
286
+ "to Registry#service"
287
+ else
288
+ return pkg.configuration_point( id )
289
+ end
290
+ end
291
+ end
292
+
293
+ # Adds the given package to the registry. If there is already a package
294
+ # in the registry by that name, a DuplicatePackageError will be raised.
295
+ def add_package( pkg )
296
+ if @packages[ pkg.name ]
297
+ raise DuplicatePackageError, pkg.name
298
+ end
299
+
300
+ @packages[ pkg.name ] = pkg
301
+ end
302
+
303
+ # Iterates over each package in the registry.
304
+ def each_package( &block )
305
+ @packages.each_value( &block )
306
+ self
307
+ end
308
+
309
+ # Returns the number of packages in the registry.
310
+ def package_count
311
+ @packages.size
312
+ end
313
+
314
+ # When Registry#fixate! is called for the first time, it will extend this
315
+ # module.
316
+ module Fixated
317
+ # Does nothing
318
+ def fixate!
319
+ # do nothing
320
+ end
321
+
322
+ # Returns +true+.
323
+ def fixated?
324
+ true
325
+ end
326
+ end
327
+
328
+ # When Registry#shutdown is called for the first time, it will extend this
329
+ # module. This will make several operations illegal, like querying a
330
+ # service.
331
+ module Shutdown
332
+ # Does nothing.
333
+ def shutdown( *args )
334
+ # do nothing
335
+ end
336
+
337
+ # Returns +true+.
338
+ def shutdown?
339
+ true
340
+ end
341
+
342
+ # Raises a DisallowedOperationException.
343
+ def package( *args )
344
+ raise DisallowedOperationException,
345
+ "cannot get package from registry after shutdown"
346
+ end
347
+
348
+ # Raises a DisallowedOperationException.
349
+ def service( *args )
350
+ raise DisallowedOperationException,
351
+ "cannot get service from registry after shutdown"
352
+ end
353
+
354
+ # Raises a DisallowedOperationException.
355
+ def configuration_point( *args )
356
+ raise DisallowedOperationException,
357
+ "cannot get configuration point from registry after shutdown"
358
+ end
359
+
360
+ # Raises a DisallowedOperationException.
361
+ def add_package( *args )
362
+ raise DisallowedOperationException,
363
+ "cannot add package to registry after shutdown"
364
+ end
365
+ end
366
+ end
367
+
368
+ end