rtm 0.1.1 → 0.1.3

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.
@@ -75,17 +75,30 @@ module ActiveTopicMaps
75
75
  # EOS
76
76
  # end
77
77
 
78
- def name(name, type="")
78
+ def name(name, type=nil)
79
+ type ||= name
79
80
  prop_getter(name, type, "-")
80
81
  prop_setter(name, type, "-")
81
82
  end
82
-
83
+
84
+ def names(name, type=nil)
85
+ type ||= name.singularize
86
+ prop_getters(name, type, "-")
87
+ prop_adderremover(name, type, "-")
88
+ end
89
+
83
90
  def occurrence(name, type=nil)
84
91
  type ||= name
85
92
  prop_getter(name, type)
86
93
  prop_setter(name, type)
87
94
  end
88
-
95
+
96
+ def occurrences(name, type=nil)
97
+ type ||= name.singularize
98
+ prop_getters(name, type)
99
+ prop_adderremover(name, type)
100
+ end
101
+
89
102
  def prop_getter(name, type, prefix="")
90
103
  self.class_eval <<-EOS
91
104
  def #{name}
@@ -94,6 +107,14 @@ module ActiveTopicMaps
94
107
  EOS
95
108
  end
96
109
 
110
+ def prop_getters(name, type, prefix="")
111
+ self.class_eval <<-EOS
112
+ def #{name}
113
+ @#{name} ||= @topic["#{prefix}#{type}"].map{|n| n.value}
114
+ end
115
+ EOS
116
+ end
117
+
97
118
  def prop_setter(name, type, prefix="")
98
119
  self.class_eval <<-EOS
99
120
  def #{name}=(name)
@@ -107,7 +128,18 @@ module ActiveTopicMaps
107
128
  EOS
108
129
  end
109
130
 
110
- def binassoc(name, rt1, at, rt2, klass)
131
+ def prop_adderremover(name, type, prefix="")
132
+ self.class_eval <<-EOS
133
+ def add_#{name.singularize}(name)
134
+ @topic["#{prefix}#{type}"] = name
135
+ end
136
+ def remove_#{name.singularize}(name)
137
+ @topic["#{prefix}#{type}"].each {|item| item.remove if item.value == name}
138
+ end
139
+ EOS
140
+ end
141
+
142
+ def has_many(name, rt1, at, rt2, klass)
111
143
  eval(<<-EOS)
112
144
  class RTM::AR::Topic
113
145
  index_property_set :#{name}, :type => :Topic, :rule => {
@@ -138,7 +170,12 @@ module ActiveTopicMaps
138
170
  end
139
171
 
140
172
  def create(ref)
141
- self.new(@tm.get!(ref))
173
+ t = @tm.get!(ref)
174
+ if @psi
175
+ @ty ||= @tm.get!(@psi)
176
+ t.add_type @ty unless t.types.include? @ty
177
+ end
178
+ self.new(t)
142
179
  end
143
180
  alias get! create
144
181
 
@@ -147,6 +184,16 @@ module ActiveTopicMaps
147
184
  self.new(t) if t
148
185
  end
149
186
  alias get find
187
+
188
+ def find_by_type(ref=nil)
189
+ ref ||= @psi
190
+ ty = @tm.get(ref)
191
+ ty.instances.map {|t| self.new(t)}
192
+ end
193
+
194
+ def psi(ref)
195
+ @psi = ref
196
+ end
150
197
  end
151
198
  end
152
199
  end
@@ -157,6 +204,7 @@ end
157
204
  #
158
205
  #class Person < ActiveTopicMaps::Base
159
206
  # topic_map "urn:/base"
207
+ # psi "person"
160
208
  #
161
209
  # name :name
162
210
  # occurrence :age
@@ -172,6 +220,7 @@ end
172
220
  # include ActiveTopicMaps::Topic
173
221
  #
174
222
  # topic_map "urn:/base"
223
+ # psi "country"
175
224
  #
176
225
  # acts_as_topic
177
226
  #
@@ -179,7 +228,7 @@ end
179
228
  # occurrence :population
180
229
  # occurrence :size
181
230
  #
182
- # binassoc :inhabitants, "country", "country-inhabitant", "inhabitant", :Person
231
+ # has_many :inhabitants, "country", "country-inhabitant", "inhabitant", :Person
183
232
  #
184
233
  # def to_s
185
234
  # "There is a country called #{name} with #{population} inhabitants and a size of #{size} km^2.\n" +
data/lib/rtm.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # RTM: Ruby Topic Maps.
2
- # Copyright (c) 2007 Benjamin Bock.
2
+ # Copyright (c) 2007-2008 Benjamin Bock.
3
3
  # All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without
@@ -35,12 +35,27 @@
35
35
  module RTM
36
36
 
37
37
  end
38
- require 'rubygems'
39
- gem 'activerecord'
40
- old_verbose=$VERBOSE
41
- $VERBOSE=false
42
- require 'active_record'
43
- $VERBOSE=old_verbose
38
+
39
+ $:.unshift(File.dirname(__FILE__)) unless
40
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
41
+
42
+ unless defined? ActiveRecord
43
+ active_record_path = File.dirname(__FILE__) + "/../../activerecord/lib"
44
+ if File.exist?(active_record_path)
45
+ $:.unshift active_record_path
46
+ old_verbose=$VERBOSE
47
+ $VERBOSE=false
48
+ require 'active_record'
49
+ $VERBOSE=old_verbose
50
+ else
51
+ require 'rubygems'
52
+ gem 'activerecord'
53
+ old_verbose=$VERBOSE
54
+ $VERBOSE=false
55
+ require 'active_record'
56
+ $VERBOSE=old_verbose
57
+ end
58
+ end
44
59
 
45
60
  require 'rtm/core_ext'
46
61
  require 'rtm/helpers'
@@ -54,7 +69,11 @@ require 'rtm/io/to_string'
54
69
  require 'rtm/io/to_xtm2'
55
70
  require 'rtm/io/to_yaml'
56
71
  require 'rtm/io/from_xtm2'
57
- require 'rtm/io/from_xtm2_libxml.rb'
72
+ begin
73
+ require 'rtm/io/from_xtm2_libxml.rb'
74
+ rescue Exception
75
+ warn("LibXML could not be loaded, only (slow) REXML import available")
76
+ end
58
77
  #require 'rtm/io/from_xtm2_hpricot'
59
78
  require 'rtm/merging/merging'
60
79
  require 'rtm/pimp_my_api'
@@ -5,30 +5,34 @@ class Class
5
5
  rtm_old_const_missing(const_name)
6
6
  end
7
7
  end
8
- class Array
9
- alias :rtm_old_method_missing :method_missing
10
- def method_missing(method_name, *args)
11
- if size > 0 && first.respond_to?(method_name) && (![:__getobj__, :__setobj__].include?(method_name))
12
- inject([]) {|all,single| all << single.send(method_name, *args)}
13
- else
14
- rtm_old_method_missing(method_name, *args)
15
- end
16
- end
17
-
18
- alias :rtm_old_respond_to? :respond_to?
19
- def respond_to?(method_name, *args)
20
- resp = rtm_old_respond_to?(method_name, *args)
21
- return resp if resp # i.e. if true
22
- # check for special calls we don't want to pass
23
- return false if [:__getobj__, :__setobj__].include?(method_name)
24
- # ... and ask first child otherwise
25
- @obj && @obj.size > 0 && first.respond_to?(method_name, *args)
26
- end
27
-
28
- def compact_empty
29
- compact.reject{|e| e.empty?}
30
- end
31
- end
8
+ # The following methods allow automatic +map+ping on arrays to their containees properties.
9
+ # However this is a dirt hack and did no longer work in ActiveRecord 2.1.0.
10
+ # This should be fixed some time later.
11
+ #
12
+ #class Array
13
+ # alias :rtm_old_method_missing :method_missing
14
+ # def method_missing(method_name, *args)
15
+ # if size > 0 && first.respond_to?(method_name) && (![:__getobj__, :__setobj__].include?(method_name))
16
+ # inject([]) {|all,single| all << single.send(method_name, *args)}
17
+ # else
18
+ # rtm_old_method_missing(method_name, *args)
19
+ # end
20
+ # end
21
+ #
22
+ # alias :rtm_old_respond_to? :respond_to?
23
+ # def respond_to?(method_name, *args)
24
+ # resp = rtm_old_respond_to?(method_name, *args)
25
+ # return resp if resp # i.e. if true
26
+ # # check for special calls we don't want to pass
27
+ # return false if [:__getobj__, :__setobj__].include?(method_name)
28
+ # # ... and ask first child otherwise
29
+ # @obj && @obj.size > 0 && first.respond_to?(method_name, *args)
30
+ # end
31
+ #
32
+ # def compact_empty
33
+ # compact.reject{|e| e.empty?}
34
+ # end
35
+ #end
32
36
  class ActiveRecord::Associations::AssociationProxy
33
37
  # this is needed to prevent the associations from being instantly loaded
