copland 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/doc/README +88 -0
- data/doc/manual-html/chapter-1.html +454 -0
- data/doc/manual-html/chapter-10.html +399 -0
- data/doc/manual-html/chapter-11.html +600 -0
- data/doc/manual-html/chapter-12.html +406 -0
- data/doc/manual-html/chapter-2.html +382 -0
- data/doc/manual-html/chapter-3.html +424 -0
- data/doc/manual-html/chapter-4.html +432 -0
- data/doc/manual-html/chapter-5.html +381 -0
- data/doc/manual-html/chapter-6.html +364 -0
- data/doc/manual-html/chapter-7.html +434 -0
- data/doc/manual-html/chapter-8.html +373 -0
- data/doc/manual-html/chapter-9.html +324 -0
- data/doc/manual-html/copland.png +0 -0
- data/doc/manual-html/index.html +331 -0
- data/doc/manual-html/manual.css +179 -0
- data/doc/manual-html/tutorial-1.html +407 -0
- data/doc/manual-html/tutorial-2.html +451 -0
- data/doc/manual-html/tutorial-3.html +484 -0
- data/doc/manual-html/tutorial-4.html +446 -0
- data/doc/manual-html/tutorial-5.html +520 -0
- data/doc/manual/chapter.erb +18 -0
- data/doc/manual/example.erb +18 -0
- data/doc/manual/img/copland.png +0 -0
- data/doc/manual/index.erb +30 -0
- data/doc/manual/manual.css +179 -0
- data/doc/manual/manual.rb +239 -0
- data/doc/manual/manual.yml +2643 -0
- data/doc/manual/page.erb +102 -0
- data/doc/manual/tutorial.erb +30 -0
- data/doc/packages/copland.html +764 -0
- data/doc/packages/copland.lib.html +439 -0
- data/doc/packages/copland.remote.html +2096 -0
- data/doc/packages/copland.webrick.html +925 -0
- data/doc/packages/index.html +49 -0
- data/doc/packages/packrat.css +125 -0
- data/examples/calc/calc.rb +47 -0
- data/examples/calc/package.yml +35 -0
- data/examples/calc/services.rb +74 -0
- data/examples/solitaire-cipher/README +11 -0
- data/examples/solitaire-cipher/Rakefile +57 -0
- data/examples/solitaire-cipher/bin/main.rb +14 -0
- data/examples/solitaire-cipher/lib/cipher.rb +230 -0
- data/examples/solitaire-cipher/lib/cli.rb +24 -0
- data/examples/solitaire-cipher/lib/package.yml +106 -0
- data/examples/solitaire-cipher/test/tc_deck.rb +30 -0
- data/examples/solitaire-cipher/test/tc_key-stream.rb +19 -0
- data/examples/solitaire-cipher/test/tc_keying-algorithms.rb +31 -0
- data/examples/solitaire-cipher/test/tc_solitaire-cipher.rb +66 -0
- data/examples/solitaire-cipher/test/tc_unkeyed-algorithm.rb +17 -0
- data/examples/solitaire-cipher/test/tests.rb +2 -0
- data/lib/copland.rb +56 -0
- data/lib/copland/class-factory.rb +95 -0
- data/lib/copland/configuration-point.rb +38 -0
- data/lib/copland/configuration-point/common.rb +203 -0
- data/lib/copland/configuration-point/errors.rb +44 -0
- data/lib/copland/configuration-point/list.rb +59 -0
- data/lib/copland/configuration-point/map.rb +59 -0
- data/lib/copland/configuration/errors.rb +43 -0
- data/lib/copland/configuration/loader.rb +113 -0
- data/lib/copland/configuration/yaml/configuration-point.rb +87 -0
- data/lib/copland/configuration/yaml/implementor.rb +134 -0
- data/lib/copland/configuration/yaml/interceptor.rb +63 -0
- data/lib/copland/configuration/yaml/listener.rb +56 -0
- data/lib/copland/configuration/yaml/loader.rb +122 -0
- data/lib/copland/configuration/yaml/package.rb +125 -0
- data/lib/copland/configuration/yaml/parser.rb +71 -0
- data/lib/copland/configuration/yaml/schema.rb +165 -0
- data/lib/copland/configuration/yaml/service-point.rb +116 -0
- data/lib/copland/configuration/yaml/utils.rb +82 -0
- data/lib/copland/default-schema-processor.rb +144 -0
- data/lib/copland/errors.rb +82 -0
- data/lib/copland/event-producer.rb +95 -0
- data/lib/copland/impl/builder-factory.rb +112 -0
- data/lib/copland/impl/copland-config.yml +1 -0
- data/lib/copland/impl/include-exclude.rb +140 -0
- data/lib/copland/impl/logging-interceptor.rb +106 -0
- data/lib/copland/impl/package.yml +217 -0
- data/lib/copland/impl/startup.rb +116 -0
- data/lib/copland/impl/symbol-source-manager.rb +131 -0
- data/lib/copland/impl/symbol-source.rb +63 -0
- data/lib/copland/instantiator.rb +38 -0
- data/lib/copland/instantiator/abstract.rb +91 -0
- data/lib/copland/instantiator/complex.rb +96 -0
- data/lib/copland/instantiator/identity.rb +58 -0
- data/lib/copland/instantiator/simple.rb +68 -0
- data/lib/copland/interceptor-chain.rb +166 -0
- data/lib/copland/interceptor.rb +139 -0
- data/lib/copland/log-factory.rb +206 -0
- data/lib/copland/models.rb +39 -0
- data/lib/copland/models/abstract.rb +78 -0
- data/lib/copland/models/prototype-deferred.rb +58 -0
- data/lib/copland/models/prototype.rb +58 -0
- data/lib/copland/models/proxy.rb +100 -0
- data/lib/copland/models/singleton-deferred.rb +59 -0
- data/lib/copland/models/singleton.rb +77 -0
- data/lib/copland/models/threaded.rb +65 -0
- data/lib/copland/ordering.rb +123 -0
- data/lib/copland/package.rb +246 -0
- data/lib/copland/registry.rb +368 -0
- data/lib/copland/schema.rb +206 -0
- data/lib/copland/service-point.rb +282 -0
- data/lib/copland/utils.rb +221 -0
- data/lib/copland/version.rb +47 -0
- data/test/conf-test/list-bad-key.yml +30 -0
- data/test/conf-test/list-bad-missing.yml +28 -0
- data/test/conf-test/list-bad-type.yml +28 -0
- data/test/conf-test/list-good.yml +29 -0
- data/test/conf-test/map-bad-key.yml +25 -0
- data/test/conf-test/map-bad-missing.yml +24 -0
- data/test/conf-test/map-bad-type.yml +23 -0
- data/test/conf-test/map-good.yml +25 -0
- data/test/configuration-point/package.yml +52 -0
- data/test/configuration/yaml/config/copland-config.yml +2 -0
- data/test/configuration/yaml/config/module.yml +2 -0
- data/test/configuration/yaml/config/subdir/copland-config.yml +2 -0
- data/test/configuration/yaml/config/subdir/package.yml +4 -0
- data/test/configuration/yaml/defaults/package.yml +5 -0
- data/test/configuration/yaml/defaults/subdir/package.yml +4 -0
- data/test/configuration/yaml/tc_config-loader.rb +86 -0
- data/test/configuration/yaml/tc_configuration-point-processor.rb +134 -0
- data/test/configuration/yaml/tc_implementor-processor.rb +104 -0
- data/test/configuration/yaml/tc_interceptor-processor.rb +85 -0
- data/test/configuration/yaml/tc_listener-processor.rb +69 -0
- data/test/configuration/yaml/tc_loader.rb +74 -0
- data/test/configuration/yaml/tc_package-processor.rb +120 -0
- data/test/configuration/yaml/tc_parser.rb +94 -0
- data/test/configuration/yaml/tc_schema-parser.rb +160 -0
- data/test/configuration/yaml/tc_service-point-processor.rb +104 -0
- data/test/configuration/yaml/tc_type-validator.rb +90 -0
- data/test/custom-logger.yml +3 -0
- data/test/impl/logging/package.yml +44 -0
- data/test/impl/logging/services.rb +84 -0
- data/test/impl/startup/package.yml +46 -0
- data/test/impl/startup/services.rb +47 -0
- data/test/impl/symbols/package.yml +24 -0
- data/test/impl/symbols/services.rb +38 -0
- data/test/impl/tc_builder-factory.rb +173 -0
- data/test/impl/tc_logging-interceptor.rb +148 -0
- data/test/impl/tc_startup.rb +59 -0
- data/test/impl/tc_symbol-sources.rb +61 -0
- data/test/logger.yml +6 -0
- data/test/mock.rb +201 -0
- data/test/schema/bad-package.yml +65 -0
- data/test/schema/package.yml +102 -0
- data/test/schema/services.rb +5 -0
- data/test/services/package.yml +79 -0
- data/test/services/simple.rb +87 -0
- data/test/tc_class-factory.rb +93 -0
- data/test/tc_complex-instantiator.rb +107 -0
- data/test/tc_configuration-point-contrib.rb +74 -0
- data/test/tc_configuration-point-schema.rb +122 -0
- data/test/tc_configuration-point.rb +91 -0
- data/test/tc_default-schema-processor.rb +297 -0
- data/test/tc_identity-instantiator.rb +61 -0
- data/test/tc_interceptors.rb +84 -0
- data/test/tc_logger.rb +131 -0
- data/test/tc_models.rb +176 -0
- data/test/tc_package.rb +165 -0
- data/test/tc_proxy.rb +65 -0
- data/test/tc_registry.rb +141 -0
- data/test/tc_schema.rb +78 -0
- data/test/tc_service-point.rb +178 -0
- data/test/tc_service.rb +70 -0
- data/test/tc_simple-instantiator.rb +61 -0
- data/test/tests.rb +93 -0
- data/tutorial/01/main.rb +7 -0
- data/tutorial/01/package.yml +8 -0
- data/tutorial/01/tutorial.rb +7 -0
- data/tutorial/02/main.rb +10 -0
- data/tutorial/02/package.yml +27 -0
- data/tutorial/02/tutorial.rb +46 -0
- data/tutorial/03/main.rb +24 -0
- data/tutorial/03/package.yml +29 -0
- data/tutorial/03/tutorial.rb +48 -0
- data/tutorial/04/main.rb +24 -0
- data/tutorial/04/package.yml +35 -0
- data/tutorial/04/tutorial.rb +48 -0
- data/tutorial/05/functions/package.yml +16 -0
- data/tutorial/05/functions/services.rb +15 -0
- data/tutorial/05/main.rb +10 -0
- data/tutorial/05/package.yml +35 -0
- data/tutorial/05/tutorial.rb +53 -0
- metadata +260 -0
|
@@ -0,0 +1,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 'copland/utils'
|
|
35
|
+
|
|
36
|
+
module Copland
|
|
37
|
+
|
|
38
|
+
# This class represents a single node in a schema "tree". The entire tree is
|
|
39
|
+
# the actual schema, where it is recursively defined using instances of this
|
|
40
|
+
# class.
|
|
41
|
+
class Schema
|
|
42
|
+
|
|
43
|
+
# The children nodes of this schema node. If +nil+, then this is a leaf
|
|
44
|
+
# node.
|
|
45
|
+
attr_reader :definition
|
|
46
|
+
|
|
47
|
+
# The optional description of this schema.
|
|
48
|
+
attr_reader :description
|
|
49
|
+
|
|
50
|
+
# The path description that identifies the class to use when processing
|
|
51
|
+
# this schema.
|
|
52
|
+
attr_reader :processor_path
|
|
53
|
+
|
|
54
|
+
# The optional name of this schema.
|
|
55
|
+
attr_reader :name
|
|
56
|
+
|
|
57
|
+
# Whether or not this node is required.
|
|
58
|
+
attr_reader :required
|
|
59
|
+
|
|
60
|
+
# The type of this node. If +nil+, then it may be of any type.
|
|
61
|
+
attr_reader :type
|
|
62
|
+
|
|
63
|
+
# The owning package of this schema.
|
|
64
|
+
attr_accessor :owner
|
|
65
|
+
|
|
66
|
+
# The schema that this schema extends.
|
|
67
|
+
attr_accessor :extends
|
|
68
|
+
|
|
69
|
+
# Create a new schema node with the given name, processor class,
|
|
70
|
+
# description, required-flag, type, and definition. If processor is +nil+,
|
|
71
|
+
# then the DefaultSchemaProcessor class will be used.
|
|
72
|
+
def initialize( name, processor, description,
|
|
73
|
+
required, type, definition,
|
|
74
|
+
extends )
|
|
75
|
+
#begin
|
|
76
|
+
@definition, @description, @name = definition, description, name
|
|
77
|
+
@processor_path, @required, @type = processor, required, type
|
|
78
|
+
@extends = extends
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Returns the fully-qualified name of this schema, but only if the schema
|
|
82
|
+
# has both a name and an owning package. Otherwise, it returns +nil+.
|
|
83
|
+
def full_name
|
|
84
|
+
if @owner && @name
|
|
85
|
+
@owner.name + "." + @name
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Validate the given data against this schema node. The +owner+ is the
|
|
90
|
+
# service point (or configuration point) that the schema is being
|
|
91
|
+
# processed for, and the +client+ is the service point (or configuration
|
|
92
|
+
# point) that the +data+ is being processed for. If +path+ is given, it
|
|
93
|
+
# represents the path through the schema leading to this node. If
|
|
94
|
+
# processor is given, then it should be used to process this node as long
|
|
95
|
+
# as it is of the same type as this node's processor.
|
|
96
|
+
def validate( owner, client, data, path="/", processor=nil )
|
|
97
|
+
# always make sure the schema has been fixated before we try to validate
|
|
98
|
+
# anything with it.
|
|
99
|
+
fixate!
|
|
100
|
+
|
|
101
|
+
processor = get_processor( processor )
|
|
102
|
+
|
|
103
|
+
if processor.respond_to?( :validate )
|
|
104
|
+
if data.is_a?( Array ) ||
|
|
105
|
+
( data.respond_to?( :type_id ) && data.type_id == "array" )
|
|
106
|
+
# then
|
|
107
|
+
data = data.value if data.respond_to?( :type_id )
|
|
108
|
+
index = 0
|
|
109
|
+
data.each do |item|
|
|
110
|
+
processor.validate( owner, client, self, item, "#{path}[#{index}]" )
|
|
111
|
+
index += 1
|
|
112
|
+
end
|
|
113
|
+
else
|
|
114
|
+
processor.validate( owner, client, self, data, path )
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Preprocess the given data against this schema node. The +owner+ is the
|
|
120
|
+
# service point (or configuration point) that the schema is being
|
|
121
|
+
# processed for, and the +client+ is the service point (or configuration
|
|
122
|
+
# point) that the +data+ is being processed for. If +path+ is given, it
|
|
123
|
+
# represents the path through the schema leading to this node. If
|
|
124
|
+
# processor is given, then it should be used to process this node as long
|
|
125
|
+
# as it is of the same type as this node's processor.
|
|
126
|
+
#
|
|
127
|
+
# The preprocessed data is returned.
|
|
128
|
+
def process( owner, client, data, path="/", processor=nil )
|
|
129
|
+
processor = get_processor( processor )
|
|
130
|
+
|
|
131
|
+
if processor.respond_to?( :process )
|
|
132
|
+
return processor.process( owner, client, self, data, path )
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
return data
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Returns the processor instance for this node. If +default+ is given, it
|
|
139
|
+
# will be used if either this instance's processor is +nil+, or if this
|
|
140
|
+
# instance's processor is of the same type as +default+. If both +default+
|
|
141
|
+
# and this instance's processor are +nil+, then an instance of the
|
|
142
|
+
# DefaultSchemaProcessor will be returned.
|
|
143
|
+
def get_processor( default )
|
|
144
|
+
return default if default && @processor_path.nil?
|
|
145
|
+
|
|
146
|
+
unless @processor_class
|
|
147
|
+
@processor_path ||=
|
|
148
|
+
"copland/default-schema-processor/Copland::DefaultSchemaProcessor"
|
|
149
|
+
|
|
150
|
+
@processor_file, @processor_class =
|
|
151
|
+
Copland::get_class_ref_parts( @processor_path )
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
return default if default && default.class.name == @processor_class
|
|
155
|
+
|
|
156
|
+
@processor ||= Copland::get_class( @processor_path ).new
|
|
157
|
+
return @processor
|
|
158
|
+
end
|
|
159
|
+
private :get_processor
|
|
160
|
+
|
|
161
|
+
# Fixate the schema by recursively processing each subschema. If any
|
|
162
|
+
# subschema is actually a string, it is replaced by the schema with
|
|
163
|
+
# that name.
|
|
164
|
+
def fixate!
|
|
165
|
+
extend Fixated
|
|
166
|
+
|
|
167
|
+
if @extends
|
|
168
|
+
new_definition = @owner.find_schema( @extends ).definition.dup
|
|
169
|
+
new_definition.update @definition if @definition
|
|
170
|
+
@definition = new_definition
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if @definition.is_a?( String )
|
|
174
|
+
@definition = @owner.find_schema( @definition ).definition
|
|
175
|
+
elsif @definition.is_a?( Hash )
|
|
176
|
+
@definition.each_key do |key|
|
|
177
|
+
value = @definition[ key ]
|
|
178
|
+
if value.is_a?( String )
|
|
179
|
+
@definition[ key ] = @owner.find_schema( value )
|
|
180
|
+
else
|
|
181
|
+
value.fixate!
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Returns false.
|
|
188
|
+
def fixated?
|
|
189
|
+
false
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Used to extend a schema that has been fixated. This makes the #fixate!
|
|
193
|
+
# method do nothing.
|
|
194
|
+
module Fixated
|
|
195
|
+
def fixate!
|
|
196
|
+
# does nothing
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def fixated?
|
|
200
|
+
true
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
end
|
|
@@ -0,0 +1,282 @@
|
|
|
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/interceptor'
|
|
37
|
+
require 'copland/interceptor-chain'
|
|
38
|
+
require 'copland/models'
|
|
39
|
+
require 'copland/utils'
|
|
40
|
+
|
|
41
|
+
module Copland
|
|
42
|
+
|
|
43
|
+
# A "service point" is the definition of a service. Just as a class describes
|
|
44
|
+
# an object, a service point describes a service. Just as an object is the
|
|
45
|
+
# instantiation of a class, so is a service the instantiation of a service
|
|
46
|
+
# point.
|
|
47
|
+
#
|
|
48
|
+
# A service point consists of an "instantiator" (which describes _how_ the
|
|
49
|
+
# service is to be instantiated) and a service model (which describes _when_
|
|
50
|
+
# the service is to be instantiated). Optionally, a service point may also
|
|
51
|
+
# have _interceptors_ (which act as filters on the methods of a service),
|
|
52
|
+
# _event producers_ (which send events to the service) and a _schema_
|
|
53
|
+
# (which is only useful for factory services, and describes what the format
|
|
54
|
+
# of the parameters that the factory understands).
|
|
55
|
+
class ServicePoint
|
|
56
|
+
|
|
57
|
+
# This is the service model that will be used when no other has been
|
|
58
|
+
# specified, and no other default was given in the options when the
|
|
59
|
+
# service point was created.
|
|
60
|
+
DEFAULT_SERVICE_MODEL = "singleton-deferred"
|
|
61
|
+
|
|
62
|
+
# The package the owns this service point.
|
|
63
|
+
attr_reader :owner
|
|
64
|
+
|
|
65
|
+
# The unqualified name of this service point.
|
|
66
|
+
attr_reader :name
|
|
67
|
+
|
|
68
|
+
# An array of service points that instances of this service point will
|
|
69
|
+
# listen to for events.
|
|
70
|
+
attr_reader :event_producers
|
|
71
|
+
|
|
72
|
+
# An array of Interceptor objects that should be instantiated when this
|
|
73
|
+
# service is instantiated.
|
|
74
|
+
attr_reader :interceptors
|
|
75
|
+
|
|
76
|
+
# An (optional) description of this service point.
|
|
77
|
+
attr_accessor :description
|
|
78
|
+
|
|
79
|
+
# The instantiator that will be used to instantiate this service point.
|
|
80
|
+
# (When a service point is first created, this is +nil+, and the service
|
|
81
|
+
# point cannot be instantiated until an instantiator is specified.)
|
|
82
|
+
attr_accessor :instantiator
|
|
83
|
+
|
|
84
|
+
# The (optional) schema specification that identifies the valid parameters
|
|
85
|
+
# that can be given to an instance of this service point. This only applies
|
|
86
|
+
# when the service point describes a factory service, in which case the
|
|
87
|
+
# schema will be used to validate and preprocess the parameters that are
|
|
88
|
+
# passed to the factory when a new instance is required.
|
|
89
|
+
attr_accessor :schema
|
|
90
|
+
|
|
91
|
+
# Create a new service point, contained by the +owner+ package. The only
|
|
92
|
+
# recognized option currently is <tt>:default_service_model</tt>, which
|
|
93
|
+
# is used to identify the service model to use when no other service model
|
|
94
|
+
# has been specified.
|
|
95
|
+
def initialize( owner, name, opts={} )
|
|
96
|
+
@owner = owner
|
|
97
|
+
@name = name
|
|
98
|
+
|
|
99
|
+
use_service_model opts[ :default_service_model ] || DEFAULT_SERVICE_MODEL
|
|
100
|
+
|
|
101
|
+
@event_producers = []
|
|
102
|
+
@interceptors = []
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Returns the fully-qualified name of this service point, which will be the
|
|
106
|
+
# name of the package and the name of the service point, separated by a
|
|
107
|
+
# dot.
|
|
108
|
+
def full_name
|
|
109
|
+
owner.name + "." + name
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Return the service model instance that will be used to regulate the
|
|
113
|
+
# instantiation of this service point.
|
|
114
|
+
def service_model
|
|
115
|
+
@model
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Specify the name of service model to use for this service point. An
|
|
119
|
+
# instance of the corresponding service model will be looked up in the
|
|
120
|
+
# class factory.
|
|
121
|
+
def use_service_model( service_model_name )
|
|
122
|
+
@model =
|
|
123
|
+
Copland::ClassFactory.instance.get(
|
|
124
|
+
Copland::ServiceModel::POOL_NAME,
|
|
125
|
+
service_model_name,
|
|
126
|
+
self )
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Add the given service point as an event producer for this service point.
|
|
130
|
+
def add_event_producer( producer )
|
|
131
|
+
( @event_producers << producer ).uniq!
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Add the given Interceptor object to this service point, to be
|
|
135
|
+
# instantiated and applied when a new service is created.
|
|
136
|
+
def add_interceptor( interceptor )
|
|
137
|
+
( @interceptors << interceptor ).uniq!
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Returns the instance returned by the service model. This is the
|
|
141
|
+
# preferred way of instantiating a service point, since it takes advantage
|
|
142
|
+
# of the regulatory services offered by the service model.
|
|
143
|
+
#
|
|
144
|
+
# If a block is given, it will be used to do one-time initialization of
|
|
145
|
+
# the new service.
|
|
146
|
+
def instance( &block )
|
|
147
|
+
@model.instance( &block )
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Directly instantiates the service point. This also applies the
|
|
151
|
+
# interceptors and sends out notifications that the service has been
|
|
152
|
+
# instantiated.
|
|
153
|
+
#
|
|
154
|
+
# This should never be called except by service model instances. If you
|
|
155
|
+
# need to programmatically instantiate a service point, you should use the
|
|
156
|
+
# #instance method; otherwise the service model will be bypassed.
|
|
157
|
+
def instantiate( &init )
|
|
158
|
+
service = @instantiator.instantiate
|
|
159
|
+
|
|
160
|
+
service.instance_eval &init if init
|
|
161
|
+
|
|
162
|
+
# the service is registered with each of its event producers before
|
|
163
|
+
# the interceptors are added, thus enabling the event notifications to
|
|
164
|
+
# bypass the interceptor layer
|
|
165
|
+
@event_producers.each do |producer|
|
|
166
|
+
producer.instance.add_listener service
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Construct the interceptor layer around the service
|
|
170
|
+
service = InterceptorChainBuilder.build( service, @interceptors )
|
|
171
|
+
|
|
172
|
+
# TODO: lifecycle notifications: "service_instantiated"
|
|
173
|
+
|
|
174
|
+
return service
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Searches for (and instantiates) the service with the given name in the
|
|
178
|
+
# registry, giving preference to services in this service point's package
|
|
179
|
+
# (i.e., when an unqualified service name is given). If a block is
|
|
180
|
+
# specified, it will be invoked when the package and service point name
|
|
181
|
+
# have been resolved, allowing more than just services to be returned by
|
|
182
|
+
# this method.
|
|
183
|
+
def find_service( name, &block )
|
|
184
|
+
Copland::get_possibly_local_service( owner.registry,
|
|
185
|
+
owner, name, &block )
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Searches for the service point with the given name, giving preference to
|
|
189
|
+
# service points within this service point's package (i.e., when an
|
|
190
|
+
# unqualified service point name is given). If an invalid package name is
|
|
191
|
+
# given, this will raise a PackageNotFound exception. If the named
|
|
192
|
+
# service point does not exist, this will raise a ServicePointNotFound
|
|
193
|
+
# exception. Otherwise, it will return the requested service point.
|
|
194
|
+
def find_service_point( name )
|
|
195
|
+
point = find_service( name ) do |pkg, id|
|
|
196
|
+
raise PackageNotFound, name unless pkg
|
|
197
|
+
pkg.service_point( id )
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
raise ServicePointNotFound, name unless point
|
|
201
|
+
|
|
202
|
+
return point
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Adds a "pending" (i.e., unvalidated) interceptor definition to this
|
|
206
|
+
# service point. This method is only valid before the service point has
|
|
207
|
+
# been fixated (see #fixate!). The +definition+ parameter should be a hash
|
|
208
|
+
# that contains the definition of the interceptor. The Interceptor itself
|
|
209
|
+
# will be instantiated when the service point is fixated.
|
|
210
|
+
def add_pending_interceptor( definition )
|
|
211
|
+
( @pending_interceptors ||= [] ).push definition
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Fixates the service point. After calling this, #add_pending_interceptor
|
|
215
|
+
# becomes illegal to call.
|
|
216
|
+
#
|
|
217
|
+
# Fixating a service point will cause its instantiator to be validated
|
|
218
|
+
# (via the #validate! method of the corresponding instantiator). Also, any
|
|
219
|
+
# pending interceptors will be processed and added to the service point.
|
|
220
|
+
# Then, if the schema value that was associated with this service point is
|
|
221
|
+
# a string value, then it is treated as a reference to an "external"
|
|
222
|
+
# schema, which is then looked up.
|
|
223
|
+
def fixate!
|
|
224
|
+
extend Fixated
|
|
225
|
+
|
|
226
|
+
if @schema.is_a?( String )
|
|
227
|
+
@schema = @owner.find_schema( @schema )
|
|
228
|
+
elsif !@schema.nil?
|
|
229
|
+
@schema.fixate!
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
instantiator.validate!
|
|
233
|
+
|
|
234
|
+
if @pending_interceptors
|
|
235
|
+
@pending_interceptors.each do |definition|
|
|
236
|
+
interceptor = Interceptor.new( self, definition )
|
|
237
|
+
add_interceptor interceptor
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
remove_instance_variable :@pending_interceptors
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# do lazy evaluation of the actual event producer services, so that
|
|
244
|
+
# no one is actually instantiated until needed.
|
|
245
|
+
@event_producers.map! do |name|
|
|
246
|
+
find_service_point( name )
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Returns +false+. Once the service point has been fixated, this method
|
|
251
|
+
# will be overridden with a method that returns +true+. (See the
|
|
252
|
+
# Fixated module).
|
|
253
|
+
def fixated?
|
|
254
|
+
false
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# When a service point is fixated, it will extend this module. This
|
|
258
|
+
# causes certain operations on the service point to become illegal, or
|
|
259
|
+
# to do nothing.
|
|
260
|
+
module Fixated
|
|
261
|
+
|
|
262
|
+
# Raises DisallowedOperationException.
|
|
263
|
+
def add_pending_interceptor( *args )
|
|
264
|
+
raise DisallowedOperationException,
|
|
265
|
+
"cannot add pending interceptors to a fixated service point"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Does nothing.
|
|
269
|
+
def fixate!
|
|
270
|
+
# does nothing
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Returns +true+.
|
|
274
|
+
def fixated?
|
|
275
|
+
true
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
end
|