rtm-activerecord 0.2.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/DISCLAIMER +13 -0
- data/LICENSE +201 -0
- data/README +4 -0
- data/lib/rtm/activerecord/001_initial_schema.rb +119 -0
- data/lib/rtm/activerecord/association_and_role.rb +54 -0
- data/lib/rtm/activerecord/base.rb +83 -0
- data/lib/rtm/activerecord/connect.rb +106 -0
- data/lib/rtm/activerecord/io/from_xtm2.rb +266 -0
- data/lib/rtm/activerecord/io/from_xtm2_libxml.rb +97 -0
- data/lib/rtm/activerecord/io/to_jtm.rb +141 -0
- data/lib/rtm/activerecord/io/to_string.rb +130 -0
- data/lib/rtm/activerecord/io/to_xtm1.rb +162 -0
- data/lib/rtm/activerecord/io/to_xtm2.rb +163 -0
- data/lib/rtm/activerecord/io/to_yaml.rb +132 -0
- data/lib/rtm/activerecord/literal_index.rb +38 -0
- data/lib/rtm/activerecord/locators.rb +58 -0
- data/lib/rtm/activerecord/merging.rb +310 -0
- data/lib/rtm/activerecord/name_variant_occurrence.rb +86 -0
- data/lib/rtm/activerecord/persistent_code_output.rb +22 -0
- data/lib/rtm/activerecord/quaaxtm2rtm.rb +116 -0
- data/lib/rtm/activerecord/quaaxtm2rtmviews.rb +137 -0
- data/lib/rtm/activerecord/set_wrapper.rb +101 -0
- data/lib/rtm/activerecord/sugar/association/hash_access.rb +22 -0
- data/lib/rtm/activerecord/sugar/role/counterparts.rb +56 -0
- data/lib/rtm/activerecord/sugar/topic/characteristics.rb +15 -0
- data/lib/rtm/activerecord/sugar/topic/counterparts.rb +18 -0
- data/lib/rtm/activerecord/sugar/topic/hash_access.rb +78 -0
- data/lib/rtm/activerecord/sugar/topic/identifier_direct.rb +14 -0
- data/lib/rtm/activerecord/sugar/topic/predefined_associations.rb +53 -0
- data/lib/rtm/activerecord/tm_construct.rb +66 -0
- data/lib/rtm/activerecord/tm_delegator.rb +417 -0
- data/lib/rtm/activerecord/tm_set_delegator.rb +226 -0
- data/lib/rtm/activerecord/tmdm.rb +309 -0
- data/lib/rtm/activerecord/topic.rb +204 -0
- data/lib/rtm/activerecord/topic_map.rb +435 -0
- data/lib/rtm/activerecord/traverse_associations.rb +90 -0
- data/lib/rtm/activerecord.rb +106 -0
- metadata +120 -0
@@ -0,0 +1,204 @@
|
|
1
|
+
# Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
|
2
|
+
# License: Apache License, Version 2.0
|
3
|
+
|
4
|
+
module RTM::AR
|
5
|
+
class Topic < Construct
|
6
|
+
include RTM::Topic
|
7
|
+
wrapper_cache
|
8
|
+
|
9
|
+
# parent :topic_map
|
10
|
+
alias :parent :topic_map
|
11
|
+
property_set :subject_identifiers, :aka => [:si,:sid], :type => :SubjectIdentifier, :wrap => true,
|
12
|
+
#:create => :subject_identifier, :create_aka => [:csi,:csid],
|
13
|
+
#:create_args => [{:name => :reference, :type => :String}]
|
14
|
+
:add => :subject_identifier, :remove => :subject_identifier
|
15
|
+
alias :getSubjectIdentifiers :subject_identifiers
|
16
|
+
|
17
|
+
property_set :subject_locators, :aka => [:sl,:slo], :type => :SubjectLocator, :wrap => true,
|
18
|
+
#:create => :subject_locator, :create_aka => [:csl, :cslo],
|
19
|
+
#:create_args => [{:name => :reference, :type => :String}]
|
20
|
+
:add => :subject_locator, :remove => :subject_locator
|
21
|
+
alias :getSubjectLocators :subject_locators
|
22
|
+
|
23
|
+
property :reified, :computable => true, :type => :Reifiable, :wrap => true
|
24
|
+
|
25
|
+
property_set :names, :aka => [:n, :topic_names], :type => :Name, :wrap => true,
|
26
|
+
:create => :name_internal, # :create_aka => [:create_topic_name, :cn],
|
27
|
+
:create_args => [ {:name => :value, :type => :String}, {:name => :type, :type => :Topic, :optional => true}, {:name => :scope, :type => :Collection} ]
|
28
|
+
|
29
|
+
alias :names_by :names # intermediate solution, check real TMAPI usage
|
30
|
+
|
31
|
+
|
32
|
+
def create_name(arg1,arg2 = :nothing, arg3 = :nothing)
|
33
|
+
if (arg2 == :nothing) && (arg3 == :nothing) #arg1 = value
|
34
|
+
raise "create_name(value): value must be a String" unless arg1.is_a?(String)
|
35
|
+
return create_name_internal(:value => arg1, :type => self.parent._topic_by_locator!(RTM::PSI[:name_type]))
|
36
|
+
end
|
37
|
+
if (arg3 == :nothing)
|
38
|
+
if arg2.is_a?(Array) #arg1 = value, arg2 = scope
|
39
|
+
raise "create_name(value, scope): value must be a String" unless arg1.is_a?(String)
|
40
|
+
arg2.each do |theme|
|
41
|
+
raise "create_name(value, scope): scope must be an Array of Topics/Topic-References" unless (theme.is_a?(String) || theme.is_a?(RTM::Topic) || theme.is_a?(RTM::Locator))
|
42
|
+
end
|
43
|
+
return arg2.empty? ? create_name_internal(:value => arg1) : create_name_internal(:value => arg1, :scope => self.parent._topic_by_locator!(arg2))
|
44
|
+
elsif arg2.is_a?(String) #arg1 = type, arg2 = value
|
45
|
+
raise "create_name(type, value): type must be a Topic/Topic-Reference" unless (arg1.is_a?(String) || arg1.is_a?(RTM::Topic) || arg1.is_a?(RTM::Locator))
|
46
|
+
return create_name_internal(:type => self.parent._topic_by_locator!(arg1), :value => arg2)
|
47
|
+
else
|
48
|
+
raise "create_name(?,?): arguments don't match"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
#arg1 = type, arg2 = value, arg3 = scope
|
52
|
+
raise "create_name(type, value, scope): type must be a Topic/Topic-Reference" unless (arg1.is_a?(String) || arg1.is_a?(RTM::Topic) || arg1.is_a?(RTM::Locator))
|
53
|
+
raise "create_name(type, value, scope): value must be a String" unless arg2.is_a?(String)
|
54
|
+
raise "create_name(type, value, scope): scope must be an Array" unless arg3.is_a?(Array)
|
55
|
+
arg3.each do |theme|
|
56
|
+
raise "create_name(type, value, scope): scope must be an Array of Topics/Topic-References" unless (theme.is_a?(String) || theme.is_a?(RTM::Topic) || theme.is_a?(RTM::Locator))
|
57
|
+
end
|
58
|
+
return create_name_internal(:type => self.parent._topic_by_locator!(arg1), :value => arg2, :scope => self.parent._topic_by_locator!(arg3))
|
59
|
+
end
|
60
|
+
alias :cn :create_name
|
61
|
+
alias :create_topic_name :create_name
|
62
|
+
|
63
|
+
property_set :occurrences, :aka => :o, :type => :Occurrence, :wrap => true,
|
64
|
+
:create => :occurrence_internal, :create_aka => :co,
|
65
|
+
:create_args => [ {:name => :type, :type => :Topic}, {:name => :value, :type => [:String, :Locator]}, {:name => :scope, :type => :Collection} ]
|
66
|
+
|
67
|
+
alias :occurrences_by :occurrences # intermediate solution, check real TMAPI usage
|
68
|
+
|
69
|
+
def create_occurrence(type,value,params={})
|
70
|
+
if params[:scope]
|
71
|
+
if value.is_a?(RTM::Locator)
|
72
|
+
return create_occurrence_internal(topic_map.get!(type),value,topic_map._topic_by_locator!(params[:scope]), :datatype => RTM::PSI[:IRI])
|
73
|
+
elsif !params[:datatype]
|
74
|
+
return create_occurrence_internal(topic_map.get!(type),value,topic_map._topic_by_locator!(params[:scope]))
|
75
|
+
else # datatype given, no locator
|
76
|
+
return create_occurrence_internal(topic_map.get!(type),value,topic_map.create_locator(params[:datatype]),topic_map._topic_by_locator!(params[:scope]))
|
77
|
+
end
|
78
|
+
else #no scope
|
79
|
+
if value.is_a?(RTM::Locator)
|
80
|
+
return create_occurrence_internal(topic_map.get!(type),value, :datatype => RTM::PSI[:IRI])
|
81
|
+
elsif !params[:datatype]
|
82
|
+
return create_occurrence_internal(topic_map.get!(type),value, params)
|
83
|
+
else #datatype given, no locator
|
84
|
+
return create_occurrence_internal(topic_map.get!(type),value,topic_map.create_locator(params[:datatype]))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def characteristics(*args)
|
90
|
+
names(*args).to_a + occurrences(*args).to_a
|
91
|
+
end
|
92
|
+
alias :children :characteristics
|
93
|
+
alias :characteristics_by :characteristics # intermediate solution, check real TMAPI usage
|
94
|
+
|
95
|
+
property_set :roles, :aka => [:r, :roles_played, :association_roles], :computable => true, :type => :Role, :wrap => true
|
96
|
+
|
97
|
+
def filter_roles(*args)
|
98
|
+
puts
|
99
|
+
puts "topic filter roles:"
|
100
|
+
puts args.inspect
|
101
|
+
if args.size == 1
|
102
|
+
a1 = args.shift
|
103
|
+
# if a1 == :any
|
104
|
+
# a1 = nil
|
105
|
+
# end
|
106
|
+
role_type = topic_map.get(a1)
|
107
|
+
return self.roles unless role_type
|
108
|
+
if role_type.respond_to?(:each)
|
109
|
+
query_conditions = (["ttype_id = ?"] * role_type.size).join(" OR ")
|
110
|
+
puts
|
111
|
+
puts "qq:"
|
112
|
+
puts query_conditions
|
113
|
+
puts role_type.map{|rt| rt.id}
|
114
|
+
puts
|
115
|
+
return self.roles.find(:all,:conditions => [query_conditions, role_type.map{|rt| rt.id}])
|
116
|
+
else
|
117
|
+
return self.roles.find(:all,:conditions => ["ttype_id = ?", role_type.id])
|
118
|
+
end
|
119
|
+
elsif args.size == 2
|
120
|
+
role_type = args.shift
|
121
|
+
# if role_type == :any
|
122
|
+
# role_type = nil
|
123
|
+
# end
|
124
|
+
role_type = topic_map.get(role_type)
|
125
|
+
|
126
|
+
assoc_type = args.shift
|
127
|
+
# if assoc_type == :any
|
128
|
+
# assoc_type = nil
|
129
|
+
# end
|
130
|
+
assoc_type = topic_map.get(assoc_type)
|
131
|
+
|
132
|
+
if role_type && assoc_type
|
133
|
+
puts "trying to return topic.roles for #{role_type.inspect} and #{assoc_type.inspect}"
|
134
|
+
res = self.roles.find(:all, :conditions => ["roles.ttype_id = ? and associations.ttype_id = ?", role_type.id, assoc_type.id])
|
135
|
+
puts res.inspect
|
136
|
+
return res
|
137
|
+
elsif assoc_type
|
138
|
+
return self.roles.find(:all, :conditions => ["associations.ttype_id = ?", assoc_type.id])
|
139
|
+
elsif role_type
|
140
|
+
return self.roles.find(:all,:conditions => ["ttype_id = ?", role_type.id])
|
141
|
+
else
|
142
|
+
warn("Topic#roles got 2 arguments but doesn't know how to handle (or referenced topics where not found)")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
false
|
146
|
+
end
|
147
|
+
|
148
|
+
property_set :associations, :aka => [:a, :associations_played], :type => :Association, :wrap => true
|
149
|
+
|
150
|
+
property_set :scoped, :aka => :scoped_objects, :type => :Construct, :wrap => true
|
151
|
+
property_set :scoped_associations, :type => :Association, :wrap => true
|
152
|
+
property_set :scoped_names, :aka => :scoped_topic_names, :type => :Name, :wrap => true
|
153
|
+
property_set :scoped_variants, :type => :Variant, :wrap => true
|
154
|
+
property_set :scoped_occurrences, :type => :Occurrence, :wrap => true
|
155
|
+
|
156
|
+
property_set :typed, :aka => :typed_objects, :type => :Construct, :wrap => true
|
157
|
+
property_set :typed_associations, :aka => [:ta,:associations_typed,:at], :type => :Association, :wrap => true
|
158
|
+
property_set :typed_roles, :aka => [:rt,:roles_typed,:association_roles_typed,:art,:rt], :type => :Role, :wrap => true
|
159
|
+
property_set :typed_names, :aka => [:nt,:names_typed,:topic_names_typed,:tnt,:nt], :type => :Name, :wrap => true
|
160
|
+
property_set :typed_occurrences, :aka => [:to,:occurrences_typed,:ot], :type => :Occurrence, :wrap => true
|
161
|
+
|
162
|
+
def id
|
163
|
+
__getobj__.id
|
164
|
+
end
|
165
|
+
|
166
|
+
# class:Topic equality, http://www.isotopicmaps.org/sam/sam-model/#d0e1029
|
167
|
+
#
|
168
|
+
# This method is all crap. These ActiveRecord Arrays can't be intersected,
|
169
|
+
# so I map the identifiers to their reference which seems long winded.
|
170
|
+
# Still I find it better than using their ID in the long.
|
171
|
+
#
|
172
|
+
def ==(o)
|
173
|
+
return false unless o
|
174
|
+
return false unless o.respond_to?(:subject_identifiers)
|
175
|
+
return false unless o.respond_to?(:subject_locators)
|
176
|
+
return false unless o.respond_to?(:names)
|
177
|
+
# Two topic items are equal if they have:
|
178
|
+
# * at least one equal string in their [subject identifiers] properties,
|
179
|
+
# -> test if intersection are > 0
|
180
|
+
my_si = self.subject_identifiers.map{|si| si.reference}
|
181
|
+
ot_si = o.subject_identifiers.map{|si| si.reference}
|
182
|
+
return true if ( my_si & ot_si).size > 0
|
183
|
+
|
184
|
+
# * at least one equal string in their [item identifiers] properties,
|
185
|
+
my_ii = self.item_identifiers.map{|ii| ii.reference}
|
186
|
+
ot_ii = o.item_identifiers.map{|ii| ii.reference}
|
187
|
+
return true if (my_ii & ot_ii).size > 0
|
188
|
+
|
189
|
+
# * at least one equal string in their [subject locators] properties,
|
190
|
+
my_sl = self.subject_locators.map{|sl| sl.reference}
|
191
|
+
ot_sl = o.subject_locators.map{|sl| sl.reference}
|
192
|
+
return true if (my_sl & ot_sl).size > 0
|
193
|
+
|
194
|
+
# * an equal string in the [subject identifiers] property of the one topic item and the [item identifiers] property of the other, or
|
195
|
+
return true if (my_si & ot_ii).size > 0
|
196
|
+
return true if (my_ii & ot_si).size > 0
|
197
|
+
|
198
|
+
# * the same information item in their [reified] properties.
|
199
|
+
return true if self.reified != nil && o.reified != nil && (self.reified == o.reified)
|
200
|
+
# ... otherwise
|
201
|
+
false
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,435 @@
|
|
1
|
+
# Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
|
2
|
+
# License: Apache License, Version 2.0
|
3
|
+
|
4
|
+
module RTM::AR
|
5
|
+
class TopicMap < Reifiable
|
6
|
+
include RTM::TopicMap
|
7
|
+
|
8
|
+
def close
|
9
|
+
end
|
10
|
+
|
11
|
+
wrapper_cache
|
12
|
+
property_set :topics, :aka => :t, :type => :Topic, :wrap => true,
|
13
|
+
:create => :topic, :create_aka => :ct
|
14
|
+
|
15
|
+
property_set :associations, :aka => [:a, :assocs], :type => :Association, :wrap => true,
|
16
|
+
:create => :association_internal, :create_aka => :ca,
|
17
|
+
:create_args => [{:name => :type, :type => :Topic}]
|
18
|
+
|
19
|
+
def create_association(type, scope = :default)
|
20
|
+
raise "type must be a Topic or Topic-Reference" unless type.is_a?(RTM::Topic) || type.is_a?(String) || type.is_a?(RTM::Locator)
|
21
|
+
if scope.eql? :default
|
22
|
+
return create_association_internal(get!(type))
|
23
|
+
else
|
24
|
+
raise "scope must be an Array" unless scope.is_a?(Array)
|
25
|
+
a = create_association_internal(get!(type))
|
26
|
+
scope.each {|s| a.add_scope(get!(s))}
|
27
|
+
return a
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
delegate :base_locator
|
32
|
+
|
33
|
+
property_set :association_types, :aka => :at, :type => :Topic, :wrap => true
|
34
|
+
property_set :role_types, :aka => [:association_role_types,:art,:rt], :type => :Topic, :wrap => true
|
35
|
+
property_set :name_types, :aka => [:topic_name_types,:tnt,:nt], :type => :Topic, :wrap => true
|
36
|
+
property_set :occurrence_types, :aka => :ot, :type => :Topic, :wrap => true
|
37
|
+
|
38
|
+
property_set :names, :aka => [:topic_names,:n], :type => :Name, :wrap => :true
|
39
|
+
property_set :occurrences, :aka => :o, :type => :Occurrence, :wrap => :true
|
40
|
+
property_set :roles, :aka => [:association_roles, :r], :type => :Role, :wrap => :true
|
41
|
+
|
42
|
+
def types
|
43
|
+
topics.select{|t| t.instances.size > 0}
|
44
|
+
end
|
45
|
+
alias :topic_types :types
|
46
|
+
def fast_types
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
# This fetches all topics who have no topic-instances (but they might be types for associations etc.).
|
51
|
+
# See indivduals
|
52
|
+
def non_types
|
53
|
+
topics.select{|t| t.instances.size == 0}
|
54
|
+
end
|
55
|
+
|
56
|
+
# This fetches all topics which are not type for something else (including topics, associations etc.).
|
57
|
+
# See non_types
|
58
|
+
def individuals
|
59
|
+
non_types.select{|t| t.associations_typed.size==0 && t.roles_typed.size==0 && t.names_typed.size==0 && t.occurrences_typed.size==0}
|
60
|
+
end
|
61
|
+
def instances
|
62
|
+
topics.select{|t| t.types.size > 0}
|
63
|
+
end
|
64
|
+
def non_instances
|
65
|
+
topics.reject{|t| t.types.size > 0}
|
66
|
+
end
|
67
|
+
def internal_occurrences
|
68
|
+
occurrences.select{|o| o.datatype != RTM::PSI[:IRI]}
|
69
|
+
end
|
70
|
+
def external_occurrences
|
71
|
+
occurrences.select{|o| o.datatype == RTM::PSI[:IRI]}
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.create(base_locator, params={})
|
75
|
+
bl_uri = URI.parse(base_locator)
|
76
|
+
raise "The locator for the Topic Map must be absolute! \"#{base_locator}\" is not an absolute locator." unless bl_uri.absolute?
|
77
|
+
tm = self.wrap(TMDM::TopicMap.find_or_create_by_base_locator(base_locator))
|
78
|
+
yield tm if block_given?
|
79
|
+
tm
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.topic_maps
|
83
|
+
TopicMaps.wrap(TMDM::TopicMap.find(:all))
|
84
|
+
end
|
85
|
+
|
86
|
+
# Resolves an IRI or fragment relative to the base_locator of this topic_map or an optional given alternative_base_locator
|
87
|
+
#
|
88
|
+
# Absolute IRIs are taken as is.
|
89
|
+
#
|
90
|
+
# Relative IRIs:
|
91
|
+
# If the base_locator is a directory (ends with "/") it is appended like a file, i.e. directly
|
92
|
+
# If the base_locator is a file it is appended as a fragment (with # in between)
|
93
|
+
def resolve(obj,alternative_base_locator=nil)
|
94
|
+
uri = obj.to_s
|
95
|
+
# TODO uri = URI.decode(obj.to_s) # this InvalidURIError somethimes :(
|
96
|
+
begin
|
97
|
+
uri_uri = URI.parse(uri)
|
98
|
+
rescue URI::InvalidComponentError => ice
|
99
|
+
warn "Catched an URI::InvalidComponentError for URI: #{uri}, message was \"#{ice.message}\"\n" +
|
100
|
+
"Will continue using the UNRESOLVED IRI, claiming it is absolute."
|
101
|
+
return uri
|
102
|
+
end
|
103
|
+
if uri_uri.absolute?
|
104
|
+
return uri_uri.to_s
|
105
|
+
end
|
106
|
+
|
107
|
+
uri = uri[1..-1] if uri[0] == "#"[0]
|
108
|
+
bl = alternative_base_locator || base_locator
|
109
|
+
if bl[-1,1] == "/"
|
110
|
+
return bl + uri
|
111
|
+
else
|
112
|
+
return bl + "#" + uri
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
#private
|
117
|
+
|
118
|
+
def _item_identifier(iid)
|
119
|
+
__getobj__.locators.find_by_reference(resolve(iid)) # doesn't work :( --> , :include => [ :construct ])
|
120
|
+
end
|
121
|
+
def _item_identifier!(iid)
|
122
|
+
__getobj__.locators.find_or_create_by_reference(resolve(iid)) # doesn't work :( --> , :include => [ :construct ])
|
123
|
+
end
|
124
|
+
|
125
|
+
def _subject_identifier(iid)
|
126
|
+
__getobj__.subject_identifiers.find_by_reference(iid, :include => :topic)
|
127
|
+
end
|
128
|
+
def _subject_identifier!(iid)
|
129
|
+
__getobj__.subject_identifiers.find_or_create_by_reference(iid, :include => :topic)
|
130
|
+
end
|
131
|
+
|
132
|
+
def _subject_locator(iid)
|
133
|
+
__getobj__.subject_locators.find_by_reference(iid, :include => :topic)
|
134
|
+
end
|
135
|
+
def _subject_locator!(iid)
|
136
|
+
__getobj__.subject_locators.find_or_create_by_reference(iid, :include => :topic)
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
# internal helper for by_item_identifier, doesn't wrap result
|
141
|
+
def _by_item_identifier(iid)
|
142
|
+
iid = RTM::LocatorHelpers.iid2iri(iid) if RTM::LocatorHelpers.is_a_iid?(iid)
|
143
|
+
ii = __getobj__.locators.find_by_reference(resolve(iid.to_s.sub(/^(\^|ii:)\s*/,""))) #, :include => [ :construct ])
|
144
|
+
return ii.construct if ii
|
145
|
+
nil
|
146
|
+
end
|
147
|
+
def _topic_by_item_identifier(iid)
|
148
|
+
t = _by_item_identifier(iid)
|
149
|
+
return nil unless t
|
150
|
+
return t if t.class.name =~ /::Topic$/ # only return something if the thing is a topic
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# internal helper for topic_by_item_identifier!, doesn't wrap result
|
155
|
+
def _topic_by_item_identifier!(iid)
|
156
|
+
iid = RTM::LocatorHelpers.iid2iri(iid) if RTM::LocatorHelpers.is_a_iid?(iid)
|
157
|
+
ii = __getobj__.locators.find_or_create_by_reference(resolve(iid.to_s.sub(/^(\^|ii:)\s*/,"")))#, :include => [ :construct ])
|
158
|
+
return ii.construct if ii.construct && ii.construct_type =~ /::Topic$/
|
159
|
+
top2 = _topic_by_subject_identifier(resolve(iid))
|
160
|
+
if top2
|
161
|
+
ii.construct = top2
|
162
|
+
else
|
163
|
+
ii.construct = create_topic.__getobj__
|
164
|
+
end
|
165
|
+
ii.save
|
166
|
+
ii.construct
|
167
|
+
end
|
168
|
+
# internal helper for topic_by_subject_identifier, doesn't wrap result
|
169
|
+
def _topic_by_subject_identifier(sid)
|
170
|
+
si = __getobj__.subject_identifiers.find_by_reference(resolve(sid.to_s.sub(/^(si:)\s*/,"")), :include => [ :topic ])
|
171
|
+
return si.topic if si
|
172
|
+
nil
|
173
|
+
end
|
174
|
+
# internal helper for topic_by_subject_identifier!, doesn't wrap result
|
175
|
+
def _topic_by_subject_identifier!(sid)
|
176
|
+
si = __getobj__.subject_identifiers.find_or_create_by_reference(resolve(sid.to_s.sub(/^(si:)\s*/,"")), :include => [ :topic ])
|
177
|
+
return si.topic if si.topic
|
178
|
+
begin
|
179
|
+
top2 = _by_item_identifier(sid)
|
180
|
+
rescue ActiveRecord::RecordNotFound => rnf
|
181
|
+
si.topic = create_topic.__getobj__
|
182
|
+
else
|
183
|
+
if top2 && top2.respond_to?(:subject_identifiers)
|
184
|
+
si.topic = top2
|
185
|
+
else
|
186
|
+
si.topic = create_topic.__getobj__
|
187
|
+
end
|
188
|
+
end
|
189
|
+
si.save
|
190
|
+
si.topic
|
191
|
+
end
|
192
|
+
|
193
|
+
# internal helper for topic_by_subject_locator, doesn't wrap result
|
194
|
+
def _topic_by_subject_locator(slo)
|
195
|
+
sl = __getobj__.subject_locators.find_by_reference(resolve(RTM::LocatorHelpers.slo2iri(slo))) #, :include => [ :topic ])
|
196
|
+
return sl.topic if sl
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
|
200
|
+
# internal helper for topic_by_subject_locator!, doesn't wrap result
|
201
|
+
def _topic_by_subject_locator!(slo)
|
202
|
+
sl = __getobj__.subject_locators.find_or_create_by_reference(resolve(RTM::LocatorHelpers.slo2iri(slo)), :include => [ :topic ])
|
203
|
+
return sl.topic if sl.topic
|
204
|
+
sl.topic = create_topic.__getobj__
|
205
|
+
sl.save
|
206
|
+
sl.topic
|
207
|
+
end
|
208
|
+
def _topic_by_locator(iri)
|
209
|
+
raise "Locator may not be nil" unless iri
|
210
|
+
return iri if iri.is_a?(RTM::AR::TMDM::Topic)
|
211
|
+
return iri.__getobj__ if iri.is_a?(RTM::AR::Topic)
|
212
|
+
if iri.is_a?(Array)
|
213
|
+
return iri.map{|i| _topic_by_locator(i)}
|
214
|
+
end
|
215
|
+
if RTM::LocatorHelpers.is_a_slo?(iri)
|
216
|
+
_topic_by_subject_locator(iri)
|
217
|
+
elsif RTM::LocatorHelpers.is_a_iid?(iri)
|
218
|
+
_topic_by_item_identifier(iri)
|
219
|
+
#elsif URI.parse(iri).absolute?
|
220
|
+
# _topic_by_subject_identifier(iri)
|
221
|
+
else
|
222
|
+
_topic_by_subject_identifier(iri)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def _topic_by_locator!(iri)
|
227
|
+
raise "Locator may not be nil" unless iri
|
228
|
+
return iri if iri.is_a?(RTM::AR::TMDM::Topic)
|
229
|
+
return iri.__getobj__ if iri.is_a?(RTM::AR::Topic)
|
230
|
+
if iri.is_a?(Array)
|
231
|
+
return iri.map{|i| _topic_by_locator!(i)}
|
232
|
+
end
|
233
|
+
if RTM::LocatorHelpers.is_a_slo?(iri)
|
234
|
+
_topic_by_subject_locator!(iri)
|
235
|
+
elsif RTM::LocatorHelpers.is_a_iid?(iri)
|
236
|
+
_topic_by_item_identifier!(iri)
|
237
|
+
#elsif RTM::LocatorHelpers.is_a_sid?(iri)
|
238
|
+
# _topic_by_subject_identifier!(iri)
|
239
|
+
else
|
240
|
+
_topic_by_subject_identifier!(iri)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
%w[topic association role name occurrence variant].each do |x|
|
245
|
+
eval <<-EOI
|
246
|
+
def _#{x}_by_id(id)
|
247
|
+
__getobj__.#{x}s.find(id)
|
248
|
+
end
|
249
|
+
EOI
|
250
|
+
end
|
251
|
+
public
|
252
|
+
# returns an item identifier from the topic_map if it exists
|
253
|
+
def item_identifier(iid)
|
254
|
+
ItemIdentifier.wrap(_item_identifier(iid))
|
255
|
+
end
|
256
|
+
# returns an item identififier from the topic_map or creates it if it doesn't exist
|
257
|
+
def item_identifier!(iid)
|
258
|
+
ItemIdentifier.wrap(_item_identifier!(iid))
|
259
|
+
end
|
260
|
+
alias :create_locator :item_identifier!
|
261
|
+
|
262
|
+
# Returns a topic map construct by it's item identifier or nil if not found.
|
263
|
+
# It's the equivalent to TMAPI's TopicMapObjectIndex.getTopicMapObjectBySourceLocator
|
264
|
+
def by_item_identifier(iid)
|
265
|
+
Construct.wrap(_by_item_identifier(iid))
|
266
|
+
end
|
267
|
+
alias :get_construct_by_item_identifier :by_item_identifier
|
268
|
+
|
269
|
+
# Returns a topic by it's item identifier. The topic will be created if not found.
|
270
|
+
def topic_by_item_identifier!(iid)
|
271
|
+
Topic.wrap(_topic_by_item_identifier!(iid))
|
272
|
+
end
|
273
|
+
alias :create_topic_by_item_identifier :topic_by_item_identifier!
|
274
|
+
|
275
|
+
# Returns a topic by it's subject identifier or nil if not found.
|
276
|
+
# It's the equivalent to TMAPI's TopicsIndex.getTopicBySubjectIdentifier.
|
277
|
+
def topic_by_subject_identifier(sid)
|
278
|
+
Topic.wrap(_topic_by_subject_identifier(sid))
|
279
|
+
end
|
280
|
+
alias :get_topic_by_subject_identifier :topic_by_subject_identifier
|
281
|
+
|
282
|
+
# returns a topic by it's subject identifier. The topic will be created if not found.
|
283
|
+
def topic_by_subject_identifier!(sid)
|
284
|
+
Topic.wrap(_topic_by_subject_identifier!(sid))
|
285
|
+
end
|
286
|
+
alias :create_topic_by_subject_identifier :topic_by_subject_identifier!
|
287
|
+
|
288
|
+
# Returns a topic by it's subject locator or nil if not found.
|
289
|
+
# It's the equivalent to TMAPI's TopicsIndex.getTopicBySubjectLocator
|
290
|
+
def topic_by_subject_locator(slo)
|
291
|
+
Topic.wrap(_topic_by_subject_locator(slo))
|
292
|
+
end
|
293
|
+
alias :get_topic_by_subject_locator :topic_by_subject_locator
|
294
|
+
|
295
|
+
# returns a topic by it's subject locator. The topic will be created if not found.
|
296
|
+
def topic_by_subject_locator!(slo)
|
297
|
+
Topic.wrap(_topic_by_subject_locator!(slo))
|
298
|
+
end
|
299
|
+
alias :create_topic_by_subject_locator :topic_by_subject_locator!
|
300
|
+
|
301
|
+
# Gets a topic from this topic map using its (relative) item identifier,
|
302
|
+
# its (absolute) subject identifier or its (absolute and by "=" prepended) subject locator)
|
303
|
+
#
|
304
|
+
# Returns nil if the Topic does not exist.
|
305
|
+
#
|
306
|
+
# It's also possible to pass a number, which is used to fetch a topic using the internal id. In this case, an exception is thrown, if the topic does not exist.
|
307
|
+
#
|
308
|
+
def topic_by_locator(iri)
|
309
|
+
return nil unless iri
|
310
|
+
return iri if iri.is_a? Topic
|
311
|
+
return topic_by_id(iri) if iri.is_a? Integer
|
312
|
+
if iri.is_a?(Array)
|
313
|
+
return iri.map{|i| topic_by_locator(i)}
|
314
|
+
end
|
315
|
+
# @tblc ||= {}
|
316
|
+
# t = @tblc[iri]
|
317
|
+
# return t if t
|
318
|
+
Topic.wrap(_topic_by_locator(iri)) # t = ...
|
319
|
+
# @tblc[iri] = t if t
|
320
|
+
# t
|
321
|
+
end
|
322
|
+
alias :get :topic_by_locator
|
323
|
+
|
324
|
+
# Gets a topic from this topic map using its (relative) item identifier,
|
325
|
+
# its (absolute) subject identifier or its (absolute and by "=" prepended) subject locator)
|
326
|
+
#
|
327
|
+
# If there is no topic with this item identifier, subject identifier or subject locator, it is created.
|
328
|
+
# Please be aware, the creation does not work with the internal (numeric) ids.
|
329
|
+
#
|
330
|
+
def topic_by_locator!(iri)
|
331
|
+
return nil unless iri
|
332
|
+
return iri if iri.is_a? Topic
|
333
|
+
return topic_by_id(iri) if iri.is_a? Integer
|
334
|
+
if iri.is_a?(Array)
|
335
|
+
return iri.map{|i| topic_by_locator!(i)}
|
336
|
+
end
|
337
|
+
# @tblc ||= {}
|
338
|
+
# t = @tblc[iri]
|
339
|
+
# return t if t
|
340
|
+
Topic.wrap(_topic_by_locator!(iri)) # t = ...
|
341
|
+
# @tblc[iri] = t
|
342
|
+
# t
|
343
|
+
end
|
344
|
+
alias :get! :topic_by_locator!
|
345
|
+
|
346
|
+
# Assumes identifier is an IRI (Locator or String) or an Array of IRIs.
|
347
|
+
# Returns an existing or created Topic or an Array of Topics according
|
348
|
+
# to the nature of the IRI.
|
349
|
+
#
|
350
|
+
# :call-seq:
|
351
|
+
# create_topic_by(identifier) -> Topic
|
352
|
+
# create_topic_by(identifier-Array) -> Array of Topics
|
353
|
+
#
|
354
|
+
def create_topic_by(identifier)
|
355
|
+
case identifier
|
356
|
+
when RTM::Locator
|
357
|
+
return create_topic_by_subject_identifier(identifier)
|
358
|
+
when String
|
359
|
+
reroute_for_create(identifier)
|
360
|
+
when Array
|
361
|
+
return identifier.map{|i| create_topic_by(i)}
|
362
|
+
else
|
363
|
+
return nil
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Identifies the identifier as item identifier, subject locator or
|
368
|
+
# subject identifier and calls create_topic_by_.. accordingly. Creates
|
369
|
+
# new topic if topic does not exist yet. Returns the topic.
|
370
|
+
#
|
371
|
+
# :call-seq:
|
372
|
+
# reroute_for_create(identifier) -> Topic
|
373
|
+
#
|
374
|
+
def reroute_for_create(identifier)
|
375
|
+
case identifier
|
376
|
+
when /^(\^|ii:)\s*(.*)/ #identifiers starts with ^ or ii:
|
377
|
+
create_topic_by_item_identifier(create_locator($2))
|
378
|
+
when /^(=|sl:)\s*(.*)/ #identifier starts with = or sl:
|
379
|
+
create_topic_by_subject_locator(create_locator($2))
|
380
|
+
when /^(si:)\s*(.*)/ #identifier starts with si:
|
381
|
+
create_topic_by_subject_identifier(create_locator($2))
|
382
|
+
else #identifier does not start with a special character
|
383
|
+
create_topic_by_subject_identifier(create_locator(identifier.lstrip))#lstrip: leading whitespace chars removed
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# Identifies the identifier as item identifier, subject locator or
|
388
|
+
# subject identifier and calls get_construct/topic_by_.. accordingly.
|
389
|
+
# Returns the topic.
|
390
|
+
#
|
391
|
+
# :call-seq:
|
392
|
+
# reroute_for_get(identifier) -> Topic
|
393
|
+
#
|
394
|
+
def reroute_for_get(identifier)
|
395
|
+
case identifier
|
396
|
+
when /^(si:|sl:|ii:|=|^)$/
|
397
|
+
return nil
|
398
|
+
when /^(\^|ii:)\s*(.*)/ #identifiers starts with ^ or ii:
|
399
|
+
get_construct_by_item_identifier(create_locator($2))
|
400
|
+
when /^(=|sl:)\s*(.+)/ #identifier starts with = or sl:
|
401
|
+
get_topic_by_subject_locator(create_locator($2))
|
402
|
+
when /^(si:)\s*(.+)/ #identifier starts with si:
|
403
|
+
get_topic_by_subject_identifier(create_locator($2))
|
404
|
+
else #identifier does not start with a special character
|
405
|
+
get_topic_by_subject_identifier(create_locator(identifier.lstrip)) #lstrip: leading whitespace chars removed
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
%w[topic association role name occurrence variant].each do |x|
|
410
|
+
eval <<-EOI
|
411
|
+
def #{x}_by_id(id)
|
412
|
+
#{x.camelize}.wrap(_#{x}_by_id(id))
|
413
|
+
end
|
414
|
+
alias :#{x[0].chr}id :#{x}_by_id
|
415
|
+
EOI
|
416
|
+
end
|
417
|
+
|
418
|
+
# I am not sure if this is at all correct. TMDM doesn't say a word about it
|
419
|
+
# and the approach to compare topic maps is CXTM. I chose this because we
|
420
|
+
# should (at least at the time of writing) have only one topic with a given
|
421
|
+
# base locator in RTM. If you don't like it, drop me a mail and explain why
|
422
|
+
# and propose something better.
|
423
|
+
equality [:base_locator]
|
424
|
+
|
425
|
+
def children
|
426
|
+
topics.to_a + associations.to_a
|
427
|
+
end
|
428
|
+
|
429
|
+
def literal_index
|
430
|
+
LiteralIndex.new(self)
|
431
|
+
end
|
432
|
+
#def type_instance_index
|
433
|
+
#end
|
434
|
+
end
|
435
|
+
end
|