34
38
  alias :rtm_old_respond_to? :respond_to?
@@ -45,6 +49,7 @@ module RTM::AR
45
49
  require 'rtm/backend/active_record/tm_set_delegator'
46
50
  require 'rtm/backend/active_record/tm_construct'
47
51
  require 'rtm/backend/active_record/topic_map'
52
+ require 'rtm/backend/active_record/traverse_associations'
48
53
  require 'rtm/backend/active_record/topic'
49
54
  require 'rtm/backend/active_record/association_and_role'
50
55
  require 'rtm/backend/active_record/name_variant_occurrence'
@@ -24,31 +24,10 @@ module RTM::AR
24
24
  property :player, :aka => [:pl,:topic], :rw => true, :type => :Topic, :wrap => true
25
25
  property :type, :aka => [:ty,:t], :rw => true, :type => :Topic, :wrap => true
26
26
 
27
- def counterparts
28
- self.parent.roles.reject{|r| r.id==self.id}
29
- end
30
- def counterplayers
31
- self.counterparts.map{|r| r.player}
32
- end
33
- def counterpart
34
- n = self.parent.roles.size
35
- raise "Association must be unary or binary to use counterpart method. Please use counterparts for n-ary associations." if n > 2
36
- return nil if n == 1
37
- self.parent.roles.reject{|r| r.id==self.id}.first
38
- end
39
- def counterplayer
40
- n = self.parent.roles.size
41
- raise "Association must be unary or binary to use counterplayer method. Please use counterplayers for n-ary associations." if n > 2
42
- return nil if n == 1
43
- self.parent.roles.reject{|r| r.id==self.id}.first.player
44
- end
45
- def peers
46
- self.counterpart.player.roles.select{|r| r.type == cp.type}.counterpart
47
- end
48
- def peerplayers
49
- self.peers.map{|r| r.player}
50
- end
27
+ require 'rtm/sugar/role/counterparts'
28
+ include RTM::Sugar::Role::Counterparts
51
29
 
52
30
  equality [:type, :player, :parent]
53
31
  end
32
+ Role = AssociationRole
54
33
  end
@@ -16,6 +16,8 @@ module RTM::AR
16
16
  delegate :reference, :rw => true, :save => true
17
17
  alias :value :reference
18
18
  alias :value= :reference=
19
+ alias :v :reference
20
+ alias :v= :reference=
19
21
 
20
22
  equality [:reference]
21
23
 
@@ -7,21 +7,22 @@ module RTM::AR
7
7
  property :type, :aka => [:ty,:t], :rw => true, :type => :Topic, :wrap => true
8
8
  property_set :scope, :type => :Topic, :wrap => true
9
9
 
10
- property_set :variants, :aka => :v, :type => :Variant, :wrap => true,
10
+ property_set :variants, :aka => :vs, :type => :Variant, :wrap => true,
11
11
  :create => :variant, :create_aka => :cv,
12
12
  :create_args => [ {:name => :value, :type => [:String, :Locator]}, {:name => :scope, :type => :Collection}]
13
13
 
14
- property :value, :rw => true, :type => :String
14
+ property :value, :rw => true, :type => :String, :aka => :v
15
15
 
16
16
  equality [:value, :type, :scope, :parent]
17
17
  end
18
+ Name = TopicName
18
19
 
19
20
  class Variant < Reifiable
20
21
  include RTM::Variant
21
22
  wrapper_cache
22
23
 
23
24
  parent :topic_name, :aka => :name
24
- property :value, :rw => true, :type => :String
25
+ property :value, :rw => true, :type => :String, :aka => :v
25
26
  property :datatype, :rw => true, :type => :Locator #, :wrap => true
26
27
  property_set :scope, :type => :Topic, :wrap => true
27
28
 
@@ -33,7 +34,7 @@ module RTM::AR
33
34
  wrapper_cache
34
35
 
35
36
  parent :topic
36
- property :value, :rw => true, :type => :String
37
+ property :value, :rw => true, :type => :String, :aka => :v
37
38
  property :datatype, :rw => true, :type => :String
38
39
 
39
40
  property_set :scope, :type => :Topic, :wrap => true
@@ -291,81 +291,6 @@ RUBY
291
291
  end
292
292
  end
293
293
 
