tripod 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +1 -0
- data/.rspec +1 -0
- data/Gemfile +2 -1
- data/README.md +27 -16
- data/lib/tripod/criteria/execution.rb +100 -0
- data/lib/tripod/criteria.rb +56 -0
- data/lib/tripod/errors/bad_data_request.rb +15 -0
- data/lib/tripod/errors/bad_sparql_request.rb +15 -0
- data/lib/tripod/errors/rdf_parse_failed.rb +7 -0
- data/lib/tripod/errors/rdf_type_not_set.rb +9 -0
- data/lib/tripod/errors.rb +6 -1
- data/lib/tripod/finders.rb +21 -76
- data/lib/tripod/persistence.rb +4 -4
- data/lib/tripod/resource.rb +3 -1
- data/lib/tripod/sparql_client.rb +45 -13
- data/lib/tripod/sparql_query.rb +86 -0
- data/lib/tripod/version.rb +1 -1
- data/lib/tripod.rb +5 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/tripod/criteria_execution_spec.rb +118 -0
- data/spec/tripod/criteria_spec.rb +65 -0
- data/spec/tripod/finders_spec.rb +66 -46
- data/spec/tripod/resource_spec.rb +4 -2
- data/spec/tripod/sparql_client_spec.rb +46 -0
- data/spec/tripod/sparql_query_spec.rb +133 -0
- metadata +37 -20
data/.autotest
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "autotest/bundler"
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -55,21 +55,10 @@ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fu
|
|
55
55
|
p.important_dates = [Date.new(2011,1,1)]
|
56
56
|
p.save!
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
SELECT ?person ?graph
|
62
|
-
WHERE {
|
63
|
-
GRAPH ?graph {
|
64
|
-
?person ?p ?o .
|
65
|
-
?person a <http://person> .
|
66
|
-
}
|
67
|
-
}",
|
68
|
-
:uri_variable => 'person' ) # optionally, set a different name for the uri parameter (default: uri)
|
69
|
-
# => returns an array of Person objects, containing all data we know about them.
|
58
|
+
people = Person.all.resources #=> returns all people as an array
|
59
|
+
|
60
|
+
ric = Person.find('http://ric') #=> returns a single Person object.
|
70
61
|
|
71
|
-
ric = Person.find('http://ric')
|
72
|
-
# => returns a single Person object.
|
73
62
|
|
74
63
|
## Some Other interesting features
|
75
64
|
|
@@ -89,20 +78,42 @@ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fu
|
|
89
78
|
## Defining a graph at instantiation-time
|
90
79
|
|
91
80
|
class Resource
|
92
|
-
|
81
|
+
include Tripod::Resource
|
82
|
+
field :label, RDF::RDFS.label
|
93
83
|
|
94
84
|
# notice also that you don't need to supply an rdf type or graph here!
|
95
85
|
end
|
96
86
|
|
97
87
|
r = Resource.new('http://foo', 'http://mygraph')
|
88
|
+
r.label = "example"
|
89
|
+
r.save
|
98
90
|
|
99
|
-
# if you don't supply a graph at any point, you will get an error when you try to persist the resource.
|
91
|
+
# Note: if you don't supply a graph at any point (i.e. class or instance level), you will get an error when you try to persist the resource.
|
100
92
|
|
101
93
|
## Reading and writing arbitrary predicates
|
102
94
|
|
103
95
|
r.write_predicate(RDF.type, 'http://myresource/type')
|
104
96
|
r.read_predicate(RDF.type) # => RDF::URI.new("http://myresource/type")
|
105
97
|
|
98
|
+
## Finders and criteria
|
99
|
+
|
100
|
+
Person.all #=> returns a Tripod::Criteria which selets all resources of rdf_type http://person
|
101
|
+
|
102
|
+
Resource.all #=> returns all resources in the database (as no rdf_type specified at class level)
|
103
|
+
|
104
|
+
Person.all.resources #=> returns all the actual resources for the criteria, as an array
|
105
|
+
|
106
|
+
Person.first #=> returns the first person
|
107
|
+
|
108
|
+
Person.count #=> returns the count of all people
|
109
|
+
|
110
|
+
# note that you need to use ?uri as the variable for the subject.
|
111
|
+
Person.where("?uri <http://name> 'Joe'") #=> returns a Tripod::Criteria
|
112
|
+
|
113
|
+
## Chainable criteria
|
114
|
+
|
115
|
+
Person.all.where("?uri <http://name> 'Ric'").where("?uri <http://knows> <http://asa>).first
|
116
|
+
|
106
117
|
|
107
118
|
|
108
119
|
[Full Documentation](http://rubydoc.info/gems/tripod/frames)
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# This module defines behaviour for criteria
|
2
|
+
module Tripod
|
3
|
+
|
4
|
+
# this module provides execution methods to a criteria object
|
5
|
+
module CriteriaExecution
|
6
|
+
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
# Execute the query and return an array of all hydrated resources
|
10
|
+
def resources
|
11
|
+
resources_from_sparql(build_select_query)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Execute the query and return the first result as a hydrated resource
|
15
|
+
def first
|
16
|
+
sq = Tripod::SparqlQuery.new(build_select_query)
|
17
|
+
first_sparql = sq.as_first_query_str
|
18
|
+
resources_from_sparql(first_sparql).first
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return how many records the current criteria would return
|
22
|
+
def count
|
23
|
+
sq = Tripod::SparqlQuery.new(build_select_query)
|
24
|
+
count_sparql = sq.as_count_query_str
|
25
|
+
result = Tripod::SparqlClient::Query.select(count_sparql)
|
26
|
+
result[0][".1"]["value"].to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
# PRIVATE:
|
30
|
+
|
31
|
+
included do
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def resources_from_sparql(sparql)
|
36
|
+
uris_and_graphs = select_uris_and_graphs(sparql)
|
37
|
+
create_and_hydrate_resources(uris_and_graphs)
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_select_query
|
41
|
+
select_query = "SELECT ?uri ?graph WHERE { GRAPH ?graph { "
|
42
|
+
select_query += self.where_clauses.join(" . ")
|
43
|
+
# TODO: Deal with extras.
|
44
|
+
select_query += " } }"
|
45
|
+
end
|
46
|
+
|
47
|
+
# create and hydrate the resources identified in uris_and_graphs.
|
48
|
+
# Note: if any of the graphs are not set, those resources can still be constructed, but not persisted back to DB.
|
49
|
+
def create_and_hydrate_resources(uris_and_graphs)
|
50
|
+
|
51
|
+
graph = self.resource_class.describe_uris(uris_and_graphs.keys) #uses the resource_class on the criteria object
|
52
|
+
repo = self.resource_class.add_data_to_repository(graph)
|
53
|
+
|
54
|
+
resources = []
|
55
|
+
|
56
|
+
uris_and_graphs.each_pair do |u,g|
|
57
|
+
|
58
|
+
# instantiate a new resource
|
59
|
+
r = self.resource_class.new(u,g)
|
60
|
+
|
61
|
+
# make a graph of data for this resource's uri
|
62
|
+
data_graph = RDF::Graph.new
|
63
|
+
repo.query( [RDF::URI.new(u), :predicate, :object] ) do |statement|
|
64
|
+
data_graph << statement
|
65
|
+
end
|
66
|
+
|
67
|
+
# use it to hydrate this resource
|
68
|
+
r.hydrate!(:graph => data_graph)
|
69
|
+
r.new_record = false
|
70
|
+
resources << r
|
71
|
+
end
|
72
|
+
|
73
|
+
resources
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
# based on the query passed in, build a hash of uris->graphs
|
78
|
+
# @param [ String] sparql. The sparql query
|
79
|
+
# @param [ Hash ] opts. A hash of options.
|
80
|
+
#
|
81
|
+
# @option options [ String ] uri_variable The name of the uri variable in the query, if not 'uri'
|
82
|
+
# @option options [ String ] graph_variable The name of the uri variable in thh query, if not 'graph'
|
83
|
+
def select_uris_and_graphs(sparql, opts={})
|
84
|
+
select_results = Tripod::SparqlClient::Query.select(sparql)
|
85
|
+
|
86
|
+
uris_and_graphs = {}
|
87
|
+
|
88
|
+
select_results.each do |r|
|
89
|
+
uri_variable = opts[:uri_variable] || 'uri'
|
90
|
+
graph_variable = opts[:graph_variable] || 'graph'
|
91
|
+
uris_and_graphs[ r[uri_variable]["value"] ] = r[graph_variable]["value"]
|
92
|
+
end
|
93
|
+
|
94
|
+
uris_and_graphs
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "tripod/criteria/execution"
|
3
|
+
|
4
|
+
module Tripod
|
5
|
+
|
6
|
+
# This module defines behaviour for criteria
|
7
|
+
class Criteria
|
8
|
+
|
9
|
+
include Tripod::CriteriaExecution
|
10
|
+
|
11
|
+
# the resource class that this criteria is for.
|
12
|
+
attr_accessor :resource_class
|
13
|
+
|
14
|
+
# array of all the where clauses in this criteria
|
15
|
+
attr_accessor :where_clauses
|
16
|
+
|
17
|
+
# array of all the extra clauses in this criteria
|
18
|
+
attr_accessor :extra_clauses
|
19
|
+
|
20
|
+
def initialize(resource_class)
|
21
|
+
self.resource_class = resource_class
|
22
|
+
self.where_clauses = []
|
23
|
+
self.extra_clauses = []
|
24
|
+
|
25
|
+
if resource_class._RDF_TYPE
|
26
|
+
self.where("?uri a <#{resource_class._RDF_TYPE.to_s}>")
|
27
|
+
else
|
28
|
+
self.where("?uri ?p ?o")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# they're equal if they return the same query
|
33
|
+
def ==(other)
|
34
|
+
build_select_query == other.send(:build_select_query)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Takes a string and adds a where clause to this criteria.
|
38
|
+
# Returns a criteria object.
|
39
|
+
# Note: the subject being returned by the query must be identified by ?uri
|
40
|
+
# e.g. my_criteria.where("?uri a <http://my-type>")
|
41
|
+
#
|
42
|
+
# TODO: make it also take a hash?
|
43
|
+
def where(sparql_snippet)
|
44
|
+
where_clauses << sparql_snippet
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# takes a string and adds an extra clause to this criteria.
|
49
|
+
# TODO: make it also take a hash?
|
50
|
+
def extras(sparql_snippet)
|
51
|
+
extra_clauses << sparql_snippet
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Tripod::Errors
|
3
|
+
|
4
|
+
# field not present error.
|
5
|
+
class BadDataRequest < StandardError
|
6
|
+
|
7
|
+
attr_accessor :parent_bad_request
|
8
|
+
|
9
|
+
def initialize(message=nil, parent_bad_request_error=nil)
|
10
|
+
super(message)
|
11
|
+
parent_bad_request = parent_bad_request_error
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Tripod::Errors
|
3
|
+
|
4
|
+
# field not present error.
|
5
|
+
class BadSparqlRequest < StandardError
|
6
|
+
|
7
|
+
attr_accessor :parent_bad_request
|
8
|
+
|
9
|
+
def initialize(message=nil, parent_bad_request_error=nil)
|
10
|
+
super(message)
|
11
|
+
parent_bad_request = parent_bad_request_error
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/lib/tripod/errors.rb
CHANGED
@@ -3,4 +3,9 @@ require 'tripod/errors/field_not_present'
|
|
3
3
|
require 'tripod/errors/resource_not_found'
|
4
4
|
require 'tripod/errors/uri_not_set'
|
5
5
|
require 'tripod/errors/graph_uri_not_set'
|
6
|
-
require 'tripod/errors/
|
6
|
+
require 'tripod/errors/rdf_type_not_set'
|
7
|
+
require 'tripod/errors/validations'
|
8
|
+
require 'tripod/errors/rdf_parse_failed'
|
9
|
+
|
10
|
+
require 'tripod/errors/bad_sparql_request'
|
11
|
+
require 'tripod/errors/bad_data_request'
|
data/lib/tripod/finders.rb
CHANGED
@@ -48,23 +48,26 @@ module Tripod::Finders
|
|
48
48
|
resource
|
49
49
|
end
|
50
50
|
|
51
|
-
#
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
#
|
59
|
-
#
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def
|
66
|
-
|
67
|
-
|
51
|
+
# execute a where clause on this resource.
|
52
|
+
# returns a criteria object
|
53
|
+
def where(sparql_snippet)
|
54
|
+
criteria = Tripod::Criteria.new(self)
|
55
|
+
criteria.where(sparql_snippet)
|
56
|
+
end
|
57
|
+
|
58
|
+
# execute a query to return all objects (restricted by this class's rdf_type if specified)
|
59
|
+
# returns a criteria object
|
60
|
+
def all
|
61
|
+
criteria = Tripod::Criteria.new(self)
|
62
|
+
criteria
|
63
|
+
end
|
64
|
+
|
65
|
+
def count
|
66
|
+
self.all.count
|
67
|
+
end
|
68
|
+
|
69
|
+
def first
|
70
|
+
self.all.first
|
68
71
|
end
|
69
72
|
|
70
73
|
# returns a graph of triples which describe the uris passed in.
|
@@ -75,7 +78,7 @@ module Tripod::Finders
|
|
75
78
|
uris_sparql_str = uris.map{ |u| "<#{u.to_s}>" }.join(" ")
|
76
79
|
|
77
80
|
# Do a big describe statement, and read the results into an in-memory repo
|
78
|
-
triples_string = Tripod::SparqlClient::Query
|
81
|
+
triples_string = Tripod::SparqlClient::Query.describe("DESCRIBE #{uris_sparql_str}")
|
79
82
|
|
80
83
|
RDF::Reader.for(:ntriples).new(triples_string) do |reader|
|
81
84
|
reader.each_statement do |statement|
|
@@ -88,63 +91,5 @@ module Tripod::Finders
|
|
88
91
|
graph
|
89
92
|
end
|
90
93
|
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
# FOLLOWING METHODS NOT PART OF THE PUBLIC API:
|
95
|
-
def self.included(base)
|
96
|
-
|
97
|
-
class << base
|
98
|
-
|
99
|
-
private
|
100
|
-
|
101
|
-
# create and hydrate the resources identified in uris_and_graphs.
|
102
|
-
# Note: if any of the graphs are not set, those resources can still be constructed, but not persisted back to DB.
|
103
|
-
def create_and_hydrate_resources(uris_and_graphs)
|
104
|
-
|
105
|
-
graph = describe_uris(uris_and_graphs.keys)
|
106
|
-
repo = add_data_to_repository(graph)
|
107
|
-
|
108
|
-
resources = []
|
109
|
-
|
110
|
-
uris_and_graphs.each_pair do |u,g|
|
111
|
-
|
112
|
-
# instantiate a new resource
|
113
|
-
r = self.new(u,g)
|
114
|
-
|
115
|
-
# make a graph of data for this resource's uri
|
116
|
-
data_graph = RDF::Graph.new
|
117
|
-
repo.query( [RDF::URI.new(u), :predicate, :object] ) do |statement|
|
118
|
-
data_graph << statement
|
119
|
-
end
|
120
|
-
|
121
|
-
# use it to hydrate this resource
|
122
|
-
r.hydrate!(:graph => data_graph)
|
123
|
-
r.new_record = false
|
124
|
-
resources << r
|
125
|
-
end
|
126
|
-
|
127
|
-
resources
|
128
|
-
end
|
129
|
-
|
130
|
-
|
131
|
-
# based on the query passed in, build a hash of uris->graphs
|
132
|
-
def select_uris_and_graphs(criteria, opts)
|
133
|
-
select_results = Tripod::SparqlClient::Query.select(criteria)
|
134
|
-
|
135
|
-
uris_and_graphs = {}
|
136
|
-
|
137
|
-
select_results.each do |r|
|
138
|
-
uri_variable = opts[:uri_variable] || 'uri'
|
139
|
-
graph_variable = opts[:graph_variable] || 'graph'
|
140
|
-
uris_and_graphs[ r[uri_variable]["value"] ] = r[graph_variable]["value"]
|
141
|
-
end
|
142
|
-
|
143
|
-
uris_and_graphs
|
144
|
-
end
|
145
|
-
|
146
|
-
|
147
|
-
end
|
148
|
-
|
149
94
|
end
|
150
95
|
end
|
data/lib/tripod/persistence.rb
CHANGED
@@ -32,7 +32,7 @@ module Tripod::Persistence
|
|
32
32
|
transaction && transaction.class == Tripod::Persistence::Transaction
|
33
33
|
end
|
34
34
|
|
35
|
-
def self.
|
35
|
+
def self.get_transaction(trans)
|
36
36
|
transaction = nil
|
37
37
|
|
38
38
|
if Tripod::Persistence::Transaction.valid_transaction(trans)
|
@@ -70,7 +70,7 @@ module Tripod::Persistence
|
|
70
70
|
|
71
71
|
raise Tripod::Errors::GraphUriNotSet.new() unless @graph_uri
|
72
72
|
|
73
|
-
transaction = Tripod::Persistence::Transaction.
|
73
|
+
transaction = Tripod::Persistence::Transaction.get_transaction(opts[:transaction])
|
74
74
|
|
75
75
|
if self.valid?
|
76
76
|
|
@@ -114,7 +114,7 @@ module Tripod::Persistence
|
|
114
114
|
# if we get in here, save failed.
|
115
115
|
|
116
116
|
# abort the transaction
|
117
|
-
transaction = Tripod::Persistence::Transaction.
|
117
|
+
transaction = Tripod::Persistence::Transaction.get_transaction(opts[:transaction])
|
118
118
|
transaction.abort() if transaction
|
119
119
|
|
120
120
|
self.class.fail_validate!(self) # throw an exception
|
@@ -126,7 +126,7 @@ module Tripod::Persistence
|
|
126
126
|
|
127
127
|
def destroy(opts={})
|
128
128
|
|
129
|
-
transaction = Tripod::Persistence::Transaction.
|
129
|
+
transaction = Tripod::Persistence::Transaction.get_transaction(opts[:transaction])
|
130
130
|
|
131
131
|
query = "
|
132
132
|
# delete from default graph:
|
data/lib/tripod/resource.rb
CHANGED
@@ -92,9 +92,11 @@ module Tripod::Resource
|
|
92
92
|
other.class == Class ? self <= other : other.is_a?(self)
|
93
93
|
end
|
94
94
|
|
95
|
+
# makes a "field" on this model called rdf_type
|
96
|
+
# and sets a class level _RDF_TYPE variable with the rdf_type passed in.
|
95
97
|
def rdf_type(new_rdf_type)
|
96
98
|
field :rdf_type, RDF.type
|
97
|
-
self._RDF_TYPE = new_rdf_type
|
99
|
+
self._RDF_TYPE = RDF::URI.new(new_rdf_type.to_s)
|
98
100
|
end
|
99
101
|
|
100
102
|
def graph_uri(new_graph_uri)
|
data/lib/tripod/sparql_client.rb
CHANGED
@@ -23,13 +23,8 @@ module Tripod::SparqlClient
|
|
23
23
|
:timeout => Tripod.timeout_seconds,
|
24
24
|
)
|
25
25
|
rescue RestClient::BadRequest => e
|
26
|
-
|
27
|
-
|
28
|
-
# TODO: this is a SPARQL parsing exception. Do something different.
|
29
|
-
raise e
|
30
|
-
else
|
31
|
-
raise e
|
32
|
-
end
|
26
|
+
# just re-raise as a BadSparqlRequest Exception
|
27
|
+
raise Tripod::Errors::BadSparqlRequest.new(e.http_body, e)
|
33
28
|
end
|
34
29
|
end
|
35
30
|
|
@@ -67,6 +62,21 @@ module Tripod::SparqlClient
|
|
67
62
|
return response.body
|
68
63
|
end
|
69
64
|
|
65
|
+
# Executes an ASK +query+ against the SPARQL endpoint.
|
66
|
+
# Executes the +query+ and returns text by default
|
67
|
+
#
|
68
|
+
# @example Run a ASK query
|
69
|
+
# Tripod::SparqlClient::Query.select('ASK <http://foo>')
|
70
|
+
#
|
71
|
+
# @param [ String ] query The query to run
|
72
|
+
# @param [ String ] accept_header The format parameter to send to the database. Valud valid formats are text, xml, json
|
73
|
+
#
|
74
|
+
# @return [ String ] the raw response from the endpoint
|
75
|
+
def self.ask(query, format='text')
|
76
|
+
response = self.query(query, format)
|
77
|
+
return response.body
|
78
|
+
end
|
79
|
+
|
70
80
|
# Executes a CONSTRUCT +query+ against the SPARQL endpoint.
|
71
81
|
# Executes the +query+ and returns ntriples by default
|
72
82
|
#
|
@@ -102,15 +112,37 @@ module Tripod::SparqlClient
|
|
102
112
|
)
|
103
113
|
true
|
104
114
|
rescue RestClient::BadRequest => e
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
115
|
+
# just re-raise as a BadSparqlRequest Exception
|
116
|
+
raise Tripod::Errors::BadSparqlRequest.new(e.http_body, e)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
module Data
|
123
|
+
class DataClient
|
124
|
+
def self.submit(graph_uri, data, method)
|
125
|
+
url = "#{Tripod.data_endpoint}?graph=#{graph_uri}"
|
126
|
+
begin
|
127
|
+
RestClient::Request.execute(
|
128
|
+
:method => method,
|
129
|
+
:url => url,
|
130
|
+
:timeout => Tripod.timeout_seconds,
|
131
|
+
:payload => data
|
132
|
+
)
|
133
|
+
true
|
134
|
+
rescue RestClient::BadRequest => e
|
135
|
+
raise Tripod::Errors::BadDataRequest.new(e.http_body, e)
|
111
136
|
end
|
112
137
|
end
|
113
138
|
end
|
114
139
|
|
140
|
+
def self.append(graph_uri, data)
|
141
|
+
DataClient.submit(graph_uri, data, :post)
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.replace(graph_uri, data)
|
145
|
+
DataClient.submit(graph_uri, data, :put)
|
146
|
+
end
|
115
147
|
end
|
116
148
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Tripod
|
3
|
+
|
4
|
+
class SparqlQueryError < StandardError; end
|
5
|
+
|
6
|
+
class SparqlQuery
|
7
|
+
|
8
|
+
attr_reader :query # the original query string
|
9
|
+
attr_reader :query_type # symbol representing the type (:select, :ask etc)
|
10
|
+
attr_reader :body # the body of the query
|
11
|
+
attr_reader :prefixes # any prefixes the query may have
|
12
|
+
|
13
|
+
cattr_accessor :PREFIX_KEYWORDS
|
14
|
+
@@PREFIX_KEYWORDS = %w(BASE PREFIX)
|
15
|
+
cattr_accessor :KEYWORDS
|
16
|
+
@@KEYWORDS = %w(CONSTRUCT ASK DESCRIBE SELECT)
|
17
|
+
|
18
|
+
def initialize(query_string, parent_query=nil)
|
19
|
+
@query = query_string
|
20
|
+
@parent_query = parent_query
|
21
|
+
|
22
|
+
if self.has_prefixes?
|
23
|
+
@prefixes, @body = self.extract_prefixes
|
24
|
+
else
|
25
|
+
@body = self.query
|
26
|
+
end
|
27
|
+
|
28
|
+
@query_type = get_query_type
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_prefixes?
|
32
|
+
self.class.PREFIX_KEYWORDS.each do |k|
|
33
|
+
return true if /^#{k}/i.match(query)
|
34
|
+
end
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
def extract_prefixes
|
39
|
+
i = self.class.KEYWORDS.map {|k| self.query.index(/#{k}/i) || self.query.size+1 }.min
|
40
|
+
p = query[0..i-1]
|
41
|
+
b = query[i..-1]
|
42
|
+
return p.strip, b.strip
|
43
|
+
end
|
44
|
+
|
45
|
+
def as_count_query_str
|
46
|
+
# only allow for selects
|
47
|
+
raise SparqlQueryError.new("Can't turn this into a subquery") unless self.query_type == :select
|
48
|
+
|
49
|
+
count_query = "SELECT COUNT(*) { #{self.body} }"
|
50
|
+
count_query = "#{self.prefixes} #{count_query}" if self.prefixes
|
51
|
+
|
52
|
+
# just returns the string representing the count query for this query.
|
53
|
+
count_query
|
54
|
+
end
|
55
|
+
|
56
|
+
def as_first_query_str
|
57
|
+
# only allow for selects
|
58
|
+
raise SparqlQueryError.new("Can't turn this into a subquery") unless self.query_type == :select
|
59
|
+
|
60
|
+
first_query = "SELECT * { #{self.body} } LIMIT 1"
|
61
|
+
first_query = "#{self.prefixes} #{first_query}" if self.prefixes
|
62
|
+
|
63
|
+
# just returns the string representing the 'first' query for this query.
|
64
|
+
first_query
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def get_query_type
|
70
|
+
if /^CONSTRUCT/i.match(self.body)
|
71
|
+
:construct
|
72
|
+
elsif /^ASK/i.match(self.body)
|
73
|
+
:ask
|
74
|
+
elsif /^DESCRIBE/i.match(self.body)
|
75
|
+
:describe
|
76
|
+
elsif /^SELECT/i.match(self.body)
|
77
|
+
:select
|
78
|
+
else
|
79
|
+
:unknown
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/lib/tripod/version.rb
CHANGED
data/lib/tripod.rb
CHANGED
@@ -45,6 +45,9 @@ module Tripod
|
|
45
45
|
mattr_accessor :query_endpoint
|
46
46
|
@@query_endpoint = 'http://127.0.0.1:3030/tripod/sparql'
|
47
47
|
|
48
|
+
mattr_accessor :data_endpoint
|
49
|
+
@@data_endpoint = 'http://127.0.0.1:3030/tripod/data'
|
50
|
+
|
48
51
|
mattr_accessor :timeout_seconds
|
49
52
|
@@timeout_seconds = 30
|
50
53
|
|
@@ -64,12 +67,14 @@ end
|
|
64
67
|
|
65
68
|
require "tripod/extensions"
|
66
69
|
require "tripod/sparql_client"
|
70
|
+
require "tripod/sparql_query"
|
67
71
|
|
68
72
|
require "tripod/predicates"
|
69
73
|
require "tripod/attributes"
|
70
74
|
require "tripod/errors"
|
71
75
|
require "tripod/repository"
|
72
76
|
require "tripod/fields"
|
77
|
+
require "tripod/criteria"
|
73
78
|
require "tripod/finders"
|
74
79
|
require "tripod/persistence"
|
75
80
|
require "tripod/eager_loading"
|