module RTM::AR class TMDelegator def initialize(obj) @obj = obj end def __getobj__ @obj end def __setobj__(obj) @obj = obj end def self.delegate(sym, options={}) to = options[:to] || sym module_eval(<<-EOS, "(__TMDELEGATOR__)", 1) def #{sym}(*args, &block) __getobj__.send(:#{to}, *args, &block) end EOS if options[:rw] module_eval(<<-EOS, "(__TMDELEGATOR2__)", 1) def #{sym}=(*args, &block) __getobj__.send(:#{to}=, *args, &block) #{options[:save] ? "__getobj__.save" : "" } end EOS end if options[:aka] options[:aka] = [options[:aka]] unless options[:aka].respond_to? :each options[:aka].each do |aka| module_eval(<<-EOS, "(__TMDELEGATOR3__)", 1) alias :#{aka} :#{sym} EOS if options[:rw] module_eval(<<-EOS, "(__TMDELEGATOR3__)", 1) alias :#{aka}= :#{sym}= EOS end end end end def self.class_delegate(sym, options={}) module_eval(<<-EOS, "(__TMDELEGATOR__)", 1) class << self def #{sym}(*args, &block) __getobj__.send(:#{sym}, *args, &block) end end EOS if options[:aka] options[:aka] = [options[:aka]] unless options[:aka].respond_to? :each options[:aka].each do |aka| module_eval(<<-EOS, "(__TMDELEGATOR2__)", 1) class << self alias :#{aka} :#{sym} end EOS end end end def self.parent(sym, options={}) module_eval(<<-EOS, "(__TMDELEGATOR__)", 1) def #{sym} TopicMapConstruct.wrap(__getobj__.send(:#{sym})) end EOS aka_property(sym, [:parent, :p]) end def self.property_set(prop, options={}) # puts "In #{self.name.to_s.ljust(20)} we have type #{options[:type].to_s.ljust(20)} for property #{prop}" if options[:wrap] #puts "#{self}: #{options[:type]}" module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY_SET_W__)", 1) def #{prop}(*args) if args.size == 0 # fetch normal #{options[:type]}s.wrap(__getobj__.#{prop}, self, :#{options[:type]}) else # fetch with filter #puts args.inspect # TODO enhance/fix condition transform code a = {}.merge(args.first) if a[:type] && !a[:ttype] a[:ttype] = a[:type] a.delete(:type) end a.each do |k,v| if v.respond_to?(:__getobj__) a[(k.to_s + "_id").to_sym] = v.id end end #{options[:type]}s.wrap(__getobj__.#{prop}.find(:all, :conditions=> a), self, :#{options[:type]}) end end EOS else module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY_SET_NW__)", 1) def #{prop} __getobj__.#{prop} end EOS end if options[:create] # some :create_args: # # create_locator reference:string # [ :name => :reference, :type => :String] # # create_topic_name value:String, scope:Collection # create_topic_name value:String, type: Topic, scope:Collection # [ {:name => :value, :type => :String}, {:name => :type, :type => :Topic, :optional => true}, {:name => :scope, :type => :Collection} ] # # create_occurrence value:String, type: Topic, scope:Collection # create_occurrence resource: Locator, type: Topic, scope:Collection # [ {:name => :value, :type => [:String, :Locator]}, {:name => :type, :type => :Topic}, {:name => :scope, :type => :Collection} ] # # create_association_role player:topic, type: topic # [ {:name => :player, :type => :Topic}, {:name => :type, :type => :Topic}] # # create_variant value:string, :scope:Collection # create_variant resource:locator, :scope:Collection # [ {:name => :value, :type => [:String, :Locator]}, {:name => :scope, :type => :Collection}] module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY_SET_CREATE__)", 1) def create_#{ options[:create]}(*args, &block) arg_defs = #{ (options[:create_args] || nil ).inspect} a = parse_args(args, arg_defs) a = enhance_args_hash(a, arg_defs) if arg_defs a = a.reject{|k,v| v.nil?} # hack to change :type to :ttype for AR backend # puts a.inspect unless a.is_a? Hash if a[:type] && !a[:ttype] a[:ttype] = a[:type] a.delete(:type) end if [:Locator,:ItemIdentifier, :SubjectIdentifier, :SubjectLocator].include?(:#{options[:type]}) unless a[:topic_map] a[:topic_map] = topic_map.__getobj__ end end if block_given? obj = #{options[:type]}.wrap( __getobj__.#{options[:create]}s.new(a) ) yield obj obj.save else obj = #{options[:type]}.wrap( __getobj__.#{options[:create]}s.create(a) ) end obj end EOS if options[:create_aka] aka_property("create_#{options[:create]}", options[:create_aka]) end end if options[:add] module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_ADD__)", 1) def add_#{prop.to_s.singularize}(*args, &block) #{prop}.add(*args, &block) end RUBY aka_property("add_#{prop.to_s.singularize}", options[:aka].map{|a| "add_#{a}"}) aka_property("add_#{prop.to_s.singularize}", options[:aka].map{|a| "a#{a}"}) end if options[:remove] module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_REMOVE__)", 1) def remove_#{prop.to_s.singularize}(*args, &block) #{prop}.remove(*args, &block) end RUBY aka_property("remove_#{prop.to_s.singularize}", options[:aka].map{|a| "remove_#{a}"}) aka_property("remove_#{prop.to_s.singularize}", options[:aka].map{|a| "r#{a}"}) end # TODO: aliases for property_set.add as add_property aka_property(prop, options[:aka]) if options[:aka] end private def parse_args(args, arg_defs) a = {} a = args.pop if args.last.is_a? Hash # We are finished if there are no more parameters or we have no arg_def. # Special case: non optional parameters must have been in the Hash, just a hash is also allowed. return a if args.size == 0 || arg_defs == nil # we have some args if args.size == arg_defs.size # all are given return args2hash(a, args,arg_defs) elsif args.size == arg_defs.reject { |d| d[:optional]} return args2hash(a, args, arg_defs.reject {|d| d[:optional]}) end #warn("Functions with more than one optional parameter are not supported. This will only work if the left out ones are the last.") return args2hash(a, args,arg_defs) end def args2hash(a, args, arg_defs) arg_defs.zip(args) do |argd,arg| a[argd[:name]] = arg end return a end def enhance_args_hash(a, arg_defs) #puts "enhance_args: #{a.inspect} ---\n#{arg_defs.inspect}" return a if a.empty? arg_defs.each do |argd| #puts "enhancing #{argd[:name]}, which is a #{a[argd[:name]].class}" if argd[:type] == :Topic if a[argd[:name]].is_a? String a[argd[:name]] = topic_map._topic_by_locator!(a[argd[:name]]) elsif a[argd[:name]].is_a?(RTM::Topic) a[argd[:name]] = a[argd[:name]].__getobj__ end end if argd[:type] == :Locator || (argd[:type].respond_to?(:include?) && argd[:type].include?(:Locator)) a[argd[:name]] = a[argd[:name]].to_s end end a end public def self.property(prop, options={}) module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY__)", 1) def #{prop} #{options[:wrap]? "#{options[:type]}.wrap":""}( __getobj__.#{prop}) end EOS if options[:rw] case options[:type] when :Topic module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY2__)", 1) def #{prop}=(obj) obj = topic_map.topic_by_locator!(obj) unless obj.is_a? #{options[:type]} __getobj__.#{prop} = obj.__getobj__ __getobj__.save end EOS when :TopicMap module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY2__)", 1) def #{prop}=(obj) obj = RTM.create obj.to_s unless obj.is_a? #{options[:type]} __getobj__.#{prop} = obj.__getobj__ __getobj__.save end EOS when :String, :Locator module_eval(<<-EOS, "(__AR_DELEGATOR_PROPERTY2__)", 1) def #{prop}=(obj) __getobj__.#{prop} = obj.to_s __getobj__.save end EOS else raise "Don't know how to do wrapping for #{options[:type]}" end end aka_property(prop, options[:aka]) if options[:aka] end def self.aka_property(prop, aka) aka = [aka].flatten aka.each do |a| #puts "generating alias #{a} for #{prop}" module_eval(<<-EOS, "(__AR_DELEGATOR_AKA_PROPERTY__)", 1) alias :#{a} :#{prop} EOS end end def self.index_property_set(prop, options={}) r = options[:rule] module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET__)", 1) def direct_#{prop}_roles # prefetch typing topics rtype = @ips_#{prop}_rtype ||= self.topic_map.get("#{r[:role_type]}") atype = @ips_#{prop}_atype ||= self.topic_map.get("#{r[:association_type]}") otype = @ips_#{prop}_otype ||= self.topic_map.get("#{r[:other_role_type]}") # return nothing if any of the typing topics does not exist return [] unless rtype && atype && otype # we do that only here and not earlier because some might me nil rtype = rtype.id atype = atype.id otype = otype.id self.__getobj__.roles.map{|r| r.ttype_id != nil && r.ttype_id == rtype && r.parent.roles.size == #{r[:association_arity]} && r.parent != nil && r.parent.ttype_id == atype && (r2 = r.parent.roles.select{|r2| r2.ttype_id != nil && r2.ttype_id == otype}.first) && r2}.select{|r2| r2}.map{|r2| AssociationRole.wrap(r2)} end def direct_#{prop} direct_#{prop}_roles.map{|r2| r2.player}.uniq end EOS if r[:transitive] module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET2__)", 1) def #{prop} d = todo = self.direct_#{prop} while todo.size > 0 todo = todo.map{|dt| dt.direct_#{prop}}.flatten.uniq - d d += todo end d #d2 = self.direct_#{prop}.map {|dt| dt.#{prop}}.flatten #(d+d2).uniq end EOS else if r[:infer] module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET3__)", 1) def #{prop} (self.direct_#{prop} + self.#{r[:infer]}.map{|d2| d2.direct_#{prop}}).flatten.uniq end EOS elsif r[:infer_other] module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET4__)", 1) def #{prop} d = self.direct_#{prop} (d + d.map{|d2| d2.#{r[:infer_other]}}).flatten.uniq end EOS end end if r[:add] module_eval(<<-EOS, "(__AR_DELEGATOR_INDEX_PROPERTY_SET5__)", 1) def add_#{r[:add]}(t) a = self.topic_map.create_association("#{r[:association_type]}") a.create_role self, "#{r[:role_type]}" a.create_role t, "#{r[:other_role_type]}" a # TODO add_x in index_property_set needs to trigger reload of the topics somewhere end def remove_#{r[:add]}(t) direct_#{prop}_roles.select{|r| r.player == t}.each{|r| r.parent.remove} # TODO remove_x in index_property_set needs to trigger reload of the topics somewhere end EOS end end def self.equality(eqfields, options={}) field_condition = eqfields.map {|f| "self.#{f} == o.#{f}" }.join(" && ") module_eval(<<-EOS, "(__AR_EQUALITY__)", 1) def ==(o) return false unless o return true if #{field_condition} false end EOS end def eql?(o) return false unless o return true if self.class == o.class && self.id == o.id false end def hash return self.id.hash end def self.wrapper_cache module_eval(<<-EOS, "(__AR_WRAPPER_CACHE__)", 1) def self.wrap(obj) return nil unless obj raise "Double wrapping" if obj.respond_to?(:__getobj__) t = self.wrapped(obj) if t t.__setobj__(obj) return t end self.new(obj) end def self.wrapped(unwrapped_obj) @@wrapped ||= {} return @@wrapped[unwrapped_obj.id] if unwrapped_obj.respond_to? :id @@wrapped[unwrapped_obj] end def self.reset_wrapped @@wrapped = {} end def initialize(*args) super @@wrapped ||= {} @@wrapped[self.id]=self end EOS end alias :i :id delegate :reload end end