294
- def self.index_property_set(prop, options={})
295
- r = options[:rule]
296
- module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET__)", 1)
297
- def direct_#{prop}_roles
298
- # prefetch typing topics
299
- rtype = @ips_#{prop}_rtype ||= self.topic_map.get("#{r[:role_type]}")
300
- atype = @ips_#{prop}_atype ||= self.topic_map.get("#{r[:association_type]}")
301
- otype = @ips_#{prop}_otype ||= self.topic_map.get("#{r[:other_role_type]}")
302
- # return nothing if any of the typing topics does not exist
303
- return [] unless rtype && atype && otype
304
- # we do that only here and not earlier because some might me nil
305
- rtype = rtype.id
306
- atype = atype.id
307
- otype = otype.id
308
- self.__getobj__.roles.map{|r|
309
- r.ttype_id != nil &&
310
- r.ttype_id == rtype &&
311
- r.parent.roles.size == #{r[:association_arity]} &&
312
- r.parent != nil &&
313
- r.parent.ttype_id == atype &&
314
- (r2 = r.parent.roles.select{|r2| r2.ttype_id != nil &&
315
- r2.ttype_id == otype}.first) &&
316
- r2}.select{|r2| r2}.map{|r2| AssociationRole.wrap(r2)}
317
- end
318
- def direct_#{prop}
319
- direct_#{prop}_roles.map{|r2| r2.player}.uniq
320
- end
321
- EOS
322
-
323
- if r[:transitive]
324
- module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET2__)", 1)
325
- def #{prop}
326
- d = todo = self.direct_#{prop}
327
- while todo.size > 0
328
- todo = todo.map{|dt| dt.direct_#{prop}}.flatten.uniq - d
329
- d += todo
330
- end
331
- d
332
- #d2 = self.direct_#{prop}.map {|dt| dt.#{prop}}.flatten
333
- #(d+d2).uniq
334
- end
335
- EOS
336
- else
337
- if r[:infer]
338
- module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET3__)", 1)
339
- def #{prop}
340
- (self.direct_#{prop} + self.#{r[:infer]}.map{|d2| d2.direct_#{prop}}).flatten.uniq
341
- end
342
- EOS
343
- elsif r[:infer_other]
344
- module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET4__)", 1)
345
- def #{prop}
346
- d = self.direct_#{prop}
347
- (d + d.map{|d2| d2.#{r[:infer_other]}}).flatten.uniq
348
- end
349
- EOS
350
- end
351
- end
352
- if r[:add]
353
- module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET5__)", 1)
354
- def add_#{r[:add]}(t)
355
- a = self.topic_map.create_association("#{r[:association_type]}")
356
- a.create_role self, "#{r[:role_type]}"
357
- a.create_role t, "#{r[:other_role_type]}"
358
- a
359
- # TODO add_x in index_property_set needs to trigger reload of the topics somewhere
360
- end
361
- def remove_#{r[:add]}(t)
362
- direct_#{prop}_roles.select{|r| r.player == t}.each{|r| r.parent.remove}
363
- # TODO remove_x in index_property_set needs to trigger reload of the topics somewhere
364
- end
365
- EOS
366
- end
367
- end
368
-
369
294
  def self.equality(eqfields, options={})
370
295
  field_condition = eqfields.map {|f| "self.#{f} == o.#{f}" }.join(" && ")
371
296
  module_eval(<<-EOS, "(__AR_EQUALITY__)", 1)
@@ -36,6 +36,7 @@ module RTM::AR
36
36
  end
37
37
 
38
38
  class TopicMapConstruct < ActiveRecord::Base
39
+ self.store_full_sti_class = false
39
40
  extend Movable
40
41
  class << self
41
42
  def abstract_class?
@@ -108,6 +109,7 @@ module RTM::AR
108
109
  # end
109
110
  end
110
111
  class ScopedObjectsTopic < ActiveRecord::Base
112
+ self.store_full_sti_class = false
111
113
  extend Movable
112
114
  belongs_to :topic
113
115
  belongs_to :scoped_object, :polymorphic => true
@@ -118,6 +120,7 @@ module RTM::AR
118
120
  end
119
121
 
120
122
  class Locator < ActiveRecord::Base
123
+ self.store_full_sti_class = false
121
124
  extend Movable
122
125
  class << self
123
126
  def abstract_class?
@@ -253,8 +256,10 @@ module RTM::AR
253
256
  has_many :subject_identifiers, :dependent => :destroy
254
257
 
255
258
  has_many :topic_names, :through => :topics
259
+ alias :names :topic_names
256
260
  has_many :occurrences, :through => :topics
257
261
  has_many :association_roles, :through => :associations
262
+ alias :roles :association_roles
258
263
 
259
264
  %w[association association_role topic_name occurrence].each do |m|
260
265
  module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET__)", 1)
