agraph 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -9,8 +9,8 @@ task :default => :spec
9
9
 
10
10
  specification = Gem::Specification.new do |specification|
11
11
  specification.name = "agraph"
12
- specification.version = "0.1.0"
13
- specification.date = "2010-04-05"
12
+ specification.version = "0.1.1"
13
+ specification.date = "2010-07-05"
14
14
  specification.email = "b.phifty@gmail.com"
15
15
  specification.homepage = "http://github.com/phifty/agraph"
16
16
  specification.summary = "Client for the AllegroGraph 4.x graph database."
@@ -46,4 +46,4 @@ namespace :spec do
46
46
  task.spec_files = FileList["spec/integration/**/*_spec.rb"]
47
47
  end
48
48
 
49
- end
49
+ end
data/lib/allegro_graph.rb CHANGED
@@ -1,5 +1,14 @@
1
- require File.join(File.dirname(__FILE__), "allegro_graph", "server")
2
- require File.join(File.dirname(__FILE__), "allegro_graph", "catalog")
3
- require File.join(File.dirname(__FILE__), "allegro_graph", "repository")
4
- require File.join(File.dirname(__FILE__), "allegro_graph", "session")
5
- require File.join(File.dirname(__FILE__), "allegro_graph", "federation")
1
+
2
+ module AllegroGraph
3
+
4
+ autoload :Transport, File.join(File.dirname(__FILE__), "allegro_graph", "transport")
5
+ autoload :ExtendedTransport, File.join(File.dirname(__FILE__), "allegro_graph", "extended_transport")
6
+ autoload :Server, File.join(File.dirname(__FILE__), "allegro_graph", "server")
7
+ autoload :Catalog, File.join(File.dirname(__FILE__), "allegro_graph", "catalog")
8
+ autoload :Resource, File.join(File.dirname(__FILE__), "allegro_graph", "resource")
9
+ autoload :Repository, File.join(File.dirname(__FILE__), "allegro_graph", "repository")
10
+ autoload :Session, File.join(File.dirname(__FILE__), "allegro_graph", "session")
11
+ autoload :Proxy, File.join(File.dirname(__FILE__), "allegro_graph", "proxy")
12
+ autoload :Utility, File.join(File.dirname(__FILE__), "allegro_graph", "utility")
13
+
14
+ end
@@ -1,4 +1,3 @@
1
- require File.join(File.dirname(__FILE__), "repository")
2
1
 
3
2
  module AllegroGraph
4
3
 
@@ -0,0 +1,84 @@
1
+ require 'json'
2
+
3
+ module AllegroGraph
4
+
5
+ # Extended transport layer for http transfers. Basic authorization and JSON transfers are supported.
6
+ class ExtendedTransport < Transport
7
+
8
+ # The UnexpectedStatusCodeError is raised if the :expected_status_code option is given to
9
+ # the :request method and the responded status code is different from the expected one.
10
+ class UnexpectedStatusCodeError < StandardError
11
+
12
+ attr_reader :status_code
13
+ attr_reader :message
14
+
15
+ def initialize(status_code, message)
16
+ @status_code, @message = status_code, message
17
+ end
18
+
19
+ def to_s
20
+ "#{super} received status code #{self.status_code}" + (@message ? " [#{@message}]" : "")
21
+ end
22
+
23
+ end
24
+
25
+ attr_reader :expected_status_code
26
+ attr_reader :auth_type
27
+ attr_reader :username
28
+ attr_reader :password
29
+
30
+ def initialize(http_method, url, options = { })
31
+ super http_method, url, options
32
+ @expected_status_code = options[:expected_status_code]
33
+ @auth_type = options[:auth_type]
34
+ @username = options[:username]
35
+ @password = options[:password]
36
+ end
37
+
38
+ def perform
39
+ initialize_headers
40
+ super
41
+ check_status_code
42
+ parse_response
43
+ end
44
+
45
+ private
46
+
47
+ def initialize_headers
48
+ @headers["Accept"] = "application/json"
49
+ end
50
+
51
+ def initialize_request
52
+ super
53
+ if @auth_type == :basic
54
+ @request.basic_auth @username, @password
55
+ elsif !@auth_type.nil?
56
+ raise NotImplementedError, "the given auth_type [#{@auth_type}] is not implemented"
57
+ end
58
+ end
59
+
60
+ def initialize_request_body
61
+ super
62
+ if @body
63
+ @request.body = @body.to_json
64
+ @request["Content-Type"] = "application/json"
65
+ end
66
+ end
67
+
68
+ def check_status_code
69
+ return unless @expected_status_code
70
+ response_code = @response.code
71
+ response_body = @response.body
72
+ raise UnexpectedStatusCodeError.new(response_code.to_i, response_body) if @expected_status_code.to_s != response_code
73
+ end
74
+
75
+ def parse_response
76
+ body = @response.body
77
+ @response = body.nil? ? nil : JSON.parse(body)
78
+ rescue JSON::ParserError
79
+ @response = body.to_s
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,13 @@
1
+
2
+ module AllegroGraph
3
+
4
+ module Proxy
5
+
6
+ autoload :Statements, File.join(File.dirname(__FILE__), "proxy", "statements")
7
+ autoload :Query, File.join(File.dirname(__FILE__), "proxy", "query")
8
+ autoload :Geometric, File.join(File.dirname(__FILE__), "proxy", "geometric")
9
+ autoload :Mapping, File.join(File.dirname(__FILE__), "proxy", "mapping")
10
+
11
+ end
12
+
13
+ end
@@ -1,4 +1,3 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), "..", "utility", "parameter_mapper"))
2
1
 
3
2
  module AllegroGraph
4
3
 
@@ -1,33 +1,19 @@
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", "geometric")
6
- require File.join(File.dirname(__FILE__), "proxy", "mapping")
7
1
 
8
2
  module AllegroGraph
9
3
 
10
4
  # The Repository class wrap the corresponding resource on the AllegroGraph server. A repository acts as a scope for
11
5
  # statements. Simple management methods are provided.
12
- class Repository
6
+ class Repository < Resource
13
7
 
14
8
  attr_reader :server
15
9
  attr_reader :catalog
16
10
  attr_accessor :name
17
11
 
18
- attr_reader :statements
19
- attr_reader :query
20
- attr_reader :geometric
21
- attr_reader :mapping
22
-
23
- def initialize(server_or_catalog, name, options = { })
24
- @catalog = server_or_catalog.is_a?(AllegroGraph::Server) ? server_or_catalog.root_catalog : server_or_catalog
25
- @server = @catalog.server
26
- @name = name
27
- @statements = Proxy::Statements.new self
28
- @query = Proxy::Query.new self
29
- @geometric = Proxy::Geometric.new self
30
- @mapping = Proxy::Mapping.new self
12
+ def initialize(server_or_catalog, name)
13
+ super
14
+ @catalog = server_or_catalog.is_a?(AllegroGraph::Server) ? server_or_catalog.root_catalog : server_or_catalog
15
+ @server = @catalog.server
16
+ @name = name
31
17
  end
32
18
 
33
19
  def ==(other)
@@ -0,0 +1,22 @@
1
+
2
+ module AllegroGraph
3
+
4
+ # The Resource class acts as a base for statement resources. It provide proxy classes that provide methods
5
+ # to manipulate that statements.
6
+ class Resource
7
+
8
+ attr_reader :statements
9
+ attr_reader :query
10
+ attr_reader :geometric
11
+ attr_reader :mapping
12
+
13
+ def initialize(*arguments)
14
+ @statements = Proxy::Statements.new self
15
+ @query = Proxy::Query.new self
16
+ @geometric = Proxy::Geometric.new self
17
+ @mapping = Proxy::Mapping.new self
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -1,6 +1,3 @@
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
1
 
5
2
  module AllegroGraph
6
3
 
@@ -48,11 +45,6 @@ module AllegroGraph
48
45
  result
49
46
  end
50
47
 
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
48
  def url
57
49
  "http://#{@host}:#{@port}"
58
50
  end
@@ -1,32 +1,18 @@
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", "geometric")
5
- require File.join(File.dirname(__FILE__), "proxy", "mapping")
6
1
 
7
2
  module AllegroGraph
8
3
 
9
4
  # The Session class wrap the corresponding resource on the AllegroGraph server.
10
- class Session
5
+ class Session < Resource
11
6
 
12
7
  attr_reader :url
13
8
  attr_reader :username
14
9
  attr_reader :password
15
10
 
16
- attr_reader :statements
17
- attr_reader :query
18
- attr_reader :geometric
19
- attr_reader :mapping
20
-
21
11
  def initialize(options = { })
12
+ super
22
13
  @url = options[:url]
23
14
  @username = options[:username]
24
15
  @password = options[:password]
25
-
26
- @statements = Proxy::Statements.new self
27
- @query = Proxy::Query.new self
28
- @geometric = Proxy::Geometric.new self
29
- @mapping = Proxy::Mapping.new self
30
16
  end
31
17
 
32
18
  def path
@@ -1,7 +1,6 @@
1
1
  require 'uri'
2
2
  require 'cgi'
3
3
  require 'net/http'
4
- require 'json'
5
4
 
6
5
  module AllegroGraph
7
6
 
@@ -98,83 +97,4 @@ module AllegroGraph
98
97
 
99
98
  end
100
99
 
101
- # Extended transport layer for http transfers. Basic authorization and JSON transfers are supported.
102
- class ExtendedTransport < Transport
103
-
104
- # The UnexpectedStatusCodeError is raised if the :expected_status_code option is given to
105
- # the :request method and the responded status code is different from the expected one.
106
- class UnexpectedStatusCodeError < StandardError
107
-
108
- attr_reader :status_code
109
- attr_reader :message
110
-
111
- def initialize(status_code, message)
112
- @status_code, @message = status_code, message
113
- end
114
-
115
- def to_s
116
- "#{super} received status code #{self.status_code}" + (@message ? " [#{@message}]" : "")
117
- end
118
-
119
- end
120
-
121
- attr_reader :expected_status_code
122
- attr_reader :auth_type
123
- attr_reader :username
124
- attr_reader :password
125
-
126
- def initialize(http_method, url, options = { })
127
- super http_method, url, options
128
- @expected_status_code = options[:expected_status_code]
129
- @auth_type = options[:auth_type]
130
- @username = options[:username]
131
- @password = options[:password]
132
- end
133
-
134
- def perform
135
- initialize_headers
136
- super
137
- check_status_code
138
- parse_response
139
- end
140
-
141
- private
142
-
143
- def initialize_headers
144
- @headers["Accept"] = "application/json"
145
- end
146
-
147
- def initialize_request
148
- super
149
- if @auth_type == :basic
150
- @request.basic_auth @username, @password
151
- elsif !@auth_type.nil?
152
- raise NotImplementedError, "the given auth_type [#{@auth_type}] is not implemented"
153
- end
154
- end
155
-
156
- def initialize_request_body
157
- super
158
- if @body
159
- @request.body = @body.to_json
160
- @request["Content-Type"] = "application/json"
161
- end
162
- end
163
-
164
- def check_status_code
165
- return unless @expected_status_code
166
- response_code = @response.code
167
- response_body = @response.body
168
- raise UnexpectedStatusCodeError.new(response_code.to_i, response_body) if @expected_status_code.to_s != response_code
169
- end
170
-
171
- def parse_response
172
- body = @response.body
173
- @response = body.nil? ? nil : JSON.parse(body)
174
- rescue JSON::ParserError
175
- @response = body.to_s
176
- end
177
-
178
- end
179
-
180
100
  end
@@ -0,0 +1,10 @@
1
+
2
+ module AllegroGraph
3
+
4
+ module Utility
5
+
6
+ autoload :ParameterMapper, File.join(File.dirname(__FILE__), "utility", "parameter_mapper")
7
+
8
+ end
9
+
10
+ end
@@ -52,33 +52,6 @@
52
52
  :response:
53
53
  :code: "201"
54
54
  :body: ""
55
- -
56
- :http_method: "get"
57
- :url: "http://localhost:10035/federated"
58
- :response:
59
- :code: "200"
60
- :body:
61
- -
62
- "uri": "http://localhost:10035/federated/test_federation"
63
- "id": "test_federation"
64
- "title": "test_federation"
65
- "readable": true
66
- "writable": false
67
- -
68
- :http_method: "put"
69
- :url: "http://localhost:10035/federated/test_federation"
70
- :parameters:
71
- :repo: [ "test_repository" ]
72
- :url: [ "http://username:password@server:10035/repositories/another" ]
73
- :response:
74
- :code: "204"
75
- :body: ""
76
- -
77
- :http_method: "delete"
78
- :url: "http://localhost:10035/federated/test_federation"
79
- :response:
80
- :code: "204"
81
- :body: ""
82
55
  -
83
56
  :http_method: "get"
84
57
  :url: "http://localhost:10035/catalogs/test_catalog/repositories"
