orientdb4r 0.1.2 → 0.2.0

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
+ module Orientdb4r
2
+
3
+ ###
4
+ # This mixin extends an error to be able to track a chain of exceptions.
5
+ module ChainedError
6
+
7
+ attr_reader :cause
8
+
9
+ ###
10
+ # Constructor.
11
+ def initialize message = nil, cause = $!
12
+ super message unless message.nil?
13
+ super $! if message.nil? and !cause.nil?
14
+ @cause = cause
15
+ end
16
+
17
+ ###
18
+ # Modification of original method Error#set_backtrace
19
+ # to descend the full depth of the exception chain.
20
+ def set_backtrace bt
21
+ unless cause.nil?
22
+ cause.backtrace.reverse.each do |line|
23
+ if bt.last == line
24
+ bt.pop
25
+ next
26
+ end
27
+ break
28
+ end
29
+ bt.push "<<<CAUSED BY>>>: #{cause.class}: #{cause.message}"
30
+ bt.concat cause.backtrace
31
+ end
32
+ super bt
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -9,24 +9,39 @@ module Orientdb4r
9
9
  @connected = false
10
10
  end
11
11
 
12
+ # --------------------------------------------------------------- CONNECTION
13
+
12
14
  ###
13
15
  # Connects client to the server.
14
16
  def connect options
15
17
  raise NotImplementedError, 'this should be overridden by concrete client'
16
18
  end
17
19
 
20
+
18
21
  ###
19
22
  # Disconnects client from the server.
20
23
  def disconnect
21
24
  raise NotImplementedError, 'this should be overridden by concrete client'
22
25
  end
23
26
 
27
+
24
28
  ###
25
29
  # Gets flag whenever the client is connected or not.
26
30
  def connected?
27
31
  @connected
28
32
  end
29
33
 
34
+
35
+ ###
36
+ # Retrieve information about the connected OrientDB Server.
37
+ # Enables additional authentication to the server with an account
38
+ # that can access the 'server.info' resource.
39
+ def server(options={})
40
+ raise NotImplementedError, 'this should be overridden by concrete client'
41
+ end
42
+
43
+ # ----------------------------------------------------------------- DATABASE
44
+
30
45
  ###
31
46
  # Creates a new database.
32
47
  # You can provide an additional authentication to the server with 'database.create' resource
@@ -35,24 +50,30 @@ module Orientdb4r
35
50
  raise NotImplementedError, 'this should be overridden by concrete client'
36
51
  end
37
52
 
53
+
38
54
  ###
39
55
  # Gets informations about requested class.
40
56
  def get_class(name)
41
57
  raise NotImplementedError, 'this should be overridden by concrete client'
42
58
  end
43
59
 
60
+ # ---------------------------------------------------------------------- SQL
61
+
44
62
  ###
45
63
  # Executes a query against the database.
46
64
  def query(sql)
47
65
  raise NotImplementedError, 'this should be overridden by concrete client'
48
66
  end
49
67
 
68
+
50
69
  ###
51
70
  # Executes a command against the database.
52
- def command(sql, options={})
71
+ def command(sql)
53
72
  raise NotImplementedError, 'this should be overridden by concrete client'
54
73
  end
55
74
 
75
+ # -------------------------------------------------------------------- CLASS
76
+
56
77
  ###
57
78
  # Creates a new class in the schema.
58
79
  def create_class(name, options={})
@@ -66,7 +87,7 @@ module Orientdb4r
66
87
 
67
88
  drop_class name if options[:force]
68
89
 
69
- command sql, :http_code_500 => 'failed to create class (exists already, bad supperclass?)'
90
+ command sql
70
91
 
71
92
  if block_given?
72
93
  proxy = Orientdb4r::Utils::Proxy.new(self, name)
@@ -77,14 +98,28 @@ module Orientdb4r
77
98
  end
78
99
  end
79
100
 
101
+
80
102
  ###
81
103
  # Removes a class from the schema.
82
- def drop_class(name)
83
- # TODO in :mode=>:strict verify if the class is no a super class
104
+ def drop_class(name, options={})
84
105
  raise ArgumentError, "class name is blank" if blank?(name)
106
+
107
+ # :mode=>:strict forbids to drop a class that is a super class for other one
108
+ opt_pattern = { :mode => :nil }
109
+ verify_options(options, opt_pattern)
110
+ if :strict == options[:mode]
111
+ response = @resource["connect/#{@database}"].get
112
+ connect_info = process_response(response, :mode => :strict)
113
+ children = connect_info['classes'].select { |i| i['superClass'] == name }
114
+ unless children.empty?
115
+ raise OrientdbError, "class is super-class, cannot be deleted, name=#{name}"
116
+ end
117
+ end
118
+
85
119
  command "DROP CLASS #{name}"
86
120
  end
87
121
 
122
+
88
123
  ###
89
124
  # Creates a new property in the schema.
90
125
  # You need to create the class before.
@@ -98,7 +133,7 @@ module Orientdb4r
98
133
  verify_options(options, opt_pattern)
99
134
 
100
135
  cmd = "CREATE PROPERTY #{clazz}.#{property} #{type.to_s}"
101
- command cmd, :http_code_500 => 'failed to create property (exists already?)'
136
+ command cmd
102
137
 
103
138
  unless options.empty?
104
139
  options.each do |k,v|
@@ -107,6 +142,37 @@ module Orientdb4r
107
142
  end
108
143
  end
109
144
 
145
+ # ----------------------------------------------------------------- DOCUMENT
146
+
147
+ ###
148
+ # Create a new document.
149
+ # Returns the Record-id assigned.
150
+ def create_document(doc)
151
+ raise NotImplementedError, 'this should be overridden by concrete client'
152
+ end
153
+
154
+
155
+ ###
156
+ # Retrieves a document by given ID.
157
+ def get_document(rid)
158
+ raise NotImplementedError, 'this should be overridden by concrete client'
159
+ end
160
+
161
+
162
+ ###
163
+ # Updates an existing document.
164
+ def update_document(doc)
165
+ raise NotImplementedError, 'this should be overridden by concrete client'
166
+ end
167
+
168
+
169
+ ###
170
+ # Deletes an existing document.
171
+ def delete_document(rid)
172
+ raise NotImplementedError, 'this should be overridden by concrete client'
173
+ end
174
+
175
+
110
176
  protected
111
177
 
112
178
  ###
@@ -31,15 +31,13 @@ module Orientdb4r
31
31
  begin
32
32
  response = @resource["connect/#{@database}"].get
33
33
  rslt = process_response(response, :mode => :strict)
34
+
35
+ decorate_classes_with_model(rslt['classes'])
36
+
34
37
  @connected = true
35
- rescue ::RestClient::Exception => e
38
+ rescue
36
39
  @connected = false
37
- raise process_error e, :http_code_401 => 'connect failed (bad credentials?)'
38
- rescue Exception => e
39
- Orientdb4r::logger.error e.message
40
- Orientdb4r::logger.error e.backtrace.inspect
41
- @connected = false
42
- raise e
40
+ raise ConnectionError
43
41
  end
44
42
  rslt
45
43
  end
@@ -54,6 +52,8 @@ module Orientdb4r
54
52
  Orientdb4r::logger.warn '401 Unauthorized - bug in disconnect?'
55
53
  ensure
56
54
  @connected = false
55
+ @user = nil
56
+ @password = nil
57
57
  end
58
58
  end
59
59
 
@@ -70,11 +70,8 @@ module Orientdb4r
70
70
  resource = ::RestClient::Resource.new(url, :user => u, :password => p)
71
71
  begin
72
72
  response = resource["database/#{options[:database]}/#{options[:type]}"].post ''
73
- rescue ::RestClient::Exception => e
74
- raise process_error e, \
75
- :http_code_403 => 'forbidden operation (insufficient rights?)', \
76
- :http_code_500 => 'failed to create database (exists already?)'
77
-
73
+ rescue
74
+ raise OrientdbError
78
75
  end
79
76
  process_response(response)
80
77
  end
@@ -90,31 +87,128 @@ module Orientdb4r
90
87
  # workaround - use metadate delivered by 'connect'
91
88
  response = @resource["connect/#{@database}"].get
92
89
  connect_info = process_response(response, :mode => :strict)
93
- clazz = connect_info['classes'].select { |i| i['name'] == name }
94
- raise ArgumentError, "class not found, name=#{name}" unless 1 == clazz.size
95
- rslt = clazz[0]
96
- rslt.extend Orientdb4r::OClass
97
- rslt
90
+
91
+ classes = connect_info['classes'].select { |i| i['name'] == name }
92
+ raise ArgumentError, "class not found, name=#{name}" unless 1 == classes.size
93
+ decorate_classes_with_model(classes)
94
+ clazz = classes[0]
95
+ clazz.extend Orientdb4r::HashExtension
96
+ clazz.extend Orientdb4r::OClass
97
+ unless clazz['properties'].nil? # there can be a class without properties
98
+ clazz.properties.each do |prop|
99
+ prop.extend Orientdb4r::HashExtension
100
+ prop.extend Orientdb4r::Property
101
+ end
102
+ end
103
+
104
+
105
+ clazz
98
106
  end
99
107
 
108
+
100
109
  def query(sql) #:nodoc:
110
+ raise ArgumentError, 'query is blank' if blank? sql
111
+
101
112
  response = @resource["query/#{@database}/sql/#{URI.escape(sql)}"].get
102
113
  rslt = process_response(response)
