-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor Extended Bounded Description to own class
Creates an `ExtendedBoundedDescription` class to provide an `RDF::Enumerable`/`RDF::Queryable` running directly over the source graph. This avoids the need to copy statements into an array and enumerate them multiple times.
- Loading branch information
Tom Johnson
committed
Jul 26, 2016
1 parent
809b7cf
commit 8963a31
Showing
4 changed files
with
184 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
module ActiveTriples | ||
## | ||
# Bounds the scope of an `RDF::Queryable` to a subgraph defined from a source | ||
# graph, a starting node, and a list of "ancestors" terms, by the following | ||
# process: | ||
# | ||
# Include in the subgraph: | ||
# 1. All statements in the source graph where the subject of the statement | ||
# is the starting node. | ||
# 2. Add the starting node to the ancestors list. | ||
# 2. Recursively, for all statements already in the subgraph, include in | ||
# the subgraph the Extended Bounded Description for each object node, | ||
# unless the object is in the ancestors list. | ||
# | ||
# The list of "ancestors" is empty by default. | ||
# | ||
# This subgraph this process yields can be considered as a description of | ||
# the starting node. | ||
# | ||
# Compare to Concise Bounded Description | ||
# (https://www.w3.org/Submission/CBD/), the common subgraph scope used for | ||
# SPARQL DESCRIBE queries. | ||
# | ||
# @note this implementation requires that the `source_graph` remain unchanged | ||
# while iterating over the description. The safest way to achive this is to | ||
# use an immutable `RDF::Dataset` (e.g. a `Repository#snapshot`). | ||
class ExtendedBoundedDescription | ||
include RDF::Enumerable | ||
include RDF::Queryable | ||
|
||
## | ||
# @!attribute ancestors [r] | ||
# @return Array<RDF::Term> | ||
# @!attribute source_graph [r] | ||
# @return RDF::Queryable | ||
# @!attribute starting_node [r] | ||
# @return RDF::Term | ||
attr_reader :ancestors, :source_graph, :starting_node | ||
|
||
## | ||
# By analogy to Concise Bounded Description. | ||
# | ||
# @param source_graph [RDF::Queryable] | ||
# @param starting_node [RDF::Term] | ||
# @param ancestors [Array<RDF::Term>] default: [] | ||
def initialize(source_graph, starting_node, ancestors = []) | ||
@source_graph = source_graph | ||
@starting_node = starting_node | ||
@ancestors = ancestors | ||
end | ||
|
||
## | ||
# @see RDF::Enumerable#each | ||
def each_statement | ||
ancestors = @ancestors.dup | ||
|
||
if block_given? | ||
statements = source_graph.query(subject: starting_node).each | ||
statements.each_statement { |st| yield st } | ||
|
||
ancestors << starting_node | ||
|
||
statements.each_object do |object| | ||
next if object.literal? || ancestors.include?(object) | ||
ExtendedBoundedDescription | ||
.new(source_graph, object, ancestors).each do |statement| | ||
yield statement | ||
end | ||
end | ||
end | ||
enum_statement | ||
end | ||
alias_method :each, :each_statement | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
spec/active_triples/util/extended_bounded_description_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# frozen_string_literal: true | ||
require "spec_helper" | ||
require 'rdf/spec/enumerable' | ||
require 'rdf/spec/queryable' | ||
require 'active_triples/util/extended_bounded_description' | ||
|
||
describe ActiveTriples::ExtendedBoundedDescription do | ||
subject { described_class.new(source_graph, starting_node, ancestors) } | ||
|
||
let(:ancestors) { [] } | ||
let(:source_graph) { RDF::Repository.new } | ||
let(:starting_node) { RDF::Node.new } | ||
|
||
it { is_expected.not_to be_mutable } | ||
|
||
shared_examples 'a bounded description' do | ||
before do | ||
source_graph.insert(*included_statements) | ||
source_graph.insert(*excluded_statements) | ||
end | ||
|
||
it 'projects over a bounded description' do | ||
expect(subject).to contain_exactly(*included_statements) | ||
end | ||
|
||
it 'can iterate repeatedly' do | ||
expect(subject).to contain_exactly(*included_statements) | ||
expect(subject).to contain_exactly(*included_statements) | ||
end | ||
|
||
it 'is queryable' do | ||
expect(subject.query([nil, nil, nil])) | ||
.to contain_exactly(*included_statements) | ||
end | ||
end | ||
|
||
## | ||
# *** We don't pass these RDF::Spec examples. | ||
# They try to test boring things, like having the triples we put in. | ||
# | ||
# @see lib/rdf/spec/enumerable.rb in rdf-spec | ||
# it_behaves_like 'an RDF::Enumerable' do | ||
# let(:graph) { RDF::Repository.new.insert(*statements) } | ||
# let(:statements) { RDF::Spec.quads } | ||
# | ||
# let(:enumerable) do | ||
# ActiveTriples::ExtendedBoundedDescription | ||
# .new(graph, statements.first.subject) | ||
# end | ||
# end | ||
# | ||
# @see lib/rdf/spec/queryable.rb in rdf-spec | ||
# it_behaves_like 'an RDF::Queryable' do | ||
# let(:graph) { RDF::Repository.new.insert(*statements) } | ||
# let(:statements) { RDF::Spec.quads } | ||
# | ||
# let(:queryable) do | ||
# ActiveTriples::ExtendedBoundedDescription | ||
# .new(graph, statements.first.subject) | ||
# end | ||
# end | ||
# | ||
# *** end boring stuff | ||
## | ||
|
||
let(:included_statements) do | ||
[RDF::Statement(starting_node, RDF::URI('p1'), 'o'), | ||
RDF::Statement(starting_node, RDF::URI('p2'), 0), | ||
RDF::Statement(starting_node, RDF::URI('p3'), :o1), | ||
RDF::Statement(:o1, RDF::URI('p4'), :o2), | ||
RDF::Statement(:o1, RDF::URI('p5'), 'w0w'), | ||
RDF::Statement(:o2, RDF::URI('p6'), :o1)] | ||
end | ||
|
||
let(:excluded_statements) do | ||
[RDF::Statement(RDF::URI('s1'), RDF::URI('p1'), 'o'), | ||
RDF::Statement(:s2, RDF::URI('p2'), 0), | ||
RDF::Statement(:s3, RDF::URI('p3'), :o), | ||
RDF::Statement(:s3, RDF::URI('p3'), starting_node)] | ||
end | ||
|
||
it_behaves_like 'a bounded description' | ||
|
||
context 'with ancestors' do | ||
before do | ||
included_statements << | ||
RDF::Statement(starting_node, RDF::URI('a'), ancestor) | ||
|
||
excluded_statements << | ||
RDF::Statement(ancestor, RDF::URI('a'), starting_node) | ||
end | ||
|
||
let(:ancestor) { RDF::Node.new(:ancestor) } | ||
let(:ancestors) { [ancestor] } | ||
|
||
it_behaves_like 'a bounded description' | ||
end | ||
end |