pho 0.4.1 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/CHANGES +18 -1
  2. data/README +10 -0
  3. data/Rakefile +2 -1
  4. data/doc/rdoc/classes/Pho.html +33 -22
  5. data/doc/rdoc/classes/Pho/DatatypeProperty.html +12 -12
  6. data/doc/rdoc/classes/Pho/{RDF_JSON.html → Enrichment.html} +8 -7
  7. data/doc/rdoc/classes/Pho/Enrichment/ResourceEnricher.html +310 -0
  8. data/doc/rdoc/classes/Pho/Etags.html +42 -42
  9. data/doc/rdoc/classes/Pho/Facet/Results.html +19 -19
  10. data/doc/rdoc/classes/Pho/Facet/Term.html +6 -6
  11. data/doc/rdoc/classes/Pho/FieldPredicateMap.html +110 -105
  12. data/doc/rdoc/classes/Pho/FieldWeighting.html +12 -12
  13. data/doc/rdoc/classes/Pho/FileManagement.html +121 -0
  14. data/doc/rdoc/classes/Pho/FileManagement/AbstractFileManager.html +443 -0
  15. data/doc/rdoc/classes/Pho/FileManagement/FileManager.html +258 -0
  16. data/doc/rdoc/classes/Pho/FileManagement/RDFManager.html +271 -0
  17. data/doc/rdoc/classes/Pho/Job.html +64 -64
  18. data/doc/rdoc/classes/Pho/Jobs.html +60 -60
  19. data/doc/rdoc/classes/Pho/QueryProfile.html +60 -60
  20. data/doc/rdoc/classes/Pho/RDFCollection.html +4 -378
  21. data/doc/rdoc/classes/Pho/ResourceHash.html +123 -0
  22. data/doc/rdoc/classes/Pho/ResourceHash/Converter.html +323 -0
  23. data/doc/rdoc/classes/Pho/{RDF_JSON → ResourceHash}/SetAlgebra.html +18 -18
  24. data/doc/rdoc/classes/Pho/Snapshot.html +35 -35
  25. data/doc/rdoc/classes/Pho/Sparql.html +137 -0
  26. data/doc/rdoc/classes/Pho/Sparql/SparqlClient.html +515 -0
  27. data/doc/rdoc/classes/Pho/Sparql/SparqlHelper.html +575 -0
  28. data/doc/rdoc/classes/Pho/Status.html +26 -26
  29. data/doc/rdoc/classes/Pho/Store.html +271 -241
  30. data/doc/rdoc/classes/Pho/Update/Changeset.html +73 -73
  31. data/doc/rdoc/classes/Pho/Update/ChangesetBuilder.html +34 -34
  32. data/doc/rdoc/classes/Pho/Update/Changesets.html +14 -14
  33. data/doc/rdoc/classes/Pho/Update/LiteralStatement.html +31 -23
  34. data/doc/rdoc/classes/Pho/Update/ResourceStatement.html +45 -21
  35. data/doc/rdoc/classes/Pho/Update/Statement.html +29 -29
  36. data/doc/rdoc/classes/String.html +1 -1
  37. data/doc/rdoc/created.rid +1 -1
  38. data/doc/rdoc/files/CHANGES.html +49 -3
  39. data/doc/rdoc/files/README.html +15 -1
  40. data/doc/rdoc/files/lib/pho/changeset_builder_rb.html +1 -1
  41. data/doc/rdoc/files/lib/pho/changeset_rb.html +1 -1
  42. data/doc/rdoc/files/lib/pho/converter_rb.html +108 -0
  43. data/doc/rdoc/files/lib/pho/enrichment_rb.html +101 -0
  44. data/doc/rdoc/files/lib/pho/etags_rb.html +1 -1
  45. data/doc/rdoc/files/lib/pho/field_predicate_map_rb.html +1 -1
  46. data/doc/rdoc/files/lib/pho/file_management_rb.html +101 -0
  47. data/doc/rdoc/files/lib/pho/file_manager_rb.html +108 -0
  48. data/doc/rdoc/files/lib/pho/rdf_collection_rb.html +1 -1
  49. data/doc/rdoc/files/lib/pho/resource_hash_rb.html +101 -0
  50. data/doc/rdoc/files/lib/pho/{rdf_json_rb.html → sparql_rb.html} +4 -4
  51. data/doc/rdoc/files/lib/pho/store_rb.html +1 -1
  52. data/doc/rdoc/files/lib/pho_rb.html +7 -2
  53. data/doc/rdoc/fr_class_index.html +12 -2
  54. data/doc/rdoc/fr_file_index.html +6 -1
  55. data/doc/rdoc/fr_method_index.html +176 -139
  56. data/examples/sparql_construct_hash.rb +26 -0
  57. data/examples/sparql_select.rb +18 -0
  58. data/lib/pho.rb +6 -1
  59. data/lib/pho/changeset.rb +24 -9
  60. data/lib/pho/changeset_builder.rb +10 -10
  61. data/lib/pho/converter.rb +74 -0
  62. data/lib/pho/enrichment.rb +81 -0
  63. data/lib/pho/etags.rb +1 -0
  64. data/lib/pho/field_predicate_map.rb +6 -1
  65. data/lib/pho/file_management.rb +102 -0
  66. data/lib/pho/file_manager.rb +61 -0
  67. data/lib/pho/rdf_collection.rb +54 -120
  68. data/lib/pho/{rdf_json.rb → resource_hash.rb} +3 -4
  69. data/lib/pho/sparql.rb +332 -0
  70. data/lib/pho/store.rb +20 -14
  71. data/tests/tc_changeset.rb +46 -0
  72. data/tests/tc_changeset_builder.rb +122 -1
  73. data/tests/tc_converter.rb +95 -0
  74. data/tests/tc_enrichment.rb +83 -0
  75. data/tests/tc_file_manager.rb +88 -0
  76. data/tests/tc_rdf_collection.rb +3 -0
  77. data/tests/{tc_rdf_json.rb → tc_resource_hash.rb} +23 -23
  78. data/tests/tc_search.rb +1 -1
  79. data/tests/tc_sparql.rb +131 -6
  80. data/tests/tc_sparql_helper.rb +214 -0
  81. data/tests/ts_pho.rb +6 -2
  82. metadata +47 -8
