orient_db_client 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 (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