Module: ActiveRubic

Defined in:
lib/active_rubic.rb,
lib/active_rubic.rb,
lib/active_rubic/base.rb,
lib/active_rubic/cacher.rb

Overview

ActiveRubic emulates ActiveRecord and provides connections to several data stores with the objective to abstract the semantic data from various engines.

The engines are also attempted to be drop-in replaceable by offering an abstraction method find(). see ActiveRubic::Base for the API reference.

  • some of it is still undocumented!!

The supported backends currently include:

  • ActiveRecord

  • ActiveRubinstein

This module abstracts the query methods (SPARQL, graph handling, SQL, Lucene index) to parameters so that different storages can be “hot-replaced” without the need to make changes to the request.

ActiveRubinstein::Base is the API which translates queries to the servers which are supported by the Rubinstein backend, which replies to queries over the DRb bridge. This also makes possible to use Java-based repositories with JRuby without running the whole Rails stack on JRuby.

Authors

Mikael Lammentausta, Lauri Miettinen

Copyright

Copyright © 2007-2008 Savonia University of Applied Sciences

License

MIT

Examples

In this example we’ll inspect the instance of the Museum of natural history of Kuopio, Finland. Launch up the Rails console:

   $ ./script/console
   Loading development environment.
     RUBIC log/irb:  * Initializing ActiveRubic, version 0.76
     RUBIC log/irb:  => loading ActiveRubinstein
     RUBIC log/irb:  => loading ActiveRubic
     RUBIC log/irb:  * Rails environment already initialized
     DEBUG log/irb:  => including ActiveRubinstein to ActiveRubic::Base
     RUBIC log/irb:  * loading ActiveRDF extensions
      DEEP log/irb: Formulating new RDFS::Resource http://www.w3.org/2000/01/rdf-schema#Resource from String
      DEEP log/irb: Formulating new RDFS::Resource http://openlab/media#image from String
      DEEP log/irb: Formulating new RDFS::Resource http://www.w3.org/2002/07/owl#Class from String
      DEEP log/irb: Formulating new RDFS::Resource http://www.w3.org/2002/07/owl#Thing from String
      DEEP log/irb: Formulating new RDFS::Resource http://www.w3.org/2002/07/owl#ObjectProperty from String
RUBINSTEIN log/irb:  * initializing JenaEndpoint: druby://192.168.0.11:2002
     DEBUG log/irb: #<DRb::DRbObject:0xb6e0ed0c @ref=nil, @uri="druby://192.168.0.11:2002">
     DEBUG log/irb:  => reply from server: Greetings -- this is jRubinstein#JenaLuc/Picard 0.84 on 192.168.0.11:2002, PID 20471808
RUBINSTEIN log/irb:  => added #<ActiveRubinstein::JenaEndpoint:0xb6e0edfc @uri="druby://192.168.0.11:2002"> to pool
      INFO log/irb: @@jena = #<ActiveRubinstein::JenaEndpoint:0xb6e0edfc @uri="druby://192.168.0.11:2002">
      INFO log/irb: @@lucene = #<ActiveRubinstein::JenaEndpoint:0xb6e0edfc @uri="druby://192.168.0.11:2002">

If everything is fine, fire up a simple query for label:

resource = KUOPIO::kuopion_museo
 => #<RDFS::Resource:0xb6ef1fa8 @uri="http://openlab/kuopio#kuopion_museo">

resource.label
 => ["Kuopion museo"]

How does this data get from Jena to Rails’ console? Let’s inspect the entire data passage.

First of all, the label method looks like this:

class class RDFS::Resource
  def label
    if @label.nil?
      @label = my( RDFS::label )
    end
    return @label
  end
end

It calls the private ‘my’ method within the model:

private
### helper
def my( predicate )
  if predicate.is_a? String then
    predicate = RDFS::Resource.new( predicate )
  end
  begin
    @@log.debug " => find #{self.uri} #{predicate} ?"
    ActiveRubic::Base.find( predicate, :subject => self.uri, 
      :from => RUBIC_DATASTORES, 
      :lang => @language
    ) || Array.new

  rescue
    @@log.error $!
    return Array.new
  end
end

Which calls the ActiveRubic::Base find() method with the subject, required predicate, and options for used data stores as well as the desired language. The find() method and the following data flow, many lines are removed to simplify the logic behind the module:

module ActiveRubic #:nodoc:
  class Base
    class << self # Class methods
      def find(*args)
        # options:
        #  :data_stores = Array
        #  :suffice_to = Array of data_stores that are trusted to return sufficient results. By default all data stores are untrusted to be complete, and the results of each are combined. The results of servers given in the array
        #  :combine = Array of data stores that are queried and the results combined. If the trusted server returns results, trust that the contents is sufficient.
        options = extract_options_from_args!(args)

        serial_query_handler( args.first, options )
      end

      private
        # runs a query on data stores sequentially
        def serial_query_handler(query, options)
          begin
            data_stores.each do |storage|
              my_results = run_query( storage, query, options )
            end
          end
          # merge and return results
        end

        # constructs the actual queries and receives the result
        def run_query( storage, query, options )
          case storage
          when :jena
            ActiveRubinstein::JenaQuery.execute( query, options )
          end
        end
      end
    end
  end