@@ -0,0 +1,61 @@
1
+ module Pho
2
+
3
+ require 'mime/types'
4
+
5
+ module FileManagement
6
+
7
+ class FileManager < AbstractFileManager
8
+
9
+ def initialize(store, dir, ok_suffix=OK, fail_suffix=FAIL, sleep=1)
10
+ super(store, dir, ok_suffix, fail_suffix, sleep)
11
+ end
12
+
13
+ #List files being managed, i.e. everything not .ok or .fail
14
+ def list()
15
+ files = []
16
+ Dir.glob( File.join(@dir, "*") ) do |file|
17
+ if File.extname(file) != ".#{@ok_suffix}" && File.extname(file) != ".#{@fail_suffix}"
18
+ files << file
19
+ end
20
+ end
21
+ return files
22
+ end
23
+
24
+ #List any new files in the directory
25
+ def new_files()
26
+ newfiles = Array.new
27
+ Dir.glob( File.join(@dir, "*") ) do |file|
28
+
29
+ if File.extname(file) != ".#{@ok_suffix}" && File.extname(file) != ".#{@fail_suffix}"
30
+ ok_file = get_ok_file_for(file)
31
+ fail_file = get_fail_file_for(file)
32
+ if !( File.exists?(ok_file) or File.exists?(fail_file) )
33
+ newfiles << file
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ return newfiles
40
+ end
41
+
42
+
43
+ protected
44
+
45
+ def store_file(file, filename)
46
+ response = @store.upload_item(file, MIME::Types.type_for(filename)[0].to_s )
47
+ if (response.status < 300 )
48
+ File.open(get_ok_file_for(filename), "w") do |file|
49
+ file.print( "OK" )
50
+ end
51
+ else
52
+ File.open(get_fail_file_for(filename), "w") do |file|
53
+ YAML::dump(response, file)
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+ end
@@ -1,129 +1,63 @@
1
1
  module Pho
2
2
 
