orient_db_client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/README.md +71 -0
  4. data/Rakefile +23 -0
  5. data/lib/orient_db_client/connection.rb +141 -0
  6. data/lib/orient_db_client/database_session.rb +107 -0
  7. data/lib/orient_db_client/deserializers/deserializer7.rb +166 -0
  8. data/lib/orient_db_client/exceptions.rb +25 -0
  9. data/lib/orient_db_client/network_message.rb +42 -0
  10. data/lib/orient_db_client/protocol_factory.rb +16 -0
  11. data/lib/orient_db_client/protocols/.DS_Store +0 -0
  12. data/lib/orient_db_client/protocols/protocol7.rb +571 -0
  13. data/lib/orient_db_client/protocols/protocol9.rb +79 -0
  14. data/lib/orient_db_client/rid.rb +39 -0
  15. data/lib/orient_db_client/serializers/serializer7.rb +208 -0
  16. data/lib/orient_db_client/server_session.rb +17 -0
  17. data/lib/orient_db_client/session.rb +10 -0
  18. data/lib/orient_db_client/types.rb +22 -0
  19. data/lib/orient_db_client/version.rb +3 -0
  20. data/lib/orient_db_client.rb +18 -0
  21. data/orient_db_client.gemspec +24 -0
  22. data/test/integration/connection_test.rb +18 -0
  23. data/test/integration/database_session_9_test.rb +82 -0
  24. data/test/integration/database_session_test.rb +222 -0
  25. data/test/integration/open_database_test.rb +28 -0
  26. data/test/integration/open_server_test.rb +24 -0
  27. data/test/integration/server_session_test.rb +37 -0
  28. data/test/support/connection_helper.rb +25 -0
  29. data/test/support/create_database.sql +33 -0
  30. data/test/support/databases.yml +8 -0
  31. data/test/support/expectation_helper.rb +13 -0
  32. data/test/support/protocol_helper.rb +25 -0
  33. data/test/support/server_config.rb +6 -0
  34. data/test/support/servers-template.yml +5 -0
  35. data/test/test_helper.rb +9 -0
  36. data/test/unit/connection_test.rb +84 -0
  37. data/test/unit/deserializers/deserializer7_test.rb +141 -0
  38. data/test/unit/network_message_test.rb +24 -0
  39. data/test/unit/orient_db_client_test.rb +16 -0
  40. data/test/unit/protocol_factory_test.rb +14 -0
  41. data/test/unit/protocols/protocol7_test.rb +658 -0
  42. data/test/unit/protocols/protocol9_test.rb +149 -0
  43. data/test/unit/rid_test.rb +32 -0
  44. data/test/unit/serializers/serializer7_test.rb +72 -0
  45. metadata +167 -0
@@ -0,0 +1,208 @@
1
+ require 'orient_db_client/rid'
2
+
3
+ require 'base64'
4
+
5
+ module OrientDbClient
6
+ module Serializers
7
+ class Serializer7
8
+ @@string_matcher = /^"[^"]*"$/
9
+
10
+ def serialize(record)
11
+ unless record[:document]
12
+ record = { :document => record }
13
+ end
14
+
15
+ serialize_document(record)
16
+ end
17
+
18
+ private
19
+
20
+ def binary_encoding
21
+ @binary_encoding = @binary_encoding || Encoding.find("ASCII-8BIT")
22
+ end
23
+
24
+ def serialize_document(record, embedded = false)
25
+ structure = record[:structure] || {}
26
+ result = []
27
+ recordClass = ''
28
+ document = nil
29
+
30
+ if record[:document]
31
+ recordClass = record[:class] ? "#{record[:class]}@" : ""
32
+ document = record[:document]
33
+ else
34
+ document = record
35
+ end
36
+
37
+ document.each do |k, v|
38
+ result << "#{k}:#{serialize_item(v, structure[k])}"
39
+ end
40
+
41
+ serialized_document = "#{recordClass}#{result.join(",")}"
42
+
43
+ serialized_document = "(#{serialized_document})" if embedded
44
+
45
+ serialized_document
46
+ end
47
+
48
+ def serialize_array(value)
49
+ result = []
50
+
51
+ value.each do |item|
52
+ result << serialize_item(item)
53
+ end
54
+
55
+ "[#{result.join(",")}]"
56
+ end
57
+
58
+ def serialize_binary(value)
59
+ "_#{Base64.encode64(value).chomp}_"
60
+ end
61
+
62
+ def serialize_boolean(value)
63
+ value ? "true" : "false"
64
+ end
65
+
66
+ def serialize_byte(value)
67
+ "#{value.ord}b"
68
+ end
69
+
70
+ def serialize_date(value)
71
+ if value.is_a?(Time)
72
+ value = Date.new(value.year, value.month, value.day)
73
+ end
74
+
75
+ value = Date.parse(value) unless value.is_a?(Date)
76
+
77
+ "#{value.to_time.to_i}a"
78
+ end
79
+
80
+ def serialize_decimal(value)
81
+ "#{value}c"
82
+ end
83
+
84
+ def serialize_double(value)
85
+ "#{value}d"
86
+ end
87
+
88
+ def serialize_float(value)
89
+ "#{value}f"
90
+ end
91
+
92
+ def serialize_item(value, type = nil)
93
+ case type
94
+ when :binary
95
+ serialize_binary(value)
96
+ when :boolean
97
+ serialize_boolean(value)
98
+ when :byte
99
+ serialize_byte(value)
100
+ when :collection
101
+ serialize_array(value)
102
+ when :date
103
+ serialize_date(value)
104
+ when :decimal
105
+ serialize_decimal(value)
106
+ when :document
107
+ serialize_document(value, true)
108
+ when :double
109
+ serialize_double(value)
110
+ when :float
111
+ serialize_float(value)
112
+ when :long
113
+ serialize_long(value)
114
+ when :map
115
+ serialize_map(value)
116
+ when :rid
117
+ serialize_rid(value)
118
+ when :short
119
+ serialize_short(value)
120
+ when :string
121
+ serialize_string(value)
122
+ when :time
123
+ serialize_time(value)
124
+ else
125
+ serialize_unknown(value)
126
+ end
127
+ end
128
+
129
+ def serialize_integer(value)
130
+ value.to_s
131
+ end
132
+
133
+ def serialize_long(value)
134
+ "#{value}l"
135
+ end
136
+
137
+ def serialize_map(value)
138
+ result = []
139
+ value.each do |k, v|
140
+ result << "\"#{k.to_s}\":#{serialize_unknown(v)}"
141
+ end
142
+
143
+ "{#{result.join(",")}}"
144
+ end
145
+
146
+ def serialize_nil
147
+ ''
148
+ end
149
+
150
+ def serialize_rid(value)
151
+ value = OrientDbClient::Rid.new(value.to_s) unless value.is_a?(OrientDbClient::Rid)
152
+
153
+ "#{value.to_s}"
154
+ end
155
+
156
+ def serialize_short(value)
157
+ "#{value}s"
158
+ end
159
+
160
+ def serialize_string(value)
161
+ "\"#{value.to_s}\""
162
+ end
163
+
164
+ def serialize_time(value)
165
+ value = DateTime.parse(value) if value.is_a?(String)
166
+ value = value.to_time if value.is_a?(DateTime)
167
+
168
+ "#{value.to_i}t"
169
+ end
170
+
171
+ def serialize_unknown(value)
172
+ if value.is_a?(OrientDbClient::Rid)
173
+ serialize_rid(value)
174
+ elsif value.is_a?(String)
175
+ if value.encoding.equal?(binary_encoding)
176
+ serialize_binary(value)
177
+ else
178
+ serialize_string(value)
179
+ end
180
+ elsif value.is_a?(Fixnum)
181
+ serialize_integer(value)
182
+ elsif value.is_a?(TrueClass)
183
+ serialize_boolean(value)
184
+ elsif value.is_a?(FalseClass)
185
+ serialize_boolean(value)
186
+ elsif value.is_a?(Float)
187
+ serialize_double(value)
188
+ elsif value.is_a?(Bignum)
189
+ serialize_long(value)
190
+ elsif value.is_a?(Array)
191
+ serialize_array(value)
192
+ elsif value.is_a?(Time)
193
+ serialize_time(value)
194
+ elsif value.is_a?(Date)
195
+ serialize_date(value)
196
+ elsif value.is_a?(Hash)
197
+ if value[:document]
198
+ serialize_document(value, true)
199
+ else
200
+ serialize_map(value)
201
+ end
202
+ elsif value.nil?
203
+ serialize_nil
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,17 @@
1
+ require 'orient_db_client/session'
2
+
3
+ module OrientDbClient
4
+ class ServerSession < Session
5
+ def create_local_database(database, options = {})
6
+ @connection.create_database(@id, database)
7
+ end
8
+
9
+ def database_exists?(database)
10
+ @connection.database_exists?(@id, database)
11
+ end
12
+
13
+ def delete_database(database)
14
+ @connection.delete_database(@id, database)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ module OrientDbClient
2
+ class Session
3
+ attr_reader :id
4
+
5
+ def initialize(id, connection)
6
+ @id = id
7
+ @connection = connection
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ module OrientDbClient
2
+ module Types
3
+ [ :BINARY,
4
+ :BOOLEAN,
5
+ :BYTE,
6
+ :COLLECTION,
7
+ :DATE,
8
+ :DECIMAL,
9
+ :DOCUMENT,
10
+ :DOUBLE,
11
+ :FLOAT,
12
+ :LONG,
13
+ :MAP,
14
+ :RID,
15
+ :SHORT,
16
+ :STRING,
17
+ :TIME ].each_with_index do |c, i|
18
+
19
+ const_set c, i
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module OrientDbClient
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,18 @@
1
+ require "orient_db_client/connection"
2
+ require "orient_db_client/version"
3
+ require "orient_db_client/rid"
4
+
5
+ require "socket"
6
+
7
+ module OrientDbClient
8
+ def connect(host, options = {})
9
+ options = { port: 2424 }.merge(options)
10
+
11
+ s = TCPSocket.open(host, options[:port])
12
+
13
+ protocol = s.read(2).unpack('s>').first
14
+
15
+ Connection.new(s, protocol)
16
+ end
17
+ module_function :connect
18
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "orient_db_client/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "orient_db_client"
7
+ s.version = OrientDbClient::VERSION
8
+ s.authors = ["Ryan Fields"]
9
+ s.email = ["ryan.fields@twoleftbeats.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Network Binary Protocol client for OrientDB Server}
12
+ s.description = %q{This gem uses the OrientDB Network Binary Protocol to provide connectivity to an OrientDB Server}
13
+
14
+ s.rubyforge_project = "orient_db_client"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "minitest"
22
+ s.add_development_dependency "mocha"
23
+ s.add_development_dependency "rake"
24
+ end
@@ -0,0 +1,18 @@
1
+ require File.join File.dirname(__FILE__), '..', 'test_helper'
2
+
3
+ class TestConnection < MiniTest::Unit::TestCase
4
+ include ServerConfig
5
+ include ConnectionHelper
6
+
7
+ def setup
8
+ @connection = connect_to_orientdb(SERVER_OPTIONS)
9
+ end
10
+
11
+ def teardown
12
+ @connection.close if @connection
13
+ end
14
+
15
+ def test_establishing_a_connection
16
+ assert_instance_of OrientDbClient::Connection, @connection
17
+ end
18
+ end
@@ -0,0 +1,82 @@
1
+ require File.join File.dirname(__FILE__), '..', 'test_helper'
2
+
3
+ class TestDatabaseSession < MiniTest::Unit::TestCase
4
+ include ServerConfig
5
+ include ConnectionHelper
6
+
7
+ def setup
8
+ @options = SERVER_OPTIONS
9
+ @connection = connect_to_orientdb(SERVER_OPTIONS)
10
+
11
+ @session = @connection.open_database(@options["database"], {
12
+ :user => @options["user"],
13
+ :password => @options["password"]
14
+ })
15
+ end
16
+
17
+ def teardown
18
+ @connection.close if @connection
19
+ end
20
+
21
+ # The protocol documentation for DB_CLOSE is very ambiguous.
22
+ # As such, this test doesn't really do anything that makes sense...
23
+ def test_close
24
+ @session.close
25
+
26
+ refute @connection.closed?
27
+ end
28
+
29
+ def test_command
30
+ result = @session.command("SELECT FROM OUser")
31
+
32
+ assert_equal @session.id, result[:session]
33
+
34
+ result[:message_content].tap do |content|
35
+ assert_equal 3, content.length
36
+
37
+ content[0].tap do |record|
38
+ assert_equal 0, record[:format]
39
+ assert_equal 4, record[:cluster_id]
40
+ assert_equal 0, record[:cluster_position]
41
+
42
+ record[:document].tap do |doc|
43
+ assert_equal 'admin', doc['name']
44
+ assert_equal 'ACTIVE', doc['status']
45
+
46
+ doc['roles'].tap do |roles|
47
+ assert roles.is_a?(Array), "expected Array, but got #{roles.class}"
48
+
49
+ assert roles[0].is_a?(OrientDbClient::Rid)
50
+ assert_equal 3, roles[0].cluster_id
51
+ assert_equal 0, roles[0].cluster_position
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def test_load_record
59
+ result = @session.load_record("#4:0")
60
+
61
+ assert_equal @session.id, result[:session]
62
+
63
+ result[:message_content].tap do |record|
64
+ assert_equal 4, record[:cluster_id]
65
+ assert_equal 0, record[:cluster_position]
66
+
67
+ record[:document].tap do |doc|
68
+ assert_equal 'admin', doc['name']
69
+ assert_equal 'ACTIVE', doc['status']
70
+
71
+ doc['roles'].tap do |roles|
72
+ assert roles.is_a?(Array), "expected Array, but got #{roles.class}"
73
+
74
+ assert roles[0].is_a?(OrientDbClient::Rid)
75
+ assert_equal 3, roles[0].cluster_id
76
+ assert_equal 0, roles[0].cluster_position
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,222 @@
1
+ require File.join File.dirname(__FILE__), '..', 'test_helper'
2
+
3
+ class TestDatabaseSession < MiniTest::Unit::TestCase
4
+ include ServerConfig
5
+ include ConnectionHelper
6
+
7
+ def setup
8
+ @options = SERVER_OPTIONS
9
+ @connection = connect_to_orientdb(SERVER_OPTIONS)
10
+
11
+ @session = @connection.open_database(@options["database"], {
12
+ :user => @options["user"],
13
+ :password => @options["password"]
14
+ })
15
+ end
16
+
17
+ def teardown
18
+ @connection.close if @connection
19
+ end
20
+
21
+ def ensure_cluster_exists(session, cluster_name)
22
+ if session.cluster_exists?(cluster_name)
23
+ cluster = session.get_cluster(cluster_name)
24
+ cluster[:id]
25
+ else
26
+ session.create_physical_cluster(cluster_name)
27
+ end
28
+ end
29
+
30
+ def ensure_cluster_does_not_exist(session, cluster_name)
31
+ if session.cluster_exists?(cluster_name)
32
+ session.delete_cluster(session.get_cluster(cluster_name)[:id])
33
+ session.reload # I know. Ridiculous
34
+ end
35
+ end
36
+
37
+ # The protocol documentation for DB_CLOSE is very ambiguous.
38
+ # As such, this test doesn't really do anything that makes sense...
39
+ def test_close
40
+ @session.close
41
+
42
+ refute @connection.closed?
43
+ end
44
+
45
+ def test_cluster_exists?
46
+ # Preform a bit of pre-test cleanup
47
+ # FIXME: This won't be needed once we have some kind of DB clean in place
48
+ cluster = @session.get_cluster("OTest")
49
+ @session.delete_cluster(cluster[:id]) unless cluster.nil?
50
+
51
+ assert @session.cluster_exists?(0)
52
+ refute @session.cluster_exists?("OTest")
53
+ end
54
+
55
+ def test_command
56
+ result = @session.command("SELECT FROM OUser")
57
+
58
+ assert_equal @session.id, result[:session]
59
+
60
+ result[:message_content].tap do |content|
61
+ assert_equal 3, content.length
62
+
63
+ content[0].tap do |record|
64
+ assert_equal 0, record[:format]
65
+ assert_equal 4, record[:cluster_id]
66
+ assert_equal 0, record[:cluster_position]
67
+
68
+ record[:document].tap do |doc|
69
+ assert_equal 'admin', doc['name']
70
+ assert_equal 'ACTIVE', doc['status']
71
+
72
+ doc['roles'].tap do |roles|
73
+ assert roles.is_a?(Array), "expected Array, but got #{roles.class}"
74
+
75
+ assert roles[0].is_a?(OrientDbClient::Rid)
76
+ assert_equal 3, roles[0].cluster_id
77
+ assert_equal 0, roles[0].cluster_position
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ def test_count
85
+ result = @session.count("ORole")
86
+
87
+ assert_equal @session.id, result[:session]
88
+ assert_equal 3, result[:message_content][:record_count]
89
+ end
90
+
91
+ def test_create_cluster
92
+ cluster = "OTest"
93
+ ensure_cluster_does_not_exist(@session, cluster)
94
+
95
+ new_cluster = @session.create_physical_cluster(cluster)
96
+
97
+ assert_equal 6, new_cluster
98
+
99
+ assert @session.cluster_exists?(cluster)
100
+
101
+ # Cleanup
102
+ # FIXME: Necessary due to lack of DB clean strategy
103
+ @session.delete_cluster(new_cluster) unless new_cluster.nil?
104
+ end
105
+
106
+ def test_create_and_delete_record
107
+ cluster = "OTest"
108
+
109
+ ensure_cluster_exists(@session, cluster)
110
+ @session.reload
111
+
112
+ cluster_id = @session.get_cluster(cluster)[:id]
113
+ record = { :key1 => "value1" }
114
+
115
+ rid = @session.create_record(cluster_id, record)
116
+ created_record = @session.load_record(rid)
117
+
118
+ assert_equal cluster_id, rid.cluster_id
119
+ assert_equal 0, rid.cluster_position
120
+
121
+ refute_nil created_record
122
+ refute_nil created_record[:document]['key1']
123
+
124
+ assert_equal record[:key1], created_record[:document]['key1']
125
+
126
+ assert @session.delete_record(rid, created_record[:record_version])
127
+ assert_nil @session.load_record(rid)
128
+
129
+ ensure_cluster_does_not_exist(@session, cluster)
130
+ end
131
+
132
+ def test_delete_cluster
133
+ # FIXME: Messy due to lack of a proper DB load/clean strategy
134
+
135
+ cluster_id = ensure_cluster_exists(@session, "OTest")
136
+
137
+ result = @session.delete_cluster(cluster_id)
138
+
139
+ refute @session.cluster_exists?("OTest")
140
+ end
141
+
142
+ def test_get_cluster_by_id
143
+ cluster_id = 0
144
+ cluster_name = 'internal'
145
+
146
+ result = @session.get_cluster(cluster_id)
147
+
148
+ refute_nil result
149
+ assert_equal cluster_id, result[:id]
150
+ assert_equal cluster_name, result[:name]
151
+ end
152
+
153
+ def test_get_cluster_by_name
154
+ cluster_id = 0
155
+ cluster_name = 'internal'
156
+
157
+ result = @session.get_cluster(cluster_name)
158
+
159
+ refute_nil result
160
+ assert_equal cluster_id, result[:id]
161
+ assert_equal cluster_name, result[:name]
162
+ end
163
+
164
+ def test_get_cluster_noexist
165
+ cluster_id = 9000
166
+
167
+ result = @session.get_cluster(cluster_id)
168
+
169
+ assert_nil result
170
+ end
171
+
172
+ def test_get_cluster_datarange
173
+ cluster_id = 0
174
+ expected_begin = 0
175
+ expected_end = 2
176
+
177
+ result = @session.get_cluster_datarange(cluster_id)
178
+
179
+ result[:message_content].tap do |m|
180
+ assert expected_begin, m[:begin]
181
+ assert expected_end, m[:end]
182
+ end
183
+ end
184
+
185
+ def test_load_record
186
+ record = @session.load_record("#4:0")
187
+
188
+ assert_equal 4, record[:cluster_id]
189
+ assert_equal 0, record[:cluster_position]
190
+
191
+ record[:document].tap do |doc|
192
+ assert_equal 'admin', doc['name']
193
+ assert_equal 'ACTIVE', doc['status']
194
+
195
+ doc['roles'].tap do |roles|
196
+ assert roles.is_a?(Array), "expected Array, but got #{roles.class}"
197
+
198
+ assert roles[0].is_a?(OrientDbClient::Rid)
199
+ assert_equal 3, roles[0].cluster_id
200
+ assert_equal 0, roles[0].cluster_position
201
+ end
202
+ end
203
+ end
204
+
205
+ def test_reload
206
+ cluster = "OTest"
207
+
208
+ # FIXME: Messy due to lack of a proper DB load/clean strategy
209
+ ensure_cluster_does_not_exist(@session, cluster)
210
+
211
+ assert_nil @session.get_cluster(cluster)
212
+ @session.create_physical_cluster(cluster)
213
+
214
+ @session.reload
215
+
216
+ refute_nil @session.get_cluster(cluster)
217
+
218
+ # Cleanup
219
+ # FIXME: Necessary due to lack of DB clean strategy
220
+ @session.delete_cluster(@session.get_cluster(cluster)[:id])
221
+ end
222
+ end
@@ -0,0 +1,28 @@
1
+ require File.join File.dirname(__FILE__), '..', 'test_helper'
2
+
3
+ class TestOpenDatabase < MiniTest::Unit::TestCase
4
+ include ServerConfig
5
+ include ConnectionHelper
6
+
7
+ def setup
8
+ @options = SERVER_OPTIONS
9
+ @connection = connect_to_orientdb(@options)
10
+ end
11
+
12
+ def teardown
13
+ @connection.close if @connection
14
+ end
15
+
16
+ def test_open_database
17
+ session = @connection.open_database(@options["database"], {
18
+ :user => @options["user"],
19
+ :password => @options["password"]
20
+ })
21
+
22
+ refute_nil session.id
23
+
24
+ refute_nil session.get_cluster("orids")
25
+ refute_nil session.get_cluster("ouser")
26
+ refute_nil session.get_cluster("orole")
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ require File.join File.dirname(__FILE__), '..', 'test_helper'
2
+
3
+ class TestOpenServer < MiniTest::Unit::TestCase
4
+ include ServerConfig
5
+ include ConnectionHelper
6
+
7
+ def setup
8
+ @options = SERVER_OPTIONS
9
+ @connection = connect_to_orientdb(SERVER_OPTIONS)
10
+ end
11
+
12
+ def teardown
13
+ @connection.close if @connection
14
+ end
15
+
16
+ def test_connect_command
17
+ session = @connection.open_server({
18
+ :user => @options["server_user"],
19
+ :password => @options["server_password"]
20
+ })
21
+
22
+ refute_nil session.id
23
+ end
24
+ end