@@ -0,0 +1,137 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib", "allegro_graph"))
3
+
4
+ describe "geo-spatial data" do
5
+
6
+ use_real_transport!
7
+
8
+ before :each do
9
+ @server = AllegroGraph::Server.new :username => "test", :password => "test"
10
+ @repository = AllegroGraph::Repository.new @server, "test_repository"
11
+ @repository.create_if_missing!
12
+ @geometric = @repository.geometric
13
+ @statements = @repository.statements
14
+ end
15
+
16
+ describe "types" do
17
+
18
+ it "should provide a cartesian type" do
19
+ result = @geometric.cartesian_type :strip_width => 1.0,
20
+ :x_min => 2.0,
21
+ :y_min => 2.0,
22
+ :x_max => 20.0,
23
+ :y_max => 20.0
24
+ result.should == "<http://franz.com/ns/allegrograph/3.0/geospatial/cartesian/2.0/20.0/2.0/20.0/1.0>"
25
+ end
26
+
27
+ it "should provide a spherical type" do
28
+ result = @geometric.spherical_type :strip_width => 1.0,
29
+ :latitude_min => 2.0,
30
+ :longitude_min => 2.0,
31
+ :latitude_max => 20.0,
32
+ :longitude_max => 20.0
33
+ result.should == "<http://franz.com/ns/allegrograph/3.0/geospatial/spherical/degrees/2.0/20.0/2.0/20.0/1.0>"
34
+ end
35
+
36
+ end
37
+
38
+ describe "creating polygon" do
39
+
40
+ before :each do
41
+ @type = @geometric.cartesian_type :strip_width => 1.0,
42
+ :x_min => 2.0,
43
+ :y_min => 2.0,
44
+ :x_max => 20.0,
45
+ :y_max => 20.0
46
+ @polygon = [ [ 2.0, 2.0 ], [ 11.0, 2.0 ], [ 11.0, 11.0 ], [ 2.0, 11.0 ] ]
47
+ end
48
+
49
+ it "should return true" do
50
+ result = @geometric.create_polygon @polygon, :name => "test_polygon", :type => @type
51
+ result.should be_true
52
+ end
53
+
54
+ end
55
+
56
+ context "in a cartesian system" do
57
+
58
+ before :each do
59
+ @type = @geometric.cartesian_type :strip_width => 1.0,
60
+ :x_min => 2.0,
61
+ :y_min => 2.0,
62
+ :x_max => 20.0,
63
+ :y_max => 20.0
64
+ @statements.create "\"test_subject\"", "\"at\"", "\"+10+10\"^^#{@type}"
65
+ @statements.create "\"another_subject\"", "\"at\"", "\"+15+15\"^^#{@type}"
66
+ end
67
+
68
+ it "should find objects inside a box" do
69
+ result = @statements.find_inside_box :type => @type,
70
+ :predicate => "\"at\"",
71
+ :x_min => 8.0,
72
+ :y_min => 8.0,
73
+ :x_max => 11.0,
74
+ :y_max => 11.0
75
+ result.should include([ "\"test_subject\"", "\"at\"", "\"+10.000000000931323+10.000000000931323\"^^#{@type}"])
76
+ result.should_not include([ "\"another_subject\"", "\"at\"", "\"+15.000000000465661+15.000000000465661\"^^#{@type}"])
77
+ end
78
+
79
+ it "should find objects inside a circle" do
80
+ result = @statements.find_inside_circle :type => @type,
81
+ :predicate => "\"at\"",
82
+ :x => 9.0,
83
+ :y => 9.0,
84
+ :radius => 2.0
85
+ result.should include([ "\"test_subject\"", "\"at\"", "\"+10.000000000931323+10.000000000931323\"^^#{@type}"])
86
+ result.should_not include([ "\"another_subject\"", "\"at\"", "\"+15.000000000465661+15.000000000465661\"^^#{@type}"])
87
+ end
88
+
89
+ context "with a defined polygon" do
90
+
91
+ before :each do
92
+ @type = @geometric.cartesian_type :strip_width => 1,
93
+ :x_min => -100,
94
+ :y_min => -100,
95
+ :x_max => 100,
96
+ :y_max => 100
97
+ @statements.create "\"test_subject\"", "\"at\"", "\"+1+1\"^^#{@type}"
98
+ @geometric.create_polygon [ [ 0, -100 ], [ 0, 100 ], [ 100, 100 ], [ 100, -100 ] ],
99
+ :name => "test_polygon",
100
+ :type => @type
101
+ end
102
+
103
+ it "should find objects inside that polygon" do
104
+ result = @statements.find_inside_polygon :type => @type,
105
+ :predicate => "\"at\"",
106
+ :polygon_name => "test_polygon"
107
+ result.should include([ "\"test_subject\"", "\"at\"", "\"+0.9999999776482582+0.9999999776482582\"^^<http://franz.com/ns/allegrograph/3.0/geospatial/cartesian/-100.0/100.0/-100.0/100.0/1.0>"])
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+
114
+ context "in a spherical system" do
115
+
116
+ before :each do
117
+ @type = @geometric.spherical_type :strip_width => 1.0,
118
+ :latitude_min => 2.0,
119
+ :longitude_min => 2.0,
120
+ :latitude_max => 20.0,
121
+ :longitude_max => 20.0
122
+ @statements.create "\"test_subject\"", "\"at\"", "\"+10.00+010.00\"^^#{@type}"
123
+ end
124
+
125
+ it "should find objects inside a haversine" do
126
+ result = @statements.find_inside_haversine :type => @type,
127
+ :predicate => "\"at\"",
128
+ :latitude => 9.0,
129
+ :longitude => 9.0,
130
+ :radius => 200.0,
131
+ :unit => :km
132
+ result.should include([ "\"test_subject\"", "\"at\"", "\"+100000+0100000\"^^<http://franz.com/ns/allegrograph/3.0/geospatial/spherical/degrees/2.0/20.0/2.0/20.0/1.0>"])
133
+ end
134
+
135
+ end
136
+
137
+ end