Skip to content

42cities/RDF-Mapper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

42 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 team@42cities.com 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

About

A Ruby ORM that is designed to play nicely with RDF data

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages