stardog-rb 0.0.1

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 (2) hide show
  1. data/lib/stardog.rb +528 -0
  2. metadata +46 -0
data/lib/stardog.rb ADDED
@@ -0,0 +1,528 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+ require 'tempfile'
4
+
5
+ require "net/http"
6
+
7
+ module Net
8
+
9
+ class HTTPGenericRequest
10
+
11
+ def write_header(sock, ver, path)
12
+ buf = "#{@method} #{path} HTTP/#{ver}\r\n"
13
+ each_capitalized do |k,v|
14
+ if k.downcase == "sd-connection-string"
15
+ k = "SD-Connection-String"
16
+ end
17
+ buf << "#{k}: #{v}\r\n"
18
+ end
19
+ buf << "\r\n"
20
+ #puts "WRITING..."
21
+ #puts buf
22
+ sock.write buf
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ module Stardog
30
+
31
+ # Current version of the library.
32
+ VERSION = "0.0.1"
33
+
34
+ DEBUG = ENV["STARDOG_RB_DEBUG"] || false
35
+
36
+ class StardogResponse
37
+ attr_reader :status, :body
38
+
39
+ def initialize(status, body)
40
+ @status = status
41
+ @body = body
42
+ end
43
+
44
+ def success?
45
+ @status.to_s =~ /^2\d{2}$/
46
+ end
47
+
48
+ def to_s
49
+ "#{status}\n--------\n#{body}\n--------\n"
50
+ end
51
+ end
52
+
53
+ class LinkedJSON
54
+
55
+ def initialize(attributes = {})
56
+ @attributes = attributes
57
+ end
58
+
59
+ def get(key)
60
+ @attributes[key]
61
+ end
62
+
63
+ def set(key,value)
64
+ @attributes[key] = value
65
+ end
66
+
67
+ def [](arg)
68
+ @attributes[arg]
69
+ end
70
+
71
+ def []=(arg,value)
72
+ @attributes[arg] = value
73
+ end
74
+
75
+ def raw_json
76
+ @attributes
77
+ end
78
+
79
+ def to_s
80
+ @attributes.to_json
81
+ end
82
+ end
83
+
84
+
85
+ class Connection
86
+
87
+ attr_accessor :endpoint, :reasoning, :credentials
88
+
89
+ def initialize
90
+ # By default (for testing)
91
+ @endpoint = 'http://localhost:5822/'
92
+ @credentials = {:user =>'admin', :password => 'admin'}
93
+ end
94
+
95
+ def set_credentials(username, password)
96
+ @credentials = {:user => username, :password => password}
97
+ end
98
+
99
+
100
+ def get_property(database, uri, property)
101
+ str_query = 'select ?val where { '+ uri +' '+ property +' ?val }'
102
+
103
+ json_res = query(database, str_query).body
104
+
105
+ if (json_res["results"] && json_res["results"]["bindings"].length > 0)
106
+ json_res["results"]["bindings"].first["val"]["value"]
107
+ else
108
+ nil
109
+ end
110
+ end
111
+
112
+ def get_db(database)
113
+ http_request("GET", database)
114
+ end
115
+
116
+ def get_db_size(database)
117
+ http_request("GET", "#{database}/size")
118
+ end
119
+
120
+ def query(database, query, options = {})
121
+ base_uri = options[:base_uri]
122
+ limit = options[:limit]
123
+ offset = options[:offset]
124
+ accept = options[:accept]
125
+
126
+ accept_header = accept ? accept : 'application/sparql-results+json'
127
+ options = {
128
+ :query => query
129
+ }
130
+
131
+ options[:"base-uri"] = base_uri if base_uri
132
+ options[:limit] = limit if limit
133
+ options[:offset] = offset if offset
134
+
135
+ http_request("GET", "#{database}/query", accept, options)
136
+ end
137
+
138
+ def add(database, body, graph_uri=nil, content_type="text/plain")
139
+ with_transaction(database) do |txId|
140
+ add_in_transaction(database, txId, body, graph_uri, content_type)
141
+ end
142
+ end
143
+
144
+ def remove(database, body, graph_uri=nil, content_type="text/plain")
145
+ with_transaction(database) do |txId|
146
+ remove_in_transaction(database, txId, body, graph_uri, content_type)
147
+ end
148
+ end
149
+
150
+ def query_graph(database, query, base_uri, options)
151
+ base_uri = options[:base_uri]
152
+ limit = options[:limit]
153
+ offset = options[:offset]
154
+ accept = options[:accept]
155
+
156
+ accept_header = accept ? accept : 'application/ld+json'
157
+ options = {
158
+ :query => query
159
+ }
160
+
161
+ options[:"base-uri"] = base_uri if base_uri
162
+ options[:limit] = limit if limit
163
+ options[:offset] = offset if offset
164
+
165
+ http_request("GET", "#{database}/query", accept, options)
166
+ end
167
+
168
+ def query_explain(database, query, options = {})
169
+ base_uri = options[:base_uri]
170
+
171
+ options = {
172
+ :query => query
173
+ }
174
+
175
+ options[:"base-uri"] = base_uri if base_uri
176
+
177
+ http_request("GET", "#{database}/explain", "text/plain", options)
178
+ end
179
+
180
+ ################
181
+ # Transactions
182
+ ################
183
+
184
+ def begin(database)
185
+ result = http_request("POST", "#{database}/transaction/begin", "text/plain", "")
186
+ if(result.status == 200)
187
+ result.body
188
+ else
189
+ raise Exception.new("Error beginning transaction #{result}")
190
+ end
191
+ end
192
+
193
+
194
+ def commit(database, txID)
195
+ http_request("POST", "#{database}/transaction/commit/#{txID}", "text/plain", "")
196
+ end
197
+
198
+ def rollback(database, txID)
199
+ http_request("POST", "#{database}/transaction/rollback/#{txID}", "text/plain", "")
200
+ end
201
+
202
+ # Initiates a transaction and handles the commit or rollback of it.
203
+ # if an exception is raised the transaction will be committed.
204
+ # It accepts a block that will receive a transaction ID as argument.
205
+ # - db_name
206
+ # - transaction block
207
+ def with_transaction(db_name)
208
+ begin
209
+ txID = self.begin(db_name)
210
+ yield txID
211
+ commit(db_name,txID)
212
+ true
213
+ rescue Exception => ex
214
+ debug "* Error in transaction #{txID}"
215
+ debug ex.message
216
+ debug ex.backtrace.join("\n")
217
+ rollback(db_name, txID)
218
+ false
219
+ end
220
+ end
221
+
222
+ def query(database, query, options = {})
223
+ base_uri = options[:base_uri]
224
+ limit = options[:limit]
225
+ offset = options[:offset]
226
+ accept = options[:accept]
227
+ ask_request = options.delete(:ask)
228
+
229
+ accept = 'text/boolean' if(ask_request)
230
+ accept_header = accept ? accept : 'application/sparql-results+json'
231
+
232
+ options = {
233
+ :query => query
234
+ }
235
+
236
+ options[:"base-uri"] = base_uri if base_uri
237
+ options[:limit] = limit if limit
238
+ options[:offset] = offset if offset
239
+
240
+ http_request("GET", "#{database}/query", accept_header, options, nil, (ask_request ? false: true))
241
+ end
242
+
243
+ def query_in_transaction(database, txID, query, options = {})
244
+ base_uri = options[:base_uri]
245
+ limit = options[:limit]
246
+ offset = options[:offset]
247
+ accept = options[:accept]
248
+ ask_request = options.delete(:ask)
249
+
250
+ accept = 'text/boolean' if(ask_request)
251
+ accept_header = accept ? accept : 'application/sparql-results+json'
252
+
253
+ options = {
254
+ :query => query
255
+ }
256
+
257
+ options["base-uri"] = base_uri if base_uri
258
+ options[:limit] = limit if limit
259
+ options[:offset] = offset if offset
260
+
261
+ http_request("GET", "#{database}/#{txID}/query", accept_header, options, nil, (ask_request ? false: true))
262
+ end
263
+
264
+ def add_in_transaction(database, txID, body, graph_uri=nil, content_type="text/plain")
265
+ options = nil
266
+ options = {"graph-uri" => graph_uri} if graph_uri
267
+
268
+ if(File.exists?(body))
269
+ body = File.open(body,"r").read
270
+ elsif(body =~ /^https?:\/\/[\S]+$/)
271
+ result = http_request("GET", body, (content_type == "text/plain" ? "*/*" : content_type), {}, nil, false)
272
+ raise Exception.new("Error adding data from remote URL #{body} => #{result.status} : #{result}") if result.status != 200
273
+ body = result.body
274
+ end
275
+
276
+ http_request("POST", "#{database}/#{txID}/add", "*/*", options, body, false, content_type, nil)
277
+ end
278
+
279
+ def remove_in_transaction(database, txID, body, graph_uri=nil, content_type="text/plain")
280
+ options = nil
281
+ options = {"graph-uri" => graph_uri} if graph_uri
282
+
283
+ if(File.exists?(body))
284
+ body = File.open(body,"r").read
285
+ elsif(body =~ /^https?:\/\/[\S]+$/)
286
+ result = http_request("GET", body, (content_type == "text/plain" ? "*/*" : content_type), {}, nil, false)
287
+ raise Exception.new("Error adding data from remote URL #{body} => #{result.status} : #{result}") if result.status != 200
288
+ body = result.body
289
+ end
290
+
291
+ http_request("POST", "#{database}/#{txID}/remove", "text/plain", options, body, false, content_type, nil)
292
+ end
293
+
294
+ def clear_db(database, txId, graph_uri = nil)
295
+ options = nil
296
+ options = {"graph-uri" => graph_uri} if graph_uri
297
+
298
+ http_request("POST", database + "/" + txId + "/clear", "text/plain", options);
299
+ end
300
+
301
+ ################
302
+ # Reasoning
303
+ ################
304
+
305
+ def reasoning_explain(database, axioms, options = {})
306
+ content_type = options[:content_type] || "text/plain"
307
+ txID = options[:tx_id]
308
+
309
+ url = "#{database}/reasoning"
310
+ url = "#{url}/#{txID}" if txID
311
+
312
+ url = "#{url}/explain"
313
+
314
+ http_request("POST", url, "application/x-turtle", {}, axioms, false, content_type)
315
+ end
316
+
317
+ def consistent?(database)
318
+ res = http_request("GET", "#{database}/reasoning/consistency", "text/boolean", {}, nil, false)
319
+ (res.body == "true" ? true : false)
320
+ end
321
+
322
+ #######################
323
+ # Database Operations
324
+ #######################
325
+
326
+ # List all available databases
327
+ def list_dbs
328
+ http_request("GET", "admin/databases", "application/json", "")
329
+ end
330
+
331
+
332
+ # Get configuration properties for the database.
333
+ # The list of properties can be found here: http://stardog.com/docs/admin/#admin-db
334
+ # By default, "database.name", "icv.enabled", "search.enabled", "database.online", "index.type" properties a requested.
335
+ def db_options(db_name, options = ["database.name", "icv.enabled", "search.enabled", "database.online", "index.type"])
336
+ http_request("PUT", "admin/databases/#{db_name}/options", "application/json", {}, options.inject({}){|ac,i| ac[i]=""; ac})
337
+ end
338
+
339
+ # Set properties for a DB.
340
+ # options and values must be passed as the second argument to the method.
341
+ # DB must be offline before being setting the new values.
342
+ # The lis of properties can be found here: http://stardog.com/docs/admin/#admin-db
343
+ # - db_name
344
+ # - options: hash with properties and values.
345
+ def set_db_options(db_name, options)
346
+ http_request("POST", "admin/databases/#{db_name}/options", "application/json", {}, options)
347
+ end
348
+
349
+ # Copy a database
350
+ # Copies a database. The source database must be offline.
351
+ # The target database will be created.
352
+ def copy_db(db_source,db_target)
353
+ http_request("PUT", "admin/databases/#{db_source}/copy", "application/json", { "to" => db_target })
354
+ end
355
+
356
+ # Creates a new database
357
+ def create_db(dbname, creation_options={})
358
+ options = creation_options[:options] || {}
359
+ files = creation_options[:files] ||= []
360
+ if(files.empty?)
361
+ http_request("POST", "admin/databases", "text/plain", {}, {:dbname => dbname, :options => options, :files => files}.to_json, true, "application/json", true)
362
+ else
363
+ f = Tempfile.new("stardog_rb_#{Time.now.to_i}")
364
+ f << "{\"dbname\":\"#{dbname}\",\"options\":#{options.to_json},\"files\":[{"
365
+ files.each_with_index do |datafile,i|
366
+ f << "\"name\":\"#{File.basename(datafile)}\", \"content\":"
367
+ f << File.open(datafile,"r").read.chomp.to_json
368
+ f << "," if i != (files.length - 1)
369
+ end
370
+ f << "}]}"
371
+ f.flush
372
+ f.close
373
+ http_request("POST", "admin/databases", "text/plain", {}, File.new(f.path), true, "application/json", true)
374
+ end
375
+ end
376
+
377
+ # Drops an existent database.
378
+ def drop_db(dbname)
379
+ http_request("DELETE", "admin/databases/#{dbname}", "application/json", "")
380
+ end
381
+
382
+
383
+ # Sets database offline.
384
+ # * strategy_op: 'WAIT' | 'NO_WAIT', default 'WAIT'.
385
+ # * timeout: timeout in ms, default 3 ms.
386
+ def offline_db(dbname, strategy_op = 'WAIT', timeout = 3)
387
+ http_request("PUT", "admin/databases/#{dbname}/offline", "application/json", {}, { "strategy" => strategy_op, "timeout" => timeout })
388
+ end
389
+
390
+ # Sets database offline.
391
+ # * strategy_op: 'WAIT' | 'NO_WAIT', default 'WAIT'.
392
+ def online_db(dbname, strategy_op)
393
+ http_request("PUT", "admin/databases/#{dbname}/online", "application/json", {}, { "strategy" => strategy_op })
394
+ end
395
+
396
+ ################
397
+ # Admin
398
+ ################
399
+ def clear_stardog
400
+ list_dbs.body["databases"].each do |db|
401
+ drop_db db
402
+ end
403
+ end
404
+
405
+ def shutdown_server()
406
+ http_request("POST", "admin/shutdown", "application/json")
407
+ end
408
+
409
+ private
410
+
411
+ def http_request(method, resource, accept = "*/*", params = {}, msg_body = nil, is_json_body = true, content_type = nil, multipart = false)
412
+ url = if(resource =~ /https?/)
413
+ resource
414
+ else
415
+ "#{@endpoint}#{resource}"
416
+ end
417
+ if(params.is_a?(Hash) && params.keys.length > 0)
418
+ params = params.keys.map{|k| "#{CGI::escape(k.to_s)}=#{CGI::escape(params[k].to_s)}" }.join('&')
419
+ url = "#{url}?#{params}"
420
+ elsif(params.is_a?(String) && params != "")
421
+ url = "#{url}?#{params}"
422
+ end
423
+ arguments = {
424
+ :method => method.to_s.downcase.to_sym,
425
+ :url => url,
426
+ :headers => {:accept => accept},
427
+ }
428
+ if @reasoning
429
+ arguments[:headers]["SD-Connection-String"] = "reasoning=#{@reasoning}"
430
+ end
431
+
432
+ #arguments.merge!(credentials) if credentials
433
+ if credentials
434
+ arguments[:url].gsub!("http://","http://#{credentials[:user]}:#{credentials[:password]}@")
435
+ end
436
+
437
+ arguments[:payload] = msg_body if msg_body
438
+
439
+ if(is_json_body)
440
+ arguments[:headers][:content_type] = "application/json"
441
+ arguments[:payload] = arguments[:payload].to_json if arguments.to_json != "" && !multipart
442
+ else
443
+ arguments[:headers][:content_type] = content_type if content_type
444
+ end
445
+
446
+ file = nil
447
+ if(multipart)
448
+
449
+ unless(arguments[:payload].is_a?(File))
450
+ file = Tempfile.new("stardog_rb_#{Time.now.to_i.to_s}")
451
+ file.write(arguments[:payload])
452
+ file.close
453
+ arguments[:payload] = File.new(file)
454
+ arguments[:multipart] = true
455
+ end
456
+ arguments[:payload] = {
457
+ :file => arguments[:payload],
458
+ :multipart => true
459
+ }
460
+ end
461
+
462
+
463
+ debug "ARGUMENTS:"
464
+ debug arguments.inspect
465
+ response = RestClient::Request.execute(arguments)
466
+ debug "RESPONSE:"
467
+ debug response.code
468
+ debug "---"
469
+ debug response.headers
470
+ debug "---"
471
+ debug response.body
472
+ debug "-----------------------\n"
473
+
474
+ if response.code.to_s =~ /^2\d{2}$/
475
+ if response.headers[:content_type] && response.headers[:content_type].index("json")
476
+ json_data = JSON.parse(response.body)
477
+
478
+ result = if json_data.is_a?(Array)
479
+ json_data.inject([]) { |ac, i| ac << i if(i["@id"] || i["@context"]); ac }
480
+ else
481
+ LinkedJSON.new(json_data)
482
+ end
483
+ StardogResponse.new(response.code, result)
484
+ elsif response.headers[:content_type] && response.headers[:content_type].index("application/xml")
485
+ StardogResponse.new(response.code, Nokogiri::XML(response.body))
486
+ else
487
+ StardogResponse.new(response.code, response.body)
488
+ end
489
+ else
490
+ StardogResponse.new(response.code, response.body)
491
+ end
492
+
493
+ rescue => exception
494
+ if(exception.respond_to?(:response))
495
+ debug "RESPONSE:"
496
+ debug exception.response.code
497
+ debug "---"
498
+ debug exception.response.headers
499
+ debug "---"
500
+ debug exception.response.body
501
+ debug "-----------------------\n"
502
+
503
+ StardogResponse.new(exception.response.code, exception.response.body)
504
+ else
505
+ raise exception
506
+ end
507
+ ensure
508
+ file.unlink unless file.nil?
509
+ end # end of http_request
510
+
511
+ def debug(msg)
512
+ puts msg if Stardog::DEBUG
513
+ end
514
+
515
+ end # end of Connection
516
+
517
+
518
+ # Returns a connection
519
+ def stardog(endpoint = nil, options = {})
520
+ connection = Connection.new
521
+ connection.endpoint = endpoint if endpoint
522
+ connection.reasoning = options[:reasoning] if options[:reasoning]
523
+ connection.set_credentials(options[:user], options[:password]) if options[:user] && options[:password]
524
+
525
+ connection
526
+ end
527
+
528
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stardog-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Antonio Garrote
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-08 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Port of the JS bindings for Stardog.
15
+ email:
16
+ - antoniogarrote@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/stardog.rb
22
+ homepage: http://antoniogarrote.com/social/stream
23
+ licenses: []
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 1.8.25
43
+ signing_key:
44
+ specification_version: 3
45
+ summary: HTTP Bindings for Stardog RDF data base
46
+ test_files: []