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
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in orientdb-client.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ OrientDB Client
2
+ ===============
3
+
4
+ This gem is a very rough initial attempt at a Network Binary Protocol driver for OrientDB.
5
+
6
+ While in this early stage of development, the public interface is subject to change with no warning.
7
+
8
+ This is my first attempt at building a gem intended for public use. Expect some difficulty getting this gem up and running in your projects.
9
+
10
+
11
+ Supported OrientDB Releases
12
+ ---------------------------
13
+
14
+ I've tested this against OrientDB-1.0rc9 which is the current official release as of writing this README.
15
+
16
+
17
+ Basic Usage
18
+ -----------
19
+
20
+
21
+ ## Server Operations
22
+
23
+ require 'orient_db_client`
24
+
25
+ connection = OrientDbClient.connect('0.0.0.0', { :port => 2424 })
26
+
27
+ server = connection.open_server({
28
+ :user => 'root',
29
+ :password => '<password>'
30
+ })
31
+
32
+ server.create_local_database("my_database")
33
+
34
+ server.delete_database(database_name)
35
+
36
+
37
+ ## Database Operations
38
+
39
+ require 'orient_db_client`
40
+
41
+ connection = OrientDbClient.connect('0.0.0.0', { :port => 2424 })
42
+
43
+ database = connection.open_database(database_name, {
44
+ :user => 'admin',
45
+ :password => 'admin'
46
+ })
47
+
48
+ cluster_id = database.create_physical_cluster("test")
49
+
50
+ record = { :document => { 'key1' => 'value1' } }
51
+
52
+ rid = database.create_record(cluster_id, record)
53
+
54
+ loaded_record = database.load_record(rid)
55
+ loaded_record[:document]['key1'] = 'updated'
56
+
57
+ version = database.update_record(loaded_record, rid, :incremental)
58
+
59
+ edited_record = database.load_record(rid)
60
+
61
+ database.delete_record(rid, edited_record[:record_version])
62
+
63
+
64
+ Testing
65
+ -------
66
+
67
+ rake test:unit
68
+
69
+ There are integration tests as well, but the testing strategy is extremely flawed. Start an instance of OrientDB before running. Most of the tests are run against the "temp" database, which is reset every time OrientDB is started. The integration test results should not be considered valid unless run against a fresh restart of the OrientDB server.
70
+
71
+ rake test:integration
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.name = 'test'
6
+ t.libs.push "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ Rake::TestTask.new do |t|
12
+ t.name = 'test:unit'
13
+ t.libs.push "lib"
14
+ t.test_files = FileList['test/unit/**/*_test.rb']
15
+ t.verbose = true
16
+ end
17
+
18
+ Rake::TestTask.new do |t|
19
+ t.name = 'test:integration'
20
+ t.libs.push "lib"
21
+ t.test_files = FileList['test/integration/**/*_test.rb']
22
+ t.verbose = true
23
+ end
@@ -0,0 +1,141 @@
1
+ require 'orient_db_client/database_session'
2
+ require 'orient_db_client/server_session'
3
+ require 'orient_db_client/protocol_factory'
4
+
5
+ module OrientDbClient
6
+ class Connection
7
+ def initialize(socket, protocol_version, options = {})
8
+ @socket = socket
9
+ @protocol = (options[:protocol_factory] || ProtocolFactory).get_protocol(protocol_version)
10
+ @sessions = {}
11
+ end
12
+
13
+ def close
14
+ @socket.close
15
+ end
16
+
17
+ def close_database(session)
18
+ @protocol.db_close(@socket, session)
19
+ end
20
+
21
+ def closed?
22
+ @socket.closed?
23
+ end
24
+
25
+ def cluster_exists?(session, cluster_id_or_name)
26
+ result = true
27
+
28
+ begin
29
+ if cluster_id_or_name.is_a?(String)
30
+ @protocol.count(@socket, session, cluster_id_or_name)
31
+ else
32
+ @protocol.datacluster_datarange(@socket, session, cluster_id_or_name)
33
+ end
34
+ rescue OrientDbClient::ProtocolError => err
35
+ case err.exception_class
36
+ when 'java.lang.IndexOutOfBoundsException', 'java.lang.IllegalArgumentException'
37
+ result = false
38
+ else
39
+ throw err
40
+ end
41
+ end
42
+
43
+ result
44
+ end
45
+
46
+ def command(session, text, options = {})
47
+ @protocol.command(@socket, session, text, options)
48
+ end
49
+
50
+ def count(session, cluster_name)
51
+ @protocol.count(@socket, session, cluster_name)
52
+ end
53
+
54
+ def create_database(session, database, options = {})
55
+ @protocol.db_create(@socket, session, database, options)
56
+ end
57
+
58
+ def create_cluster(session, type, options)
59
+ result = @protocol.datacluster_add(@socket, session, type, options)
60
+
61
+ result[:message_content][:new_cluster_number]
62
+ end
63
+
64
+ def create_record(session, cluster_id, record)
65
+ response = @protocol.record_create(@socket, session, cluster_id, record)
66
+ message_content = response[:message_content]
67
+
68
+ OrientDbClient::Rid.new(message_content[:cluster_id], message_content[:cluster_position])
69
+ end
70
+
71
+ def database_exists?(session, database)
72
+ response = @protocol.db_exist(@socket, session, database)
73
+
74
+ response[:message_content][:result] == 1
75
+ end
76
+
77
+ def delete_database(session, database)
78
+ @protocol.db_delete(@socket, session, database)
79
+ end
80
+
81
+ def delete_cluster(session, cluster_id)
82
+ @protocol.datacluster_remove(@socket, session, cluster_id)
83
+ end
84
+
85
+ def delete_record(session, rid, version)
86
+ response = @protocol.record_delete(@socket, session, rid.cluster_id, rid.cluster_position, version)
87
+
88
+ response[:message_content][:result] == 1
89
+ end
90
+
91
+ def get_cluster_datarange(session, cluster_id)
92
+ @protocol.datacluster_datarange(@socket, session, cluster_id)
93
+ end
94
+
95
+ def load_record(session, rid)
96
+ rid = OrientDbClient::Rid.new(rid) if rid.is_a?(String)
97
+
98
+ result = @protocol.record_load(@socket, session, rid)
99
+
100
+ if result[:message_content]
101
+ result[:message_content].tap do |r|
102
+ r[:cluster_id] = rid.cluster_id
103
+ r[:cluster_position] = rid.cluster_position
104
+ end
105
+ end
106
+
107
+ result
108
+ end
109
+
110
+ def open_server(options = {})
111
+ response = @protocol.connect(@socket, options)
112
+ session = response[:session]
113
+ message_content = response[:message_content]
114
+
115
+ @sessions[session] = ServerSession.new(message_content[:session], self)
116
+ end
117
+
118
+ def open_database(database, options = {})
119
+ response = @protocol.db_open(@socket, database, options)
120
+ session = response[:session]
121
+ message_content = response[:message_content]
122
+
123
+ @sessions[session] = DatabaseSession.new(message_content[:session], self, message_content[:clusters])
124
+ end
125
+
126
+ def reload(session)
127
+ result = @protocol.db_reload(@socket, session)
128
+ clusters = result[:message_content][:clusters]
129
+
130
+ @sessions.values.each do |s|
131
+ s.send :store_clusters, clusters
132
+ end
133
+ end
134
+
135
+ def update_record(session, rid, record, version)
136
+ response = @protocol.record_update(@socket, session, rid.cluster_id, rid.cluster_position, record, version)
137
+
138
+ response[:message_content][:record_version]
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,107 @@
1
+ require 'orient_db_client/session'
2
+
3
+ module OrientDbClient
4
+ class DatabaseSession < Session
5
+ attr_reader :clusters
6
+
7
+ def initialize(id, connection, clusters = [])
8
+ super id, connection
9
+
10
+ store_clusters(clusters)
11
+ end
12
+
13
+ def close
14
+ @connection.close_database(@id)
15
+ end
16
+
17
+ def cluster_exists?(cluster_id)
18
+ @connection.cluster_exists?(@id, cluster_id)
19
+ end
20
+
21
+ def command(text, options = {})
22
+ @connection.command(@id, text, options)
23
+ end
24
+
25
+ def count(cluster_name)
26
+ @connection.count(@id, cluster_name)
27
+ end
28
+
29
+ def create_physical_cluster(name, options = {})
30
+ options.merge!({ :name => name })
31
+
32
+ @connection.create_cluster(@id, :physical, options)
33
+ end
34
+
35
+ def clusters
36
+ @clusters.values
37
+ end
38
+
39
+ def create_record(cluster_id, record)
40
+ @connection.create_record(@id, cluster_id, record)
41
+ end
42
+
43
+ def delete_cluster(cluster_id)
44
+ @connection.delete_cluster(@id, cluster_id)
45
+ end
46
+
47
+ def delete_record(rid_or_cluster_id, cluster_position_or_version, version = nil)
48
+ if rid_or_cluster_id.is_a?(Fixnum)
49
+ rid = OrientDbClient::Rid.new(rid_or_cluster_id, cluster_position)
50
+ version = version
51
+ else
52
+ rid = rid_or_cluster_id
53
+ version = cluster_position_or_version
54
+ end
55
+
56
+ @connection.delete_record(@id, rid, version)
57
+ end
58
+
59
+ def get_cluster(id)
60
+ if id.kind_of?(Fixnum)
61
+ @clusters[id]
62
+ else
63
+ @clusters_by_name[id.downcase]
64
+ end
65
+ end
66
+
67
+ def get_cluster_datarange(cluster_id)
68
+ @connection.get_cluster_datarange(@id, cluster_id)
69
+ end
70
+
71
+ def load_record(rid_or_cluster_id, cluster_position = nil)
72
+ if rid_or_cluster_id.is_a?(Fixnum)
73
+ rid_or_cluster_id = OrientDbClient::Rid.new(rid_or_cluster_id, cluster_position)
74
+ end
75
+
76
+ @connection.load_record(@id, rid_or_cluster_id)[:message_content]
77
+ end
78
+
79
+ def reload
80
+ @connection.reload(@id)
81
+ end
82
+
83
+ def update_record(record, rid_or_cluster_id, cluster_position_or_version, version = nil)
84
+ if rid_or_cluster_id.is_a?(Fixnum)
85
+ rid = OrientDbClient::Rid.new(rid_or_cluster_id, cluster_position)
86
+ version = version
87
+ else
88
+ rid = rid_or_cluster_id
89
+ version = cluster_position_or_version
90
+ end
91
+
92
+ @connection.update_record(@id, rid, record, version)
93
+ end
94
+
95
+ private
96
+
97
+ def store_clusters(clusters)
98
+ @clusters = {}
99
+ @clusters_by_name = {}
100
+
101
+ clusters.each do |cluster|
102
+ @clusters[cluster[:id]] = cluster
103
+ @clusters_by_name[cluster[:name].downcase] = cluster
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,166 @@
1
+ require 'orient_db_client/rid'
2
+
3
+ require 'base64'
4
+
5
+ module OrientDbClient
6
+ module Deserializers
7
+ class Deserializer7
8
+ @@string_matcher = /^"[^"]*"$/
9
+
10
+ def deserialize(text)
11
+ result = { :document => {}, :structure => {} }
12
+ struct_info = {}
13
+
14
+ serialized_record = text
15
+
16
+ if m = serialized_record.match(/([^,@]+)@/)
17
+ result[:class] = m[1]
18
+ serialized_record.gsub!(/^[^@]*@/, '')
19
+ end
20
+
21
+ tokens = serialized_record.split(",")
22
+
23
+ while token = tokens.shift
24
+ field, value = parse_field(token, tokens, struct_info)
25
+
26
+ result[:document][field] = value
27
+ result[:structure][field] = struct_info[:type]
28
+ end
29
+
30
+ result
31
+ end
32
+
33
+ private
34
+
35
+ def close_token!(token, cap, join, tokens)
36
+ while token[token.length - 1] != cap && tokens.length > 0
37
+ token << join if join
38
+ token << tokens.shift
39
+ end
40
+
41
+ token
42
+ end
43
+
44
+ def remove_ends(token)
45
+ token[1...token.length-1]
46
+ end
47
+
48
+ def parse_date(value)
49
+ time = parse_time(value)
50
+
51
+ Date.new(time.year, time.month, time.day)
52
+ end
53
+
54
+ def parse_field(token, tokens, struct_info)
55
+ field, value = token.split(":", 2)
56
+
57
+ field = remove_ends(field) if field.match(@@string_matcher)
58
+
59
+ value = parse_value(value, tokens, struct_info)
60
+
61
+ return field, value
62
+ end
63
+
64
+ def parse_time(value)
65
+ Time.at(value[0...value.length - 1].to_i).utc
66
+ end
67
+
68
+ def parse_value(value, tokens, struct_info = {})
69
+ struct_info[:type] = nil
70
+
71
+ case value[0]
72
+ when '['
73
+ close_token!(value, ']', ',', tokens)
74
+
75
+ sub_tokens = remove_ends(value).split(',')
76
+
77
+ value = []
78
+
79
+ while element = sub_tokens.shift
80
+ value << parse_value(element, sub_tokens)
81
+ end
82
+
83
+ struct_info[:type] = :collection
84
+
85
+ value
86
+ when '{'
87
+ close_token!(value, '}', ',', tokens)
88
+
89
+ struct_info[:type] = :map
90
+
91
+ value = deserialize(remove_ends(value))[:document]
92
+ when '('
93
+ close_token!(value, ')', ',', tokens)
94
+
95
+ struct_info[:type] = :document
96
+
97
+ deserialize remove_ends(value)
98
+ when '*'
99
+ close_token!(value, '*', ',', tokens)
100
+
101
+ struct_info[:type] = :document
102
+
103
+ deserialize remove_ends(value)
104
+ when '"'
105
+ close_token!(value, '"', ',', tokens)
106
+
107
+ struct_info[:type] = :string
108
+
109
+ value.gsub! /^\"/, ''
110
+ value.gsub! /\"$/, ''
111
+ when '_'
112
+ close_token!(value, '_', ',', tokens)
113
+
114
+ struct_info[:type] = :binary
115
+ value = Base64.decode64(remove_ends(value))
116
+ when '#'
117
+ struct_info[:type] = :rid
118
+ value = OrientDbClient::Rid.new(value)
119
+ else
120
+
121
+ value = if value.length == 0
122
+ nil
123
+ elsif value == 'null'
124
+ nil
125
+ elsif value == 'true'
126
+ struct_info[:type] = :boolean
127
+ true
128
+ elsif value == 'false'
129
+ struct_info[:type] = :boolean
130
+ false
131
+ else
132
+ case value[value.length - 1]
133
+ when 'b'
134
+ struct_info[:type] = :byte
135
+ value.to_i
136
+ when 's'
137
+ struct_info[:type] = :short
138
+ value.to_i
139
+ when 'l'
140
+ struct_info[:type] = :long
141
+ value.to_i
142
+ when 'f'
143
+ struct_info[:type] = :float
144
+ value.to_f
145
+ when 'd'
146
+ struct_info[:type] = :double
147
+ value.to_f
148
+ when 'c'
149
+ struct_info[:type] = :decimal
150
+ value.to_f
151
+ when 't'
152
+ struct_info[:type] = :time
153
+ parse_time(value)
154
+ when 'a'
155
+ struct_info[:type] = :date
156
+ parse_date(value)
157
+ else
158
+ struct_info[:type] = :integer
159
+ value.to_i
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,25 @@
1
+ module OrientDbClient
2
+ class UnsupportedProtocolError < StandardError
3
+ def initialize(version)
4
+ super "The host reports protocol version #{version}, which is not currently supported by this driver."
5
+ end
6
+ end
7
+
8
+ class ProtocolError < StandardError
9
+ attr_reader :session
10
+ attr_reader :exception_class
11
+
12
+ def initialize(session, *exceptions)
13
+ @session
14
+ @exception_class = exceptions[0] && exceptions[0][:exception_class]
15
+
16
+ super exceptions.map { |exp| [ exp[:exception_class], exp[:exception_message] ].reject { |s| s.nil? }.join(': ') }.join("\n")
17
+ end
18
+ end
19
+
20
+ class RecordNotFound < ProtocolError
21
+ def initialize(session)
22
+ super session
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ module OrientDbClient
2
+ class NetworkMessage
3
+ def initialize(&block)
4
+ @components = []
5
+
6
+ yield(self) unless block.nil?
7
+ end
8
+
9
+ def add(type, value)
10
+ @components << { :type => :integer, :value => (value && value.length) || 0 } if type == :string
11
+ @components << { :type => type, :value => value } unless type == :string && value.nil?
12
+ end
13
+
14
+ def pack()
15
+ packing_list = @components.map do |c|
16
+ case c[:type]
17
+ when :byte then 'C'
18
+ when :integer then 'l>'
19
+ when :long then 'q>'
20
+ when :short then 's>'
21
+ when :string, :raw_string then 'a*'
22
+ end
23
+ end
24
+
25
+ content = @components.map do |c|
26
+ value = c[:value]
27
+
28
+ if c[:type] == :byte && value.is_a?(String)
29
+ c[:value] = value.length > 0 ? value[0].ord : 0
30
+ end
31
+
32
+ c[:value]
33
+ end
34
+
35
+ content.pack packing_list.join(" ")
36
+ end
37
+
38
+ def to_s
39
+ self.pack
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,16 @@
1
+ require File.join(File.dirname(__FILE__), 'protocols', 'protocol7')
2
+ require File.join(File.dirname(__FILE__), 'protocols', 'protocol9')
3
+
4
+ module OrientDbClient
5
+ class ProtocolFactory
6
+
7
+ PROTOCOLS = {
8
+ '7' => Protocols::Protocol7,
9
+ '9' => Protocols::Protocol9
10
+ }
11
+
12
+ def self.get_protocol(version)
13
+ PROTOCOLS[version.to_s] or raise UnsupportedProtocolError.new(version)
14
+ end
15
+ end
16
+ end