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.
Files changed (38) hide show
  1. data/DISCLAIMER +13 -0
  2. data/LICENSE +201 -0
  3. data/README +4 -0
  4. data/lib/rtm/activerecord/001_initial_schema.rb +119 -0
  5. data/lib/rtm/activerecord/association_and_role.rb +54 -0
  6. data/lib/rtm/activerecord/base.rb +83 -0
  7. data/lib/rtm/activerecord/connect.rb +106 -0
  8. data/lib/rtm/activerecord/io/from_xtm2.rb +266 -0
  9. data/lib/rtm/activerecord/io/from_xtm2_libxml.rb +97 -0
  10. data/lib/rtm/activerecord/io/to_jtm.rb +141 -0
  11. data/lib/rtm/activerecord/io/to_string.rb +130 -0
  12. data/lib/rtm/activerecord/io/to_xtm1.rb +162 -0
  13. data/lib/rtm/activerecord/io/to_xtm2.rb +163 -0
  14. data/lib/rtm/activerecord/io/to_yaml.rb +132 -0
  15. data/lib/rtm/activerecord/literal_index.rb +38 -0
  16. data/lib/rtm/activerecord/locators.rb +58 -0
  17. data/lib/rtm/activerecord/merging.rb +310 -0
  18. data/lib/rtm/activerecord/name_variant_occurrence.rb +86 -0
  19. data/lib/rtm/activerecord/persistent_code_output.rb +22 -0
  20. data/lib/rtm/activerecord/quaaxtm2rtm.rb +116 -0
  21. data/lib/rtm/activerecord/quaaxtm2rtmviews.rb +137 -0
  22. data/lib/rtm/activerecord/set_wrapper.rb +101 -0
  23. data/lib/rtm/activerecord/sugar/association/hash_access.rb +22 -0
  24. data/lib/rtm/activerecord/sugar/role/counterparts.rb +56 -0
  25. data/lib/rtm/activerecord/sugar/topic/characteristics.rb +15 -0
  26. data/lib/rtm/activerecord/sugar/topic/counterparts.rb +18 -0
  27. data/lib/rtm/activerecord/sugar/topic/hash_access.rb +78 -0
  28. data/lib/rtm/activerecord/sugar/topic/identifier_direct.rb +14 -0
  29. data/lib/rtm/activerecord/sugar/topic/predefined_associations.rb +53 -0
  30. data/lib/rtm/activerecord/tm_construct.rb +66 -0
  31. data/lib/rtm/activerecord/tm_delegator.rb +417 -0
  32. data/lib/rtm/activerecord/tm_set_delegator.rb +226 -0
  33. data/lib/rtm/activerecord/tmdm.rb +309 -0
  34. data/lib/rtm/activerecord/topic.rb +204 -0
  35. data/lib/rtm/activerecord/topic_map.rb +435 -0
  36. data/lib/rtm/activerecord/traverse_associations.rb +90 -0
  37. data/lib/rtm/activerecord.rb +106 -0
  38. metadata +120 -0
