rb_cObject
An RDF graph class
$Id$
Portions of this file (namely the graph-equivalence stuff) were ported to Ruby from the Test::RDF Perl module written by Michael Hendricks. His copyright is:
Copyright (C) 2006 Michael Hendricks <michael@palmcluster.org> This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
Copyright © 2008-2009, Michael Granger All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Return a hash describing all model types supported by the underlying Redland library.
static VALUE rleaf_redleaf_graph_s_model_types( VALUE klass )
Create a new Redleaf::Graph object. If the optional store object is given, it is used as the backing store for the graph. If none is specified a new Redleaf::MemoryHashStore is used.
static VALUE rleaf_redleaf_graph_initialize( int argc, VALUE *argv, VALUE self )
Return a Hash of supported serializers from the underlying Redland library.
Redleaf::Parser.serializers
# => { "rss-1.0" => "RSS 1.0",
# "rdfxml" => "RDF/XML",
# "json-triples" => "RDF/JSON Triples",
# "rdfxml-abbrev" => "RDF/XML (Abbreviated)",
# "rdfxml-xmp" => "RDF/XML (XMP Profile)",
# "turtle" => "Turtle",
# "ntriples" => "N-Triples",
# "json" => "RDF/JSON Resource-Centric",
# "dot" => "GraphViz DOT format",
# "atom" => "Atom 1.0" }
static VALUE rleaf_redleaf_graph_s_serializers( VALUE klass )
Returns true if the specified format is supported by the Redland backend.
# File lib/redleaf/graph.rb, line 86
86: def self::valid_format?( format )
87: Redleaf.logger.debug "Checking validity of format %p" % [ format ]
88: return self.serializers.key?( format )
89: end
Append statements to the graph, either as Redleaf::Statements, valid triples in Arrays, or a subgraph of nodes expressed in a Hash.
require 'redleaf/constants'
incude Redleaf::Constants::CommonNamespaces # (for the FOAF namespace constant)
MY_FOAF = Redleaf::Namspace.new( 'http://deveiate.org/foaf.xml#' )
michael = MY_FOAF[:me]
graph = Redleaf::Graph.new
statement1 = Redleaf::Statement.new( michael, FOAF[:family_name], 'Granger' )
statement2 = [ michael, FOAF[:givenname], 'Michael' ]
graph.append( statement1, statement2 )
graph << [ michael, FOAF[:homepage], URI('http://deveiate.org/') ]
static VALUE rleaf_redleaf_graph_append( int argc, VALUE *argv, VALUE self )
Return a string describing the status of contexts in the receiving graph. This will be a count of the contexts if they are enabled, or “contexts not enabled” if they aren’t enabled.
# File lib/redleaf/graph.rb, line 196
196: def context_info
197: if self.contexts_enabled?
198: return "%d contexts" % [ self.contexts.length ]
199: else
200: return "contexts disabled"
201: end
202: end
Returns an Array of URIs describing the contexts in the receiving graph.
static VALUE rleaf_redleaf_graph_contexts( VALUE self )
Duplicate the receiver and return the copy.
static VALUE rleaf_redleaf_graph_dup( VALUE self )
Call block once for each statement in the graph.
static VALUE rleaf_redleaf_graph_each_statement( VALUE self )
Returns true if the graph does not contain any statements.
# File lib/redleaf/graph.rb, line 101
101: def empty?
102: return self.size.zero?
103: end
Run the query in the given query string (qstring) against the graph. The query language specifies the query language, and limit, and offset can be used to limit the results. The #query method is the public interface to this method.
static VALUE rleaf_redleaf_graph_execute_query( int argc, VALUE *argv, VALUE self )
Returns true if the graph has at least one statement with the specified subject and predicate.
static VALUE rleaf_redleaf_graph_has_predicate_about_p( VALUE self, VALUE subject, VALUE predicate )
Returns true if the graph has at least one statement with the specified predicate and object.
static VALUE rleaf_redleaf_graph_has_predicate_entailing_p( VALUE self, VALUE predicate, VALUE object )
Return true if the receiver contains the specified statement, which can be either a Redleaf::Statement object or a valid triple in an Array.
static VALUE rleaf_redleaf_graph_include_p( VALUE self, VALUE statement )
Returns true if the graph contains any statements with the specified object.
static VALUE rleaf_redleaf_graph_include_object_p( VALUE self, VALUE object )
Returns true if the graph contains any statements with the specified subject.
static VALUE rleaf_redleaf_graph_include_subject_p( VALUE self, VALUE subject )
Return a human-readable representation of the object suitable for debugging.
# File lib/redleaf/graph.rb, line 184
184: def inspect
185: return "#<%s:0x%x %d statements, %s>" % [
186: self.class.name,
187: self.object_id * 2,
188: self.size,
189: self.context_info
190: ]
191: end
call-seq:
graph.is_equivalent_to?( other_graph ) -> true or false graph === other_graph -> true or false
Equivalence method — compare the receiving graph with other_graph according to the graph equivalency rules in:
http://www.w3.org/TR/rdf-concepts/#section-graph-equality
# File lib/redleaf/graph.rb, line 145
145: def is_equivalent_to?( other_graph )
146: unless other_graph.size == self.size
147: self.log.debug "Graphs differ in size (%d vs. %d)" % [ self.size, other_graph.size ]
148: return false
149: end
150:
151: other = other_graph.dup
152: bnode_map = BnodeMap.new
153: difference = nil
154:
155: difference = self.find do |stmt|
156: # If the copy of the other graph has an equivalent statement, remove it
157: # and move on to the next one.
158: if other.remove_equivalent_statement( stmt, bnode_map )
159: next
160:
161: # Otherwise, we've found a difference
162: else
163: stmt
164: end
165: end
166:
167: buf = ''
168: PP.pp( bnode_map, buf )
169: self.log.debug "Bnode map after comparison:\n%s" % [ buf ]
170:
171: if difference
172: self.log.debug "%p is not equivalent to %p because it does not contain %p" %
173: [ self, other_graph, difference ]
174: return false
175: else
176: return true
177: end
178: end
Parse the RDF at the specified uri into the receiving graph. Returns the number of statements added to the graph (if the underlying store supports ).
graph = Redleaf::Graph.new graph.load( "http://bigasterisk.com/foaf.rdf" ) graph.load( "http://www.w3.org/People/Berners-Lee/card.rdf" ) graph.load( "http://danbri.livejournal.com/data/foaf" ) graph.size
static VALUE rleaf_redleaf_graph_load( VALUE self, VALUE uri )
Return one object of a statement with the specified subject and predicate.
static VALUE rleaf_redleaf_graph_object( VALUE self, VALUE subject, VALUE predicate )
Return an Array of object nodes from the graph that have the specified subject and predicate.
static VALUE rleaf_redleaf_graph_objects( VALUE self, VALUE subject, VALUE predicate )
Return one predicate of a statement with the specified subject and object.
static VALUE rleaf_redleaf_graph_predicate( VALUE self, VALUE subject, VALUE object )
Return an Array of predicate nodes from the graph that have the specified subject and object.
static VALUE rleaf_redleaf_graph_predicates( VALUE self, VALUE subject, VALUE object )
Returns an Array of predicates (URI objects) that point from the specified subject.
static VALUE rleaf_redleaf_graph_predicates_about( VALUE self, VALUE subject )
Returns an Array of predicates (URI objects) that point to the specified object.
static VALUE rleaf_redleaf_graph_predicates_entailing( VALUE self, VALUE object )
Run a SPARQL query against the graph. The optional prefixes hash can be used to set up prefixes in the query.
require 'redleaf/constants'
include Redleaf::Constants::CommonNamespaces
# Declare a custom namespace and create a graph with a node about its title
book = Redleaf::Namespace.new( 'http://example.org/book' )
graph = Redleaf::Graph.new
graph << [ book[:book1], DC[:title], "SPARQL Tutorial" ]
qstring = 'SELECT ?title WHERE { book:book1 dc:title ?title }'
res = graph.query( qstring, :book => book, :dc => DC )
# => #<Redleaf::BindingsQueryResult:0x07466b3>
res.each do |row|
puts row.title
end
# File lib/redleaf/graph.rb, line 126
126: def query( querystring, *args )
127: prefixes = args.last.is_a?( Hash ) ? args.last : {}
128:
129:
130: prelude = prefixes.collect {|prefix, uri| "PREFIX %s: <%s>\n" % [ prefix, uri ] }.join
131: querystring = prelude + querystring
132: self.log.debug "Querystring is: %p" % [ querystring ]
133:
134: return self.execute_query( querystring )
135: end
Removes one or more statements from the graph that match the specified statement (either a Redleaf::Statement or a valid triple in an Array) and returns any that were removed.
Any nil values in the statement will match any value.
# Set a new home page for the Redleaf project, preserving the old one
# as the 'old_homepage'
stmt = graph.remove([ :Redleaf, DOAP[:homepage], nil ])
stmt.predicate = DOAP[:old_homepage]
graph.append( stmt )
graph.append([ :Redleaf, DOAP[:homepage],
URL.parse('http://deveiate.org/projects/Redleaf') ])
static VALUE rleaf_redleaf_graph_remove( VALUE self, VALUE statement )
Search for statements in the graph with the specified subject, predicate, and object and return them. If subject, predicate, or object are nil, they will match any value.
# Match any statements about authors graph.load( 'http://deveiant.livejournal.com/data/foaf' ) # graph[ nil, FOAF[:knows], nil ] # => [...]
static VALUE rleaf_redleaf_graph_search( VALUE self, VALUE subject, VALUE predicate, VALUE object )
Return the graph serialized to a String in the specified format. Valid +format+s are keys of the Hash returned by ::serializers.
The nshash argument can be used to set namespaces in the output (for serializers that support them). It should be of the form:
{ :nsname => <namespace URI> }
Examples:
turtle = graph.serialized_as( 'turtle' ) xml = graph.serialized_as( 'rdfxml-abbrev', :foaf => 'http://xmlns.com/foaf/0.1/' )
static VALUE rleaf_redleaf_graph_serialized_as( int argc, VALUE *argv, VALUE self )
Return the number of statements in the graph. If the underlying store doesn’t support fetching the size of the graph, the return value will be negative.
static VALUE rleaf_redleaf_graph_size( VALUE self )
Return an Array of all the statements in the graph.
static VALUE rleaf_redleaf_graph_statements( VALUE self )
Return the Redleaf::Store associated with the receiver.
static VALUE rleaf_redleaf_graph_store( VALUE self )
Associate the given new_store with the receiver, breaking the association between it and any previous Store.
static VALUE rleaf_redleaf_graph_store_eq( VALUE self, VALUE storeobj )
Return one subject of a statement with the specified predicate and object.
static VALUE rleaf_redleaf_graph_subject( VALUE self, VALUE predicate, VALUE object )
Return an Array of subject nodes from the graph that have the specified predicate and object.
static VALUE rleaf_redleaf_graph_subjects( VALUE self, VALUE predicate, VALUE object )
Returns true if the receiving model supports contexts.
static VALUE rleaf_redleaf_graph_supports_contexts_p( VALUE self )
Synchronise the model to the model implementation.
static VALUE rleaf_redleaf_graph_sync( VALUE self )
Proxy method — handle #to_«format» methods by invoking a serializer for the specified format.
# File lib/redleaf/graph.rb, line 225
225: def method_missing( sym, *args )
226: super unless sym.to_s =~ /^to_(\w+)$/
227:
228: format = $1.tr( '_', '-' )
229: super unless self.class.valid_format?( format )
230:
231: serializer = lambda { self.serialized_as(format) }
232:
233: # Install the closure as a new method and call it
234: self.class.send( :define_method, sym, &serializer )
235: return self.method( sym ).call
236: end
Given the specified bnode_map (a BnodeMap object which contains an equivalence mapping between blank nodes in the receiver and statement), remove the given statement from the receiver if an equivalent one exists. If an equivalent exists, return it, otherwise return nil. This was ported from Michael Hendricks’s Test::RDF Perl module. :TODO: Refactor into multiple methods
# File lib/redleaf/graph.rb, line 245
245: def remove_equivalent_statement( statement, bnode_map )
246: subject = statement.subject
247: predicate = statement.predicate
248: object = statement.object
249:
250: subject_is_floating = false
251: object_is_floating = false
252:
253: # anchor the subject if possible
254: if subject.is_a?( Symbol )
255: subject_is_floating = true
256: if mapped = bnode_map[ subject ]
257: subject = mapped
258: subject_is_floating = false
259: end
260: end
261:
262: # anchor the object if possible
263: if object.is_a?( Symbol )
264: object_is_floating = true
265: if mapped = bnode_map[ object ]
266: object = mapped
267: object_is_floating = false
268: end
269: end
270:
271: # If both the subject and object are unmapped bnodes, select the first
272: # triple with the same predicate and unmapped bnode subject and objects
273: if subject_is_floating && object_is_floating
274: self.log.debug "Anchoring a subject (%p) and an object (%p)" %
275: [ subject, object ]
276: equivalent = self[ nil, predicate, nil ].find do |stmt|
277: if stmt.subject.is_a?( Symbol ) &&
278: stmt.object.is_a?( Symbol ) &&
279: bnode_map.valid?( subject, stmt.subject ) &&
280: bnode_map.valid?( object, stmt.object )
281:
282: bnode_map[ subject ] = stmt.subject
283: bnode_map[ object ] = stmt.object
284:
285: stmt
286: end
287: end
288:
289: # If the subject is an unanchored bnode, select the first
290: # triple with the same predicate and object and an unmapped subject
291: elsif subject_is_floating
292: self.log.debug "Anchoring a subject (%p)" % [ subject ]
293: equivalent = self[ nil, predicate, object ].find do |stmt|
294: if stmt.subject.is_a?( Symbol ) &&
295: bnode_map.valid?( subject, stmt.subject )
296:
297: bnode_map[ subject ] = stmt.subject
298: stmt
299: end
300: end
301:
302: # Do the same for an unanchored object
303: elsif object_is_floating
304: self.log.debug "Anchoring an object (%p)" % [ object ]
305: equivalent = self[ subject, predicate, nil ].find do |stmt|
306: if stmt.object.is_a?( Symbol ) &&
307: bnode_map.valid?( object, stmt.object )
308:
309: bnode_map[ object ] = stmt.object
310: stmt
311: end
312: end
313:
314: # If the statement's nodes are all either mapped or not bnodes, just
315: # search for an equivalent
316: else
317: self.log.debug "Searching for an anchored statement {%p, %p, %p}" %
318: [ subject, predicate, object ]
319: equivalent = self[ subject, predicate, object ].first
320: end
321:
322: self.log.debug "Equivalent is: %p" % [ equivalent ]
323:
324: return self.remove( equivalent ).first if equivalent
325: return nil
326: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.