rtm-activerecord 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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,22 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR::Sugar
5
+ module Association
6
+ # This module implements methods for Hash-like access in Associations.
7
+ module HashAccess
8
+ # Returns the roles of the association which match the given role type.
9
+ # The role type can be specified as String-reference or as Topic.
10
+ def [](type_reference=:any)
11
+ return roles if type_reference == :any
12
+ return roles unless type_reference
13
+ if type_reference.is_a? String
14
+ roles.select{|o| o.type == topic_map.get(type_reference)}
15
+ else
16
+ # assume topics are given
17
+ roles.select{|o| o.type == type_reference}
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR::Sugar
5
+ module Role
6
+ module Counterparts
7
+
8
+ # Fetches all roles from the parent association of this role,
9
+ # except itself (i.e. current role).
10
+ # A filter-hash may be used to filter for the type of the other role (:otype =>topic_reference).
11
+ # Examples:
12
+ # this_role.counterparts # returns all
13
+ # this_role.counterparts(:otype => some_topic) # returns only other roles which have type some_topic
14
+ # this_role.counterparts(:otype => "some_reference") # as above, looking up the reference first
15
+ def counterparts(filter={})
16
+ self.parent.roles.reject{|r| r.id==self.id}.
17
+ select{|r| filter[:otype] ? r.type == self.topic_map.get(filter[:otype]) : true}
18
+ end
19
+
20
+ # This methods fetches all players of the parent association of this role, except the player of itself.
21
+ # It accepts a filter-hash like the counterparts-method.
22
+ def counterplayers(*args)
23
+ self.counterparts(*args).map{|r| r.player}
24
+ end
25
+
26
+ # Fetches the other role, if the parent association is binary.
27
+ def counterpart
28
+ n = self.parent.roles.size
29
+ raise "Association must be unary or binary to use counterpart method. Please use counterparts for n-ary associations." if n > 2
30
+ return nil if n == 1
31
+ self.counterparts.first
32
+ end
33
+
34
+ # Fetches the player of the other role, if the parent association is binary.
35
+ def counterplayer
36
+ n = self.parent.roles.size
37
+ raise "Association must be unary or binary to use counterplayer method. Please use counterplayers for n-ary associations." if n > 2
38
+ return nil if n == 1
39
+ self.counterparts.first.player
40
+ end
41
+
42
+ # Fetches all roles of the (binary) parent associations which play the same role in the same with the counterplayer
43
+ # TODO: filter not only otype but also atype and rtype.
44
+ def peers
45
+ cp = self.counterpart
46
+ cp.player.roles.select{|r| r.type == cp.type}.map{|r| r.counterpart}
47
+ end
48
+ # Fetches all players of roles being in the same association with another topic as self
49
+ # Example: current_role is a role of player "me" and type "employee"
50
+ # current_role.peerplayers # returns all employees of my company (including me)
51
+ def peerplayers
52
+ self.peers.map{|r| r.player}
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,15 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR::Sugar
5
+ module Topic
6
+ module Characteristics
7
+ def internal_occurrences
8
+ occurrences.select{|o| o.datatype != RTM::PSI[:IRI]}
9
+ end
10
+ def external_occurrences
11
+ occurrences.select{|o| o.datatype == RTM::PSI[:IRI]}
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR::Sugar
5
+ module Topic
6
+ module Counterparts
7
+ def counterparts(filter={})
8
+ self.roles.
9
+ select{|r| filter[:rtype] ? r.type == self.topic_map.get(filter[:rtype]) : true}.
10
+ select{|r| filter[:atype] ? r.parent.type == self.topic_map.get(filter[:atype]) : true}.
11
+ inject([]){|all,r| all+=r.counterparts(filter)}
12
+ end
13
+ def counterplayers(*args)
14
+ return self.counterparts(*args).map{|r| r.player}
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,78 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR::Sugar
5
+ module Topic
6
+ # This module implements methods for Hash-like access in Topics.
7
+ module HashAccess
8
+
9
+ # Returns a list of names or occurrences depending on the arguments given.
10
+ #
11
+ def [](cref)
12
+ # return occurrences by type if topic is given
13
+ return occurrences.select{|o| o.type == cref} if cref.is_a? Topic
14
+ raise "Characteristic reference must be a Topic or a String" unless cref.is_a? String
15
+ # parse the given String to extract kind (name/occ), type and scope
16
+ is_name, type_reference, scope_reference = resolve_characteristic(cref)
17
+ if is_name
18
+ type_reference = RTM::PSI[:name_type] if type_reference == :any
19
+ ret = names.select{|n| n.type == topic_map.get(type_reference)}
20
+ else
21
+ raise "No occurrence type given" if type_reference == :any
22
+ ret = occurrences.select{|o| o.type == topic_map.get(type_reference)}
23
+ end
24
+ # we now have a list of names or occurrences, now we have to select for scope
25
+ return ret if scope_reference == :any
26
+ if scope_reference == :ucs
27
+ ret.select{|n| n.scope.size == 0}
28
+ else
29
+ ret.select{|n| scope_reference.all?{|sr| srt = topic_map.get(sr); srt && n.scope.include?(srt)}}
30
+ end
31
+ end
32
+
33
+ def []=(cref, value)
34
+ is_name, type_reference, scope_reference = resolve_characteristic(cref)
35
+ if is_name
36
+ type_reference = nil if type_reference == :any
37
+ nametype = type_reference || topic_map.get!(RTM::PSI[:name_type])
38
+ ret = create_name(nametype, value)
39
+ else
40
+ raise "No occurrence type given" if type_reference == :any
41
+ ret = create_occurrence(type_reference,value)
42
+ end
43
+ # return object without scope if any or ucs
44
+ return ret if [:any, :ucs].include? scope_reference
45
+ # set scope
46
+ scope_reference.each do |sr|
47
+ ret.scope << topic_map.get!(sr)
48
+ end
49
+ end
50
+
51
+ private
52
+ def resolve_characteristic(ref)
53
+ ref =~ /^(-\s*)?(.+)?$/
54
+ is_name = $1 ? true : false
55
+ type, scope = resolve_type_and_scope($2)
56
+ [is_name, type, scope]
57
+ end
58
+
59
+ def resolve_type_and_scope(ref)
60
+ return [:any, :any] unless ref
61
+ return [ref, :ucs] if ref.is_a? Topic
62
+ type, scope = ref.split('@', -1)
63
+ if type.blank?
64
+ type = :any
65
+ else
66
+ type.strip!
67
+ end
68
+ if scope
69
+ scope = scope.strip.split(/\s+/)
70
+ scope = :ucs if scope.blank?
71
+ else
72
+ scope = :any
73
+ end
74
+ [type, scope]
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,14 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR::Sugar
5
+ module Topic
6
+ module IdentifierDirect
7
+ def identifiers
8
+ subject_identifiers.map{|si| si.to_s} +
9
+ subject_locators.map{|sl| "=#{sl.to_s}"} +
10
+ item_identifiers.map{|ii| "^#{ii.to_s}"}
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,53 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR::Sugar
5
+ module Topic
6
+ module PredefinedAssociations
7
+ extend RTM::AR::TraverseAssociations
8
+ # maybe these pairs could be declared each with a single statement and a reversible option
9
+ define_association :types, :type => :Topic, :rule => {
10
+ :transitive => false,
11
+ :role_type => RTM::PSI[:instance],
12
+ :association_type => RTM::PSI[:type_instance],
13
+ :association_arity => 2,
14
+ :other_role_type => RTM::PSI[:type],
15
+ :infer_other => :supertypes,
16
+ :add => :type,
17
+ }
18
+ alias :addType :add_type
19
+
20
+ define_association :instances, :type => :Topic, :rule => {
21
+ :transitive => false,
22
+ :role_type => RTM::PSI[:type],
23
+ :association_type => RTM::PSI[:type_instance],
24
+ :association_arity => 2,
25
+ :other_role_type => RTM::PSI[:instance],
26
+ :infer => :subtypes,
27
+ :add => :instance,
28
+ }
29
+ alias :addInstance :add_instance
30
+
31
+ define_association :supertypes, :type => :Topic, :rule => {
32
+ :transitive => true,
33
+ :role_type => RTM::PSI[:subtype],
34
+ :association_type => RTM::PSI[:supertype_subtype],
35
+ :association_arity => 2,
36
+ :other_role_type => RTM::PSI[:supertype],
37
+ :add => :supertype,
38
+ }
39
+ alias :addSupertype :add_supertype
40
+
41
+ define_association :subtypes, :type => :Topic, :rule => {
42
+ :transitive => true,
43
+ :role_type => RTM::PSI[:supertype],
44
+ :association_type => RTM::PSI[:supertype_subtype],
45
+ :association_arity => 2,
46
+ :other_role_type => RTM::PSI[:subtype],
47
+ :add => :subtype,
48
+ }
49
+ alias :addSubtype :add_subtype
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,66 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR
5
+ class Construct < TMDelegator
6
+ include RTM::Construct
7
+
8
+ def self.abstract_class?
9
+ self == Construct
10
+ end
11
+ property_set :item_identifiers, :aka => [:ii, :iid, :source_locators], :type => :ItemIdentifier, :wrap => true,
12
+ #:create => :item_identifier, :create_aka => :cii
13
+ :add => true, :remove => true
14
+ alias :getItemIdentifiers :item_identifiers
15
+
16
+ delegate :remove, :to => :destroy
17
+
18
+ delegate :id
19
+ # property_parent :parent # mmh..
20
+
21
+ property :topic_map, :rw => true, :type => :TopicMap, :wrap => true
22
+
23
+ #class_delegate :create
24
+
25
+ def self.wrap(obj)
26
+ return nil unless obj
27
+ raise "Double wrapping" if obj.respond_to?(:__getobj__)
28
+ case obj.class.name
29
+ when "RTM::AR::TMDM::Topic"
30
+ Topic.wrap(obj)
31
+ when "RTM::AR::TMDM::Variant"
32
+ Variant.wrap(obj)
33
+ when "RTM::AR::TMDM::Name"
34
+ Name.wrap(obj)
35
+ when "RTM::AR::TMDM::Occurrence"
36
+ Occurrence.wrap(obj)
37
+ when "RTM::AR::TMDM::Association"
38
+ Association.wrap(obj)
39
+ when "RTM::AR::TMDM::Role"
40
+ Role.wrap(obj)
41
+ when "RTM::AR::TMDM::TopicMap"
42
+ TopicMap.wrap(obj)
43
+ else
44
+ raise "Can't wrap object. Class for wrapping #{obj.class} unknown (object: #{obj})"
45
+ end
46
+ end
47
+
48
+ def self.find(*args)
49
+ res = RTM::AR::TMDM.const_get(name.split("::").last).find(*args)
50
+ if res.respond_to? :each
51
+ Constructs.wrap(res)
52
+ else
53
+ Construct.wrap(res)
54
+ end
55
+ end
56
+ end
57
+
58
+ class Reifiable < Construct
59
+ include RTM::Reifiable
60
+
61
+ def self.abstract_class?
62
+ self == Reifiable
63
+ end
64
+ property :reifier, :type => :Topic, :rw => :true, :wrap => true
65
+ end
66
+ end
@@ -0,0 +1,417 @@
1
+ # Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig.
2
+ # License: Apache License, Version 2.0
3
+
4
+ module RTM::AR
5
+ class TMDelegator
6
+ def initialize(obj)
7
+ @obj = obj
8
+ end
9
+
10
+ def __getobj__
11
+ @obj
12
+ end
13
+
14
+ def __setobj__(obj)
15
+ @obj = obj
16
+ end
17
+
18
+ def self.delegate(sym, options={})
19
+ to = options[:to] || sym
20
+ options[:reload] ||= sym==:remove ? true : false # dirty hack to get a bit of reloading after delete
21
+ module_eval(<<-EOS, "(__TMDELEGATOR__)", 1)
22
+ def #{sym}(*args, &block)
23
+ __getobj__.send(:#{to}, *args, &block)
24
+ #{options[:reload] ? "parent.__getobj__.reload" :""}
25
+ end
26
+ EOS
27
+
28
+ if options[:rw]
29
+ module_eval(<<-EOS, "(__TMDELEGATOR2__)", 1)
30
+ def #{sym}=(*args, &block)
31
+ __getobj__.send(:#{to}=, *args, &block)
32
+ #{options[:save] ? "__getobj__.save" : "" }
33
+ end
34
+ EOS
35
+ end
36
+
37
+ if options[:aka]
38
+ options[:aka] = [options[:aka]] unless options[:aka].respond_to? :each
39
+ options[:aka].each do |aka|
40
+ module_eval(<<-EOS, "(__TMDELEGATOR3__)", 1)
41
+ alias :#{aka} :#{sym}
42
+ EOS
43
+
44
+ if options[:rw]
45
+ module_eval(<<-EOS, "(__TMDELEGATOR3__)", 1)
46
+ alias :#{aka}= :#{sym}=
47
+ EOS
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.class_delegate(sym, options={})
54
+ module_eval(<<-EOS, "(__TMDELEGATOR__)", 1)
55
+ class << self
56
+ def #{sym}(*args, &block)
57
+ __getobj__.send(:#{sym}, *args, &block)
58
+ end
59
+ end
60
+ EOS
61
+
62
+ if options[:aka]
63
+ options[:aka] = [options[:aka]] unless options[:aka].respond_to? :each
64
+ options[:aka].each do |aka|
65
+ module_eval(<<-EOS, "(__TMDELEGATOR2__)", 1)
66
+ class << self
67
+ alias :#{aka} :#{sym}
68
+ end
69
+ EOS
70
+ end
71
+ end
72
+ end
73
+
74
+ def self.parent(sym, options={})
75
+ module_eval(<<-EOS, "(__TMDELEGATOR__)", 1)
76
+ def #{sym}
77
+ Construct.wrap(__getobj__.send(:#{sym}))
78
+ end
79
+ EOS
80
+ aka_property(sym, [:parent, :p])
81
+ end
82
+
83
+ def self.property_set(prop, options={})
84
+ # puts "In #{self.name.to_s.ljust(20)} we have type #{options[:type].to_s.ljust(20)} for property #{prop}"
85
+ if options[:wrap]
86
+ #puts "#{self}: #{options[:type]}"
87
+ module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY_SET_W__)", 1)
88
+ def #{prop}(*args)
89
+ if args.size == 0
90
+ # fetch normal
91
+ #{options[:type]}s.wrap(__getobj__.#{prop}, self, :#{options[:type]})
92
+ else
93
+ # fetch with filter
94
+ # see if we have a custom filtering method
95
+ if self.respond_to?(:filter_#{prop})
96
+ res = filter_#{prop}(*args) # tries a custom query, returns false if it doesn't know how to handle args
97
+ return res if res # return if successful, go on otherwise
98
+ end
99
+ # no custom filtering, use regular one
100
+ #puts args.inspect
101
+ # TODO enhance/fix condition transform code
102
+ a1 = args.first
103
+ unless a1.is_a?(Hash)
104
+ a1 = topic_map.get(a1) || {}
105
+ end
106
+ a = {}.merge(a1)
107
+ if a[:type] && !a[:ttype]
108
+ a[:ttype] = a[:type]
109
+ a.delete(:type)
110
+ end
111
+ puts "#{"#"}{self.class}:#{prop} -- #{"#"}{args.first.inspect}"
112
+ puts
113
+ puts a.inspect
114
+ b = {}
115
+ a.each do |k,v|
116
+ if RTM::AR::TMDM::#{options[:type]}.columns.map{|c| c.name}.include?(k)
117
+ puts "#{options[:type]} hat spalte: #{"#"}{k}"
118
+ else
119
+ puts "#{options[:type]} hat NICHT spalte: #{"#"}{k}"
120
+ b[k] = v
121
+ a.delete(k)
122
+ end
123
+ end
124
+
125
+ a.each do |k,v|
126
+ if v.respond_to?(:__getobj__)
127
+ a[(k.to_s + "_id").to_sym] = v.id
128
+ end
129
+ end
130
+ res = #{options[:type]}s.wrap(__getobj__.#{prop}.find(:all, :conditions=> a), self, :#{options[:type]})
131
+ if args.size == 1
132
+ a1 = args.first
133
+ res = res.select{|t| t}
134
+ end
135
+ res
136
+ end
137
+ end
138
+ EOS
139
+ else
140
+ module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY_SET_NW__)", 1)
141
+ def #{prop}
142
+ __getobj__.#{prop}
143
+ end
144
+ EOS
145
+ end
146
+
147
+ if options[:create]
148
+ # some :create_args:
149
+ #
150
+ # create_locator reference:string
151
+ # [ :name => :reference, :type => :String]
152
+ #
153
+ # create_topic_name value:String, scope:Collection
154
+ # create_topic_name value:String, type: Topic, scope:Collection
155
+ # [ {:name => :value, :type => :String}, {:name => :type, :type => :Topic, :optional => true}, {:name => :scope, :type => :Collection} ]
156
+ #
157
+ # create_occurrence value:String, type: Topic, scope:Collection
158
+ # create_occurrence resource: Locator, type: Topic, scope:Collection
159
+ # [ {:name => :value, :type => [:String, :Locator]}, {:name => :type, :type => :Topic}, {:name => :scope, :type => :Collection} ]
160
+ #
161
+ # create_association_role player:topic, type: topic
162
+ # [ {:name => :player, :type => :Topic}, {:name => :type, :type => :Topic}]
163
+ #
164
+ # create_variant value:string, :scope:Collection
165
+ # create_variant resource:locator, :scope:Collection
166
+ # [ {:name => :value, :type => [:String, :Locator]}, {:name => :scope, :type => :Collection}]
167
+
168
+ module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY_SET_CREATE__)", 1)
169
+ def create_#{ options[:create]}(*args, &block)
170
+ #puts "#{ options[:create]}"
171
+ #puts args.inspect
172
+ arg_defs = #{ (options[:create_args] || nil ).inspect}
173
+
174
+ a = parse_args(args, arg_defs)
175
+ #raise "could not parse parameters:" + a.inspect + " from " + args.inspect if a.empty?
176
+
177
+ a = enhance_args_hash(a, arg_defs) if arg_defs
178
+
179
+ a = a.reject{|k,v| v.nil?}
180
+
181
+ # hack to change :type to :ttype for AR backend
182
+ # puts a.inspect unless a.is_a? Hash
183
+ if a[:type] && !a[:ttype]
184
+ a[:ttype] = a[:type]
185
+ a.delete(:type)
186
+ end
187
+ if [:Locator,:ItemIdentifier, :SubjectIdentifier, :SubjectLocator].include?(:#{options[:type]})
188
+ unless a[:topic_map]
189
+ a[:topic_map] = topic_map.__getobj__
190
+ end
191
+ end
192
+
193
+ if block_given?
194
+ # if a.respond_to?(:__getobj__)
195
+ # a = a.__getobj__
196
+ # end
197
+ obj = #{options[:type]}.wrap( __getobj__.#{prop}.new(a) )
198
+ yield obj
199
+ obj.save
200
+ else
201
+ # if a.respond_to?(:__getobj__)
202
+ # a = a.__getobj__
203
+ # end
204
+ # raise a.inspect unless a.is_a?(RTM::AR::TMDM::Topic)
205
+ obj = #{options[:type]}.wrap( __getobj__.#{prop}.create(a) )
206
+ end
207
+ self.reload
208
+ obj
209
+ end
210
+ EOS
211
+
212
+ if options[:create_aka]
213
+ aka_property("create_#{options[:create]}", options[:create_aka])
214
+ end
215
+ end
216
+
217
+ if options[:add] && options[:add] == :scope
218
+ module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_ADD__)", 1)
219
+ def add_scope(theme)
220
+ if theme.is_a?(Array)
221
+ theme.each {|s| add_scope(s)}
222
+ end
223
+ scope.add(theme.__getobj__)
224
+ end
225
+ RUBY
226
+ # elsif options[:add] && (options[:add] == :subject_identifier || options[:add] == :subject_locator)
227
+ # module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_ADD__)", 1)
228
+ # def add_#{prop.to_s.singularize}(*args, &block)
229
+ # puts "adding something to #{prop}: #{"#"}{args.inspect}"
230
+ # obj.#{prop}.add(*args, &block)
231
+ # end
232
+ # RUBY
233
+ # aka_property("add_#{prop.to_s.singularize}", options[:aka].map{|a| "add_#{a}"})
234
+ # aka_property("add_#{prop.to_s.singularize}", options[:aka].map{|a| "a#{a}"})
235
+ elsif options[:add]
236
+ module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_ADD__)", 1)
237
+ def add_#{prop.to_s.singularize}(*args, &block)
238
+ #{prop}.add(*args, &block)
239
+ end
240
+ RUBY
241
+ aka_property("add_#{prop.to_s.singularize}", options[:aka].map{|a| "add_#{a}"})
242
+ aka_property("add_#{prop.to_s.singularize}", options[:aka].map{|a| "a#{a}"})
243
+ end
244
+
245
+ if options[:remove] && options[:remove] == :scope
246
+ module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_REMOVE__)", 1)
247
+ def remove_scope(theme)
248
+ scope.remove(theme)
249
+ end
250
+ RUBY
251
+ elsif options[:remove]
252
+ module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_REMOVE__)", 1)
253
+ def remove_#{prop.to_s.singularize}(*args, &block)
254
+ #{prop}.remove(*args, &block)
255
+ end
256
+ RUBY
257
+ aka_property("remove_#{prop.to_s.singularize}", options[:aka].map{|a| "remove_#{a}"})
258
+ aka_property("remove_#{prop.to_s.singularize}", options[:aka].map{|a| "r#{a}"})
259
+ end
260
+
261
+ # TODO: aliases for property_set.add as add_property
262
+
263
+ aka_property(prop, options[:aka]) if options[:aka]
264
+ end
265
+
266
+ private
267
+ def parse_args(args, arg_defs)
268
+ a = {}
269
+ a = args.pop if args.last.is_a? Hash
270
+ # We are finished if there are no more parameters or we have no arg_def.
271
+ # Special case: non optional parameters must have been in the Hash, just a hash is also allowed.
272
+ return a if args.size == 0 || arg_defs == nil
273
+
274
+ # we have some args
275
+ if args.size == arg_defs.size # all are given
276
+ return args2hash(a, args,arg_defs)
277
+ elsif args.size == arg_defs.reject { |d| d[:optional]}
278
+ return args2hash(a, args, arg_defs.reject {|d| d[:optional]})
279
+ end
280
+ #warn("Functions with more than one optional parameter are not supported. This will only work if the left out ones are the last.")
281
+ return args2hash(a, args,arg_defs)
282
+ end
283
+
284
+ def args2hash(a, args, arg_defs)
285
+ arg_defs.zip(args) do |argd,arg|
286
+ a[argd[:name]] = arg
287
+ end
288
+
289
+ return a
290
+ end
291
+
292
+ def enhance_args_hash(a, arg_defs)
293
+ #puts "enhance_args: #{a.inspect} ---\n#{arg_defs.inspect}"
294
+ return a if a.empty?
295
+ arg_defs.each do |argd|
296
+ #puts "enhancing #{argd[:name]}, which is a #{a[argd[:name]].class}"
297
+ if argd[:type] == :Topic
298
+ if a[argd[:name]].is_a? String
299
+ a[argd[:name]] = topic_map._topic_by_locator!(a[argd[:name]])
300
+ elsif a[argd[:name]].is_a?(RTM::Topic)
301
+ a[argd[:name]] = a[argd[:name]].__getobj__
302
+ end
303
+ end
304
+ if argd[:type] == :Locator || (argd[:type].respond_to?(:include?) && argd[:type].include?(:Locator))
305
+ a[argd[:name]] = a[argd[:name]].to_s
306
+ end
307
+ end
308
+ a
309
+ end
310
+
311
+ public
312
+ def self.property(prop, options={})
313
+ module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY__)", 1)
314
+ def #{prop}
315
+ #{options[:wrap]? "#{options[:type]}.wrap":""}( __getobj__.#{prop})
316
+ end
317
+ EOS
318
+
319
+ if options[:rw]
320
+ case options[:type]
321
+ when :Topic
322
+ module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY2__)", 1)
323
+ def #{prop}=(obj)
324
+ obj = topic_map.topic_by_locator!(obj) unless obj.is_a? #{options[:type]}
325
+ __getobj__.#{prop} = obj.__getobj__
326
+ __getobj__.save
327
+ end
328
+ EOS
329
+ when :TopicMap
330
+ module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY2__)", 1)
331
+ def #{prop}=(obj)
332
+ obj = RTM.create obj.to_s unless obj.is_a? #{options[:type]}
333
+ __getobj__.#{prop} = obj.__getobj__
334
+ __getobj__.save
335
+ end
336
+ EOS
337
+ when :String, :Locator
338
+ module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY2__)", 1)
339
+ def #{prop}=(obj)
340
+ __getobj__.#{prop} = obj.to_s
341
+ __getobj__.save
342
+ end
343
+ EOS
344
+ else
345
+ raise "Don't know how to do wrapping for #{options[:type]}"
346
+ end
347
+
348
+ end
349
+
350
+ aka_property(prop, options[:aka]) if options[:aka]
351
+ end
352
+
353
+ def self.aka_property(prop, aka)
354
+ aka = [aka].flatten
355
+ aka.each do |a|
356
+ #puts "generating alias #{a} for #{prop}"
357
+ module_eval(<<-EOS, "(__AR_DELEGATOR_AKA_PROPERTY__)", 1)
358
+ alias :#{a} :#{prop}
359
+ EOS
360
+ end
361
+ end
362
+
363
+ def self.equality(eqfields, options={})
364
+ field_condition = eqfields.map {|f| "self.#{f} == o.#{f}" }.join(" && ")
365
+ module_eval(<<-EOS, "(__AR_EQUALITY__)", 1)
366
+ def ==(o)
367
+ #puts "hoho: #{"#"}{self.inspect} vs. #{"#"}{o.inspect}"
368
+ return false unless o
369
+ #{eqfields.map{|x| "#puts '#{x}'; puts self.#{x}.inspect;puts o.#{x}.inspect\n"}}
370
+ # puts '#{field_condition}'
371
+ return true if #{field_condition}
372
+ false
373
+ end
374
+ EOS
375
+ end
376
+ def eql?(o)
377
+ return false unless o
378
+ return true if self.class == o.class && self.id == o.id
379
+ false
380
+ end
381
+ def hash
382
+ return self.id.hash
383
+ end
384
+
385
+ def self.wrapper_cache
386
+ module_eval(<<-EOS, "(__AR_WRAPPER_CACHE__)", 1)
387
+ def self.wrap(obj)
388
+ return nil unless obj
389
+ raise "Double wrapping" if obj.respond_to?(:__getobj__)
390
+ t = self.wrapped(obj)
391
+ if t
392
+ t.__setobj__(obj)
393
+ return t
394
+ end
395
+ self.new(obj)
396
+ end
397
+
398
+ def self.wrapped(unwrapped_obj)
399
+ @@wrapped ||= {}
400
+ return @@wrapped[unwrapped_obj.id] if unwrapped_obj.respond_to? :id
401
+ @@wrapped[unwrapped_obj]
402
+ end
403
+ def self.reset_wrapped
404
+ @@wrapped = {}
405
+ end
406
+ def initialize(*args)
407
+ super
408
+ @@wrapped ||= {}
409
+ @@wrapped[self.id]=self
410
+ end
411
+ EOS
412
+ end
413
+
414
+ alias :i :id
415
+ delegate :reload
416
+ end
417
+ end