# Copyright: Copyright 2009 Topic Maps Lab, University of Leipzig. # License: Apache License, Version 2.0 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 options[:reload] ||= sym==:remove ? true : false # dirty hack to get a bit of reloading after delete module_eval(<<-EOS, "(__TMDELEGATOR__)", 1) def #{sym}(*args, &block) __getobj__.send(:#{to}, *args, &block) #{options[:reload] ? "parent.__getobj__.reload" :""} 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} Construct.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 # see if we have a custom filtering method if self.respond_to?(:filter_#{prop}) res = filter_#{prop}(*args) # tries a custom query, returns false if it doesn't know how to handle args return res if res # return if successful, go on otherwise end # no custom filtering, use regular one #puts args.inspect # TODO enhance/fix condition transform code a1 = args.first unless a1.is_a?(Hash) a1 = topic_map.get(a1) || {} end a = {}.merge(a1) if a[:type] && !a[:ttype] a[:ttype] = a[:type] a.delete(:type) end puts "#{"#"}{self.class}:#{prop} -- #{"#"}{args.first.inspect}" puts puts a.inspect b = {} a.each do |k,v| if RTM::AR::TMDM::#{options[:type]}.columns.map{|c| c.name}.include?(k) puts "#{options[:type]} hat spalte: #{"#"}{k}" else puts "#{options[:type]} hat NICHT spalte: #{"#"}{k}" b[k] = v a.delete(k) end end a.each do |k,v| if v.respond_to?(:__getobj__) a[(k.to_s + "_id").to_sym] = v.id end end res = #{options[:type]}s.wrap(__getobj__.#{prop}.find(:all, :conditions=> a), self, :#{options[:type]}) if args.size == 1 a1 = args.first res = res.select{|t| t} end res 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) #puts "#{ options[:create]}" #puts args.inspect arg_defs = #{ (options[:create_args] || nil ).inspect} a = parse_args(args, arg_defs) #raise "could not parse parameters:" + a.inspect + " from " + args.inspect if a.empty? 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? # if a.respond_to?(:__getobj__) # a = a.__getobj__ # end obj = #{options[:type]}.wrap( __getobj__.#{prop}.new(a) ) yield obj obj.save else # if a.respond_to?(:__getobj__) # a = a.__getobj__ # end # raise a.inspect unless a.is_a?(RTM::AR::TMDM::Topic) obj = #{options[:type]}.wrap( __getobj__.#{prop}.create(a) ) end self.reload obj end EOS if options[:create_aka] aka_property("create_#{options[:create]}", options[:create_aka]) end end if options[:add] && options[:add] == :scope module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_ADD__)", 1) def add_scope(theme) if theme.is_a?(Array) theme.each {|s| add_scope(s)} end scope.add(theme.__getobj__) end RUBY # elsif options[:add] && (options[:add] == :subject_identifier || options[:add] == :subject_locator) # module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_ADD__)", 1) # def add_#{prop.to_s.singularize}(*args, &block) # puts "adding something to #{prop}: #{"#"}{args.inspect}" # obj.#{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}"}) elsif 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] && options[:remove] == :scope module_eval(<<-RUBY, "(__AR_DELEGATOR_PROPERTY_SET_REMOVE__)", 1) def remove_scope(theme) scope.remove(theme) end RUBY elsif 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.equality(eqfields, options={}) field_condition = eqfields.map {|f| "self.#{f} == o.#{f}" }.join(" && ") module_eval(<<-EOS, "(__AR_EQUALITY__)", 1) def ==(o) #puts "hoho: #{"#"}{self.inspect} vs. #{"#"}{o.inspect}" return false unless o #{eqfields.map{|x| "#puts '#{x}'; puts self.#{x}.inspect;puts o.#{x}.inspect\n"}} # puts '#{field_condition}' 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