103
114
  rslt['result']
104
115
  end
105
116
 
106
- def command(sql, options={}) #:nodoc:
117
+
118
+ def command(sql) #:nodoc:
119
+ raise ArgumentError, 'command is blank' if blank? sql
107
120
  begin
108
121
  #puts "REQ #{sql}"
109
122
  response = @resource["command/#{@database}/sql/#{URI.escape(sql)}"].post ''
110
123
  rslt = process_response(response)
111
124
  rslt
112
125
  #puts "RESP #{response.code}"
113
- rescue Exception => e
114
- raise process_error e, options.select { |k,v| k.to_s.start_with? 'http_code' }
126
+ rescue
127
+ raise OrientdbError
128
+ end
129
+ end
130
+
131
+
132
+ def server(options={}) #:nodoc:
133
+ options_pattern = { :user => :optional, :password => :optional }
134
+ verify_options(options, options_pattern)
135
+
136
+
137
+ u = options.include?(:user) ? options[:user] : user
138
+ p = options.include?(:password) ? options[:password] : password
139
+ resource = ::RestClient::Resource.new(url, :user => u, :password => p)
140
+ begin
141
+ response = resource['server'].get
142
+ rescue
143
+ raise OrientdbError
144
+ end
145
+ process_response(response)
146
+ end
147
+
148
+
149
+ # ----------------------------------------------------------------- DOCUMENT
150
+
151
+ def create_document(doc)
152
+ begin
153
+ response = @resource["document/#{@database}"].post doc.to_json, :content_type => 'application/json'
154
+ rescue
155
+ raise DataError
156
+ end
157
+ rid = process_response(response)
158
+ raise ArgumentError, "invalid RID format, RID=#{rid}" unless rid =~ /^#[0-9]+:[0-9]+/
159
+ rid
160
+ end
161
+
162
+
163
+ def get_document(rid) #:nodoc:
164
+ raise ArgumentError, 'blank RID' if blank? rid
165
+ # remove the '#' prefix
166
+ rid = rid[1..-1] if rid.start_with? '#'
167
+
168
+ begin
169
+ response = @resource["document/#{@database}/#{rid}"].get
170
+ rescue
171
+ raise NotFoundError
115
172
  end
173
+ rslt = process_response(response)
174
+ rslt.extend Orientdb4r::DocumentMetadata
175
+ rslt
116
176
  end
117
177
 
178
+
179
+ def update_document(doc) #:nodoc:
180
+ raise ArgumentError, 'document is nil' if doc.nil?
181
+ raise ArgumentError, 'document has no RID' if doc.doc_rid.nil?
182
+ raise ArgumentError, 'document has no version' if doc.doc_version.nil?
183
+
184
+ rid = doc.delete '@rid'
185
+ rid = rid[1..-1] if rid.start_with? '#'
186
+
187
+ begin
188
+ @resource["document/#{@database}/#{rid}"].put doc.to_json, :content_type => 'application/json'
189
+ rescue
190
+ raise DataError
191
+ end
192
+ # empty http response
193
+ end
194
+
195
+
196
+ def delete_document(rid) #:nodoc:
197
+ raise ArgumentError, 'blank RID' if blank? rid
198
+ # remove the '#' prefix
199
+ rid = rid[1..-1] if rid.start_with? '#'
200
+
201
+ begin
202
+ response = @resource["document/#{@database}/#{rid}"].delete
203
+ puts "DELETE '#{response}'"
204
+ rescue
205
+ raise DataError
206
+ end
207
+ # empty http response
208
+ end
209
+
210
+ # ------------------------------------------------------------------ Helpers
211
+
118
212
  private
119
213
 
120
214
  ###
@@ -156,10 +250,17 @@ module Orientdb4r
156
250
  rslt
157
251
  end
158
252
 
159
- def process_error(error, messages={})
160
- code = "http_code_#{error.http_code}".to_sym
161
- msg = messages.include?(code) ? "#{messages[code]}, cause = " : ''
162
- OrientdbError.new "#{msg}#{error.to_s}"
253
+ def decorate_classes_with_model(classes)
254
+ classes.each do |clazz|
255
+ clazz.extend Orientdb4r::HashExtension
256
+ clazz.extend Orientdb4r::OClass
257
+ unless clazz['properties'].nil? # there can be a class without properties
258
+ clazz.properties.each do |prop|
259
+ prop.extend Orientdb4r::HashExtension
260
+ prop.extend Orientdb4r::Property
261
+ end
262
+ end
263
+ end
163
264
  end
164
265
 
165
266
  end
