agraph 0.1.0.beta1

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.
@@ -0,0 +1,37 @@
1
+
2
+ module AllegroGraph
3
+
4
+ module Proxy
5
+
6
+ class Query
7
+
8
+ LANGUAGES = [ :sparql, :prolog ].freeze unless defined?(LANGUAGES)
9
+
10
+ attr_reader :server
11
+ attr_reader :resource
12
+ attr_reader :language
13
+
14
+ def initialize(resource)
15
+ @resource = resource
16
+ @language = :sparql
17
+ end
18
+
19
+ def path
20
+ @resource.path
21
+ end
22
+
23
+ def language=(value)
24
+ raise NotImplementedError, "query langauge [#{value}] is not implemented" unless LANGUAGES.include?(value.to_sym)
25
+ @language = value.to_sym
26
+ end
27
+
28
+ def perform(query)
29
+ parameters = { :query => query, :queryLn => @language.to_s }
30
+ @resource.request :get, self.path, :parameters => parameters, :expected_status_code => 200
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,59 @@
1
+
2
+ module AllegroGraph
3
+
4
+ module Proxy
5
+
6
+ class Statements
7
+
8
+ attr_reader :resource
9
+
10
+ def initialize(resource)
11
+ @resource = resource
12
+ end
13
+
14
+ def path
15
+ "#{@resource.path}/statements"
16
+ end
17
+
18
+ def create(subject, predicate, object, context = nil)
19
+ statement = [ subject, predicate, object ]
20
+ statement << context if context
21
+ @resource.request :post, self.path, :body => [ statement ], :expected_status_code => 204
22
+ true
23
+ end
24
+
25
+ def find(options = { })
26
+ parameters = { }
27
+
28
+ { :subject => :subj, :predicate => :pred, :object => :obj, :context => :context }.each do |option_key, parameter_key|
29
+ value = options[option_key]
30
+ parameters.merge! value.is_a?(Array) ?
31
+ { :"#{parameter_key}" => value[0], :"#{parameter_key}End" => value[1] } :
32
+ { parameter_key => value } if value
33
+ end
34
+
35
+ [ :offset, :limit, :infer ].each do |key|
36
+ parameters.merge! key => options[key] if options.has_key?(key)
37
+ end
38
+
39
+ parameters = nil if parameters.empty?
40
+
41
+ @resource.request :get, self.path, :parameters => parameters, :expected_status_code => 200
42
+ end
43
+
44
+ def delete(options = { })
45
+ parameters = { }
46
+
47
+ { :subject => :subj, :predicate => :pred, :object => :obj, :context => :context }.each do |option_key, parameter_key|
48
+ value = options[option_key]
49
+ parameters.merge! parameter_key => value if value
50
+ end
51
+
52
+ @resource.request :delete, self.path, :parameters => parameters, :expected_status_code => 200
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,89 @@
1
+ require File.join(File.dirname(__FILE__), "server")
2
+ require File.join(File.dirname(__FILE__), "session")
3
+ require File.join(File.dirname(__FILE__), "proxy", "statements")
4
+ require File.join(File.dirname(__FILE__), "proxy", "query")
5
+ require File.join(File.dirname(__FILE__), "proxy", "geo")
6
+ require File.join(File.dirname(__FILE__), "proxy", "mapping")
7
+
8
+ module AllegroGraph
9
+
10
+ class Repository
11
+
12
+ attr_reader :server
13
+ attr_reader :catalog
14
+ attr_accessor :name
15
+
16
+ attr_reader :statements
17
+ attr_reader :query
18
+ attr_reader :geo
19
+ attr_reader :mapping
20
+
21
+ def initialize(server_or_catalog, name, options = { })
22
+ @catalog = server_or_catalog.is_a?(AllegroGraph::Server) ? server_or_catalog.root_catalog : server_or_catalog
23
+ @server = @catalog.server
24
+ @name = name
25
+ @statements = Proxy::Statements.new self
26
+ @query = Proxy::Query.new self
27
+ @geo = Proxy::Geo.new self
28
+ @mapping = Proxy::Mapping.new self
29
+ end
30
+
31
+ def ==(other)
32
+ other.is_a?(self.class) && self.catalog == other.catalog && self.name == other.name
33
+ end
34
+
35
+ def path
36
+ "#{@catalog.path}/repositories/#{@name}"
37
+ end
38
+
39
+ def request(http_method, path, options = { })
40
+ @server.request http_method, path, options
41
+ end
42
+
43
+ def exists?
44
+ @catalog.repositories.include? self
45
+ end
46
+
47
+ def create!
48
+ @server.request :put, self.path, :expected_status_code => 204
49
+ true
50
+ rescue ExtendedTransport::UnexpectedStatusCodeError => error
51
+ return false if error.status_code == 400
52
+ raise error
53
+ end
54
+
55
+ def create_if_missing!
56
+ create! unless exists?
57
+ end
58
+
59
+ def delete!
60
+ @server.request :delete, self.path, :expected_status_code => 200
61
+ true
62
+ rescue ExtendedTransport::UnexpectedStatusCodeError => error
63
+ return false if error.status_code == 400
64
+ raise error
65
+ end
66
+
67
+ def delete_if_exists!
68
+ delete! if exists?
69
+ end
70
+
71
+ def size
72
+ response = @server.request :get, self.path + "/size", :type => :text, :expected_status_code => 200
73
+ response.to_i
74
+ end
75
+
76
+ def transaction(&block)
77
+ session = Session.create self
78
+ begin
79
+ session.instance_eval &block
80
+ rescue Object => error
81
+ session.rollback
82
+ raise error
83
+ end
84
+ session.commit
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,68 @@
1
+ require File.join(File.dirname(__FILE__), "transport")
2
+ require File.join(File.dirname(__FILE__), "catalog")
3
+ require File.join(File.dirname(__FILE__), "federation")
4
+
5
+ module AllegroGraph
6
+
7
+ # The Server class provides methods to retrieve informations about a AllegroGraph server.
8
+ class Server
9
+
10
+ attr_reader :host
11
+ attr_reader :port
12
+ attr_reader :username
13
+ attr_reader :password
14
+ attr_reader :root_catalog
15
+
16
+ def initialize(options = { })
17
+ @host = options[:host] || "localhost"
18
+ @port = options[:port] || "10035"
19
+ @username = options[:username]
20
+ @password = options[:password]
21
+
22
+ @root_catalog = Catalog.new self, "root", :root => true
23
+ end
24
+
25
+ def ==(other)
26
+ other.is_a?(self.class) && @host == other.host && @port == other.port
27
+ end
28
+
29
+ def request(http_method, path, options = { })
30
+ ExtendedTransport.request http_method, self.url + path, credentials.merge(options)
31
+ end
32
+
33
+ def version
34
+ {
35
+ :version => self.request(:get, "/version", :expected_status_code => 200),
36
+ :date => self.request(:get, "/version/date", :expected_status_code => 200),
37
+ :revision => self.request(:get, "/version/revision", :expected_status_code => 200)
38
+ }
39
+ end
40
+
41
+ def catalogs
42
+ result = [ @root_catalog ]
43
+ catalogs = self.request :get, "/catalogs", :expected_status_code => 200
44
+ catalogs.each do |catalog|
45
+ id = catalog["id"]
46
+ result << Catalog.new(self, id.sub(/^\//, "")) unless id == "/"
47
+ end
48
+ result
49
+ end
50
+
51
+ def federations
52
+ federations = self.request :get, "/federated", :expected_status_code => 200
53
+ federations.map { |federation| Federation.new self, federation["id"] }
54
+ end
55
+
56
+ def url
57
+ "http://#{@host}:#{@port}"
58
+ end
59
+
60
+ private
61
+
62
+ def credentials
63
+ { :auth_type => :basic, :username => @username, :password => @password }
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,64 @@
1
+ require File.join(File.dirname(__FILE__), "transport")
2
+ require File.join(File.dirname(__FILE__), "proxy", "statements")
3
+ require File.join(File.dirname(__FILE__), "proxy", "query")
4
+ require File.join(File.dirname(__FILE__), "proxy", "geo")
5
+ require File.join(File.dirname(__FILE__), "proxy", "mapping")
6
+
7
+ module AllegroGraph
8
+
9
+ class Session
10
+
11
+ attr_reader :url
12
+ attr_reader :username
13
+ attr_reader :password
14
+
15
+ attr_reader :statements
16
+ attr_reader :query
17
+ attr_reader :geo
18
+ attr_reader :mapping
19
+
20
+ def initialize(options = { })
21
+ @url = options[:url]
22
+ @username = options[:username]
23
+ @password = options[:password]
24
+
25
+ @statements = Proxy::Statements.new self
26
+ @query = Proxy::Query.new self
27
+ @geo = Proxy::Geo.new self
28
+ @mapping = Proxy::Mapping.new self
29
+ end
30
+
31
+ def path
32
+ ""
33
+ end
34
+
35
+ def request(http_method, path, options = { })
36
+ ExtendedTransport.request http_method, self.url + path, credentials.merge(options)
37
+ end
38
+
39
+ def commit
40
+ self.request :post, "/commit", :expected_status_code => 204
41
+ true
42
+ end
43
+
44
+ def rollback
45
+ self.request :post, "/rollback", :expected_status_code => 204
46
+ true
47
+ end
48
+
49
+ def self.create(repository)
50
+ url = repository.request :post, repository.path + "/session", :expected_status_code => 200
51
+ url.sub! /^"/, ""
52
+ url.sub! /"$/, ""
53
+ new :url => url, :username => repository.server.username, :password => repository.server.password
54
+ end
55
+
56
+ private
57
+
58
+ def credentials
59
+ { :auth_type => :basic, :username => @username, :password => @password }
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,169 @@
1
+ require 'uri'
2
+ require 'cgi'
3
+ require 'net/http'
4
+ require 'base64'
5
+ require 'json'
6
+
7
+ module AllegroGraph
8
+
9
+ # Common transport layer for http transfers.
10
+ class Transport
11
+
12
+ attr_reader :http_method
13
+ attr_reader :url
14
+ attr_reader :options
15
+ attr_reader :headers
16
+ attr_reader :parameters
17
+ attr_reader :body
18
+ attr_reader :response
19
+
20
+ def initialize(http_method, url, options = { })
21
+ @http_method = http_method
22
+ @uri = URI.parse url
23
+ @headers = options[:headers] || { }
24
+ @parameters = options[:parameters] || { }
25
+ @body = options[:body]
26
+ end
27
+
28
+ def perform
29
+ initialize_request_class
30
+ initialize_request_path
31
+ initialize_request
32
+ initialize_request_body
33
+ perform_request
34
+ end
35
+
36
+ private
37
+
38
+ def initialize_request_class
39
+ request_class_name = @http_method.capitalize
40
+ raise NotImplementedError, "the request method #{http_method} is not implemented" unless Net::HTTP.const_defined?(request_class_name)
41
+ @request_class = Net::HTTP.const_get request_class_name
42
+ end
43
+
44
+ def initialize_request_path
45
+ serialize_parameters
46
+ @request_path = @uri.path + @serialized_parameters
47
+ end
48
+
49
+ def serialize_parameters
50
+ quote_parameters
51
+ @serialized_parameters = if @parameters.nil? || @parameters.empty?
52
+ ""
53
+ else
54
+ "?" + @quoted_parameters.collect do |key, value|
55
+ value.is_a?(Array) ?
56
+ value.map{ |element| "#{key}=#{element}" }.join("&") :
57
+ "#{key}=#{value}"
58
+ end.join("&")
59
+ end
60
+ end
61
+
62
+ def quote_parameters
63
+ @quoted_parameters = { }
64
+ @parameters.each do |key, value|
65
+ if value.is_a?(Array)
66
+ @quoted_parameters[ CGI.escape("#{key}") ] = value.map{ |element| CGI.escape element }
67
+ else
68
+ @quoted_parameters[ CGI.escape("#{key}") ] = CGI.escape value
69
+ end
70
+ end
71
+ end
72
+
73
+ def initialize_request
74
+ @request = @request_class.new @request_path, @headers
75
+ end
76
+
77
+ def initialize_request_body
78
+ return unless [ :post, :put ].include?(@http_method.to_sym)
79
+ @request.body = @body ? @body : @serialized_parameters.sub(/^\?/, "")
80
+ end
81
+
82
+ def perform_request
83
+ @response = Net::HTTP.start(@uri.host, @uri.port) do |connection|
84
+ connection.request @request
85
+ end
86
+ end
87
+
88
+ def self.request(http_method, url, options = { })
89
+ transport = new http_method, url, options
90
+ transport.perform
91
+ transport.response
92
+ end
93
+
94
+ end
95
+
96
+ class ExtendedTransport < Transport
97
+
98
+ # The UnexpectedStatusCodeError is raised if the :expected_status_code option is given to
99
+ # the :request method and the responded status code is different from the expected one.
100
+ class UnexpectedStatusCodeError < StandardError
101
+
102
+ attr_reader :status_code
103
+ attr_reader :message
104
+
105
+ def initialize(status_code, message)
106
+ @status_code, @message = status_code, message
107
+ end
108
+
109
+ def to_s
110
+ "#{super} received status code #{self.status_code}" + (@message ? " [#{@message}]" : "")
111
+ end
112
+
113
+ end
114
+
115
+ attr_reader :expected_status_code
116
+ attr_reader :auth_type
117
+ attr_reader :username
118
+ attr_reader :password
119
+
120
+ def initialize(http_method, url, options = { })
121
+ super http_method, url, options
122
+ @expected_status_code = options[:expected_status_code]
123
+ @auth_type = options[:auth_type]
124
+ @username = options[:username]
125
+ @password = options[:password]
126
+ end
127
+
128
+ def perform
129
+ initialize_headers
130
+ super
131
+ check_status_code
132
+ parse_response
133
+ end
134
+
135
+ private
136
+
137
+ def initialize_headers
138
+ if @auth_type == :basic
139
+ @headers["Authorization"] = "Basic " + Base64.encode64("#{@username}:#{@password}")
140
+ elsif !@auth_type.nil?
141
+ raise NotImplementedError, "the given auth_type [#{@auth_type}] is not implemented"
142
+ end
143
+ @headers["Accept"] = "application/json"
144
+ end
145
+
146
+ def initialize_request_body
147
+ super
148
+ if @body
149
+ @request.body = @body.to_json
150
+ @request["Content-Type"] = "application/json"
151
+ end
152
+ end
153
+
154
+ def check_status_code
155
+ return unless @expected_status_code
156
+ response_code = @response.code
157
+ response_body = @response.body
158
+ raise UnexpectedStatusCodeError.new(response_code.to_i, response_body) if @expected_status_code.to_s != response_code
159
+ end
160
+
161
+ def parse_response
162
+ @response = @response.body.nil? ? nil : JSON.parse(@response.body)
163
+ rescue JSON::ParserError
164
+ @response = @response.body.to_s
165
+ end
166
+
167
+ end
168
+
169
+ end