1. Linked Open Data for Rubyists
What the Semantic Web brings to Ruby
Gregg Kellogg
gregg@greggkellogg.net
Twitter: @gkellogg
G+: greggkellogg
2. Why RDF for Ruby?
✤ Major creative force in Web 2.0
✤ Rich eco-system (Gems/Rails/…)
✤ Fair support for XML (Nokogiri, anyway)
✤ Great environment for prototyping and getting
stuff done.
✤ RDF is relatively virgin territory in Ruby
6 Dec 2012 Ruby SemWeb 2
5. Simple Graph Manipulation
require 'rdf'
include RDF
g = Graph.new
g << Statement.new(
RDF::URI.new("https://github.com/gkellogg/rdf"),
RDF::URI.new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
RDF::URI.new("http://usefulinc.com/ns/doap#GitRepository"))
# Using common vocabularies
proj = Node.new
g << Statement.new(proj, RDF.type, DOAP.Project)
g << Statement.new(proj, DOAP.repository,
RDF::URI.new("https://github.com/gkellogg/rdf"))
puts g.dump(:ntriples)
6 Dec 2012 Ruby SemWeb 5
6. Serializing with Writers
✤ Graphs can be serialized with available Writers
require 'rdf/ntriples'
require 'rdf/turtle'
puts NTriples::Writer.buffer {|writer| writer << g}
# Also, you can include other formats
Turtle::Writer.buffer {|writer| writer << g}
# Use Graph.dump or Writer.open to save to a file
puts g.dump(:ttl, :standard_prefixes => true)
Turtle::Writer.open('example2.ttl') {|w| w << g}
puts File.read('example2.ttl')
6 Dec 2012 Ruby SemWeb 6
7. RDFa Serialization with Haml
✤ RDFa writer uses Haml templates write a graph to
HTML
➡ Define your own templates
➡ Other examples from Structured Data Linter:
➡ http://linter.structured-data.org
➡ https://github.com/structured-data/linter/blob/master/lib/rdf/linter/
rdfa_template.rb
✤ In principle, this can be used to write any XML-
based format by defining an appropriate template
✤ More information in RDFa gem
6 Dec 2012 Ruby SemWeb 7
8. Finding Formats
✤ Find a format for reading or writing
require 'rdf/rdfa'
require 'rdf/rdfxml'
Writer.for(:ttl)
Writer.for(:content_type => "text/html")
Reader.for('example2.ttl')
# List available formats
RDF::Format.to_a.map(&:to_sym)
# Open a URL and use format detection to find a writer
puts Graph.load('http://greggkellogg.net/foaf').
dump(:ttl, :base_uri => 'http://greggkellogg.net/foaf',
:standard_prefixes => true)
f = "http://greggkellogg.net/github-lod/doap.ttl"
Turtle::Reader.open(f) do |reader|
reader.each {|st| puts st.inspect}
end
6 Dec 2012 Ruby SemWeb 8
9. BGP Query support
f = "http://greggkellogg.net/github-lod/doap.ttl"
doap = Graph.load(f)
✤ Query with
# using RDF::Query
query = Query.new(
RDF::Query
:person => {
RDF.type => FOAF.Person,
FOAF.name => :name,
FOAF.mbox => :email,
})
query.execute(doap).each do |soln|
puts "name: #{soln.name}, email: #{soln[:email]}"
end; nil
# using Query::Pattern
query = Query.new do
pattern [:project, DOAP.developer, :person]
pattern [:person, FOAF.name, :name]
end
query.execute(doap).each do |soln|
puts "project: #{soln.project} name: #{soln.name}"
end; nil
6 Dec 2012 Ruby SemWeb 9
10. SPARQL
require 'sparql' ❖ SPARQL gem
f = "./dumps/github-lod.nt" executes locally
doap = Graph.load(f)
for against
query = SPARQL.parse(%q( RDF::Queryable
PREFIX doap: <http://usefulinc.com/ns/doap#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/> object.
SELECT ?repo ?name
❖ SPARQL::Client
WHERE { gem executes
[ a doap:Project;
doap:name ?repo;
against a remote
doap:developer [ a foaf:Person; repository
foaf:name ?name
]
➡ Best for querying
] large datasets.
}
ORDER BY DESC(?repo)
LIMIT 20
))
query.execute(doap).each do |soln|
puts "project: #{soln.repo} name: #{soln.name}"
end; nil
10
11. RDF Behavior
✤ Classes can behave like RDF
➡ RDF::Countable – #empty?, #count, #size
➡ RDF::Durable
➡ RDF::Enumerable – must implement #each
– #statements, #each, #triples, #quads, ...
➡ RDF::Writable – must implement #(insert/delete/each)_statement
– #load, #insert, #<<, #update, #delete
➡ RDF::Queryable – must implement #each
➡ should implement #query_pattern & #query_execute
– #query, #first, #first_(subject,predicate,object)
– RDF::TypeCheck – raises TypeError on illegal comparison
6 Dec 2012 Ruby SemWeb 11
12. ActiveRecord with RDF
✤ Import #RDF::Enumerable and implement #each
require 'github-api-client'
class GitHub::User
include RDF::Enumerable
def each
u = RDF::URI("http://github.com/#{login}")
yield RDF::Statement.new(u, RDF::FOAF.name, name)
yield RDF::Statement.new(u, RDF::mbox, RDF::URI("mailto:#{email}")) unless email.nil?
end
end
u = GitHub::User.get('gkellogg')
puts u.dump(:ttl, :standard_prefixes => true)
6 Dec 2012 Ruby SemWeb 12
13. Other Resources
✤ Spira
➡ Get Ruby classes from RDF datastores
✤ Distiller
➡ Transform between RDF formats, including RDFa generation
➡ http://rdf.greggkellogg.net
✤ Documentation
➡ Comprehensive documentation of Ruby LinkedData related gems available at
http://rdf.greggkellogg.net/yard/index.html
✤ GitHub LOD Demo
➡ Examples used in this presentation, along with a demo Sinatra application
illustrating Ruby RDF usage is available:
➡ GitHub project: http://github.com/gkellogg/github-lod (Public Domain)
➡ Running demo: http://greggkellogg.net/github-lod
6 Dec 2012 Ruby SemWeb 13