rdf-virtuoso 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,104 @@
1
+ require 'api_smith'
2
+ require 'rdf'
3
+
4
+ module RDF
5
+ module Virtuoso
6
+ class Repository
7
+ include APISmith::Client
8
+
9
+ RESULT_JSON = 'application/sparql-results+json'.freeze
10
+ RESULT_XML = 'application/sparql-results+xml'.freeze
11
+
12
+ class Parser::SparqlJson < HTTParty::Parser
13
+ SupportedFormats.merge!({ RESULT_JSON => :json })
14
+ end
15
+
16
+ class ClientError < StandardError; end
17
+ class MalformedQuery < ClientError; end
18
+ class NotAuthorized < ClientError; end
19
+ class ServerError < StandardError; end
20
+
21
+ # TODO: Look at issues with HTTParty Connection reset
22
+ #persistent
23
+ maintain_method_across_redirects true
24
+
25
+ attr_reader :username, :password, :uri, :auth_method
26
+
27
+ def initialize(uri, opts={})
28
+ self.class.base_uri uri
29
+ @uri = uri
30
+ @username = opts[:username] ||= nil
31
+ @password = opts[:password] ||= nil
32
+ @auth_method = opts[:auth_method] ||= 'digest'
33
+ end
34
+
35
+ READ_METHODS = %w(select ask construct describe)
36
+ WRITE_METHODS = %w(query insert insert_data update delete delete_data create drop clear)
37
+
38
+ READ_METHODS.each do |m|
39
+ define_method m do |*args|
40
+ response = api_get *args
41
+ end
42
+ end
43
+
44
+ WRITE_METHODS.each do |m|
45
+ define_method m do |*args|
46
+ response = api_post *args
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def check_response_errors(response)
53
+ case response.code
54
+ when 401
55
+ raise NotAuthorized.new
56
+ when 400
57
+ raise MalformedQuery.new(response.parsed_response)
58
+ when 500..599
59
+ raise ServerError.new(response.body)
60
+ end
61
+ end
62
+
63
+ def headers
64
+ { 'Accept' => [RESULT_JSON, RESULT_XML].join(', ') }
65
+ end
66
+
67
+ def base_query_options
68
+ { :format => 'json' }
69
+ end
70
+
71
+ def base_request_options
72
+ { :headers => headers }
73
+ end
74
+
75
+ def extra_request_options
76
+ case @auth_method
77
+ when 'basic'
78
+ { :basic_auth => auth }
79
+ when 'digest'
80
+ { :digest_auth => auth }
81
+ end
82
+ end
83
+
84
+ def auth
85
+ { :username => @username, :password => @password }
86
+ end
87
+
88
+ def api_get(query, options = {})
89
+ self.class.endpoint 'sparql'
90
+ get '/', :extra_query => { :query => query }.merge(options),
91
+ :transform => RDF::Virtuoso::Parser::JSON
92
+ end
93
+
94
+ def api_post(query, options = {})
95
+ self.class.endpoint 'sparql-auth'
96
+ post '/', :extra_body => { :query => query }.merge(options),
97
+ :extra_request => extra_request_options,
98
+ :response_container => [
99
+ "results", "bindings", 0, "callret-0", "value"]
100
+ end
101
+
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,5 @@
1
+ module RDF
2
+ module Virtuoso
3
+ VERSION = "0.0.11"
4
+ end
5
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ class Resource < ActiveRDF::Model
4
+ end
5
+
6
+ describe ActiveRDF::Persistence do
7
+
8
+ let(:resource) { Resource.new }
9
+
10
+ before do
11
+ resource.stub(:id).and_return("some_unique_id")
12
+ client = double("client")
13
+ resource.stub(:connection).and_return client
14
+ end
15
+
16
+ describe :destroy do
17
+
18
+ it "formats the destroy query correctly" do
19
+ subject = resource.subject_for(resource.id)
20
+ query =
21
+ <<-q
22
+ DELETE FROM <#{resource.graph}> { <#{subject}> ?p ?o }
23
+ WHERE { <#{subject}> ?p ?o }
24
+ q
25
+
26
+ resource.connection.should_receive(:delete).with(query)
27
+ resource.destroy
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,48 @@
1
+ require_relative '../lib/rdf/virtuoso/prefixes'
2
+ require 'rdf'
3
+
4
+ describe RDF::Virtuoso::Prefixes do
5
+ subject { RDF::Virtuoso::Prefixes.new(foo: 'bar', baz: 'quux') }
6
+
7
+ it "takes a hash when initialized" do
8
+ subject.should be_a RDF::Virtuoso::Prefixes
9
+ end
10
+
11
+ it "responds to to_a" do
12
+ subject.should respond_to :to_a
13
+ end
14
+
15
+ it "returns a nice array" do
16
+ subject.to_a.should == ["foo: <bar>", "baz: <quux>"]
17
+ end
18
+
19
+ it "presents itself nicely" do
20
+ subject.to_s.should == "{:foo=>\"bar\", :baz=>\"quux\"}"
21
+ end
22
+
23
+ context "when creating prefixes" do
24
+ let(:uris) { [RDF::DC.title.to_s, "http://example.org/foo/bar", "http://hash.org/foo#bar"] }
25
+
26
+ it "creates prefixes from uris" do
27
+ RDF::Virtuoso::Prefixes.parse(uris).should == [
28
+ "purl: <http://purl.org/dc/terms/>",
29
+ "example: <http://example.org/foo/>",
30
+ "hash: <http://hash.org/foo#>"
31
+ ]
32
+ end
33
+
34
+ it "only creates unique prefixes from uris" do
35
+ uris << 'http://example.org/foo/baz'
36
+ RDF::Virtuoso::Prefixes.parse(uris).should == [
37
+ "purl: <http://purl.org/dc/terms/>",
38
+ "example: <http://example.org/foo/>",
39
+ "hash: <http://hash.org/foo#>"
40
+ ]
41
+ end
42
+
43
+ it "returns an error object if a disallowed param is sent" do
44
+ RDF::Virtuoso::Prefixes.parse({}).should be_a RDF::Virtuoso::UnProcessable
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,278 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe RDF::Virtuoso::Query do
4
+ before :each do
5
+ @query = RDF::Virtuoso::Query
6
+ end
7
+
8
+ context "when building queries" do
9
+ it "should support ASK queries" do
10
+ @query.should respond_to(:ask)
11
+ end
12
+
13
+ it "should support SELECT queries" do
14
+ @query.should respond_to(:select)
15
+ end
16
+
17
+ it "should support DESCRIBE queries" do
18
+ @query.should respond_to(:describe)
19
+ end
20
+
21
+ it "should support CONSTRUCT queries" do
22
+ @query.should respond_to(:construct)
23
+ end
24
+
25
+ it "should support INSERT DATA queries" do
26
+ @query.should respond_to(:insert_data)
27
+ end
28
+
29
+ it "should support INSERT WHERE queries" do
30
+ @query.should respond_to(:insert)
31
+ end
32
+
33
+ it "should support DELETE DATA queries" do
34
+ @query.should respond_to(:delete_data)
35
+ end
36
+
37
+ it "should support DELETE WHERE queries" do
38
+ @query.should respond_to(:delete)
39
+ end
40
+
41
+ it "should support CREATE GRAPH queries" do
42
+ @query.should respond_to(:create)
43
+ end
44
+
45
+ end
46
+
47
+
48
+ context "when building update queries" do
49
+ before :each do
50
+ @graph = "http://example.org/"
51
+ @uri = RDF::Vocabulary.new "http://example.org/"
52
+ end
53
+ # TODO add support for advanced inserts (moving copying between different graphs)
54
+ it "should support INSERT DATA queries" do
55
+ @query.insert_data([@uri.ola, @uri.type, @uri.something]).graph(RDF::URI.new(@graph)).to_s.should == "INSERT DATA INTO GRAPH <#{@graph}> { <#{@graph}ola> <#{@graph}type> <#{@graph}something> . }"
56
+ @query.insert_data([@uri.ola, @uri.name, "two words"]).graph(RDF::URI.new(@graph)).to_s.should == "INSERT DATA INTO GRAPH <#{@graph}> { <#{@graph}ola> <#{@graph}name> \"two words\" . }"
57
+ end
58
+
59
+ it "should support INSERT DATA queries with arrays" do
60
+ @query.insert_data([@uri.ola, @uri.type, @uri.something],[@uri.ola, @uri.type, @uri.something_else]).graph(RDF::URI.new(@graph)).to_s.should == "INSERT DATA INTO GRAPH <#{@graph}> { <#{@graph}ola> <#{@graph}type> <#{@graph}something> . <#{@graph}ola> <#{@graph}type> <#{@graph}something_else> . }"
61
+ end
62
+
63
+ it "should support INSERT DATA queries with RDF::Statements" do
64
+ statements = [RDF::Statement.new(RDF::URI('http://test'), RDF.type, RDF::URI('http://type')), RDF::Statement.new(RDF::URI('http://test'), RDF.type, RDF::URI('http://type2'))]
65
+ @query.insert_data(statements).graph(RDF::URI.new(@graph)).to_s.should == "INSERT DATA INTO GRAPH <#{@graph}> { <http://test> <#{RDF.type}> <http://type> .\n <http://test> <#{RDF.type}> <http://type2> .\n }"
66
+ end
67
+
68
+ it "should support INSERT WHERE queries with symbols and patterns" do
69
+ @query.insert([:s, :p, :o]).graph(RDF::URI.new(@graph)).where([:s, :p, :o]).to_s.should == "INSERT INTO GRAPH <#{@graph}> { ?s ?p ?o . } WHERE { ?s ?p ?o . }"
70
+ @query.insert([:s, @uri.newtype, :o]).graph(RDF::URI.new(@graph)).where([:s, @uri.type, :o]).to_s.should == "INSERT INTO GRAPH <#{@graph}> { ?s <#{@graph}newtype> ?o . } WHERE { ?s <#{@graph}type> ?o . }"
71
+ end
72
+
73
+ it "should support DELETE DATA queries" do
74
+ @query.delete_data([@uri.ola, @uri.type, @uri.something]).graph(RDF::URI.new(@graph)).to_s.should == "DELETE DATA FROM <#{@graph}> { <#{@graph}ola> <#{@graph}type> <#{@graph}something> . }"
75
+ @query.delete_data([@uri.ola, @uri.name, RDF::Literal.new("myname")]).graph(RDF::URI.new(@graph)).to_s.should == "DELETE DATA FROM <#{@graph}> { <#{@graph}ola> <#{@graph}name> \"myname\" . }"
76
+ end
77
+
78
+ it "should support DELETE DATA queries with arrays" do
79
+ @query.delete_data([@uri.ola, @uri.type, @uri.something],[@uri.ola, @uri.type, @uri.something_else]).graph(RDF::URI.new(@graph)).to_s.should == "DELETE DATA FROM <#{@graph}> { <#{@graph}ola> <#{@graph}type> <#{@graph}something> . <#{@graph}ola> <#{@graph}type> <#{@graph}something_else> . }"
80
+ end
81
+
82
+ it "should support DELETE DATA queries with RDF::Statements" do
83
+ statements = [RDF::Statement.new(RDF::URI('http://test'), RDF.type, RDF::URI('http://type')), RDF::Statement.new(RDF::URI('http://test'), RDF.type, RDF::URI('http://type2'))]
84
+ @query.delete_data(statements).graph(RDF::URI.new(@graph)).to_s.should == "DELETE DATA FROM <#{@graph}> { <http://test> <#{RDF.type}> <http://type> .\n <http://test> <#{RDF.type}> <http://type2> .\n }"
85
+ end
86
+
87
+ it "should support DELETE WHERE queries with symbols and patterns" do
88
+ @query.delete([:s, :p, :o]).graph(RDF::URI.new(@graph)).where([:s, :p, :o]).to_s.should == "DELETE FROM <#{@graph}> { ?s ?p ?o . } WHERE { ?s ?p ?o . }"
89
+ @query.delete([:s, @uri.newtype, :o]).graph(RDF::URI.new(@graph)).where([:s, @uri.newtype, :o]).to_s.should == "DELETE FROM <#{@graph}> { ?s <#{@graph}newtype> ?o . } WHERE { ?s <#{@graph}newtype> ?o . }"
90
+ end
91
+
92
+ it "should support CREATE GRAPH queries" do
93
+ @query.create(RDF::URI.new(@graph)).to_s.should == "CREATE GRAPH <#{@graph}>"
94
+ @query.create(RDF::URI.new(@graph), :silent => true).to_s.should == "CREATE SILENT GRAPH <#{@graph}>"
95
+ end
96
+
97
+ it "should support DROP GRAPH queries" do
98
+ @query.drop(RDF::URI.new(@graph)).to_s.should == "DROP GRAPH <#{@graph}>"
99
+ @query.drop(RDF::URI.new(@graph), :silent => true).to_s.should == "DROP SILENT GRAPH <#{@graph}>"
100
+
101
+ end
102
+
103
+ end
104
+
105
+ context "when building ASK queries" do
106
+ it "should support basic graph patterns" do
107
+ @query.ask.where([:s, :p, :o]).to_s.should == "ASK WHERE { ?s ?p ?o . }"
108
+ @query.ask.whether([:s, :p, :o]).to_s.should == "ASK WHERE { ?s ?p ?o . }"
109
+ end
110
+ end
111
+
112
+ context "when building SELECT queries" do
113
+ it "should support basic graph patterns" do
114
+ @query.select.where([:s, :p, :o]).to_s.should == "SELECT * WHERE { ?s ?p ?o . }"
115
+ end
116
+
117
+ it "should support projection" do
118
+ @query.select(:s).where([:s, :p, :o]).to_s.should == "SELECT ?s WHERE { ?s ?p ?o . }"
119
+ @query.select(:s, :p).where([:s, :p, :o]).to_s.should == "SELECT ?s ?p WHERE { ?s ?p ?o . }"
120
+ @query.select(:s, :p, :o).where([:s, :p, :o]).to_s.should == "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }"
121
+ end
122
+
123
+ it "should support SELECT with complex WHERE patterns" do
124
+ @query.select.where(
125
+ [:s, :p, :o],
126
+ [:s, RDF.type, RDF::DC.Document]
127
+ ).to_s.should ==
128
+ "SELECT * WHERE { ?s ?p ?o . ?s <#{RDF.type}> <#{RDF::DC.Document}> . }"
129
+ end
130
+
131
+ it "should support string objects in SPARQL queries" do
132
+ @query.select.where([:s, :p, "dummyobject"]).to_s.should == "SELECT * WHERE { ?s ?p \"dummyobject\" . }"
133
+ end
134
+
135
+ #it "should support raw string SPARQL queries" do
136
+ # q = "SELECT * WHERE { ?s <#{RDF.type}> ?o . }"
137
+ # @query.query(q).should == "SELECT * WHERE { ?s <#{RDF.type}> ?o . }"
138
+ #end
139
+
140
+ it "should support FROM" do
141
+ uri = "http://example.org/dft.ttl"
142
+ @query.select.from(RDF::URI.new(uri)).where([:s, :p, :o]).to_s.should ==
143
+ "SELECT * FROM <#{uri}> WHERE { ?s ?p ?o . }"
144
+ end
145
+
146
+ it "should support DISTINCT" do
147
+ @query.select(:s, :distinct => true).where([:s, :p, :o]).to_s.should == "SELECT DISTINCT ?s WHERE { ?s ?p ?o . }"
148
+ @query.select(:s).distinct.where([:s, :p, :o]).to_s.should == "SELECT DISTINCT ?s WHERE { ?s ?p ?o . }"
149
+ end
150
+
151
+ it "should support REDUCED" do
152
+ @query.select(:s, :reduced => true).where([:s, :p, :o]).to_s.should == "SELECT REDUCED ?s WHERE { ?s ?p ?o . }"
153
+ @query.select(:s).reduced.where([:s, :p, :o]).to_s.should == "SELECT REDUCED ?s WHERE { ?s ?p ?o . }"
154
+ end
155
+
156
+ it "should support aggregate COUNT" do
157
+ @query.select.where([:s, :p, :o]).count(:s).to_s.should == "SELECT (COUNT (?s) AS ?count) WHERE { ?s ?p ?o . }"
158
+ @query.select.count(:s).where([:s, :p, :o]).to_s.should == "SELECT (COUNT (?s) AS ?count) WHERE { ?s ?p ?o . }"
159
+ end
160
+
161
+ it "should support aggregates SUM, MIN, MAX, AVG, SAMPLE" do
162
+ @query.select.where([:s, :p, :o]).sum(:s).to_s.should == "SELECT (SUM (?s) AS ?sum) WHERE { ?s ?p ?o . }"
163
+ @query.select.where([:s, :p, :o]).min(:s).to_s.should == "SELECT (MIN (?s) AS ?min) WHERE { ?s ?p ?o . }"
164
+ @query.select.where([:s, :p, :o]).max(:s).to_s.should == "SELECT (MAX (?s) AS ?max) WHERE { ?s ?p ?o . }"
165
+ @query.select.where([:s, :p, :o]).avg(:s).to_s.should == "SELECT (AVG (?s) AS ?avg) WHERE { ?s ?p ?o . }"
166
+ @query.select.where([:s, :p, :o]).sample(:s).to_s.should == "SELECT (sql:SAMPLE (?s) AS ?sample) WHERE { ?s ?p ?o . }"
167
+ @query.select.where([:s, :p, :o]).group_concat(:s, '_').to_s.should == "SELECT (sql:GROUP_CONCAT (?s, '_' ) AS ?group_concat) WHERE { ?s ?p ?o . }"
168
+ @query.select.where([:s, :p, :o]).group_digest(:s, '_', 1000, 1).to_s.should == "SELECT (sql:GROUP_DIGEST (?s, '_', '1000', '1' ) AS ?group_digest) WHERE { ?s ?p ?o . }"
169
+ end
170
+
171
+ it "should support ORDER BY" do
172
+ @query.select.where([:s, :p, :o]).order_by(:o).to_s.should == "SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o"
173
+ @query.select.where([:s, :p, :o]).order_by('?o').to_s.should == "SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o"
174
+ # @query.select.where([:s, :p, :o]).order_by(:o => :asc).to_s.should == "SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o ASC"
175
+ @query.select.where([:s, :p, :o]).order_by('?o ASC').to_s.should == "SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o ASC"
176
+ # @query.select.where([:s, :p, :o]).order_by(:o => :desc).to_s.should == "SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o DESC"
177
+ @query.select.where([:s, :p, :o]).order_by('?o DESC').to_s.should == "SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o DESC"
178
+ end
179
+
180
+ it "should support OFFSET" do
181
+ @query.select.where([:s, :p, :o]).offset(100).to_s.should == "SELECT * WHERE { ?s ?p ?o . } OFFSET 100"
182
+ end
183
+
184
+ it "should support LIMIT" do
185
+ @query.select.where([:s, :p, :o]).limit(10).to_s.should == "SELECT * WHERE { ?s ?p ?o . } LIMIT 10"
186
+ end
187
+
188
+ it "should support OFFSET with LIMIT" do
189
+ @query.select.where([:s, :p, :o]).offset(100).limit(10).to_s.should == "SELECT * WHERE { ?s ?p ?o . } OFFSET 100 LIMIT 10"
190
+ @query.select.where([:s, :p, :o]).slice(100, 10).to_s.should == "SELECT * WHERE { ?s ?p ?o . } OFFSET 100 LIMIT 10"
191
+ end
192
+
193
+ it "should support PREFIX" do
194
+ prefixes = ["dc: <http://purl.org/dc/elements/1.1/>", "foaf: <http://xmlns.com/foaf/0.1/>"]
195
+ @query.select.prefix(prefixes[0]).prefix(prefixes[1]).where([:s, :p, :o]).to_s.should ==
196
+ "PREFIX #{prefixes[0]} PREFIX #{prefixes[1]} SELECT * WHERE { ?s ?p ?o . }"
197
+ end
198
+
199
+ it "constructs PREFIXes" do
200
+ prefixes = RDF::Virtuoso::Prefixes.new dc: RDF::DC, foaf: RDF::FOAF
201
+ @query.select.prefixes(prefixes).where([:s, :p, :o]).to_s.should ==
202
+ "PREFIX dc: <#{RDF::DC}> PREFIX foaf: <#{RDF::FOAF}> SELECT * WHERE { ?s ?p ?o . }"
203
+ end
204
+
205
+ it "should support custom PREFIXes in hash array" do
206
+ prefixes = RDF::Virtuoso::Prefixes.new foo: "http://foo.com/", bar: "http://bar.net"
207
+ @query.select.prefixes(prefixes).where([:s, :p, :o]).to_s.should ==
208
+ "PREFIX foo: <http://foo.com/> PREFIX bar: <http://bar.net> SELECT * WHERE { ?s ?p ?o . }"
209
+ end
210
+
211
+ it "should support OPTIONAL" do
212
+ @query.select.where([:s, :p, :o]).optional([:s, RDF.type, :o], [:s, RDF::DC.abstract, :o]).to_s.should ==
213
+ "SELECT * WHERE { ?s ?p ?o . OPTIONAL { ?s <#{RDF.type}> ?o . ?s <#{RDF::DC.abstract}> ?o . } }"
214
+ end
215
+
216
+ it "should support multiple OPTIONALs" do
217
+ @query.select.where([:s, :p, :o]).optional([:s, RDF.type, :o]).optional([:s, RDF::DC.abstract, :o]).to_s.should ==
218
+ "SELECT * WHERE { ?s ?p ?o . OPTIONAL { ?s <#{RDF.type}> ?o . } OPTIONAL { ?s <#{RDF::DC.abstract}> ?o . } }"
219
+ end
220
+
221
+ it "should support MINUS, also with an array pattern" do
222
+ @query.select.where([:s, :p, :o]).minus([:s, RDF.type, :o], [:s, RDF::DC.abstract, :o]).to_s.should ==
223
+ "SELECT * WHERE { ?s ?p ?o . MINUS { ?s <#{RDF.type}> ?o . ?s <#{RDF::DC.abstract}> ?o . } }"
224
+ end
225
+
226
+ it "should support multiple MINUSes" do
227
+ @query.select.where([:s, :p, :o]).minus([:s, RDF.type, :o]).minus([:s, RDF::DC.abstract, :o]).to_s.should ==
228
+ "SELECT * WHERE { ?s ?p ?o . MINUS { ?s <#{RDF.type}> ?o . } MINUS { ?s <#{RDF::DC.abstract}> ?o . } }"
229
+ end
230
+
231
+ it "should support UNION" do
232
+ @query.select.where([:s, RDF::DC.abstract, :o]).union([:s, RDF.type, :o]).to_s.should ==
233
+ "SELECT * WHERE { { ?s <#{RDF::DC.abstract}> ?o . } UNION { ?s <#{RDF.type}> ?o . } }"
234
+ end
235
+
236
+ it "should support FILTER" do
237
+ @query.select.where([:s, RDF::DC.abstract, :o]).filter('lang(?text) != "nb"').to_s.should ==
238
+ "SELECT * WHERE { ?s <#{RDF::DC.abstract}> ?o . FILTER(lang(?text) != \"nb\") }"
239
+ end
240
+
241
+ it "should support multiple FILTERs" do
242
+ filters = ['lang(?text) != "nb"', 'regex(?uri, "^https")']
243
+ @query.select.where([:s, RDF::DC.abstract, :o]).filters(filters).to_s.should ==
244
+ "SELECT * WHERE { ?s <#{RDF::DC.abstract}> ?o . FILTER(lang(?text) != \"nb\") FILTER(regex(?uri, \"^https\")) }"
245
+ end
246
+
247
+ end
248
+
249
+ context "when building DESCRIBE queries" do
250
+ it "should support basic graph patterns" do
251
+ @query.describe.where([:s, :p, :o]).to_s.should == "DESCRIBE * WHERE { ?s ?p ?o . }"
252
+ end
253
+
254
+ it "should support projection" do
255
+ @query.describe(:s).where([:s, :p, :o]).to_s.should == "DESCRIBE ?s WHERE { ?s ?p ?o . }"
256
+ @query.describe(:s, :p).where([:s, :p, :o]).to_s.should == "DESCRIBE ?s ?p WHERE { ?s ?p ?o . }"
257
+ @query.describe(:s, :p, :o).where([:s, :p, :o]).to_s.should == "DESCRIBE ?s ?p ?o WHERE { ?s ?p ?o . }"
258
+ end
259
+
260
+ it "should support RDF::URI arguments" do
261
+ uris = ['http://www.bbc.co.uk/programmes/b007stmh#programme', 'http://www.bbc.co.uk/programmes/b00lg2xb#programme']
262
+ @query.describe(RDF::URI.new(uris[0]),RDF::URI.new(uris[1])).to_s.should ==
263
+ "DESCRIBE <#{uris[0]}> <#{uris[1]}>"
264
+ end
265
+ end
266
+
267
+ context "when building CONSTRUCT queries" do
268
+ it "should support basic graph patterns" do
269
+ @query.construct([:s, :p, :o]).where([:s, :p, :o]).to_s.should == "CONSTRUCT { ?s ?p ?o . } WHERE { ?s ?p ?o . }"
270
+ end
271
+
272
+ it "should support complex constructs" do
273
+ @query.construct([:s, :p, :o], [:s, :q, RDF::Literal.new("new")]).where([:s, :p, :o], [:s, :q, "old"]).to_s.should == "CONSTRUCT { ?s ?p ?o . ?s ?q \"new\" . } WHERE { ?s ?p ?o . ?s ?q \"old\" . }"
274
+ end
275
+
276
+
277
+ end
278
+ end