RDFMapper – Object-relation mapping for RDF data
RDFMapper is an ORM written in Ruby that is designed to play nicely with RDF data.
Features
-
100% Ruby code based on a slim & smart RDF.rb library
-
All the usual Rails methods: find, create, belongs_to, has_many – you name it
-
Built with performance in mind: all objects are lazy-loaded by default
-
Supports REST, SPARQL and ActiveRecord as RDF data sources
-
Supports XML, N-Triples and JSON out of the box
Installation
The prefered method of installing RDFMapper is through its gem file (requires RubyGems):
% [sudo] gem install rdf-mapper
The latest version of RDFMapper can be found at
Contribute
Please note that RDFMapper in under heavy development right now, it’s not yet suitable for production environment. Any contribution (bug tickets, code patches) is more than welcome. Email us at [email protected] or submit a ticket on GitHub.
5-minute crash course
Idea behind RDF models
Models in RDFMapper are essentially RDF nodes that have an ID and at least one triple with an rdf:type predicate. Consider the following example:
<http://example.org/people/237643> rdf:type <http://www.example.org/schema#Person>
<http://example.org/people/237643> example:name "John Smith"
<http://example.org/people/237643> example:age "27"^^xsd:integer
This set of triples defines a node (with an ID of <example.org/people/237643>) that has three ‘attributes’: ‘example:name`, `example:age`, and `rdf:type`. Now `rdf:type` predicate tells us that there’s a class (<www.example.org/schema#Person>) with more or less predefined behavior. And our node (<example.org/people/237643>) is an instance of that class. We could replicate the same logic in Ruby:
class Person
attr_accessor :id
attr_accessor :name
attr_accessor :age
end
person = Person.new
person.id = "http://example.org/people/237643"
person.name = "John Smith"
person.age = 27
That’s essentially what RDFMapper does. It accepts RDF triples (XML or N-triples), creates instances, assigns attributes and binds models together (via Rails-like belongs_to and has_many associations).
Defining a model
Before you start working with RDFMapper, you need to define at least one model. The only required setting is its namespace (think XML namespace) or type (think rdf:type). If you specify the namespace, it will be used by the model itself (to figure out its rdf:type) and by its attributes (to figure out RDF predicates).
class Person < RDFMapper::Model
namespace "http://example.org/#"
attribute :name, :type => :text
attribute :homepage, :type => :uri, :predicate => 'http://xmlns.com/foaf/0.1/homepage'
end
Person.namespace #=> #<RDF::Vocabulary(http://example.org/#)>
Person.type #=> #<RDF::URI(http://example.org/#Person)>
Person.name.type #=> #<RDF::URI(http://example.org/#name")>
Person.homepage.type #=> #<RDF::URI(http://xmlns.com/foaf/0.1/homepage)>
For more information on RDF::URI, RDF::Vocabulary and other classes within RDF namespace, refer to RDF.rb documentation.
Defining the data source
By this moment you can work with RDFMapper models with no additional settings. However, if you want to load, save and search for your objects, you need to specify their data source. RDFMapper comes with 3 different flavors of data sources: REST, SPARQL and Rails.
-
SPARQL [read-only] – the standard for RDF data. RDFMapper will query specified SPARQL server over HTTP using standard SPARQL syntax. Currently it supports only a few functions (no subqueries, updates, aggregates, etc.)
-
REST [read-only] – good old HTTP-based data storage. It assumes that an object’s ID (which is an URI) is the place to look when you want to get object’s properties. For example, if an object has an ID ‘example.org/people/237643`, RDFMapper will download data from this address and parse any RDF triples it finds along the way.
-
Rails [read/write] – gets the data from an ActiveRecord model (that is Rails model). This adapter assumes an RDFMapper model has a ‘mirror’ ActiveRecord model with the same attributes and associations.
Assigning data source to a model is easy:
class Person < RDFMapper::Model
adapter :rails # There should be a `Person` class that subclasses ActiveRecord::Base
end
class Person < RDFMapper
adapter :rails, :class_name => 'Employee' # ActiveRecord::Base model is called `Employee`
end
class Person < RDFMapper
adapter :sparql, {
:server => ‘some-sparql-server.com’ :headers => { ‘API-Key’ => ‘89d7sfd9sfs’ } }
end
Searching
If you search objects by an ID, it’s up to the adapter (REST, SPARQL, or Rails) to decide what type of ID it requires (an URI, a database column or something else). Check out the documentation for each adapter to see how works.
Person.all #=> #<PersonCollection:23784623>
Person.find('132987') #=> #<Person:217132856>
Person.find(:all, :conditions => { :name => 'John' }) #=> #<PersonCollection:32462387>
Note, the objects above are not loaded. RDFMapper will load them once you access an attribute of a collection or an object. The following 3 objects are loaded instantly, since RDFMapper needs to figure out what their attributes are (in this case ‘nil?`, `name` and `length`).
Person.find('132987').nil? #=> false
Person.find('132987').name #=> "John"
Person.find(:all, :conditions => { :name => 'John' }).length #=> 3
You should take extra care when dealing with lazy-loaded models, since exceptions may occur when a model is not found:
Person.find('132987') #=> #<Person:217132856>
Person.find('132987').name #=> NoMethodError: undefined method `name' for nil:NilClass
Instead, you should first check if a model exists:
@person = Person.find('132987')
@person.name unless @person.nil?
Working with attributes
Attributes in RDFMapper work just as you would expect them to work with just one small exception. Since any attribute of a model is essentially an RDF triple, you can access attributes by their predicates as well:
class Person < RDFMapper::Model
namespace "http://example.org/#"
attribute :name, :type => :text
attribute :homepage, :type => :uri, :predicate => 'http://xmlns.com/foaf/0.1/homepage'
end
instance = Person.new
instance.name #=> "John Smith"
instance[:name] #=> "John Smith"
instance['http://example.org/#name'] #=> "John Smith"
instance.homepage #=> #<RDF::URI(http://johnsmith.com/")>
instance['http://xmlns.com/foaf/0.1/homepage'] #=> #<RDF::URI(http://johnsmith.com/")>
That’s pretty much all you need to know. Go try and let us know what you think!
License
RDFMapper is free and unencumbered public domain software. For more information, see unlicense.org or the accompanying UNLICENSE file.
Roadmap
Several important features are not yet implemented. Here’s a rough list of what is still to be done:
-
Test coverage is extremely low (~10%)
-
Documentation coverage is mediocre
-
REST adapter is missing
-
SPARQL adapter supports only simple ‘DESCRIBE` queries. At later stages it will most likely use sparql-client library.
-
JSON support is missing. Will use rdf-json library.
-
‘has_one` and `has_and_belongs_to_many` are missing