pho 0.4.1 → 0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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