active-orient 0.79 → 0.80

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.
@@ -1,8 +1,26 @@
1
1
 
2
2
  class Array
3
+
4
+ @@accepted_methods = []
5
+ ## dummy for refining
6
+
7
+ def method_missing method, *args, &b
8
+ return if [:to_hash, :to_str].include? method
9
+ if @@accepted_methods.include? method
10
+ self.map{|x| x.public_send(method, *args, &b)}
11
+ else
12
+ raise ArgumentError.new("Method #{method} does not exist")
13
+ end
14
+ end
15
+
16
+
3
17
  # Class extentions to manage to_orient and from_orient
4
18
  def to_orient
5
- map( &:to_orient) # .join(',')
19
+ if all?{ |x| x.respond_to?(:rid?)} && any?( &:rid? )
20
+ "["+ map{|x| x.rid? ? x.rid : x.to_or }.join(', ') + ']'
21
+ else
22
+ map(&:to_orient) # .join(',')
23
+ end
6
24
  end
7
25
 
8
26
  def to_or
@@ -16,6 +34,7 @@ class Array
16
34
  def to_human
17
35
  map &:to_human
18
36
  end
37
+
19
38
  # used to enable
20
39
  # def abc *key
21
40
  # where key is a Range, an comma separated List or an item
@@ -28,8 +47,15 @@ class Array
28
47
  else
29
48
  self
30
49
  end
31
-
32
50
  end
51
+
52
+ def orient_flatten
53
+ while( first.is_a?(Array) )
54
+ self.flatten!(1)
55
+ end
56
+ self.compact!
57
+ self ## return object
58
+ end
33
59
  end
34
60
 
35
61
  class Symbol
@@ -44,7 +70,7 @@ class Symbol
44
70
  "'"+self.to_orient+"'"
45
71
  end
46
72
 
47
- # inserted to prevent error message while initialising a model-recorda
73
+ # inserted to prevent error message while initializing a model-recorda
48
74
  =begin
49
75
  2.6.1 :008 > ii.first.attributes
50
76
  => {:zwoebelkuchen=>[7, 9, 9, 7, 8, 8, 3, 6, 9, ":zt:", ":zt:", ":zg:", ":zg:", ":tzu:", ":grotte:"], :created_at=>Wed, 27 Mar 2019 14:59:45 +0000}
