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,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