stardog-rb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []