require 'rtm' # Yep, this is the easter egg I told of on the website. # See the source code at the very end of this file for an explanation. # Pleace note, this is not beta code, not even alpha - it's just an experiment. module ActiveTopicMaps class Base def self.inherited(klass) klass.send(:include, ActiveTopicMaps::Topic) end end module Topic def self.included(klass) klass.extend(ClassMethods) end def initialize(topic) @topic = topic end def topic;@topic;end module ClassMethods def acts_as_topic self.class_eval <<-EOS def method_missing(name, *args) @topic.send(name, *args) end alias atm_old_respond_to? respond_to? def respond_to?(*args) atm_old_respond_to?(*args) || @topic.respond_to?(*args) end alias old_methods methods def methods @methods ||= (old_methods + @topic.methods).uniq end EOS end # def acts_smart # self.class_eval <<-EOS # alias atm_old_method_missing method_missing # def method_missing(name, *args) # if @topic.respond_to?(name) # @topic.send(name, *args) # else # begin # if name =~ /=$/ # smart_setter(name, *args) # else # smart_getter(name, *args) # end # rescue # atm_old_method_missing(name, *args) # end # end # end # # def smart_getter(name, *args) # val = instance_variable_set(("smart_prop_" + name.to_s).to_sym, smart_prop_name) # prefix = "" # smart_prop_name ||= (iname = @topic["#{prefix}#{name}"].first) ? iname.value : nil # instance_variable_set(name, smart_prop_name) # end # # def smart_setter(name, *args) # # end # # alias atm_old_respond_to? respond_to? # def respond_to?(*args) # atm_old_respond_to?(*args) || @topic.respond_to?(*args) # end # EOS # end def name(name, type=nil) type ||= name prop_getter(name, type, "-") prop_setter(name, type, "-") end def names(name, type=nil) type ||= name.singularize prop_getters(name, type, "-") prop_adderremover(name, type, "-") end def occurrence(name, type=nil) type ||= name prop_getter(name, type) prop_setter(name, type) end def occurrences(name, type=nil) type ||= name.singularize prop_getters(name, type) prop_adderremover(name, type) end def prop_getter(name, type, prefix="") self.class_eval <<-EOS def #{name} @#{name} ||= (iname = @topic["#{prefix}#{type}"].first) ? iname.value : nil end EOS end def prop_getters(name, type, prefix="") self.class_eval <<-EOS def #{name} @#{name} ||= @topic["#{prefix}#{type}"].map{|n| n.value} end EOS end def prop_setter(name, type, prefix="") self.class_eval <<-EOS def #{name}=(name) old = @topic["#{prefix}#{type}"].first @#{name} = if old old.value = name else @topic["#{prefix}#{type}"] = name end end EOS end def prop_adderremover(name, type, prefix="") self.class_eval <<-EOS def add_#{name.singularize}(name) @topic["#{prefix}#{type}"] = name end def remove_#{name.singularize}(name) @topic["#{prefix}#{type}"].each {|item| item.remove if item.value == name} end EOS end def has_many(name, rt1, at, rt2, klass) eval(<<-EOS) class RTM::AR::Topic index_property_set :#{name}, :type => :Topic, :rule => { :transitive => true, :role_type => "#{rt1}", :association_type => "#{at}", :association_arity => 2, :other_role_type => "#{rt2}", :add => "#{name.to_s.singularize}", } end EOS self.class_eval <<-EOS def #{name} @topic.#{name}.map{|o| #{klass}.new(o)} end def add_#{name.to_s.singularize}(o) @topic.add_#{name.to_s.singularize}(o.topic) end def remove_#{name.to_s.singularize}(o) @topic.remove_#{name.to_s.singularize}(o.topic) end EOS end def topic_map(base_uri_or_tm) @tm = base_uri_or_tm.is_a?(RTM::TopicMap) ? base_uri_or_tm : RTM.create(base_uri_or_tm) end def create(ref) t = @tm.get!(ref) if @psi @ty ||= @tm.get!(@psi) t.add_type @ty unless t.types.include? @ty end self.new(t) end alias get! create def find(ref) t = @tm.get(ref) self.new(t) if t end alias get find def find_by_type(ref=nil) ref ||= @psi ty = @tm.get(ref) ty.instances.map {|t| self.new(t)} end def psi(ref) @psi = ref end end end end #require 'activetopicmaps' #RTM.connect # #class Person < ActiveTopicMaps::Base # topic_map "urn:/base" # psi "person" # # name :name # occurrence :age # occurrence :shoesize, "http://rtm.rubyforge.org/psi/shoesize" # this tells ActiveTM to use this as occurrence type # # def to_s # a = " (#{age})" if age # "#{name}#{a}" # end #end # #class Country # include ActiveTopicMaps::Topic # # topic_map "urn:/base" # psi "country" # # acts_as_topic # # name :name # occurrence :population # occurrence :size # # has_many :inhabitants, "country", "country-inhabitant", "inhabitant", :Person # # def to_s # "There is a country called #{name} with #{population} inhabitants and a size of #{size} km^2.\n" + # "These are some of the inhabitants: #{inhabitants.map{|i| i.to_s}.join(", ")}." # end #end # ## create a new object with a (new?) topic #p = Person.create("max") # ## set name, age, shoesize #p.name = "Max Mustermann" #p.age = 30 #p.shoesize = 44 # european ;) # ## print out the name #puts p.name # ## create a new object, using a topic (might be queried before, ... you know) #h = Person.new(RTM[0].get!("hans")) # ## set name #h.name = "Hans Maier" ## set another name (overwrites the old one! this is different from topic["-name"] !!!) #h.name = "Hans Meyer" # ## don't know what that does, maybe you? #puts h.name # ## Norway is the item identifier, here #no=Country.create("Norway") # #no.name="Norway" #no.population = "4.743.000" #no.size = "385.199" # ## use real person objects here #no.add_inhabitant h #no.add_inhabitant p # ## use this nice to_s method we provided above #puts no.to_s # ## find hans #puts "Trying to find Hans: #{Person.find("hans")}" ## won't find otto #puts "Trying to find Otto: #{Person.find("otto")}"