@@ -0,0 +1,140 @@
1
+ module Orientdb4r
2
+
3
+
4
+ ###
5
+ # Extends a Hash produced by JSON.parse.
6
+ module HashExtension
7
+
8
+ ###
9
+ # Gets an attribute value that has to be presented.
10
+ def get_mandatory_attribute(name)
11
+ key = name.to_s
12
+ raise ::ArgumentError, "unknown attribute, name=#{key}" unless self.include? key
13
+ self[key]
14
+ end
15
+
16
+ end
17
+
18
+
19
+ ###
20
+ # This module represents API to OrientDB's class.
21
+ module OClass
22
+
23
+ ###
24
+ # Gets name of the class.
25
+ def name
26
+ get_mandatory_attribute :name
27
+ end
28
+
29
+ ###
30
+ # Gets properties of the class.
31
+ # Returns nil for a class without properties.
32
+ def properties
33
+ self['properties']
34
+ end
35
+
36
+ ###
37
+ # Gets a property with the given name.
38
+ def property(name)
39
+ raise ArgumentError, 'no properties defined on class' if properties.nil?
40
+ props = properties.select { |i| i['name'] == name.to_s }
41
+ raise ::ArgumentError, "unknown property, name=#{name}" if props.empty?
42
+ raise ::ArgumentError, "too many properties found, name=#{name}" if props.size > 1 # just to be sure
43
+ props[0]
44
+ end
45
+
46
+ ###
47
+ # Gets the super-class.
48
+ def super_class
49
+ get_mandatory_attribute :superClass
50
+ end
51
+
52
+ ###
53
+ # Gets clusters of the class.
54
+ def clusters
55
+ get_mandatory_attribute :clusters
56
+ end
57
+
58
+ ###
59
+ # Gets the default cluster.
60
+ def default_cluster
61
+ get_mandatory_attribute :defaultCluster
62
+ end
63
+
64
+ end
65
+
66
+
67
+ ###
68
+ # This module represents API to OrientDB's property.
69
+ module Property
70
+
71
+ ###
72
+ # Gets name of the property.
73
+ def name
74
+ get_mandatory_attribute :name
75
+ end
76
+
77
+ ###
78
+ # Gets type of the property.
79
+ def type
80
+ get_mandatory_attribute :type
81
+ end
82
+
83
+ ###
84
+ # Gets the 'mandatory' flag.
85
+ def mandatory
86
+ get_mandatory_attribute :mandatory
87
+ end
88
+
89
+ ###
90
+ # Gets the 'notNull' flag.
91
+ def not_null
92
+ get_mandatory_attribute :notNull
93
+ end
94
+
95
+ ###
96
+ # Gets the minimal allowed value.
97
+ def min
98
+ self['min']
99
+ end
100
+
101
+ ###
102
+ # Gets the maximal allowed value.
103
+ def max
104
+ self['max']
105
+ end
106
+
107
+ end
108
+
109
+
110
+ ###
111
+ # This module represents API to document metadata.
112
+ module DocumentMetadata
113
+
114
+ ###
115
+ # Gets the document class.
116
+ def doc_class
117
+ self['@class']
118
+ end
119
+
120
+ ###
121
+ # Gets the document ID.
122
+ def doc_rid
123
+ self['@rid']
124
+ end
125
+
126
+ ###
127
+ # Gets the document version.
128
+ def doc_version
129
+ self['@version']
130
+ end
131
+
132
+ ###
133
+ # Gets the document type.
134
+ def doc_type
135
+ self['@type']
136
+ end
137
+
138
+ end
139
+
140
+ end
@@ -3,6 +3,8 @@ module Orientdb4r
3
3
  module Utils
4
4
 
5
5
  def verify_options(options, pattern)
6
+ raise ArgumentError, 'options cannot be nil' if options.nil?
7
+
6
8
  # unknown key?
7
9
  options.keys.each do |k|
8
10
  raise ArgumentError, "unknow option: #{k}" unless pattern.keys.include? k
@@ -11,6 +13,13 @@ module Orientdb4r
11
13
  pattern.each do |k,v|
12
14
  raise ArgumentError, "missing mandatory option: #{k}" if v == :mandatory and !options.keys.include? k
13
15
  end
16
+ # option in a set of allowed values
17
+ pattern.each do |k,v|
18
+ if v.instance_of? Array
19
+ raise ArgumentError, "value '#{options[k]}' not in #{v.inspect}, key=#{k}" unless v.include?(options[k])
20
+ end
21
+ end
22
+
14
23
  options
15
24
  end
16
25
 
@@ -2,6 +2,7 @@ module Orientdb4r
2
2
 
3
3
  # Version history.
