require 'rdf/spec' module RDF_Queryable extend RSpec::SharedContext include RDF::Spec::Matchers before :each do raise '+@queryable+ must be defined in a before(:each) block' unless instance_variable_get('@queryable') @doap = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'etc', 'doap.nq')) @doaps = RDF::NQuads::Reader.new(File.open(@doap)).to_a @statements = @doaps if @queryable.empty? if @queryable.respond_to?(:<<) @doaps.each { |statement| @queryable << statement } else raise "@queryable must respond to #<< or be pre-populated with the statements in #{@doap} in a before(:each) block" end end @subject = RDF::URI('http://rubygems.org/gems/rdf') end describe RDF::Queryable do ## # @see RDF::Queryable#query describe "#query" do it "should respond to #query" do @queryable.should respond_to(:query) end context "when called" do it "requires an argument" do lambda { @queryable.query }.should raise_error(ArgumentError) end it "accepts a pattern argument" do lambda { @queryable.query(RDF::Query::Pattern.new(nil, nil, nil)) }.should_not raise_error(ArgumentError) lambda { @queryable.query(RDF::Query::Pattern.new(:s, :p, :o)) }.should_not raise_error(ArgumentError) end it "accepts a statement argument" do lambda { @queryable.query(RDF::Statement.new(nil, nil, nil)) }.should_not raise_error(ArgumentError) end it "accepts a triple argument" do lambda { @queryable.query([nil, nil, nil]) }.should_not raise_error(ArgumentError) end it "accepts a quad argument" do lambda { @queryable.query([nil, nil, nil, nil]) }.should_not raise_error(ArgumentError) end it "accepts a hash argument" do lambda { @queryable.query({}) }.should_not raise_error(ArgumentError) end it "does not alter a given hash argument" do query = {:subject => @subject, :predicate => RDF::DOAP.name, :object => RDF::FOAF.Person} original_query = query.dup @queryable.query(query) query.should == original_query end it "rejects other kinds of arguments" do lambda { @queryable.query(nil) }.should raise_error(ArgumentError) end context "with a block" do it "yields statements" do @queryable.query([nil, nil, nil]) do |statement| statement.should be_a_statement end end end context "without a block" do it "returns an enumerator" do @queryable.query([nil, nil, nil]).should be_an_enumerator end it "returns an enumerable enumerator" do @queryable.query([nil, nil, nil]).should be_enumerable end it "returns a queryable enumerator" do @queryable.query([nil, nil, nil]).should be_queryable end it "returns statements" do @queryable.query([nil, nil, nil]).each do |statement| statement.should be_a_statement end end it "returns the correct number of results for array queries" do @queryable.query([nil, nil, nil]).size.should == @statements.size @queryable.query([@subject, nil, nil]).size.should == File.readlines(@doap).grep(/^/).size @queryable.query([RDF::URI("http://ar.to/#self"), nil, nil]).size.should == File.readlines(@doap).grep(/^/).size @queryable.query([@subject, RDF::DOAP.name, nil]).size.should == 1 @queryable.query([nil, nil, RDF::DOAP.Project]).size.should == 1 end it "returns the correct number of results for hash queries" do @queryable.query({}).size.should == @statements.size @queryable.query(:subject => @subject) .size.should == File.readlines(@doap).grep(/^/).size @queryable.query(:subject => @subject, :predicate => RDF::DOAP.name).size.should == 1 @queryable.query(:object => RDF::DOAP.Project).size.should == 1 end end context "with specific patterns from SPARQL" do context "triple pattern combinations" do it "?s p o" do @queryable.query(:predicate => RDF::URI("http://example.org/p"), :object => RDF::Literal.new(1)).to_a.should == [RDF::Statement.new(RDF::URI("http://example.org/xi1"), RDF::URI("http://example.org/p"), 1), RDF::Statement.new(RDF::URI("http://example.org/xi2"), RDF::URI("http://example.org/p"), 1)] end it "s ?p o" do @queryable.query(:subject => RDF::URI("http://example.org/xi2"), :object => RDF::Literal.new(1)).to_a.should == [RDF::Statement.new(RDF::URI("http://example.org/xi2"), RDF::URI("http://example.org/p"), 1)] end end # From data-r2/expr-equals context "data/r2/expr-equals" do context "graph-1" do before(:each) do @solutions = @queryable.query(:predicate => RDF::URI("http://example.org/p"), :object => RDF::Literal::Integer.new(1)).to_a end it "has two solutions" do @solutions.count.should == 2 end it "has xi1 as a solution" do @solutions.any? {|s| s.subject == RDF::URI("http://example.org/xi1")}.should be_true end it "has xi2 as a solution" do @solutions.any? {|s| s.subject == RDF::URI("http://example.org/xi2")}.should be_true end end context "graph-2" do before(:each) do @solutions = @queryable.query(:predicate => RDF::URI("http://example.org/p"), :object => RDF::Literal::Double.new("1.0e0")).to_a end it "has one solution" do @solutions.count.should == 1 end it "has xd1 as a solution" do @solutions.any? {|s| s.subject == RDF::URI("http://example.org/xd1")}.should be_true end end end end end end ## # @see RDF::Queryable#query_pattern describe "#query_pattern" do it "defines a protected #query_pattern method" do @queryable.class.protected_method_defined?(:query_pattern).should be_true end context "when called" do it "requires an argument" do lambda { @queryable.send(:query_pattern) }.should raise_error(ArgumentError) end it "yields to the given block" do called = false @queryable.send(:query_pattern, RDF::Query::Pattern.new) do |statement| called = true break end called.should be_true end it "yields statements" do @queryable.send(:query_pattern, RDF::Query::Pattern.new) do |statement| statement.should be_a_statement end end context "with specific patterns" do # Note that "01" should not match 1, per data-r2/expr-equal/sameTerm { [RDF::URI("http://example.org/xi1"), RDF::URI("http://example.org/p"), 1] => [RDF::Statement.from([RDF::URI("http://example.org/xi1"), RDF::URI("http://example.org/p"), 1])], [RDF::URI("http://example.org/xi1"), RDF::URI("http://example.org/p"), nil] => [RDF::Statement.from([RDF::URI("http://example.org/xi1"), RDF::URI("http://example.org/p"), 1])], [RDF::URI("http://example.org/xi1"), nil, 1] => [RDF::Statement.from([RDF::URI("http://example.org/xi1"), RDF::URI("http://example.org/p"), 1])], [nil, RDF::URI("http://example.org/p"), 1] => [RDF::Statement.from([RDF::URI("http://example.org/xi1"), RDF::URI("http://example.org/p"), 1]), RDF::Statement.from([RDF::URI("http://example.org/xi2"), RDF::URI("http://example.org/p"), 1])], [nil, nil, 1] => [RDF::Statement.from([RDF::URI("http://example.org/xi1"), RDF::URI("http://example.org/p"), 1]), RDF::Statement.from([RDF::URI("http://example.org/xi2"), RDF::URI("http://example.org/p"), 1])], [nil, RDF::URI("http://example.org/p"), RDF::Literal::Double.new("1.0e0")] => [RDF::Statement.from([RDF::URI("http://example.org/xd1"), RDF::URI("http://example.org/p"), RDF::Literal::Double.new("1.0e0")])], }.each do |pattern, result| pattern = RDF::Query::Pattern.from(pattern) it "returns #{result.inspect} given #{pattern.inspect}" do solutions = [] @queryable.send(:query_pattern, pattern) {|s| solutions << s} solutions.should == result end end end context "with context" do it "returns statements from all contexts with no context" do pattern = RDF::Query::Pattern.new(nil, nil, nil, :context => nil) solutions = [] @queryable.send(:query_pattern, pattern) {|s| solutions << s} solutions.size.should == @statements.size end it "returns statements from unnamed contexts with false context" do pattern = RDF::Query::Pattern.new(nil, nil, nil, :context => false) solutions = [] @queryable.send(:query_pattern, pattern) {|s| solutions << s} context_statements = @queryable.statements.reject {|st| st.has_context?}.length solutions.size.should == context_statements end it "returns statements from named contexts with variable context" do unless @queryable.contexts.to_a.empty? pattern = RDF::Query::Pattern.new(nil, nil, nil, :context => :c) solutions = [] @queryable.send(:query_pattern, pattern) {|s| solutions << s} context_statements = @queryable.statements.select {|st| st.has_context?}.length solutions.size.should == context_statements end end it "returns statements from specific context with URI context" do unless @queryable.contexts.to_a.empty? pattern = RDF::Query::Pattern.new(nil, nil, nil, :context => RDF::URI("http://ar.to/#self")) solutions = [] @queryable.send(:query_pattern, pattern) {|s| solutions << s} solutions.size.should == File.readlines(@doap).grep(/^/).size end end end end end ## # @see RDF::Queryable#first describe "#first" do before :all do @failing_pattern = [RDF::Node.new] * 3 end it "should respond to #first" do @queryable.should respond_to(:first) end it "returns enumerator without a pattern" do lambda { @queryable.first }.should_not raise_error(ArgumentError) @queryable.first.should == @queryable.each.first # uses an Enumerator end it "returns the correct value when the pattern matches", :pending => (defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java') do matching_patterns = [[nil, nil, nil], @queryable.each.first] matching_patterns.each do |matching_pattern| @queryable.first(matching_pattern).should == @queryable.query(matching_pattern).each.first end end it "returns nil when the pattern fails to match anything" do @queryable.first(@failing_pattern).should be_nil end it "returns nil when self is empty" do queryable = [].extend(RDF::Queryable) queryable.first.should be_nil queryable.first(@failing_pattern).should be_nil end end ## # @see RDF::Queryable#first_subject describe "#first_subject" do before :all do @failing_pattern = [RDF::Node.new, nil, nil] end it "should respond to #first_subject" do @queryable.should respond_to(:first_subject) end it "returns enumerator without a pattern", :pending => (defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java') do lambda { @queryable.first_subject }.should_not raise_error(ArgumentError) @queryable.first_subject.should == @queryable.first.subject end it "returns the correct value when the pattern matches", :pending => (defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java') do matching_patterns = [[nil, nil, nil], [@queryable.first.subject, nil, nil]] matching_patterns.each do |matching_pattern| @queryable.first_subject(matching_pattern).should == @queryable.query(matching_pattern).first.subject end end it "returns nil when the pattern fails to match anything" do @queryable.first_subject(@failing_pattern).should be_nil end it "returns nil when self is empty" do queryable = [].extend(RDF::Queryable) queryable.first_subject.should be_nil queryable.first_subject(@failing_pattern).should be_nil end end ## # @see RDF::Queryable#first_predicate describe "#first_predicate" do before :all do @failing_pattern = [nil, RDF::Node.new, nil] end it "should respond to #first_predicate" do @queryable.should respond_to(:first_predicate) end it "returns enumerator without a pattern", :pending => (defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java') do lambda { @queryable.first_predicate }.should_not raise_error(ArgumentError) @queryable.first_predicate.should == @queryable.first.predicate end it "returns the correct value when the pattern matches", :pending => (defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java') do matching_patterns = [[nil, nil, nil], [nil, @queryable.first.predicate, nil]] matching_patterns.each do |matching_pattern| @queryable.first_predicate(matching_pattern).should == @queryable.query(matching_pattern).first.predicate end end it "returns nil when the pattern fails to match anything" do @queryable.first_predicate(@failing_pattern).should be_nil end it "returns nil when self is empty" do queryable = [].extend(RDF::Queryable) queryable.first_predicate.should be_nil queryable.first_predicate(@failing_pattern).should be_nil end end ## # @see RDF::Queryable#first_object describe "#first_object" do before :all do @failing_pattern = [nil, nil, RDF::Node.new] end it "should respond to #first_object" do @queryable.should respond_to(:first_object) end it "returns enurator without a pattern", :pending => (defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java') do lambda { @queryable.first_object }.should_not raise_error(ArgumentError) @queryable.first_object.should == @queryable.first.object end it "returns the correct value when the pattern matches", :pending => (defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java') do matching_patterns = [[nil, nil, nil], [nil, nil, @queryable.first.object]] matching_patterns.each do |matching_pattern| @queryable.first_object(matching_pattern).should == @queryable.query(matching_pattern).first.object end end it "returns nil when the pattern fails to match anything" do @queryable.first_object(@failing_pattern).should be_nil end it "returns nil when self is empty" do queryable = [].extend(RDF::Queryable) queryable.first_object.should be_nil queryable.first_object(@failing_pattern).should be_nil end end ## # @see RDF::Queryable#first_literal it "returns to #first_literal" do @queryable.should respond_to(:first_literal) end describe "#first_literal" do before :each do # FIXME: these tests should be using the provided @queryable, if possible. @queryable = RDF::Graph.new do |graph| @subject = RDF::Node.new graph << [@subject, RDF.type, RDF::DOAP.Project] graph << [@subject, RDF::DC.creator, RDF::URI.new('http://example.org/#jhacker')] graph << [@subject, RDF::DC.creator, @literal = RDF::Literal.new('J. Random Hacker')] end @failing_pattern = [nil, nil, RDF::Node.new] end it "returns a literal without a pattern" do lambda { @queryable.first_literal }.should_not raise_error(ArgumentError) @queryable.first_literal.should == @literal end it "returns the correct value when the pattern matches" do matching_patterns = [[nil, nil, nil], [@subject, nil, nil], [nil, RDF::DC.creator, nil], [nil, nil, @literal]] matching_patterns.each do |matching_pattern| @queryable.first_literal(matching_pattern).should == @literal end end it "returns nil when the pattern fails to match anything" do @queryable.first_literal(@failing_pattern).should be_nil end it "returns nil when self is empty" do queryable = [].extend(RDF::Queryable) queryable.first_literal.should be_nil queryable.first_literal(@failing_pattern).should be_nil end end ## # @see RDF::Queryable#first_value describe "#first_value" do before :all do @failing_pattern = [nil, nil, RDF::Node.new] end it "should respond to #first_value" do @queryable.should respond_to(:first_value) end it "returns first literal without a pattern" do lambda { @queryable.first_value }.should_not raise_error(ArgumentError) @queryable.first_value.should == @queryable.first_literal.value end it "returns the correct value when the pattern matches" do matching_patterns = [] @queryable.each do |statement| if statement.object.is_a?(RDF::Literal) matching_pattern = [statement.subject, statement.predicate, nil] unless matching_patterns.include?(matching_pattern) matching_patterns << matching_pattern @queryable.first_value(matching_pattern).should == @queryable.first_literal(matching_pattern).value end end end end it "returns nil when the pattern fails to match anything" do @queryable.first_value(@failing_pattern).should be_nil end it "returns nil when self is empty" do queryable = [].extend(RDF::Queryable) queryable.first_value.should be_nil queryable.first_value(@failing_pattern).should be_nil end end end end