3
- attr_reader :dir
4
- attr_reader :store
5
-
6
- # Provides a simple mechanism for managing a directory of RDF/XML documents
7
- # and uploading them to platform store.
8
- #
9
- # Allows a collection to be mirrored into the platform
10
- class RDFCollection
11
-
12
- RDF = "rdf".freeze
13
- OK = "ok".freeze
14
- FAIL = "fail".freeze
15
-
16
- def initialize(store, dir, rdf_suffix=RDF, ok_suffix=OK, fail_suffix=FAIL, sleep=1)
17
- @store = store
18
- @dir = dir
19
- @sleep = sleep
20
- @rdf_suffix = rdf_suffix
21
- @ok_suffix = ok_suffix
22
- @fail_suffix = fail_suffix
23
- end
3
+ module FileManagement
24
4
 
25
- #Store all files that match the file name in directory
26
- def store()
27
- files_to_store = new_files()
28
- files_to_store.each do |filename|
29
- file = File.new(filename)
30
- store_file(file, filename)
31
- end
32
- end
33
-
34
- #Retry anything known to have failed
35
- def retry_failures()
36
- retries = failures()
37
- retries.each do |filename|
38
- File.delete( get_fail_file_for(filename) )
39
- #store it
40
- file = File.new(filename)
41
- store_file(file, filename)
42
- end
43
- end
44
-
45
- #Reset the directory to clear out any previous statuses
46
- #Store can also be reset at the same time: use with care!
47
- def reset(reset_store=false)
48
- Dir.glob( File.join(@dir, "*.#{@fail_suffix}") ).each do |file|
49
- File.delete(file)
50
- end
51
- Dir.glob( File.join(@dir, "*.#{@ok_suffix}") ).each do |file|
52
- File.delete(file)
53
- end
54
- end
55
-
56
- #List files being managed
57
- def list()
58
- return Dir.glob( File.join(@dir, "*.#{@rdf_suffix}") )
59
- end
60
-
61
- #List failures
62
- def failures()
63
- fails = Array.new
64
- Dir.glob( File.join(@dir, "*.#{@fail_suffix}") ).each do |file|
65
- fails << file.gsub(/\.#{@fail_suffix}/, ".#{@rdf_suffix}")
66
- end
67
- return fails
68
- end
69
-
70
- #List successes
71
- def successes()
72
- successes = Array.new
73
- Dir.glob( File.join(@dir, "*.#{@ok_suffix}") ).each do |file|
74
- successes << file.gsub(/\.#{@ok_suffix}/, ".#{@rdf_suffix}")
75
- end
76
- return successes
77
- end
78
-
79
- #List any new files in the directory
80
- def new_files()
81
- newfiles = Array.new
82
- Dir.glob( File.join(@dir, "*.#{@rdf_suffix}") ) do |file|
83
- ok_file = get_ok_file_for(file)
84
- fail_file = get_fail_file_for(file)
85
- if !( File.exists?(ok_file) or File.exists?(fail_file) )
86
- newfiles << file
5
+ # Provides a simple mechanism for managing a directory of RDF/XML documents
6
+ # and uploading them to platform store.
7
+ #
8
+ # Allows a collection to be mirrored into the platform
9
+ class RDFManager < AbstractFileManager
10
+
11
+ RDF = "rdf".freeze
12
+
13
+ def initialize(store, dir, rdf_suffix=RDF, ok_suffix=OK, fail_suffix=FAIL, sleep=1)
14
+ super(store, dir, ok_suffix, fail_suffix, sleep)
15
+ @rdf_suffix = rdf_suffix
87
16
  end
88
- end
89
- return newfiles
90
- end
91
17
 
92
- #Summarize the state of the collection to the provied IO object
93
- #Creates a simple report
94
- def summary()
95
- failures = failures()
96
- successes = successes()
97
- newfiles = new_files()
98
- total = failures.size + successes.size + newfiles.size
99
- summary = "#{@dir} contains #{total} files: #{successes.size} stored, #{failures.size} failed, #{newfiles.size} new"
100
- return summary
101
- end
102
-
103
- def get_fail_file_for(filename)
104
- return filename.gsub(/\.#{@rdf_suffix}/, ".#{@fail_suffix}")
105
- end
106
-
107
- def get_ok_file_for(filename)
108
- return filename.gsub(/\.#{@rdf_suffix}/, ".#{@ok_suffix}")
109
- end
110
-
111
- private
112
-
113
- def store_file(file, filename)
114
- response = @store.store_file(file)
115
- if (response.status < 300 )
116
- File.open(get_ok_file_for(filename), "w") do |file|
117
- file.print( "OK" )
18
+
19
+ #List files being managed
20
+ def list()
21
+ return Dir.glob( File.join(@dir, "*.#{@rdf_suffix}") )
118
22
  end
119
- else
120
- File.open(get_fail_file_for(filename), "w") do |file|
121
- YAML::dump(response, file)
122
- end
123
- end
124
- end
125
-
23
+
24
+ #List any new files in the directory
25
+ def new_files()
26
+ newfiles = Array.new
27
+ Dir.glob( File.join(@dir, "*.#{@rdf_suffix}") ) do |file|
28
+ ok_file = get_ok_file_for(file)
29
+ fail_file = get_fail_file_for(file)
30
+ if !( File.exists?(ok_file) or File.exists?(fail_file) )
31
+ newfiles << file
32
+ end
33
+ end
34
+ return newfiles
35
+ end
36
+
126
37
 
38
+ protected
39
+
40
+ def store_file(file, filename)
41
+ response = @store.store_file(file)
42
+ if (response.status < 300 )
43
+ File.open(get_ok_file_for(filename), "w") do |file|
44
+ file.print( "OK" )
45
+ end
46
+ else
47
+ File.open(get_fail_file_for(filename), "w") do |file|
48
+ YAML::dump(response, file)
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ end
55
+
127
56
  end
128
-
57
+
58
+ #Deprecated. Use Pho::FileManangement::RDFManager instead
59
+ class RDFCollection < Pho::FileManagement::RDFManager
60
+ #for backwards compatibility
61
+ end
62
+
129
63
  end
@@ -1,11 +1,10 @@
1
1
  module Pho
2
2
 
3
- #TODO
4
- #blank nodes
3
+ #TODO blank nodes
5
4
 
6
- #Module providing code for manipulating triple hashes structured according
5
+ #Module providing code for manipulating resource hashes structured according
7
6
  #to the RDF in JSON spec
8
- module RDF_JSON
7
+ module ResourceHash
9
8
 
10
9
  #Class providing set algebra methods over triple hashes
11
10
  class SetAlgebra
@@ -0,0 +1,332 @@
1
+ module Pho
2
+
3
+ #Module providing a SPARQL client library, support for parsing SPARQL query responses into Ruby objects
4
+ #and other useful behaviour
5
+ module Sparql
6
+
7
+ SPARQL_RESULTS_XML = "application/sparql-results+xml"
8
+ SPARQL_RESULTS_JSON = "application/sparql-results+json"
9
+
10
+ #A simple SPARQL client that handles the basic HTTP traffic
11
+ class SparqlClient
12
+
13
+ #URI of the endpoint
14
+ attr_reader :endpoint
15
+ #HTTPClient object
16
+ attr_reader :client
17
+ #Name of output parameter to use to control response format. If set then this parameter is added
18
+ #to the query string, rather than using Content Negotiation
19
+ attr_accessor :output_parameter_name
20
+ attr_reader :graphs
21
+ attr_reader :named_graphs
22
+
23
+ #Configures whether the remote endpoint supports the RDF-in-JSON specification for serializing
24
+ #RDF graphs as JSON. Will default to false.
25
+ attr_accessor :supports_rdf_json
26
+ #Configures whether the remote endpoint supports SPARQL JSON Results format
27
+ #Will default to true.
28
+ attr_accessor :supports_sparql_json
29
+
30
+ #Initialize a client for a specific endpoint
31
+ #
32
+ # endpoint:: uri of the SPARQL endpoint
33
+ # client:: optionally, a reference to an existing HTTPClient object instance
34
+ def initialize(endpoint, client=HTTPClient.new() )
35
+ @endpoint = endpoint
36
+ @graphs = nil
37
+ @named_graphs = nil
38
+ @client = client
39
+ @output_parameter_name = nil
40
+ @supports_rdf_json = false
41
+ @supports_sparql_json = true
42
+ end
43
+
44
+ #Add a default graph. This will be added as a default graph in the request protocol
45
+ def add_default_graph(graph_uri)
46
+ if @graphs == nil
47
+ @graphs = []
48
+ end
49
+ @graphs << graph_uri
50
+ end
51
+
52
+ #Add a named graph. This will be added as a named graph in the request protocol
53
+ def add_named_graph(graph_uri)
54
+ if @named_graphs == nil
55
+ @named_graphs = []
56
+ end
57
+ @named_graphs << graph_uri
58
+ end
59
+
60
+ #Perform a sparql query
61
+ #
62
+ # sparql:: a valid SPARQL query
63
+ # format:: specific a request format. Usually a media-type, but may be a name for a type, if not using Conneg
64
+ # graphs:: an array of default graphs
65
+ # named_graphs:: an array of named graphs
66
+ def query(sparql, format=nil, graphs=nil, named_graphs=nil)
67
+
68
+ params = {}
69
+ params["query"] = sparql
70
+
71
+ if graphs != nil
72
+ params["default-graph-uri"] = graphs
73
+ elsif @graphs != nil
74
+ params["default-graph-uri"] = @graphs
75
+ end
76
+
77
+ if named_graphs != nil
78
+ params["named-graph-uri"] = named_graphs
79
+ elsif @named_graphs != nil
80
+ params["named-graph-uri"] = @named_graphs
81
+ end
82
+
83
+ headers = {}
84
+ if format != nil
85
+
86
+ if @output_parameter_name != nil
87
+ params[@output_parameter_name] = format
88
+ else
89
+ headers["Accept"] = format
90
+ end
91
+
92
+ end
93
+
94
+ return @client.get( @endpoint, params, headers )
95
+ end
96
+
97
+ #Perform a SPARQL DESCRIBE query.
98
+ #
99
+ # query:: the SPARQL query
100
+ # format:: the preferred response format
101
+ def describe(query, format="application/rdf+xml")
102
+ return query(query, format)
103
+ end
104
+
105
+ #DESCRIBE multiple resources in a single query. The provided array should contain
106
+ #the uris that are to be described
107
+ #
108
+ #This will generate a query like:
109
+ # DESCRIBE <http://www.example.org> <http://www.example.com> ...
110
+ #
111
+ # uris:: list of the uris to be described
112
+ # format:: the preferred response format. Default is RDF/XML
113
+ def multi_describe(uris, format="application/rdf+xml")
114
+ query = "DESCRIBE " + uris.map {|u| "<#{u}>" }.join(" ")
115
+ return query(query, format)
116
+ end
117
+
118
+ #Perform a SPARQL CONSTRUCT query.
119
+ #
120
+ # query:: the SPARQL query
121
+ # format:: the preferred response format
122
+ def construct(query, format="application/rdf+xml")
123
+ return query(query, format)
124
+ end
125
+
126
+ #Perform a SPARQL ASK query.
127
+ #
128
+ # query:: the SPARQL query
129
+ # format:: the preferred response format
130
+ def ask(query, format=Pho::Sparql::SPARQL_RESULTS_XML)
131
+ return query(query, format)
132
+ end
133
+
134
+ #Perform a SPARQL SELECT query.
135
+ #
136
+ # query:: the SPARQL query
137
+ # format:: the preferred response format
138
+ def select(query, format=Pho::Sparql::SPARQL_RESULTS_XML)
139
+ return query(query, format)
140
+ end
141
+
142
+ end
143
+
144
+ #Simple helper class for manipulating and executing SPARQL queries and manipulating the results
145
+ class SparqlHelper
146
+ VARIABLE_MATCHER = /(\?|\$)([a-zA-Z]+)/
147
+
148
+ #Apply some initial bindings to parameters in a query
149
+ #
150
+ #The keys in the values hash are used to replace variables in a query
151
+ #The values are supplied as is, allowing them to be provided as URIs, or typed literals
152
+ #according to Turtle syntax.
153
+ #
154
+ #Any keys in the hash that are not in the query are ignored. Any variables not found
155
+ #in the hash remain unbound.
156
+ #
157
+ # query:: the query whose initial bindings are to be set
158
+ # values:: hash of query name to value
159
+ def SparqlHelper.apply_initial_bindings(query, bindings={})
160
+ copy = query.clone()
161
+ copy.gsub!(VARIABLE_MATCHER) do |pattern|
162
+ key = $2
163
+ if bindings.has_key?(key)
164
+ bindings[key].to_s
165
+ else
166
+ pattern
167
+ end
168
+ end
169
+ return copy
170
+ end
171
+
172
+ #Convert a SPARQL query result binding into a hash suitable for passing
173
+ #to the apply_initial_bindings method.
174
+ #
175
+ #The result param is assumed to be a Ruby hash that reflects the structure of
176
+ #a binding in a SELECT query result (i.e. the result of parsing the <tt>application/sparql-results+json</tt>
177
+ #format and extracting an specific result binding.
178
+ #
179
+ #The method is intended to be used to support cases where an initial select query is
180
+ #performed to extract some variables that can later be plugged into a subsequent
181
+ #query
182
+ #
183
+ # result:: hash conforming to structure of a <tt>binding</tt> in the SPARQL JSON format
184
+ def SparqlHelper.result_to_query_binding(result)
185
+ hash = {}
186
+ result.each_pair do |key, value|
187
+ if value["type"] == "uri"
188
+ hash[key] = "<#{value["value"]}>"
189
+ elsif (value["type"] == "literal" && !value.has_key?("datatype"))
190
+ hash[key] = "\"#{value["value"]}\""
191
+ elsif (value["type"] == "literal" && value.has_key?("datatype"))
192
+ hash[key] = "\"#{value["value"]}\"^^#{value["datatype"]}"
193
+ else
194
+ #do nothing for bnodes
195
+ end
196
+ end
197
+ return hash
198
+ end
199
+
200
+ #Convert Ruby hash structured according to SPARQL JSON format
201
+ #into an array of hashes by calling result_to_query_binding on each binding
202
+ #into the results.
203
+ #
204
+ #E.g:
205
+ # <tt>results = Pho::Sparql::SparqlHelper.select(query, sparql_client)</tt>
206
+ # <tt>bindings = Pho::Sparql::SparqlHelper.results_to_query_bindings(results)</tt>
207
+ #
208
+ # results:: hash conforming to SPARQL SELECT structure
209
+ def SparqlHelper.results_to_query_bindings(results)
210
+ bindings = []
211
+
212
+ results["results"]["bindings"].each do |result|
213
+ bindings << result_to_query_binding(result)
214
+ end
215
+ return bindings
216
+ end
217
+
218
+ #Perform a simple SELECT query on an endpoint.
219
+ #Will request the results using the SPARQL JSON results format, and parse the
220
+ #resulting JSON results. The result will therefore be a simple ruby hash of the results
221
+ #
222
+ #An error will be raised if the response is HTTP OK.
223
+ #
224
+ # query:: the SPARQL SELECT query
225
+ # sparql_client:: a configured SparqlClient object
226
+ def SparqlHelper.select(query, sparql_client)
227
+ #TODO: test whether endpoint supports json, and if not, switch to parsing XML
228
+ resp = sparql_client.select(query, Pho::Sparql::SPARQL_RESULTS_JSON)
229
+ if resp.status != 200
230
+ raise "Error performing sparql query: #{resp.status} #{resp.reason}\n#{resp.content}"
231
+ end
232
+ return JSON.parse( resp.content )
233
+ end
234
+
235
+ #Performs an ASK query on an endpoint, returing a boolean true/false response
236
+ #
237
+ #Will request the results using the SPARQL JSON results format, parse the
238
+ #resulting JSON results, and extract the true/false response.
239
+ #
240
+ # query:: the SPARQL SELECT query
241
+ # sparql_client:: a configured SparqlClient object
242
+ def SparqlHelper.ask(query, sparql_client)
243
+ json = SparqlHelper.select(query, sparql_client)
244
+ return json["boolean"] == "true"
245
+ end
246
+
247
+ #Perform a simple SELECT query on an endpoint and return a simple array of values
248
+ #
249
+ #Will request the results using the SPARQL JSON results format, and parse the
250
+ #resulting JSON results. The assumption is that the SELECT query contains a single "column"
251
+ #of values, which will be returned as an array
252
+ #
253
+ #Note this will lose any type information, only the value of the bindings are returned
254
+ #
255
+ # query:: the SPARQL SELECT query
256
+ # sparql_client:: a configured SparqlClient object
257
+ def SparqlHelper.select_values(query, sparql_client)
258
+ results = SparqlHelper.select(query, sparql_client)
259
+ v = results["head"]["vars"][0];
260
+ values = [];
261
+ results["results"]["bindings"].each do |binding|
262
+ values << binding[v]["value"]
263
+ end
264
+ return values
265
+ end
266
+
267
+ #Perform a simple SELECT query on an endpoint and return a single result
268
+ #
269
+ #Will request the results using the SPARQL JSON results format, and parse the
270
+ #resulting JSON results. The assumption is that the SELECT query returns a single
271
+ #value (i.e single variable, with single binding)
272
+ #
273
+ #Note this will lose any type information, only the value of the binding is returned
274
+ #
275
+ # query:: the SPARQL SELECT query
276
+ # sparql_client:: a configured SparqlClient object
277
+ def SparqlHelper.select_single_value(query, sparql_client)
278
+ results = SparqlHelper.select(query, sparql_client)
279
+ v = results["head"]["vars"][0];
280
+ return results["results"]["bindings"][0][v]["value"]
281
+ end
282
+
283
+ #Perform a SPARQL CONSTRUCT query against an endpoint, requesting the results in JSON
284
+ #
285
+ #Will request the results as application/json (with the expectation that it returns RDF_JSON),
286
+ #and parses the resulting JSON document.
287
+ #
288
+ # query:: the SPARQL SELECT query
289
+ # sparql_client:: a configured SparqlClient object
290
+ def SparqlHelper.construct_to_resource_hash(query, sparql_client)
291
+ #TODO: test whether endpoint supports json, and if not, switch to parsing XML
292
+ resp = sparql_client.construct(query, "application/json")
293
+ if resp.status != 200
294
+ raise "Error performing sparql query: #{resp.status} #{resp.reason}\n#{resp.content}"
295
+ end
296
+ return Pho::ResourceHash::Converter.parse_json( resp.content )
297
+ end
298
+
299
+ #Perform a SPARQL DESCRIBE query against an endpoint, requesting the results in JSON
300
+ #
301
+ #Will request the results as application/json (with the expectation that it returns RDF_JSON),
302
+ #and parses the resulting JSON document.
303
+ #
304
+ # query:: the SPARQL SELECT query
305
+ # sparql_client:: a configured SparqlClient object
306
+ def SparqlHelper.describe_to_resource_hash(query, sparql_client)
307
+ #TODO: test whether endpoint supports json, and if not, switch to parsing XML
308
+ resp = sparql_client.describe(query, "application/json")
309
+ if resp.status != 200
310
+ raise "Error performing sparql query: #{resp.status} #{resp.reason}\n#{resp.content}"
311
+ end
312
+ return Pho::ResourceHash::Converter.parse_json( resp.content )
313
+ end
314
+
315
+ #DESCRIBE multiple resources in a single SPARQL request
316
+ #
317
+ # uris:: an array of URIs
318
+ # sparql_client:: a configured SparqlClient objec
319
+ def SparqlHelper.multi_describe(uris, sparql_client)
320
+ #TODO: test whether endpoint supports json, and if not, switch to parsing XML
321
+ resp = sparql_client.multi_describe(uris, "application/json")
322
+ if resp.status != 200
323
+ raise "Error performing sparql query: #{resp.status} #{resp.reason}\n#{resp.content}"
324
+ end
325
+ return Pho::ResourceHash::Converter.parse_json( resp.content )
326
+ end
327
+
328
+ end
329
+
330
+ end
331
+
332
+ end