@@ -85,9 +111,9 @@ iv [7, 9, 9, 7, 8, 8, 3, 6, 9, ":zt:", ":zt:", ":zg:", ":zg:", ":tzu:", ":grotte
85
111
  ./active-orient-console:51:in `<main>'
86
112
  =end
87
113
 
88
- def coerce a #nodoc#
89
- nil
90
- end
114
+ #def coerce a #nodoc#
115
+ # nil
116
+ #end
91
117
  end
92
118
 
93
119
  class Object
@@ -101,6 +127,23 @@ class Object
101
127
  end
102
128
 
103
129
 
130
+
131
+ class Time
132
+ def to_or
133
+ "date(\'#{self.strftime("%Y%m%d%H%M%S")}\',\'yyyyMMddHHmmss\')"
134
+ end
135
+ end
136
+
137
+ class Object
138
+ def to_or
139
+ self
140
+ end
141
+ end
142
+ class NilClass
143
+ def to_or
144
+ "NULL"
145
+ end
146
+ end
104
147
  class Date
105
148
  def to_orient
106
149
  if RUBY_PLATFORM == 'java'
@@ -109,8 +152,16 @@ class Date
109
152
  self
110
153
  end
111
154
  end
155
+ def to_or
156
+ "date(\'#{self.to_s}\',\'yyyy-MM-dd\')"
157
+ end
112
158
  end
113
159
 
160
+ class DateTime
161
+ def to_or
162
+ "date(\'#{self.strftime("%Y%m%d%H%M%S")}\',\'yyyyMMddHHmmss\')"
163
+ end
164
+ end
114
165
 
115
166
  class Numeric
116
167
 
@@ -143,11 +194,18 @@ class String
143
194
  end
144
195
  end
145
196
 
197
+ # from orient translates the database response into active-orient objects
198
+ #
199
+ # symbols are representated via ":{something]:}"
200
+ #
201
+ # database records respond to the "rid"-method
202
+ #
203
+ # other values are not modified
146
204
  def from_orient
147
205
  if rid?
148
206
  ActiveOrient::Model.autoload_object self
149
- elsif
150
- self =~ /^:.*:$/
207
+ elsif self =~ /^:.*:$/
208
+ # symbol-representation in the database
151
209
  self[1..-2].to_sym
152
210
  else
153
211
  self
@@ -169,6 +227,7 @@ class String
169
227
  def rid
170
228
  self["#"].nil? ? self : self[1..-1] if rid?
171
229
  end
230
+ alias rrid rid
172
231
 
173
232
  def to_classname
174
233
  if self[0] == '$'
@@ -200,71 +259,64 @@ class String
200
259
  end
201
260
  end
202
261
 
203
- def coerce a #nodoc#
204
- nil
205
- end
262
+ # def coerce a #nodoc#
263
+ # nil
264
+ # end
206
265
 
207
266
  def to_human
208
267
  self
209
268
  end
210
269
  end
211
270
 
212
- class Hash #WithIndifferentAccess
271
+ class Hash
213
272
 
214
- # converts "abc" => {anything} to :abc => {anything}
215
- # converts "nn" => {anything} to nn => {anything}
273
+ # converts :abc => {anything} to "abc" => {anything}
274
+ #
275
+ # converts nn => {anything} to nn => {anything}
276
+ #
277
+ # leaves "abc" => {anything} untouched
216
278
  def to_orient # converts hast from activeorient to db
217
- substitute_hash = {} # HashWithIndifferentAccess.new
218
- # puts "here to hash"
219
- #keys.each{|k| puts self[k].inspect}
220
- keys.each do |k|
279
+ map do | k, v|
221
280
  orient_k = case k
222
281
  when Numeric
223
282
  k
224
283
  when Symbol, String
225
284
  k.to_s
226
285
  else
227
- nil
286
+ raise "not supported key: #[k} -- must a sting, symbol or number"
228
287
  end
229
- substitute_hash[orient_k] = self[k].to_orient
230
- end
231
- substitute_hash
288
+ [orient_k, v.to_orient]
289
+ end.to_h
232
290
  end
233
291
 
234
- def from_orient # converts hash from db to activeorient
292
+ # converts hash from db to activeorient
293
+ #
294
+ #
295
+ def from_orient
235
296
  #puts "here hash.from_orient --> #{self.inspect}"
236
297
  if keys.include?("@class" )
237
298
  ActiveOrient::Model.orientdb_class( name: self["@class"] ).new self
299
+ # create a dummy class and fill with attributes from result-set
300
+ elsif keys.include?("@type") && self["@type"] == 'd'
301
+ ActiveOrient::Model.orientdb_class(name: 'query' ).new self
238
302
  else
239
- substitute_hash = Hash.new
240
- keys.each do |k|
303
+ # populate the hash by converting keys: stings to symbols, values: proprocess through :from_orient
304
+ map do |k,v|
241
305
  orient_k = if k.to_s.to_i.to_s == k.to_s
242
306
  k.to_i
243
307
  else
244
308
  k.to_sym
245
309
  end
246
310
 
247
- substitute_hash[orient_k] = self[k].from_orient
248
- end
249
- substitute_hash
311
+ [orient_k, v.from_orient]
312
+ end.to_h
250
313
  end
251
- end
314
+ end
252
315
 
253
316
  # converts a hash to a string appropiate to include in raw queries
254
317
  def to_or
255
- "{ " + to_orient.map{|k,v| "#{k.to_s.to_or}: #{v.to_or}"}.join(',') + "}"
318
+ "{ " + to_orient.map{|k,v| "#{k.to_or}: #{v.to_or}"}.join(',') + "}"
256
319
  end
257
- ## needs testing!!
258
- # def as_json o=nli
259
- # #puts "here hash"
260
- # substitute_hash = Hash.new
261
- # keys.each{|k| substitute_hash[k] = self[k].as_json}
262
- # substitute_hash
263
- # end
264
- # def nested_under_indifferent_access
265
- # HashWithIndifferentAccess.new self
266
- # self
267
- # end
268
320
  end
269
321
 
270
322
  #class RecordList
@@ -31,6 +31,7 @@ module RestChange
31
31
  def update record, attributes , version=0
32
32
  r = ActiveOrient::Model.autoload_object record.rid
33
33
  return(false) unless r.is_a?(ActiveOrient::Model)
34
+ raise "Cannot update the record, version-information missing. please reload ! " if version.nil?
34
35
  version = r.version if version.zero?
35
36
  result = patch_record(r.rid) do
36
37
  attributes.merge({'@version' => version, '@class' => r.class.ref_name })
@@ -82,20 +83,27 @@ Returns the JSON-Response.
82
83
  def patch_record rid # :nodoc: (used by #update )
83
84
  logger.progname = 'RestChange#PatchRecord'
84
85
  content = yield
85
- if content.is_a? Hash
86
- begin
87
- @res["/document/#{ActiveOrient.database}/#{rid}"].patch content.to_orient.to_json
88
- rescue RestClient::InternalServerError => e
89
- sentence= JSON.parse( e.response)['errors'].last['content']
90
- logger.error{sentence}
91
- logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
92
- logger.error{e.message.to_s}
93
- end
94
- else
95
- logger.error{"PATCH FAILED: The Block must provide an Hash with properties to be updated"}
96
- end
97
- end
98
- alias patch_document patch_record
86
+ if content.is_a? Hash
87
+ begin
88
+ ActiveOrient.db_pool.checkout do | conn |
89
+ conn["/document/#{ActiveOrient.database}/#{rid}"].patch content.to_orient.to_json
90
+ end
91
+ rescue RestClient::Conflict => e # (409)
92
+ # most probably the server is busy. we wait for a second print an Error-Message and retry
93
+ sleep(1)
94
+ logger.error{ "RestClient::Error(409): Server is signaling a conflict ... retrying" }
95
+ retry
96
+ rescue RestClient::InternalServerError => e
97
+ sentence= JSON.parse( e.response)['errors'].last['content']
98
+ logger.error{sentence}
99
+ logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
100
+ logger.error{e.message.to_s}
101
+ end
102
+ else
103
+ logger.error{"PATCH FAILED: The Block must provide an Hash with properties to be updated"}
104
+ end
105
+ end
106
+ alias patch_document patch_record
99
107
 
100
108
 
101
109
  #### EXPERIMENTAL ##########
@@ -132,12 +140,7 @@ Returns the JSON-Response.
132
140
  return 0
133
141
  end
134
142
 
135
- name_class = classname(o_class)
136
- execute name_class, transaction: false do # To execute commands
137
- [{ type: "cmd",
138
- language: 'sql',
139
- command: "ALTER PROPERTY #{name_class}.#{property} #{attribute} #{alteration}"}]
140
- end
143
+ execute { "ALTER PROPERTY #{class_name(o_class)}.#{property} #{attribute} #{alteration}"}
141
144
  rescue Exception => e
142
145
  logger.error{e.message}
143
146
  end
@@ -13,19 +13,21 @@ module RestCreate
13
13
  old_d = ActiveOrient.database
14
14
  ActiveOrient.database_classes = {}
15
15
  ActiveOrient.database = database
16
- begin
17
- response = @res["database/#{ActiveOrient.database}/#{type}"].post ""
18
- if response.code == 200
19
- logger.info{"Database #{ActiveOrient.database} successfully created and stored as working database"}
20
- else
16
+ begin
17
+ response = ActiveOrient.db_pool.checkout do | conn |
18
+ conn["database/#{ActiveOrient.database}/#{type}"].post ""
19
+ end
20
+ if response.code == 200
21
+ logger.info{"Database #{ActiveOrient.database} successfully created and stored as working database"}
22
+ else
23
+ logger.error{"Database #{ActiveOrient.database} was NOT created. Working Database is still #{ActiveOrient.database}"}
24
+ ActiveOrient.database = old_d
25
+ end
26
+ rescue RestClient::InternalServerError => e
21
27
  logger.error{"Database #{ActiveOrient.database} was NOT created. Working Database is still #{ActiveOrient.database}"}
22
28
  ActiveOrient.database = old_d
23
29
  end
24
- rescue RestClient::InternalServerError => e
25
- logger.error{"Database #{ActiveOrient.database} was NOT created. Working Database is still #{ActiveOrient.database}"}
26
- ActiveOrient.database = old_d
27
- end
28
- ActiveOrient.database # return_value
30
+ ActiveOrient.database # return_value
29
31
  end
30
32
 
31
33
 
@@ -51,26 +53,23 @@ def create_this_class *db_classname #nodoc#
51
53
  abstract = nil
52
54
  end
53
55
  #
54
- command= db_classname.map do | database_class |
56
+ db_classname.map do | database_class |
55
57
  c = if superclass.present?
56
58
  "CREATE CLASS #{database_class} EXTENDS #{superclass}"
57
59
  else
58
60
  "CREATE CLASS #{database_class} "
59
61
  end
60
62
  c << " ABSTRACT" if abstract.present?
61
- # { type: "cmd", language: 'sql', command: c } # return value 4 command
62
- c
63
+ execute( tolerated_error_code: /already exists/ ){ c }
63
64
  end
64
65
  # execute anything as batch, don't roll back in case of an error
65
66
 
66
- execute transaction: false, tolerated_error_code: /already exists/ do
67
- command
68
- end
67
+ # execute transaction: false, tolerated_error_code: /already exists/ do
68
+ # command
69
+ # end
69
70
 
70
71
  rescue ArgumentError => e
71
72
  logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
72
- rescue ActiveOrient::Error::ServerError
73
- # do nothing
74
73
  end
75
74
 
76
75
 
@@ -88,37 +87,44 @@ Creates a Record with the attributes provided in the attributes-hash e.g.
88
87
 
89
88
  Puts the database-response into the cache by default
90
89
 
90
+ Argument: silence
91
+ if silence is specified, no Error-Messages are raised. Instead
92
+
93
+ * if a record failes to be created, because an index-error occured, it is replaced by that specified in the database response
91
94
 
92
95
  =end
93
96
 
94
- def create_record o_class, attributes: {}, cache: true # use Model#create instead
97
+ def create_record o_class, attributes: {}, cache: true, silence: true # use Model#create instead
95
98
  logger.progname = 'RestCreate#CreateRecord'
96
99
  attributes = yield if attributes.empty? && block_given?
97
100
  # @class must not quoted! Quote only attributes(strings)
98
101
  post_argument = {'@class' => classname(o_class)}.merge(attributes.to_orient)
102
+ response = nil
99
103
  begin
100
- response = @res["/document/#{ActiveOrient.database}"].post post_argument.to_json
104
+ response = ActiveOrient.db_pool.checkout do | conn |
105
+ conn["/document/#{ActiveOrient.database}"].post post_argument.to_json
106
+ end
101
107
  data = JSON.parse(response.body)
102
108
  the_object = ActiveOrient::Model.orientdb_class(name: data['@class']).new data ## return_value
103
109
  if cache
104
- ActiveOrient::Base.store_rid( the_object )
110
+ ActiveOrient::Base.store_rid( the_object )
105
111
  else
106
- the_object
112
+ the_object
107
113
  end
108
114
  rescue RestClient::InternalServerError => e
109
115
  sentence= JSON.parse( e.response)['errors'].last['content']
110
- # puts sentence.to_s
111
116
  if sentence =~ /found duplicated key/
112
- rid = sentence.split("#").last
113
- logger.info{ "found duplicated Key --> loaded #{rid} instead of creating "}
114
- ## reading database content -- maybe update attributes?
115
- get_record rid
116
- else
117
- response = JSON.parse(e.response)['errors'].pop
118
- logger.error{response['content'].split(':')[1..-1].join(':')}
119
- logger.error{"No Object allocated"}
120
- nil # return_value
121
- end
117
+ raise "Duplicate Key" unless silence
118
+ rid = sentence.split("#").last
119
+ logger.info{ "found duplicated Key --> loaded #{rid} instead of creating "}
120
+ ## reading database content -- maybe update attributes?
121
+ get_record rid
122
+ else
123
+ response = JSON.parse(e.response)['errors'].pop
124
+ logger.error{response['content'].split(':')[1..-1].join(':')}
125
+ logger.error{"No Object allocated"}
126
+ nil # return_value
127
+ end
122
128
  rescue Errno::EADDRNOTAVAIL => e
123
129
  sleep(2)
124
130
  retry
@@ -190,22 +196,24 @@ A composite index
190
196
  logger.progname = 'RestCreate#CreateProperties'
191
197
  all_properties_in_a_hash = Hash.new #WithIndifferentAccess.new
192
198
  all_properties.each{|field, args| all_properties_in_a_hash.merge! translate_property_hash(field, args)}
193
- count=0
199
+ count, response = 0, nil
194
200
  # puts "all_properties_in_a_hash #{all_properties_in_a_hash.to_json}"
195
- begin
196
- if all_properties_in_a_hash.is_a?(Hash)
197
- response = @res["/property/#{ActiveOrient.database}/#{classname(o_class)}"].post all_properties_in_a_hash.to_json
198
- # puts response.inspect
199
- # response.body.to_i returns response.code, only to_f.to_i returns the correct value
200
- count= response.body.to_f.to_i if response.code == 201
201
+ if all_properties_in_a_hash.is_a?(Hash)
202
+ begin
203
+ response = ActiveOrient.db_pool.checkout do | conn |
204
+ conn["/property/#{ActiveOrient.database}/#{classname(o_class)}"].post all_properties_in_a_hash.to_json
205
+ end
206
+ # puts response.inspect
207
+ # response.body.to_i returns response.code, only to_f.to_i returns the correct value
208
+ count= response.body.to_f.to_i if response.code == 201
209
+ rescue RestClient::InternalServerError => e
210
+ logger.progname = 'RestCreate#CreateProperties'
211
+ response = JSON.parse(e.response)['errors'].pop
212
+ error_message = response['content'].split(':').last
213
+ logger.error{"Properties in #{classname(o_class)} were NOT created"}
214
+ logger.error{"The Error was: #{response['content'].split(':').last}"}
215
+ nil
201
216
  end
202
- rescue RestClient::InternalServerError => e
203
- logger.progname = 'RestCreate#CreateProperties'
204
- response = JSON.parse(e.response)['errors'].pop
205
- error_message = response['content'].split(':').last
206
- logger.error{"Properties in #{classname(o_class)} were NOT created"}
207
- logger.error{"The Error was: #{response['content'].split(':').last}"}
208
- nil
209
217
  end
210
218
  ### index
211
219
  if block_given?# && count == all_properties_in_a_hash.size
@@ -236,22 +244,22 @@ If an index is to be specified, it's defined in the optional block
236
244
  --> creates a manual index
237
245
  =end
238
246
 
239
- def create_property o_class, field, index: nil, **args, &b
240
- logger.progname = 'RestCreate#CreateProperty'
241
- args= { type: :string} if args.blank? # the default case
242
- c = create_properties o_class, {field => args}
243
- if index.nil? && block_given?
244
- index = yield
245
- end
246
- if index.present?
247
- if index.is_a?(String) || index.is_a?(Symbol)
248
- create_index o_class, name: field, type: index
249
- elsif index.is_a? Hash
250
- bez = index.keys.first
251
- create_index o_class, name: bez, type: index[bez], on: [field]
252
- end
253
- end
254
- end
247
+ def create_property o_class, field, index: nil, **args, &b
248
+ logger.progname = 'RestCreate#CreateProperty'
249
+ args= { type: :string} if args.blank? # the default case
250
+ c = create_properties o_class, {field => args}
251
+ if index.nil? && block_given?
252
+ index = yield
253
+ end
254
+ if index.present?
255
+ if index.is_a?(String) || index.is_a?(Symbol)
256
+ create_index o_class, name: field, type: index
257
+ elsif index.is_a? Hash
258
+ bez = index.keys.first
259
+ create_index o_class, name: bez, type: index[bez], on: [field]
260
+ end
261
+ end
262
+ end
255
263
 
256
264
  ################# INDEX ###################
257
265