4
4
  VERSION_HISTORY = [
5
+ ['0.2.0', '2012-06-12', "Introduces document's CRUD operations"],
5
6
  ['0.1.2', '2012-06-10', 'Introduces new OClass module'],
6
7
  ['0.1.1', '2012-06-08', 'First working version (including unit tests) released at github.com'],
7
8
  ['0.1.0', '2012-06-02', 'Initial version on Ruby-1.9.3p194 and OrientDB-1.0.0']
data/lib/orientdb4r.rb CHANGED
@@ -8,17 +8,26 @@ require 'orientdb4r/version'
8
8
  # This module represents the entry point for using the Ruby OrientDB client.
9
9
  module Orientdb4r
10
10
 
11
- autoload :Utils, 'orientdb4r/utils'
12
- autoload :Client, 'orientdb4r/client'
13
- autoload :RestClient, 'orientdb4r/rest/client'
14
- autoload :OClass, 'orientdb4r/rest/oclass'
11
+ autoload :Utils, 'orientdb4r/utils'
12
+ autoload :Client, 'orientdb4r/client'
13
+ autoload :RestClient, 'orientdb4r/rest/client'
14
+ autoload :HashExtension, 'orientdb4r/rest/model'
15
+ autoload :OClass, 'orientdb4r/rest/model'
16
+ autoload :ChainedError, 'orientdb4r/chained_error'
15
17
 
16
18
 
17
19
  class << self
18
20
 
19
21
  ###
20
22
  # Gets a new database client or an existing for the current thread.
23
+ # === options
24
+ # * :force => true
21
25
  def client options={}
26
+ if :new == options[:instance]
27
+ options.delete :instance
28
+ return RestClient.new options
29
+ end
30
+
22
31
  Thread.current[:orientdb_client] ||= RestClient.new options
23
32
  end
24
33
 
@@ -34,8 +43,23 @@ module Orientdb4r
34
43
 
35
44
 
36
45
  ###
37
- # Basic error raised to signal an unexpected situation.
38
- class OrientdbError < StandardError; end
46
+ # Basic error that indicates an unexpected situation during the client call.
47
+ class OrientdbError < StandardError
48
+ include ChainedError
49
+ end
50
+
51
+ ###
52
+ # Error indicating problems with communicating with the database.
53
+ class ConnectionError < OrientdbError; end
54
+
55
+ ###
56
+ # Error raised to inform that an object identified by RID cannot be get.
57
+ class NotFoundError < OrientdbError; end
58
+
59
+ ###
60
+ # Error indicating that manipulation against the given data resulted in some illegal operation,
61
+ # mismatched types or incorrect cardinality.
62
+ class DataError < OrientdbError; end
39
63
 
40
64
  end
41
65
 
@@ -46,3 +70,7 @@ Orientdb4r::logger.level = Logger::INFO
46
70
 
47
71
  Orientdb4r::logger.info \
48
72
  "Orientdb4r #{Orientdb4r::VERSION}, running on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
73
+
74
+
75
+ #client = Orientdb4r.client
76
+ #client.connect :database => 'temp', :user => 'admin', :password => 'admin'
@@ -17,17 +17,24 @@ class TestDatabase < Test::Unit::TestCase
17
17
  ###
18
18
  # CONNECT
19
19
  def test_connect
20
+ assert_nothing_thrown do @client.connect :database => 'temp', :user => 'admin', :password => 'admin'; end
20
21
  rslt = @client.connect :database => 'temp', :user => 'admin', :password => 'admin'
21
22
  assert_instance_of Hash, rslt
22
23
  assert rslt.size > 0
23
24
  assert rslt.include? 'classes'
24
25
 
26
+ # connection refused
27
+ client = Orientdb4r.client :port => 2840, :instance => :new
28
+ assert_raise Orientdb4r::ConnectionError do
29
+ client.connect :database => 'temp', :user => 'admin', :password => 'admin'
30
+ end
31
+
25
32
  # bad DB name
26
- assert_raise Orientdb4r::OrientdbError do
33
+ assert_raise Orientdb4r::ConnectionError do
27
34
  @client.connect :database => 'unknown_db', :user => 'admin', :password => 'admin'
28
35
  end
29
36
  # bad credentials
30
- assert_raise Orientdb4r::OrientdbError do
37
+ assert_raise Orientdb4r::ConnectionError do
31
38
  @client.connect :database => 'temp', :user => 'admin1', :password => 'admin'
32
39
  end
33
40
  end
@@ -45,7 +52,7 @@ class TestDatabase < Test::Unit::TestCase
45
52
 
46
53
  ###
47
54
  # CREATE DATABASE
48
- # Temporary disabled bacause of unknown way how to drop a new created datatabse.
55
+ # Temporary disabled because of unknown way how to drop a new created datatabse.
49
56
  def xtest_create_database
50
57
  @client.create_database :database => 'UniT', :user => 'root', :password => 'root'
51
58
  # creating an existing DB
@@ -63,4 +70,19 @@ class TestDatabase < Test::Unit::TestCase
63
70
  #@client.command "DROP DATABASE UniT" : NOT WORKING now
64
71
  end
65
72
 
73
+
74
+ ###
75
+ # SERVER info
76
+ # Temporary disabled because of dependency to password of 'root' account
77
+ def xtest_server
78
+ # admin/admin has not 'server.info' resource access in standard installation
79
+ assert_raise Orientdb4r::OrientdbError do @client.server :user => 'admin', :password => 'admin'; end
80
+
81
+ assert_nothing_thrown do @client.server :user => 'root', :password => 'root'; end
82
+ rslt = @client.server :user => 'root', :password => 'root'
83
+ assert_instance_of Hash, rslt
84
+ assert rslt.include? 'connections'
85
+ assert_not_nil rslt['connections']
86
+ end
87
+
66
88
  end
data/test/test_ddo.rb CHANGED
@@ -11,7 +11,6 @@ class TestDdo < Test::Unit::TestCase
11
11
 
12
12
  def initialize(params)
13
13
  super params
14
-
15
14
  @client = Orientdb4r.client
16
15
  end
17
16
 
@@ -21,7 +20,7 @@ class TestDdo < Test::Unit::TestCase
21
20
 
22
21
  def teardown
23
22
  # remove the testing class after each test
24
- @client.drop_class(CLASS)
23
+ @client.drop_class(CLASS, :mode => :strict)
25
24
  @client.disconnect
26
25
  end
27
26
 
@@ -45,6 +44,14 @@ class TestDdo < Test::Unit::TestCase
45
44
  assert_instance_of Array, clazz.clusters
46
45
  assert !clazz.clusters.empty?
47
46
  assert_not_nil clazz.default_cluster
47
+ # test Property
48
+ prop = clazz.property :password
49
+ assert_equal 'password', prop.name
50
+ assert_equal 'STRING', prop.type
51
+ assert prop.mandatory
52
+ assert prop.not_null
53
+ assert_nil prop.min
54
+ assert_nil prop.min
48
55
  end
49
56
 
50
57
 
@@ -52,7 +59,7 @@ class TestDdo < Test::Unit::TestCase
52
59
  # CREATE CLASS
53
60
  def test_create_class
54
61
  assert_nothing_thrown do @client.create_class(CLASS); end
55
- assert_nothing_thrown do @client.get_class(CLASS); end
62
+ assert_nothing_thrown do @client.get_class(CLASS); end # raises an Error if no class found
56
63
  # already exist
57
64
  assert_raise Orientdb4r::OrientdbError do @client.create_class(CLASS); end
58
65
 
@@ -77,12 +84,23 @@ class TestDdo < Test::Unit::TestCase
77
84
 
78
85
  ###
79
86
  # DROP TABLE
80
- def test_drop_table
87
+ def test_drop_class
88
+ super_clazz = "#{CLASS}Sup"
89
+ @client.drop_class(super_clazz); # just to be sure if previous test failed
90
+ @client.create_class(super_clazz);
91
+ @client.create_class(CLASS);
81
92
  assert_nothing_thrown do @client.drop_class(CLASS); end
82
-
93
+ assert_raise ArgumentError do @client.get_class(CLASS); end # no info more
83
94
  # the class is not visible in class list delivered by connect
84
95
  rslt = @client.connect :database => DB, :user => 'admin', :password => 'admin'
85
- assert rslt['classes'].select { |i| i['name'] == CLASS }.empty?
96
+ assert rslt['classes'].select { |i| i.name == CLASS }.empty?
97
+
98
+ # CLASS extends super_class
99
+ @client.create_class(CLASS, :extends => super_clazz);
100
+ assert_raise Orientdb4r::OrientdbError do @client.drop_class(super_clazz, :mode => :strict); end
101
+ assert_nothing_thrown do @client.get_class(super_clazz); end # still there
102
+ @client.drop_class(CLASS);
103
+ assert_nothing_thrown do @client.drop_class(super_clazz, :mode => :strict); end
86
104
  end
87
105
 
88
106
 
@@ -0,0 +1,135 @@
1
+ require 'test/unit'
2
+ require 'orientdb4r'
3
+
4
+ ###
5
+ # This class tests Data Manipulation Operarions.
6
+ class TestDocumentCrud < Test::Unit::TestCase
7
+ include Orientdb4r::Utils
8
+
9
+ CLASS = 'testing'
10
+ DB = 'temp'
11
+ Orientdb4r::logger.level = Logger::DEBUG
12
+
13
+
14
+ def setup
15
+ @client = Orientdb4r.client
16
+ @client.connect :database => DB, :user => 'admin', :password => 'admin'
17
+ @client.create_class(CLASS) do |c|
18
+ c.property 'prop1', :integer, :notnull => :true, :min => 1, :max => 99
19
+ c.property 'prop2', :string, :mandatory => true
20
+ end
21
+ end
22
+
23
+ def teardown
24
+ # remove the testing class after each test
25
+ @client.drop_class(CLASS)
26
+ @client.disconnect
27
+ end
28
+
29
+
30
+ ###
31
+ # CREATE
32
+ def test_create_document
33
+ assert_nothing_thrown do @client.create_document( { '@class' => CLASS, 'prop1' => 99, 'prop2' => 'ipsum lorem' }); end
34
+ rid = @client.create_document({ '@class' => CLASS, 'prop1' => 1, 'prop2' => 'text' })
35
+ assert rid =~ /#[0-9]+:[0-9]+$/
36
+
37
+ # no effect if a define the version
38
+ assert_nothing_thrown do
39
+ @client.create_document({ '@class' => CLASS, '@version' => 2, 'prop1' => 1, 'prop2' => 'text' })
40
+ end
41
+ rid = @client.create_document({ '@class' => CLASS, 'prop1' => 1, 'prop2' => 'text' })
42
+ doc = @client.get_document rid
43
+ assert_equal 0, doc.doc_version
44
+
45
+ # no effect if an unknown class
46
+ assert_nothing_thrown do
47
+ @client.create_document({ '@class' => 'unknown_class', 'a' => 1, 'b' => 'text' })
48
+ end
49
+ rid = @client.create_document({ '@class' => 'unknown_class', 'a' => 11, 'b' => 'text1' })
50
+ doc = @client.get_document rid
51
+ assert_nil doc.doc_class
52
+ assert_equal 11, doc['a']
53
+ assert_equal 'text1', doc['b']
54
+
55
+ # no mandatory property
56
+ assert_raise Orientdb4r::DataError do @client.create_document({ '@class' => CLASS, 'prop1' => 1 }); end
57
+ # notNull is null, or lesser/bigger
58
+ assert_raise Orientdb4r::DataError do
59
+ @client.create_document({ '@class' => CLASS, 'prop1' => nil, 'prop2' => 'text' })
60
+ end
61
+ assert_raise Orientdb4r::DataError do
62
+ @client.create_document({ '@class' => CLASS, 'prop1' => 0, 'prop2' => 'text' })
63
+ end
64
+ assert_raise Orientdb4r::DataError do
65
+ @client.create_document({ '@class' => CLASS, 'prop1' => 100, 'prop2' => 'text' })
66
+ end
67
+ end
68
+
69
+ ###
70
+ # GET
71
+ def test_get_document
72
+ rid = @client.create_document( { '@class' => CLASS, 'prop1' => 1, 'prop2' => 'text' })
73
+
74
+ doc = @client.get_document rid
75
+ assert_equal CLASS, doc.doc_class
76
+ assert_equal rid, doc.doc_rid
77
+ assert_equal 0, doc.doc_version
78
+ assert_equal 'd', doc.doc_type
79
+ assert_equal 1, doc['prop1']
80
+ assert_equal 'text', doc['prop2']
81
+ assert_nil doc['unknown_property']
82
+
83
+ # not existing RID
84
+ rid1 = rid.sub(/[0-9]+$/, (rid.split(':')[1].to_i + 1).to_s) # '#6:0' > '#6:1' or '#6:11' > '#6:12'
85
+ assert_raise Orientdb4r::NotFoundError do @client.get_document rid1; end
86
+ end
87
+
88
+ ###
89
+ # UPDATE
90
+ def test_update_document
91
+ rid = @client.create_document( { '@class' => CLASS, 'prop1' => 1, 'prop2' => 'text' })
92
+ doc = @client.get_document rid
93
+
94
+ doc['prop1'] = 2
95
+ doc['prop2'] = 'unit'
96
+ assert_nothing_thrown do @client.update_document doc; end
97
+ doc = @client.get_document rid
98
+ assert_equal 2, doc['prop1']
99
+ assert_equal 'unit', doc['prop2']
100
+
101
+ # bad version
102
+ doc = @client.get_document rid
103
+ doc['@version'] = 2
104
+ assert_raise Orientdb4r::DataError do @client.update_document doc; end
105
+
106
+ # class cannot be changed
107
+ doc = @client.get_document rid
108
+ doc['@class'] = 'OUser'
109
+ assert_nothing_thrown do @client.update_document doc; end
110
+ assert_equal CLASS, @client.get_document(rid).doc_class
111
+
112
+ # no mandatory property
113
+ doc = @client.get_document rid
114
+ doc.delete 'prop2'
115
+ assert_raise Orientdb4r::DataError do @client.update_document doc; end
116
+ # notNull is null, or lesser/bigger
117
+ doc = @client.get_document rid
118
+ doc['prop1'] = nil
119
+ assert_raise Orientdb4r::DataError do @client.update_document doc; end
120
+ end
121
+
122
+
123
+ ###
124
+ # DELETE
125
+ def test_delete_document
126
+ rid = @client.create_document( { '@class' => CLASS, 'prop1' => 1, 'prop2' => 'text' })
127
+ doc = @client.get_document rid
128
+ assert_not_nil doc
129
+
130
+ # already deleted
131
+ @client.delete_document rid
132
+ assert_raise Orientdb4r::NotFoundError do @client.get_document rid; end
133
+ end
134
+
135
+ end
@@ -0,0 +1,30 @@
1
+ require 'test/unit'
2
+ require 'orientdb4r'
3
+
4
+ ###
5
+ # This class tests Utils methods.
6
+ class TestDmo < Test::Unit::TestCase
7
+ include Orientdb4r::Utils
8
+
9
+ def test_verify_options
10
+ opt_pattern = {:a => :mandatory, :b => :optional, :c => 'predefined', :d => [1, false]}
11
+ assert_nothing_thrown do verify_options({:a => 'A', :b => 'B', :c => 'C', :d => 1}, opt_pattern); end
12
+
13
+ # missing mandatory
14
+ assert_raise ArgumentError do verify_options({}, opt_pattern); end
15
+ # unknown key
16
+ assert_raise ArgumentError do verify_options({:a => 1, :z => 2}, opt_pattern); end
17
+ # value not in predefined set
18
+ assert_raise ArgumentError do verify_options({:a => 1, :d => 3}, opt_pattern); end
19
+ end
20
+
21
+ def test_verify_and_sanitize_options
22
+ opt_pattern = {:a => 'A', :b => 'B'}
23
+ options = {:a => 'X'}
24
+ verify_and_sanitize_options(options, opt_pattern)
25
+ assert_equal 2, options.size
26
+ assert_equal 'X', options[:a]
27
+ assert_equal 'B', options[:b]
28
+ end
29
+
30
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orientdb4r
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-10 00:00:00.000000000 Z
12
+ date: 2012-06-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
@@ -41,16 +41,18 @@ files:
41
41
  - README.rdoc
42
42
  - Rakefile
43
43
  - lib/orientdb4r.rb
44
+ - lib/orientdb4r/chained_error.rb
44
45
  - lib/orientdb4r/client.rb
45
- - lib/orientdb4r/document.rb
46
46
  - lib/orientdb4r/rest/client.rb
47
- - lib/orientdb4r/rest/oclass.rb
47
+ - lib/orientdb4r/rest/model.rb
48
48
  - lib/orientdb4r/utils.rb
49
49
  - lib/orientdb4r/version.rb
50
50
  - orientdb4r.gemspec
51
51
  - test/test_database.rb
52
52
  - test/test_ddo.rb
53
53
  - test/test_dmo.rb
54
+ - test/test_document_crud.rb
55
+ - test/test_utils.rb
54
56
  homepage: http://github.com/veny/orientdb4r
55
57
  licenses: []
56
58
  post_install_message:
@@ -80,3 +82,5 @@ test_files:
80
82
  - test/test_database.rb
81
83
  - test/test_ddo.rb
82
84
  - test/test_dmo.rb
85
+ - test/test_document_crud.rb
86
+ - test/test_utils.rb
@@ -1,6 +0,0 @@
1
- module Orientdb4r
2
-
3
- class Document
4
- end
5
-
6
- end
@@ -1,58 +0,0 @@
1
- module Orientdb4r
2
-
3
- ###
4
- # This module represents API to OrientDB's class.
5
- module OClass
6
-
7
- ###
8
- # Gets name of the class.
9
- def name
10
- get_mandatory_attribute :name
11
- end
12
-
13
- ###
14
- # Gets properties of the class.
15
- def properties
16
- get_mandatory_attribute :properties
17
- end
18
-
19
- ###
20
- # Gets a property with the given name.
21
- def property(name)
22
- props = properties.select { |i| i['name'] == name.to_s }
23
- raise ArgumentError, "unknown property, name=#{name}" if props.empty?
24
- raise ArgumentError, "too many properties found, name=#{name}" if props.size > 1 # just to be sure
25
- props[0]
26
- end
27
-
28
- ###
29
- # Gets the super-class.
30
- def super_class
31
- get_mandatory_attribute :superClass
32
- end
33
-
34
- ###
35
- # Gets clusters of the class.
36
- def clusters
37
- get_mandatory_attribute :clusters
38
- end
39
-
40
- ###
41
- # Gets the default cluster.
42
- def default_cluster
43
- get_mandatory_attribute :defaultCluster
44
- end
45
-
46
- #------------------------------------------------------------------- Helpers
47
-
48
- ###
49
- # Gets an attribute value that has to be presented.
50
- def get_mandatory_attribute(name)
51
- key = name.to_s
52
- raise ArgumentError "unknown attribute, name=#{key}" unless self.include? key
53
- self[key]
54
- end
55
-
56
- end
57
-
58
- end