orientdb4r 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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