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,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