@@ -23,16 +23,6 @@ module RTM::AR
23
23
  :create => :occurrence, :create_aka => :co,
24
24
  :create_args => [ {:name => :value, :type => [:String, :Locator]}, {:name => :type, :type => :Topic}, {:name => :scope, :type => :Collection} ]
25
25
 
26
- def characteristics
27
- topic_names.to_a + occurrences.to_a
28
- end
29
- def internal_occurrences
30
- occurrences.select{|o| o.datatype != RTM::PSI[:IRI]}
31
- end
32
- def external_occurrences
33
- occurrences.select{|o| o.datatype == RTM::PSI[:IRI]}
34
- end
35
-
36
26
  property_set :roles, :aka => [:r, :roles_played, :association_roles], :computable => true, :type => :AssociationRole, :wrap => true
37
27
 
38
28
  property_set :associations, :aka => [:a, :associations_played], :type => :Association, :wrap => true
@@ -48,72 +38,16 @@ module RTM::AR
48
38
  property_set :topic_names_typed, :aka => [:names_typed,:tnt,:nt], :type => :TopicName, :wrap => true
49
39
  property_set :occurrences_typed, :aka => :ot, :type => :Occurrence, :wrap => true
50
40
 
51
- # maybe these pairs could be declared each with a single statement and a reversible option
52
- index_property_set :types, :type => :Topic, :rule => {
53
- :transitive => false,
54
- :role_type => RTM::PSI[:instance],
55
- :association_type => RTM::PSI[:type_instance],
56
- :association_arity => 2,
57
- :other_role_type => RTM::PSI[:type],
58
- :infer_other => :supertypes,
59
- :add => :type,
60
- }
61
- index_property_set :instances, :type => :Topic, :rule => {
62
- :transitive => false,
63
- :role_type => RTM::PSI[:type],
64
- :association_type => RTM::PSI[:type_instance],
65
- :association_arity => 2,
66
- :other_role_type => RTM::PSI[:instance],
67
- :infer => :subtypes,
68
- :add => :instance,
69
- }
70
- index_property_set :supertypes, :type => :Topic, :rule => {
71
- :transitive => true,
72
- :role_type => RTM::PSI[:subtype],
73
- :association_type => RTM::PSI[:supertype_subtype],
74
- :association_arity => 2,
75
- :other_role_type => RTM::PSI[:supertype],
76
- :add => :supertype,
77
- }
78
- index_property_set :subtypes, :type => :Topic, :rule => {
79
- :transitive => true,
80
- :role_type => RTM::PSI[:supertype],
81
- :association_type => RTM::PSI[:supertype_subtype],
82
- :association_arity => 2,
83
- :other_role_type => RTM::PSI[:subtype],
84
- :add => :subtype,
85
- }
86
-
87
- def [](topicref)
88
- topicref =~ /^(-\s*)?(.+)?$/
89
- if $1
90
- nametype = $2 || RTM::PSI[:name_type]
91
- names.select{|n| n.type == topic_map.get(nametype)}
92
- else
93
- raise "No occurrence type given" unless $2 and !$2.empty?
94
- occurrences.select{|o| o.type == topic_map.get($2)}
95
- end
96
- end
97
-
98
- def []=(topicref, value)
99
- topicref =~ /^(-\s*)?(.+)?$/
100
- if $1
101
- nametype = $2 || topic_map.get!(RTM::PSI[:name_type])
102
- n = create_name(value, nametype)
103
- n
104
- else
105
- raise "No occurrence type given" unless $2
106
- o = create_occurrence(value, $2)
107
- o
108
- end
109
- end
110
-
111
- def counterparts
112
- self.roles.map{|sr| sr.parent.roles.reject{|r| r.id==sr.id}}
113
- end
114
- def counterplayers
115
- self.counterparts.map{|r| r.player}
116
- end
41
+ require 'rtm/sugar/topic/characteristics'
42
+ include RTM::Sugar::Topic::Characteristics
43
+ require 'rtm/sugar/topic/counterparts'
44
+ include RTM::Sugar::Topic::Counterparts
45
+ require 'rtm/sugar/topic/identifier_direct'
46
+ include RTM::Sugar::Topic::IdentifierDirect
47
+ require 'rtm/sugar/topic/hash_access'
48
+ include RTM::Sugar::Topic::HashAccess
49
+ require 'rtm/sugar/topic/predefined_associations'
50
+ include RTM::Sugar::Topic::PredefinedAssociations
117
51
 
118
52
  # class:Topic equality, http://www.isotopicmaps.org/sam/sam-model/#d0e1029
119
53
  #