@@ -0,0 +1,106 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ require 'active_record'
5
+ require 'rtm/backend/active_record/001_initial_schema'
6
+ require "rtm/backend/active_record/io/from_xtm2"
7
+ # require "rtm/backend/active_record/io/from_xtm2_libxml"
8
+
9
+ module RTM
10
+
11
+ class RTMAR
12
+ include Singleton
13
+ include RTM::TopicMapSystem
14
+
15
+
16
+ def create(base_locator)
17
+ AR::TopicMap.create(base_locator)
18
+ end
19
+
20
+ def topic_maps
21
+ AR::TopicMap.topic_maps
22
+ end
23
+
24
+ def [](*args)
25
+ if args.size == 0
26
+ return AR::TopicMap.topic_maps
27
+ end
28
+ args.map { |m| AR::TopicMap.topic_maps[m] }
29
+ end
30
+
31
+ # Assignes a logger to active record (or STDOUT as default) which outputs
32
+ # the sql statements.
33
+ def log(to=STDOUT)
34
+ ActiveRecord::Base.logger = Logger.new(to)
35
+ ActiveRecord::Base.colorize_logging = false if PLATFORM =~ /mswin/
36
+ end
37
+ # This function generates the database schema using the default
38
+ # ActiveRecord connection.
39
+ def generate_database
40
+ RTM::AR::TMDM::InitialSchema.migrate(:up)
41
+ end
42
+
43
+ # Connects to the sqlite3 database given in the path or to
44
+ # a default database if no path is given.
45
+ def connect_sqlite3(dbname="tmdm.sqlite3")
46
+ ActiveRecord::Base.establish_connection(
47
+ :adapter => platform_specific_adapter("sqlite3"),
48
+ :database => dbname
49
+ )
50
+ end
51
+
52
+ # Connects to a mysql database. Parameters are
53
+ # either
54
+ # database, username, password, host
55
+ # or
56
+ # only a keyword hash with these parameters.
57
+ def connect_mysql(database="rtm_development", username=nil, password=nil, host="localhost")
58
+ if database.is_a? Hash
59
+ return ActiveRecord::Base.establish_connection(database)
60
+ end
61
+ ActiveRecord::Base.establish_connection(
62
+ :adapter => platform_specific_adapter("mysql"),
63
+ :database => database,
64
+ :username => username,
65
+ :password => password,
66
+ :host => host
67
+ )
68
+ end
69
+
70
+ # Connects to a in-memory database. No parameters required.
71
+ # Schema will automatically be generated.
72
+ def connect_memory
73
+ ActiveRecord::Base.establish_connection(
74
+ :adapter => platform_specific_adapter("sqlite3"),
75
+ :database => ":memory:"
76
+ )
77
+ no_output do
78
+ generate_database
79
+ end
80
+ true
81
+ end
82
+
83
+ # Connects Active Record to a database or in Memory database if no parameters given.
84
+ def connect(*args)
85
+ if args.size == 0
86
+ return connect_memory
87
+ end
88
+ ActiveRecord::Base.establish_connection(*args)
89
+ end
90
+
91
+ private
92
+ def platform_specific_adapter(adapter)
93
+ if PLATFORM && PLATFORM =~ /java/
94
+ return "jdbc#{adapter}"
95
+ end
96
+ return adapter
97
+ end
98
+
99
+ end
100
+
101
+
102
+
103
+
104
+
105
+
106
+ end
@@ -0,0 +1,266 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ # Ruby Topic Maps (RTM) http://rtm.rubyforge.org/
5
+
6
+ class RTM::RTMAR
7
+ def from_xtm2(*args)
8
+ RTM::AR::IO::FROMXTM2.from_xtm2(self, *args)
9
+ end
10
+ end
11
+ module RTM::AR::IO
12
+ # XTM2 Export.
13
+ # Each Topic Maps Construct gets a to_xtm2 method.
14
+ # The result is a REXML::Element except for TopicMap where it is a REXML::Document.
15
+ module FROMXTM2
16
+ require 'rexml/document'
17
+ require 'rexml/parsers/sax2parser'
18
+ require 'rexml/sax2listener'
19
+ #require 'jrexml'
20
+ XTM2DEBUG = false
21
+
22
+ # Reads XTM2 from source (io object).
23
+ # Example: RTM::IO::FROMXTM2.from_xtm2(File.open(file_name),"http://rtm.rubyforge.org/topicmaps/tm1/")
24
+ # supported options:
25
+ # :strip_whitespace (defaults to false, may be set to true),
26
+ # :deprefix (defaults to nil, may be set to a string (or regex) which will be removed from the beginning of an (unresolved) item_identifier if it is there.
27
+ def self.from_xtm2(base_tms, source, base_locator, target=nil,options={})
28
+ tm = base_tms.create(base_locator) unless target
29
+ list = XTM2Listener.new(base_locator, target || tm, options)
30
+ parser = REXML::Parsers::SAX2Parser.new(source)
31
+ parser.listen(list)
32
+ parser.parse
33
+ true
34
+ end
35
+
36
+ class XTM2Listener
37
+ include REXML::SAX2Listener
38
+ def initialize(base_locator, target,options={})
39
+ @base_locator = base_locator
40
+ @target = target
41
+ @targets = []
42
+ @path = []
43
+ @options=options
44
+ end
45
+
46
+ private
47
+ def resolve(iri)
48
+ warn("resolving nil iri can have unexpected results") unless iri
49
+ # TODO handle %HH sequences, here or in backend/active_record.rb
50
+ if @options[:deprefix]
51
+ iri = iri.gsub(/^#{@options[:deprefix]}/,'')
52
+ end
53
+ @target.resolve(iri, @base_locator)
54
+ end
55
+
56
+ public
57
+ def start_element(uri, name, qname, attrs)
58
+ return unless uri == "http://www.topicmaps.org/xtm/"
59
+ a = attrs #Hash[* attrs.flatten]
60
+ #puts "Start of tag #{name} - attrs: #{a.inspect}"
61
+
62
+ case name
63
+ when "topic"
64
+ @topic = @targets.last.topic_by_item_identifier! resolve(a["id"])
65
+ @targets.push @topic
66
+ when "association"
67
+ assoc = nil
68
+ if a["reifier"]
69
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
70
+ raise "The topic reifiing this association already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::Association)
71
+ if ref.reified
72
+ assoc = ref.reified
73
+ else
74
+ assoc = @targets.last.create_association
75
+ assoc.reifier = ref
76
+ end
77
+ end
78
+ assoc ||= @targets.last.create_association
79
+ @targets.push assoc
80
+ when "role"
81
+ if a["reifier"]
82
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
83
+ raise "The topic reifiing this association role already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::AssociationRole)
84
+ if ref.reified
85
+ role = ref.reified
86
+ else
87
+ role = @targets.last.create_role
88
+ role.reifier = ref
89
+ end
90
+ end
91
+ role ||= @targets.last.create_role
92
+ @targets.push role
93
+ when "name"
94
+ if a["reifier"]
95
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
96
+ raise "The topic reifiing this topic name already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::TopicName)
97
+ if ref.reified
98
+ tname = ref.reified
99
+ else
100
+ tname = @targets.last.create_name
101
+ tname.reifier = ref
102
+ end
103
+ end
104
+ tname ||= @targets.last.create_name
105
+ @targets.push tname
106
+ when "value"
107
+ # this is handled in text()
108
+ when "variant"
109
+ if a["reifier"]
110
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
111
+ raise "The topic reifiing this topic variant already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::Variant)
112
+ if ref.reified
113
+ variant = ref.reified
114
+ else
115
+ variant = @targets.last.create_variant
116
+ variant.reifier = ref
117
+ end
118
+ end
119
+ variant ||= @targets.last.create_variant
120
+ @targets.push variant
121
+ when "scope"
122
+ # nothing to be done here :)
123
+ when "instanceOf"
124
+ # nothing to be done here :)
125
+ when "type"
126
+ # nothing to be done here :)
127
+ when "occurrence"
128
+ if a["reifier"]
129
+ ref = @targets.last.topic_map.topic_by_item_identifier! resolve(a["reifier"])
130
+ raise "The topic reifiing this topic occurrence already reifies something else: #{ref}." if ref.reified && !ref.reified.is_a?(RTM::Occurrence)
131
+ if ref.reified
132
+ occur = ref.reified
133
+ else
134
+ occur = @targets.last.create_occurrence
135
+ occur.reifier = ref
136
+ end
137
+ end
138
+ occur ||= @targets.last.create_occurrence
139
+ @targets.push occur
140
+ when "resourceData"
141
+ occur_or_variant = @targets.last
142
+ if a["datatype"]
143
+ occur_or_variant.datatype = a["datatype"]
144
+ # special handling is done in tag_end
145
+ else
146
+ occur_or_variant.datatype = RTM::PSI[:String]
147
+ end
148
+ when "topicRef"
149
+ case @path.last
150
+ when "scope"
151
+ @targets.last.scope << a["href"]
152
+ when "instanceOf"
153
+ assoc = @targets.last.topic_map.create_association :type => RTM::PSI[:type_instance]
154
+ assoc.create_role @targets.last, RTM::PSI[:instance]
155
+ assoc.create_role a["href"], RTM::PSI[:type]
156
+ when "type"
157
+ @targets.last.type = a["href"]
158
+ when "role"
159
+ @targets.last.player = a["href"]
160
+ end
161
+ when "resourceRef"
162
+ occurrence = @targets.last
163
+ occurrence.datatype = RTM::PSI[:IRI]
164
+ occurrence.value = a["href"]
165
+ when "subjectLocator"
166
+ @targets.last.subject_locators << a["href"]
167
+ when "subjectIdentifier"
168
+ @targets.last.subject_identifiers << a["href"]
169
+ when "itemIdentity"
170
+ @targets.last.item_identifiers << resolve(a["href"])
171
+ when "topicMap"
172
+ # check where we are
173
+ raise "unexpected element topicMap" unless @path.empty?
174
+ # check version
175
+ raise "Sorry, only XTM 2.0 is supported" if a["version"] != "2.0"
176
+
177
+ # reifier. if target topic map is already reified we have to merge, if not create topic
178
+ if a["reifier"]
179
+ if @target.reifier # TODO this might be no topic here. In this case something should be raised!
180
+ @target.reifier.item_identifiers << resolve(a["reifier"])
181
+ else
182
+ @target.topic_by_item_identifier! resolve(a["reifier"])
183
+ end
184
+ end
185
+ @targets.push @target
186
+ when "mergeMap"
187
+ # TODO mergeMap
188
+ warn "mergeMap is not supported yet!"
189
+ else
190
+ warn "Unhandled element: #{name}"
191
+ end
192
+
193
+ @path.push name
194
+ end
195
+
196
+ # Called when the end tag is reached. In the case of <tag/>, tag_end
197
+ # will be called immidiately after tag_start
198
+ # @p the name of the tag
199
+ def end_element(uri, name, qname)
200
+ return unless uri == "http://www.topicmaps.org/xtm/"
201
+ #puts "end of tag: #{name}"
202
+
203
+ old_name = @path.pop
204
+ raise "Tag_end did not match: expected: #{old_name}, got: #{name}." unless old_name == name
205
+ case name
206
+ when "topicMap", "topic", "association", "role", "name", "variant", "occurrence"
207
+ @targets.pop
208
+ when "resourceData"
209
+ occur = @targets.last
210
+ # puts "finalizing occurrence #{occur.inspect}"
211
+ if occur.datatype == RTM::PSI[:IRI]
212
+ occur.value = resolve(occur.value)
213
+ elsif occur.datatype == RTM::PSI[:anyType]
214
+ # TODO Content must be canonicalized according to http://www.isotopicmaps.org/sam/sam-xtm/#sect-xml-canonicalization
215
+ warn("Content canonicalization is not yet implemented!")
216
+ end
217
+ # puts "leaving finalizing occurrence"
218
+ end
219
+ # puts "after case"
220
+ end
221
+ # Called when text is encountered in the document
222
+ # @p text the text content.
223
+ def characters(text)
224
+ if @options[:strip_whitespace]
225
+ text.strip!
226
+ return if text.empty?
227
+ end
228
+ case @path.last
229
+ when "value", "resourceData"
230
+ if @targets.last.value
231
+ @targets.last.value += text
232
+ else
233
+ @targets.last.value = text
234
+ end
235
+ else
236
+ puts "Found text but don't know that to do: #{text}" unless text.strip.empty?
237
+ end
238
+ end
239
+
240
+ def cdata content
241
+ if @options[:strip_whitespace]
242
+ content.strip!
243
+ return if content.empty?
244
+ end
245
+ case @path.last
246
+ when "value", "resourceData"
247
+ if @targets.last.value
248
+ @targets.last.value += content
249
+ else
250
+ @targets.last.value = content
251
+ end
252
+ else
253
+ puts "Found cdata but don't know that to do: #{content}" unless content.strip.empty?
254
+ end
255
+ end
256
+
257
+ def xmldecl version, encoding, standalone
258
+ warn "XML Version 1.0 expected. Not sure if we can handle this, but we will try." unless version == "1.0"
259
+ warn "Be aware there is no encoding manipulation done, everything gets in as-is." if encoding
260
+ # what about standalone?
261
+ end
262
+
263
+ end
264
+
265
+ end
266
+ end
@@ -0,0 +1,97 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+
5
+ class RTM::RTMAR
6
+ def from_xtm2lx(*args)
7
+ RTM::AR::IO::FROMXTM2LX.from_xtm2(self, *args)
8
+ end
9
+ end
10
+
11
+ # class RTM::RTMAR
12
+ # def self.from_xtm2lx(*args)
13
+ # RTM::AR::IO::FROMXTM2LX.from_xtm2(self, *args)
14
+ # end
15
+ # end
16
+
17
+
18
+
19
+ module RTM::AR::IO
20
+ # XTM2 Export.
21
+ # Each Topic Maps Construct gets a to_xtm2 method.
22
+ # The result is a REXML::Element except for TopicMap where it is a REXML::Document.
23
+ module FROMXTM2LX
24
+ require 'libxml'
25
+ require 'rexml/document'
26
+ require 'rexml/parsers/sax2parser'
27
+ require 'rexml/sax2listener'
28
+ XTM2DEBUG = false
29
+
30
+ # Reads XTM2 from source (io object).
31
+ # Example: RTM::IO::FROMXTM2.from_xtm2(File.open(file_name),"http://rtm.rubyforge.org/topicmaps/tm1/")
32
+ # supported options:
33
+ # :strip_whitespace (defaults to false, may be set to true),
34
+ # :deprefix (defaults to nil, may be set to a string (or regex) which will be removed from the beginning of an (unresolved) item_identifier if it is there.
35
+ def self.from_xtm2(base_tms, source, base_locator, target=nil,options={})
36
+ tm = base_tms.create(base_locator) unless target
37
+ parser = XML::SaxParser.new
38
+ parser.callbacks = XML::LibXMLSax2wrapper.new(FROMXTM2::XTM2Listener.new(base_locator, target || tm, options))
39
+ parser.filename = source
40
+ parser.parse
41
+ #true
42
+ end
43
+ end
44
+ end
45
+
46
+ module XML
47
+ # Acts as Callback structure for the LibXML-Ruby SAX Parser and calls
48
+ # a REXML SAX2Listener API.
49
+ class LibXMLSax2wrapper
50
+ include LibXML::XML::SaxParser::Callbacks
51
+ def initialize(rexml_sax2listener)
52
+ @dest = rexml_sax2listener
53
+ @ns = Hash.new("http://www.topicmaps.org/xtm/")
54
+ end
55
+ def on_start_document
56
+ @dest.start_document
57
+ end
58
+ def on_end_document
59
+ @dest.end_document
60
+ end
61
+ def on_start_element(qname, attr)
62
+ prefix, localname = qname.split(":")
63
+ unless localname
64
+ localname = prefix
65
+ uri = @ns.default
66
+ else
67
+ uri = @ns[prefix]
68
+ end
69
+ @dest.start_element(uri, localname, qname, attr)
70
+ end
71
+ def on_end_element(qname)
72
+ prefix, localname = qname.split(":")
73
+ unless localname
74
+ localname = prefix
75
+ uri = @ns.default
76
+ else
77
+ uri = @ns[prefix]
78
+ end
79
+ @dest.end_element(uri, localname, qname)
80
+ end
81
+ def on_characters(text)
82
+ @dest.characters(text)
83
+ end
84
+ def on_cdata_block(content)
85
+ @dest.cdata(content)
86
+ end
87
+ def on_parser_error(msg)
88
+ warn("SAX Parser Error: #{msg}")
89
+ end
90
+ def on_parser_warning(*msg)
91
+ warn("SAX Parser Warning: #{msg}")
92
+ end
93
+ def on_parser_fatal_error(msg)
94
+ warn("SAX Parser Fatal Error: #{msg}")
95
+ end
96
+ end
97
+ end