rtm-activerecord 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|