rtm 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +38 -3
- data/lib/rtm.rb +25 -47
- data/lib/rtm/axes.rb +7 -4
- data/lib/rtm/axes/association.rb +1 -1
- data/lib/rtm/axes/associations.rb +1 -1
- data/lib/rtm/axes/characteristic.rb +1 -1
- data/lib/rtm/axes/characteristics.rb +1 -1
- data/lib/rtm/axes/topic.rb +3 -3
- data/lib/rtm/axes/topics.rb +3 -3
- data/lib/rtm/engine.rb +48 -5
- data/lib/rtm/extensions.rb +58 -8
- data/lib/rtm/io.rb +2 -1
- data/lib/rtm/io/tmapix_from.rb +155 -0
- data/lib/rtm/io/tmapix_to.rb +223 -0
- data/lib/rtm/io/to_hash.rb +79 -41
- data/lib/rtm/io/to_jtm.rb +1 -1
- data/lib/rtm/io/to_rdf.rb +20 -5
- data/lib/rtm/io/to_string.rb +13 -2
- data/lib/rtm/io/to_yaml.rb +39 -11
- data/lib/rtm/navigation.rb +1 -15
- data/lib/rtm/navigation/association/players.rb +1 -1
- data/lib/rtm/navigation/name/characteristics.rb +1 -1
- data/lib/rtm/navigation/occurrence/characteristics.rb +1 -1
- data/lib/rtm/navigation/topic/characteristics.rb +1 -1
- data/lib/rtm/navigation/topic/players.rb +1 -1
- data/lib/rtm/navigation/topic/supertypes.rb +21 -17
- data/lib/rtm/navigation/topic/traverse.rb +1 -1
- data/lib/rtm/navigation/topic/types.rb +6 -4
- data/lib/rtm/psi.rb +6 -0
- data/lib/rtm/sugar.rb +42 -29
- data/lib/rtm/sugar/association/hash_access.rb +3 -3
- data/lib/rtm/sugar/occurrence/dynamic_value.rb +39 -56
- data/lib/rtm/sugar/occurrence/external.rb +53 -0
- data/lib/rtm/sugar/reifiable/reifier.rb +21 -0
- data/lib/rtm/sugar/role/counterparts.rb +12 -6
- data/lib/rtm/sugar/topic/best_name.rb +74 -0
- data/lib/rtm/sugar/topic/characteristics.rb +10 -10
- data/lib/rtm/sugar/topic/counterparts.rb +131 -119
- data/lib/rtm/sugar/topic/scoped.rb +102 -53
- data/lib/rtm/sugar/topic/topic_ref.rb +63 -12
- data/lib/rtm/sugar/topic/typed.rb +50 -2
- data/lib/rtm/sugar/topic_map/query_cache.rb +66 -0
- data/lib/rtm/sugar/topic_map/themes.rb +53 -0
- data/lib/rtm/sugar/typed/types.rb +1 -1
- data/lib/rtm/validation.rb +2 -2
- data/lib/rtm/version.rb +18 -6
- data/spec/rtm/axes/string_spec.rb +7 -7
- data/spec/rtm/axes/strings_spec.rb +10 -10
- data/spec/rtm/io/tmapix_from_spec.rb +76 -0
- data/spec/rtm/io/tmapix_to_spec.rb +159 -0
- data/spec/rtm/io/to_hash_spec.rb +90 -0
- data/spec/rtm/io/to_rdf_spec.rb +37 -0
- data/spec/rtm/io/to_string_spec.rb +122 -0
- data/spec/rtm/io/to_yaml_spec.rb +89 -0
- data/spec/rtm/sugar/occurrence/dynamic_value_spec.rb +156 -1
- data/spec/rtm/sugar/occurrence/external_spec.rb +129 -0
- data/spec/rtm/sugar/reifiable/reifier_spec.rb +41 -0
- data/spec/rtm/sugar/role/counterparts_spec.rb +174 -172
- data/spec/rtm/sugar/topic/best_name_spec.rb +25 -0
- data/spec/rtm/sugar/topic/characteristics_spec.rb +20 -5
- data/spec/rtm/sugar/topic/counterparts_spec.rb +41 -1
- data/spec/rtm/sugar/topic/hash_access_spec.rb +1 -1
- data/spec/rtm/sugar/topic/scoped_spec.rb +178 -114
- data/spec/rtm/sugar/topic/topic_ref_spec.rb +10 -10
- data/spec/rtm/sugar/topic/typed_spec.rb +196 -134
- data/spec/rtm/sugar/topic_map/themes_spec.rb +67 -0
- data/spec/rtm/sugar/typed/types_spec.rb +1 -1
- data/spec/rtm/tmapi/core/association_spec.rb +2 -2
- data/spec/rtm/tmapi/core/datatype_aware_spec.rb +236 -0
- data/spec/rtm/tmapi/core/name_spec.rb +186 -1
- data/spec/rtm/tmapi/core/occurrence_spec.rb +24 -67
- data/spec/rtm/tmapi/core/reifiable_spec.rb +2 -2
- data/spec/rtm/tmapi/core/scoped_spec.rb +40 -2
- data/spec/rtm/tmapi/core/topic_map_spec.rb +98 -30
- data/spec/rtm/tmapi/core/topic_spec.rb +558 -82
- data/spec/rtm/tmapi/core/variant_spec.rb +3 -3
- data/spec/rtm_spec.rb +0 -1
- data/spec/spec_helper.rb +7 -2
- data/test/javalibs/junit-4.5.jar +0 -0
- data/test/javalibs/tmapi-2.0-tests.jar +0 -0
- data/test/tmapi_tests.rb +25 -0
- metadata +66 -11
- data/lib/rtm/io/ontopia_io.rb +0 -25
- data/lib/rtm/io/tmapix.rb +0 -234
- data/lib/rtm/sugar/occurrence/externalize.rb +0 -31
- data/spec/rtm/io/tmapix_spec.rb +0 -85
- data/test/base_unit_test.rb +0 -161
- data/test/base_unit_test_tmapi.rb +0 -165
data/README
CHANGED
@@ -1,4 +1,39 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
==== Ruby Topic Maps (RTM)
|
2
|
+
RTM is a Topic Maps engine written in Ruby. See http://rtm.topicmapslab.de/ for instructions.
|
3
|
+
Several backends and extensions are available as separate gems.
|
3
4
|
|
4
|
-
|
5
|
+
==== Overview
|
6
|
+
From a developer's perspective, RTM is a schema-less database management system.
|
7
|
+
The Topic Maps standard (described below) on which RTM is based provides a way of creating a self-describing schema just by using it.
|
8
|
+
|
9
|
+
==== Quickstart
|
10
|
+
require 'rtm'
|
11
|
+
connection = RTM.connect # uses the default Ontopia in-memory backend
|
12
|
+
topic_map = connection.create "http://example.org/my_topic_map/"
|
13
|
+
some_topic = topicmap.get!("identifier_of_the_topic")
|
14
|
+
some_topic["-"] = "default name for the topic"
|
15
|
+
topic_map.to_xtm("my_xtm_file.xtm")
|
16
|
+
|
17
|
+
==== Topic Maps
|
18
|
+
Topic Maps is an international industry standard (ISO13250) for interchangeably representing information
|
19
|
+
about the structure of information resources used to define topics, and the relationships between topics.
|
20
|
+
A set of one or more interrelated documents that employs the notation defined by this International Standard is called a topic map.
|
21
|
+
A topic map defines a multidimensional topic space - a space in which the locations are topics,
|
22
|
+
and in which the distances between topics are measurable in terms of the number of intervening topics
|
23
|
+
which must be visited in order to get from one topic to another, and the kinds of relationships that define
|
24
|
+
the path from one topic to another, if any, through the intervening topics, if any.
|
25
|
+
In addition, information objects can have properties, as well as values for those properties, assigned to them.
|
26
|
+
The Topic Maps Data Model which is used in this implementation can be found on http://www.isotopicmaps.org/sam/sam-model/.
|
27
|
+
|
28
|
+
==== Backends
|
29
|
+
* rtm-ontopia: JRuby only, recommended, uses Ontopia: http://code.google.com/p/ontopia/
|
30
|
+
* rtm-tinytim: JRuby only, uses TinyTiM: http://tinytim.sourceforge.net/
|
31
|
+
* rtm-activerecord: uses a custom ActiveRecord schema
|
32
|
+
|
33
|
+
==== Extensions
|
34
|
+
* rtm-tmql: Adds support for the Topic Maps Query Language (TMQL), http://isotopicmaps.org/tmql/
|
35
|
+
* rtm-tmcl: Adds support for the Topic Maps Constraint Language (TMCL), http://isotopicmaps.org/tmcl/
|
36
|
+
|
37
|
+
==== License
|
38
|
+
Copyright 2009 Topic Maps Lab, University of Leipzig.
|
39
|
+
Apache License, Version 2.0
|
data/lib/rtm.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
1
|
+
# Ruby Topic Maps (RTM)
|
2
|
+
# Copyright: Topic Maps Lab http://www.topicmapslab.de
|
3
|
+
# License: Apache License, Version 2.0
|
4
|
+
# Project lead: Benjamin Bock bock(at)informatik.uni-leipzig.de
|
5
|
+
# Developers: Benjamin Bock bock(at)informatik.uni-leipzig.de
|
6
|
+
# Arnim Bleier bleier(at)informatik.uni-leipzig.de
|
7
|
+
# Uta Schulze uta.schulze(at)informatik.uni-leipzig.de
|
8
|
+
# Daniel Exner exner(at)informatik.uni-leipzig.de
|
9
|
+
# Testing: Uta Schulze uta.schulze(at)informatik.uni-leipzig.de
|
10
|
+
# Documentation: Uta Schulze uta.schulze(at)informatik.uni-leipzig.de
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
10
13
|
|
11
14
|
module RTM
|
12
15
|
module Topic
|
@@ -49,6 +52,9 @@ module RTM
|
|
49
52
|
attr_accessor :engine
|
50
53
|
end
|
51
54
|
|
55
|
+
module TopicMapSystem
|
56
|
+
end
|
57
|
+
|
52
58
|
module ItemIdentifier
|
53
59
|
end
|
54
60
|
|
@@ -57,14 +63,14 @@ module RTM
|
|
57
63
|
|
58
64
|
module SubjectLocator
|
59
65
|
end
|
60
|
-
|
61
|
-
module
|
66
|
+
|
67
|
+
module DatatypeAware
|
62
68
|
end
|
63
69
|
end
|
64
70
|
|
65
71
|
require 'rtm/version'
|
66
|
-
require "rtm/engine"
|
67
72
|
require 'rtm/extensions'
|
73
|
+
require "rtm/engine"
|
68
74
|
require 'rtm/psi'
|
69
75
|
require "rtm/navigation"
|
70
76
|
require "rtm/axes"
|
@@ -84,41 +90,9 @@ module RTM
|
|
84
90
|
# RTM.connect(:implementation => :merged) -> Merged TMAPI backend
|
85
91
|
#
|
86
92
|
def self.connect(params = {}, *args)
|
87
|
-
|
88
|
-
|
89
|
-
unless implementation
|
90
|
-
if Engine.list.first
|
91
|
-
implementation = Engine.list.first
|
92
|
-
warn("No engine implementation was specified for RTM.connect. Using the first already loaded engine (#{implementation.inspect}).")
|
93
|
-
else
|
94
|
-
if RUBY_PLATFORM =~ /java/
|
95
|
-
implementation = :ontopia
|
96
|
-
else
|
97
|
-
implementation = :activerecord
|
98
|
-
warn("No engine implementation was specified for RTM.connect. Choosing default (#{implementation.inspect}).")
|
99
|
-
end
|
100
|
-
end
|
101
|
-
params[:implementation] = implementation
|
102
|
-
end
|
103
|
-
|
104
|
-
unless Engine.list.include?(implementation)
|
105
|
-
warn("Requested engine '#{implementation}' not loaded. Trying to autoload it.")
|
106
|
-
Engine.load(implementation)
|
107
|
-
if Engine.list.include?(implementation)
|
108
|
-
warn("Autoloading '#{implementation}' was successful")
|
109
|
-
else
|
110
|
-
raise "Autoloading '#{implementation}' failed. Make sure rtm-#{implementation} exists and is installed or require it manually."
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
engine = RTM::Engine[implementation]
|
115
|
-
|
93
|
+
engine = Engine.load!(params[:implementation])
|
116
94
|
connection = engine.new(params)
|
117
|
-
|
118
|
-
@connections ||= {}
|
119
|
-
@connections[implementation] ||= []
|
120
|
-
@connections[implementation] << connection
|
121
|
-
|
95
|
+
add_connection(connection)
|
122
96
|
connection
|
123
97
|
end
|
124
98
|
|
@@ -128,7 +102,7 @@ module RTM
|
|
128
102
|
# connections -> Hash
|
129
103
|
#
|
130
104
|
def self.connections
|
131
|
-
@connections ||= {}
|
105
|
+
@connections ||= Hash.new{|h,k| h[k] = []}
|
132
106
|
@connections
|
133
107
|
end
|
134
108
|
|
@@ -153,4 +127,8 @@ module RTM
|
|
153
127
|
self.included_modules.each {|im| klass.extend(im)}
|
154
128
|
end
|
155
129
|
|
130
|
+
private
|
131
|
+
def self.add_connection(connection)
|
132
|
+
self.connections[connection.class.identifier] << connection
|
133
|
+
end
|
156
134
|
end
|
data/lib/rtm/axes.rb
CHANGED
@@ -226,7 +226,8 @@ module RTM
|
|
226
226
|
end
|
227
227
|
end
|
228
228
|
|
229
|
-
module RTM::
|
229
|
+
module RTM::AxesExtension;end
|
230
|
+
module RTM::AxesExtension::Topic
|
230
231
|
|
231
232
|
# Changes from TMAPI-mode to Axes-mode.
|
232
233
|
#
|
@@ -238,7 +239,7 @@ module RTM::Topic
|
|
238
239
|
end
|
239
240
|
end
|
240
241
|
|
241
|
-
module RTM::Name
|
242
|
+
module RTM::AxesExtension::Name
|
242
243
|
|
243
244
|
# Changes from TMAPI-mode to Axes-mode.
|
244
245
|
#
|
@@ -250,7 +251,7 @@ module RTM::Name
|
|
250
251
|
end
|
251
252
|
end
|
252
253
|
|
253
|
-
module RTM::Occurrence
|
254
|
+
module RTM::AxesExtension::Occurrence
|
254
255
|
|
255
256
|
# Changes from TMAPI-mode to Axes-mode.
|
256
257
|
#
|
@@ -262,7 +263,7 @@ module RTM::Occurrence
|
|
262
263
|
end
|
263
264
|
end
|
264
265
|
|
265
|
-
module RTM::Association
|
266
|
+
module RTM::AxesExtension::Association
|
266
267
|
|
267
268
|
# Changes from TMAPI-mode to Axes-mode.
|
268
269
|
#
|
@@ -288,6 +289,8 @@ class String
|
|
288
289
|
end
|
289
290
|
end
|
290
291
|
|
292
|
+
RTM.register_extension(RTM::AxesExtension)
|
293
|
+
|
291
294
|
require "rtm/axes/topics"
|
292
295
|
require "rtm/axes/associations"
|
293
296
|
require "rtm/axes/characteristics"
|
data/lib/rtm/axes/association.rb
CHANGED
@@ -10,7 +10,7 @@ module RTM::Axes
|
|
10
10
|
# Returns all Role players of Roles in this Association.
|
11
11
|
#
|
12
12
|
# The optional identifier specifies the type of the Roles to be considered.
|
13
|
-
# The identifier may be a
|
13
|
+
# The identifier may be a topic reference.
|
14
14
|
#
|
15
15
|
# Multiple instances of the same Topic are possible.
|
16
16
|
# The result may be empty.
|
@@ -18,7 +18,7 @@ module RTM::Axes
|
|
18
18
|
# Returns all Role players of Roles in these Associations.
|
19
19
|
#
|
20
20
|
# The optional identifier specifies the type of the Roles to be considered.
|
21
|
-
# The identifier may be a
|
21
|
+
# The identifier may be a topic reference.
|
22
22
|
#
|
23
23
|
# Multiple instances of the same Topic are possible.
|
24
24
|
# The result may be empty.
|
@@ -24,7 +24,7 @@ module RTM::Axes
|
|
24
24
|
# Characteristic for its type. If the type
|
25
25
|
# does not match, nil is returned.
|
26
26
|
#
|
27
|
-
# The identifier may be a
|
27
|
+
# The identifier may be a topic reference.
|
28
28
|
#
|
29
29
|
# :call-seq:
|
30
30
|
# reverse_characteristics -> Topic
|
@@ -37,7 +37,7 @@ module RTM::Axes
|
|
37
37
|
# Characteristics for their types. If the type
|
38
38
|
# does not match, the Topic is not returned.
|
39
39
|
#
|
40
|
-
# The identifier may be a
|
40
|
+
# The identifier may be a topic reference.
|
41
41
|
#
|
42
42
|
# :call-seq:
|
43
43
|
# reverse_characteristics -> Array of Topic
|
data/lib/rtm/axes/topic.rb
CHANGED
@@ -11,7 +11,7 @@ module RTM::Axes
|
|
11
11
|
# If an identifier is given, only those Names and Occurrences are returned,
|
12
12
|
# whose type or supertypes include the identifier.
|
13
13
|
#
|
14
|
-
# Identifier may be a
|
14
|
+
# Identifier may be a topic reference.
|
15
15
|
#
|
16
16
|
# The result may be empty.
|
17
17
|
#
|
@@ -68,7 +68,7 @@ module RTM::Axes
|
|
68
68
|
#
|
69
69
|
# The optional
|
70
70
|
# identifier specifies the type of the Roles to be considered.
|
71
|
-
# The identifier may be a
|
71
|
+
# The identifier may be a topic reference.
|
72
72
|
#
|
73
73
|
# Multiple instances of the same Association are possible.
|
74
74
|
# The result may be empty.
|
@@ -177,7 +177,7 @@ module RTM::Axes
|
|
177
177
|
# The current Topic is deducted ones from the returned Array.
|
178
178
|
# The result may be empty.
|
179
179
|
#
|
180
|
-
# The identifier may be a
|
180
|
+
# The identifier may be a topic reference.
|
181
181
|
#
|
182
182
|
# :call-seq:
|
183
183
|
# traverse(identifier) -> Array of Topics
|
data/lib/rtm/axes/topics.rb
CHANGED
@@ -19,7 +19,7 @@ module RTM::Axes
|
|
19
19
|
# If an identifier is given, only those Names and Occurrences are returned,
|
20
20
|
# whose type or supertypes include the identifier.
|
21
21
|
#
|
22
|
-
# Identifier may be a
|
22
|
+
# Identifier may be a topic reference.
|
23
23
|
#
|
24
24
|
# The result may be empty.
|
25
25
|
#
|
@@ -90,7 +90,7 @@ module RTM::Axes
|
|
90
90
|
#
|
91
91
|
# The optional
|
92
92
|
# identifier specifies the type of the Roles to be considered.
|
93
|
-
# The identifier may be a
|
93
|
+
# The identifier may be a topic reference.
|
94
94
|
#
|
95
95
|
# Multiple instances of the same Association are possible.
|
96
96
|
# The result may be empty.
|
@@ -209,7 +209,7 @@ module RTM::Axes
|
|
209
209
|
# Each Topic is deducted ones from the returned Array.
|
210
210
|
# The result may be empty.
|
211
211
|
#
|
212
|
-
# The identifier may be a
|
212
|
+
# The identifier may be a topic reference.
|
213
213
|
#
|
214
214
|
# :call-seq:
|
215
215
|
# traverse(identifier) -> Array of Topics
|
data/lib/rtm/engine.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module RTM
|
2
|
-
class Engine
|
2
|
+
class Engine
|
3
|
+
include RTM::TopicMapSystem
|
4
|
+
|
3
5
|
def self.abstract?
|
4
6
|
self == Engine
|
5
7
|
end
|
@@ -38,21 +40,62 @@ module RTM
|
|
38
40
|
end
|
39
41
|
|
40
42
|
def self.load(engine_name)
|
41
|
-
|
43
|
+
if Object.const_defined?("Gem") && rtmgem = Gem.loaded_specs["rtm"]
|
42
44
|
require "rtm/#{engine_name}"
|
43
|
-
|
45
|
+
else
|
44
46
|
engine_path = File.expand_path(File.join(File.dirname(__FILE__), "../../../rtm-#{engine_name}/lib"))
|
45
47
|
if File.directory?(engine_path)
|
46
|
-
|
48
|
+
$LOAD_PATH.unshift engine_path
|
47
49
|
require "rtm/#{engine_name}"
|
48
50
|
end
|
49
51
|
end
|
52
|
+
self[engine_name]
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.load!(engine_name=nil)
|
56
|
+
engine_name = self.detect unless engine_name
|
57
|
+
unless list.include?(engine_name)
|
58
|
+
warn("Requested engine '#{engine_name}' not loaded. Trying to autoload it.")
|
59
|
+
engine = load(engine_name)
|
60
|
+
if list.include?(engine_name)
|
61
|
+
warn("Autoloading '#{engine_name}' was successful")
|
62
|
+
else
|
63
|
+
raise "Autoloading '#{engine_name}' failed. Make sure rtm-#{engine_name} exists and is installed or require it manually."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
engine || load(engine_name)
|
50
67
|
end
|
51
68
|
|
69
|
+
def self.detect(preferred=nil)
|
70
|
+
if preferred # return the users preference, if given
|
71
|
+
implementation = preferred
|
72
|
+
elsif engine_name = ENV['RTM_IMPLEMENTATION'] # inspect system environment
|
73
|
+
implementation = engine_name.to_sym
|
74
|
+
elsif engine_name = ENV['RTM_ENGINE'] # inspect system environment (alternative)
|
75
|
+
implementation = engine_name.to_sym
|
76
|
+
elsif implementation = self.list.first # check if one is already loaded
|
77
|
+
warn("No engine implementation was specified for RTM.connect. Using the first already loaded engine (#{implementation.inspect}).")
|
78
|
+
else
|
79
|
+
implementation = self.default # use hardcoded default
|
80
|
+
warn("No engine implementation was specified for RTM.connect. Choosing default (#{implementation.inspect}).")
|
81
|
+
implementation
|
82
|
+
end
|
83
|
+
implementation
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.default
|
87
|
+
if RUBY_PLATFORM =~ /java/
|
88
|
+
:ontopia
|
89
|
+
else
|
90
|
+
:activerecord
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
52
94
|
attr_reader :connections
|
95
|
+
|
53
96
|
def initialize(params={})
|
54
97
|
@params = params
|
55
98
|
end
|
56
|
-
|
99
|
+
|
57
100
|
end
|
58
101
|
end
|
data/lib/rtm/extensions.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# License: Apache License, Version 2.0
|
3
3
|
|
4
4
|
module RTM
|
5
|
-
# Register a module as extension.
|
5
|
+
# Register a module as extension to RTM.
|
6
6
|
#
|
7
7
|
# Example:
|
8
8
|
# module MyExtension
|
@@ -14,16 +14,66 @@ module RTM
|
|
14
14
|
# RTM.register_extension(self)
|
15
15
|
# end
|
16
16
|
#
|
17
|
-
def self.register_extension(
|
18
|
-
|
19
|
-
|
17
|
+
def self.register_extension(module_with_modules)
|
18
|
+
# puts "[RTMEXT] registering extension #{module_with_modules}"
|
19
|
+
module_with_modules.constants.each do |sub_module|
|
20
|
+
self.const_get(sub_module).register_extension(module_with_modules.const_get(sub_module)) if self.const_defined?(sub_module)
|
20
21
|
end
|
21
22
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
|
24
|
+
# Extend a module or class with this method to make it a receiver for RTM extensions.
|
25
|
+
# E.g. MyModule.extend(Extendable) or MyClass.extend(Extendable).
|
26
|
+
#
|
27
|
+
# A extension can then be registered using
|
28
|
+
# MyModule.register_extension(MyExtension)
|
29
|
+
#
|
30
|
+
# The module will then keep track of all modules or classes it's included in.
|
31
|
+
# These modules and classes are called implementations here.
|
32
|
+
# If an extension is added later, the implementations will also be updated which would not be the case if normal includes were used.
|
33
|
+
module Extendable
|
34
|
+
# Register a module as an extension to an Entity in RTM.
|
35
|
+
# An entity may be e.g. RTM::TopicMap, RTM::Topic or any Module which was extended with RTM::Extendable
|
36
|
+
#
|
37
|
+
# Besides normal ruby inclusion of the module, this module keeps track of its implementations which are also updated in this method.
|
38
|
+
def register_extension(mod)
|
39
|
+
include mod
|
40
|
+
return unless @implementations
|
41
|
+
@implementations.each do |impl|
|
42
|
+
next if impl.ancestors.include?(mod)
|
43
|
+
impl.send(:include, mod)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# A standard Ruby hook to be notified about inclusions of this module.
|
48
|
+
# This normally eliminates the need to register a module as implementa
|
49
|
+
def included(klass)
|
50
|
+
# super
|
51
|
+
# puts "[RTMEXT] including #{self} into #{klass} in #{caller(1).first}"
|
52
|
+
register_implementation(klass)
|
53
|
+
klass.class_eval do
|
54
|
+
def self.included(klass2)
|
55
|
+
# puts "[RTMIMPLEXT] #{self} was included into #{klass2} in #{caller(1).first}"
|
56
|
+
# self.ancestors.select {|anc| anc.respond_to?(:register_implementation)}.each{|anc| anc.register_implementation(klass2) }
|
57
|
+
self.ancestors.find {|anc| anc.respond_to?(:register_implementation)}.register_implementation(klass2)
|
58
|
+
end
|
26
59
|
end
|
27
60
|
end
|
61
|
+
|
62
|
+
# Register an implementation to this module.
|
63
|
+
# Registred implementations will be updated if later any extensions are added.
|
64
|
+
# This method should normally be called from the included hook.
|
65
|
+
# Due to Ruby's restrictions on calling protected methods from other modules extending the very same module Extendable,
|
66
|
+
# this method must be public.
|
67
|
+
def register_implementation(klass)
|
68
|
+
# super if self.class.superclass.respond_to?(:register_implementation)
|
69
|
+
@implementations ||= []
|
70
|
+
@implementations << klass
|
71
|
+
end
|
28
72
|
end
|
73
|
+
[
|
74
|
+
TopicMapSystem, Construct, Reifiable,
|
75
|
+
DatatypeAware, Scoped, Typed,
|
76
|
+
TopicMap, Topic, Name, Occurrence, Variant, Association, Role,
|
77
|
+
Locator, ItemIdentifier, SubjectIdentifier, SubjectLocator
|
78
|
+
].each{|mod| mod.extend(Extendable)}
|
29
79
|
end
|