end

The ActiveRubinstein::JenaQuery class, and ActiveRubic in general, accepts three types of ‘query’ objects:

  • Symbol for actions, eg. :parents, :children, :nearby, :properties etc.

  • RDFS::Resource (predicate) with the option :subject => uri, to fetch the object, as in the RDFS::label example

  • Query, an ActiveRDF object, as a block without the .execute call

Let’s examine the JenaQuery class:

module ActiveRubinstein #:nodoc:
  class JenaQuery < ActiveRubinstein::Base
    class << self

    public

      def execute( query, options )
        if query.is_a? Symbol or query.is_a? RDFS::Resource
          case query
            # ... process some symbols
          else # construct common SPARQL
            sparql = ActiveRubinstein::Base.construct_sparql( query, options )
            self.query( sparql, options ) if sparql
          end
        end
      end

    private

      # pretty much the most important method of all :)
      # this method sends the request to Rubinstein's DRb server.
      def method_missing(method, *args, &block)
        @@log.rubinstein "sending the request to DRb server, method #{method}"
        @@jena.send( method, *args, &block )
      end
    end
  end
end

@@jena variable is instantiated in ActiveRubinstein::Base:

module ActiveRubinstein #:nodoc:
  class Base
    def shuffle    # Initialization of servers

        $serverPool = {} unless defined? $serverPool

        RUBIC_DATASTORES.each do |adapter|
            case adapter
            when :jena
              server = JenaEndpoint.new
              if server.ping
                $serverPool.update :jena => [ server ]
                @@log.rubinstein " => added #{server.inspect} to pool"
              end
            end
        end
      end
    end

    # create the server pool, start the DRb service, and
    # init the required submodules, so that the correct service is
    # accessible as a class variable
    def initialize
      self.shuffle unless defined? $serverPool
      DRb.start_service

        RUBIC_DATASTORES.each do |adapter|
            case adapter
            ##############################
            when :jena
              # instantiate the server for the JenaQuery class
              ActiveRubinstein::JenaQuery.new
            end
        end
      end
    end
  end
end

module ActiveRubinstein #:nodoc:
  class JenaQuery < ActiveRubinstein::Base
    def initialize
      server = $serverPool[ :jena ].first
      unless server.ping
        @@log.error $!.message
      else
        @@jena = server
        @@log.info "@@jena = #{server.inspect}"
      end
    end
  end
end

Finally, here is the log of the query:

      DEEP log/irb: Formulating new RDFS::Resource http://www.w3.org/2000/01/rdf-schema#label from String
     DEBUG log/irb:  => find http://openlab/kuopio#kuopion_museo <http://www.w3.org/2000/01/rdf-schema#label> ?
     RUBIC log/irb:  * Find: <http://www.w3.org/2000/01/rdf-schema#label>, fromjenalucenesubject<http://openlab/kuopio#kuopion_museo>lang
      DEEP log/irb:   => Querying data stores: jena, lucene
      DEEP log/irb:  - Will suffice to data from lucene
     RUBIC log/irb:    => query: <http://www.w3.org/2000/01/rdf-schema#label> (RDFS::Resource)
RUBINSTEIN log/irb: Constructing SPARQL, options: data_storesjenalucenefromjenalucenesubject<http://openlab/kuopio#kuopion_museo>lang
     DEBUG log/irb:   => constructing Query to find <http://www.w3.org/2000/01/rdf-schema#label>
      DEEP log/irb: Constructing SPARQL <http://openlab/kuopio#kuopion_museo> <http://www.w3.org/2000/01/rdf-schema#label> ?
RUBINSTEIN log/irb: sending the request to DRb server, method query
     RUBIC log/irb:    => Got 1 results from jena in 0.00976 sec
     RUBIC log/irb:    => query: <http://www.w3.org/2000/01/rdf-schema#label> (RDFS::Resource)
     RUBIC log/irb:    => No results from lucene in 0.00015 sec
      DEEP log/irb:   => 1 altogether
     RUBIC log/irb:   => TOTAL: 1 unique results
      DEEP log/irb:  => Returning all 1, no limit
=> ["Kuopion museo"]

Defined Under Namespace

Classes: ActiveRubicError, AdapterNotFoundError, AdapterNotSpecifiedError, Base, Cacher, ConnectionFailedError, ConnectionNotEstablishedError, RubicNotFoundError

Constant Summary collapse

VERSION =
'0.8.2'