RubyRDF first cut

I got basic query operations working, so archiving a snapshot...

--danbri

---------- Forwarded message ----------

#!/usr/local/bin/ruby
#
# RDFWeb Ruby RDF stuff
# danbri@w3.org

# Overview:
# we use the classes 'Graph', 'Node', and 'Statement'
#
# todo:
#  - associate Nodes with Graphs
#  - implement ask(template_statement) method (slog through the variations!)
#  - implement ntriples-based i/o
#  - use method_missing to catch property queries on nodes
#  - figure out how to test this and Perl version at same time
#  - document the stuff it doesn't do w.r.t. RDF specs
#  - find out about various Ruby features I'm unclear on (see 'todo:' notes)


##############################################################################
#
class Node
  @@nodes = {}
  attr_accessor :content

  # constructor (make this private? use get* instead)
  def initialize (content)
    @content = content
  end
  def inspect
    "#@content "
  end

  # Get a Node given its URI, recycling where available
  def Node.getResource(content)
    return @@nodes[content] if @@nodes[content]
    node = Node.new(content)
    @@nodes[content]=node
    return node
  end

  # get a fresh blank node
  # notes: couldn't see how to have Node.getResource() work
  def Node.getBlank()
    content = '[' + rand(1000000).to_s() # stopgap: todo, uuid
    node = Node.new(content)
    @@nodes[content]=node
  end

  def Node.getLiteral(content)
    return @@nodes[content] if @@nodes[content]
    node = Node.new("\""+content)
    @@nodes["\""+content]=node
    return node
  end

  def to_s
    "#@content"
  end
  def method_missing(methid)
    str = methid.id2name
    print "Missing method ",str,"\n" # todo: use later for rdf property access
  end
end


#############################################################################
#
class Graph
  attr_accessor :db, :tell
  def initialize( statements )
    @db = statements
    @fp={}
    @bp={}
    # print "Initializing a graph with statements ", db
    statements.each {|statement| tell(statement) }
  end

  # tell the graph something

  def tell ( statement )

    # print "\n\nAdding and indexing statement: ", statement


    # store objects under subject+predicate
    #
    sp_list = @fp["#{statement.subject} | #{statement.predicate}"]
    if (sp_list)
      # puts "Storing object under EXISTING s/p, statement= #{statement}"
      # puts "Inspecting existing sp_list: #{sp_list.inspect} \n"
      sp_list.push(statement.object) #todo: we should order this list (and po)
    end
    if (!sp_list)
      # puts "Storing object under NEW s/p, statement = #{statement}"
      sp_list = [statement.object]
      @fp["#{statement.subject} | #{statement.predicate}"]=sp_list
    end

    # store subjects under predicate+object
    #
    po_list = @bp["#{statement.predicate} | #{statement.object}"]
    if (po_list)
      # puts "Storing subject under EXISTING p/o, statement= #{statement}"
      po_list.push(statement.subject)
    end					# todo: lookup else syntax for Ruby
    if (!po_list)
      # puts "Storing subject under NEW p/o, statement= #{statement}"
      po_list = [statement.subject]
      @bp["#{statement.predicate} | #{statement.object}"]=po_list
    end
  end


  def toNtriples()
    out = "\nSerializing Graph as Ntriples (vapourware: not NTriples yet!):\n"

    # forward pointers -- from subject+predicate to object(s)
    @fp.each_key{ |key| out += ("FP entry: \tkey='#{key}' value='#{@fp[key]}' \n") }
    out += "Inspecting FP: #{@fp.inspect()}\n\n"

    # backward pointers -- from predicate+object to subject(s)
    @bp.each_key{ |key| out += ("IP entry: \tkey='#{key}' value='#{@bp[key]}' \n")}
    out += "Inspecting BP: #{@bp.inspect()} \n\n"

    out += "End Ntriples.\n\n"
    return out
  end

  def ask(query)
    puts "Vapourware ask/query method called, template statement: #{query} "

    # ooo
    if (query.predicate==nil && query.subject==nil && query.object==nil)
       puts "ooo: dump all triples\n"
    end

    # xxo
    if (query.predicate && query.subject && query.object==nil)
      puts "xxo: get value(s) given sp"
      # puts "subject = '#{query.subject}' predicate= '#{query.predicate}' \n"
      # puts "Answer lookup: "
     return @fp["#{query.subject} | #{query.predicate}"]
    end

    if (query.predicate && query.subject==nil && query.object)
      puts "oxx: get subjects(s) given po"
      return @fp["#{query.predicate} | #{query.object}"]
    end
  end

end

###########################################################################
#
class Statement
  attr_accessor :predicate, :subject, :object
  def initialize (subject, predicate, object)
    @subject = subject
    @predicate = predicate
    @object = object
  end
  def inspect
    "<#@subject> <#@predicate> <#@object> "
  end
  def to_s
  "Statement: #@subject> <#@predicate> <#@object>\n"
  end

end


############################################################################
#
# Examples and tests

srand() # seed randomizer

FOAF = 'http://xmlns.com/foaf/0.1/'

# Get some initial nodes and properties

# some stuff to represent dan
dbhome = Node.getResource('http://rdfweb.org/people/danbri/')
danbri = Node.getBlank()
dbmail = Node.getResource('mailto:danbri@rdfweb.org')

# Some RDF vocabulary
foaf_mbox = Node.getResource(FOAF+'mbox')
foaf_livesIn = Node.getResource(FOAF+'livesIn')
foaf_homepage = Node.getResource(FOAF+'homepage')

# some more resources we'll be mentioning (no URIs known for these)
bristol=Node.getBlank()
libby = Node.getBlank()
lib2 = Node.getBlank()
damey = Node.getBlank()

s1 = Statement.new(danbri,foaf_homepage,dbhome)
s2 = Statement.new(danbri,foaf_mbox,dbmail)
s3 = Statement.new(danbri, foaf_livesIn, Node.getLiteral("Bristol"))
s4 = Statement.new(damey, foaf_livesIn, Node.getLiteral("Bristol"))


g = Graph.new( [s1, s2, s3, s4] )	# we can initialise a graph with content
g2 = Graph.new([])		# or empty (todo: allow no args constructor)
g.tell( Statement.new(libby, foaf_livesIn, Node.getLiteral("Bristol")))

g.tell(Statement.new(libby, foaf_mbox, Node.getResource('mailto:libby.miller@bristol.ac.uk')))

sl2 = Statement.new(libby, foaf_mbox, Node.getResource('mailto:libby@rdfweb.org'))
g.tell( sl2 )
g.tell(Statement.new(damey, foaf_mbox, Node.getResource('mailto:d.m.steer@lse.ac.uk')))

puts "An RDF Graph: ", g, "\n"

puts g.toNtriples()

## Test our (non-existent) query code

# query 'xxo'
puts "Asking for mailboxes of libby:\n"
t1 = g.ask( Statement.new( libby, foaf_mbox, nil))
puts "Answer was: #{t1} \n"

puts "Asking for occupants of bristol:\n"
t2 = g.ask(Statement.new( nil, foaf_livesIn, Node.getLiteral("Bristol")))
puts "Bris occupants are: #{t2} \n"


# query is 'ooo' (all unknown)
t2 = g.ask (Statement.new(nil,nil,nil))
puts "Tripledump: #t2\n" # todo: no workie yet

Received on Saturday, 8 December 2001 09:04:31 UTC