active-orient 0.79 → 0.80

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