rtm 0.1.1 → 0.1.3

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