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.
@@ -4,8 +4,9 @@ require_relative "change.rb" # manage update
4
4
  require_relative "operations.rb" # manage count, functions and execute
5
5
  require_relative "delete.rb" # manage delete
6
6
  require_relative "../support/logging"
7
- require 'cgi'
7
+ #require 'cgi'
8
8
  require 'rest-client'
9
+ require 'pond'
9
10
 
10
11
  module ActiveOrient
11
12
 
@@ -63,8 +64,10 @@ Subsequent initialisations are made to initialise namespaced database classes, i
63
64
  :port => defaults[:port] ||= 2480,
64
65
  :user => defaults[:user].to_s ,
65
66
  :password => defaults[:password].to_s }
66
-
67
- @res = get_resource
67
+ # setup connection pool
68
+ # database-settings: client.channel.maxPool = 100
69
+ ActiveOrient.db_pool ||= Pond.new( :maximum_size => 25, :timeout => 50) { get_resource }
70
+ # ActiveOrient.db_pool.collection = :stack
68
71
  connect()
69
72
  database_classes # initialize @classes-array and ActiveOrient.database_classes
70
73
  ActiveOrient::Base.logger = logger
@@ -72,41 +75,46 @@ Subsequent initialisations are made to initialise namespaced database classes, i
72
75
  ActiveOrient::Model.db = self
73
76
  ActiveOrient::Model.keep_models_without_file ||= nil
74
77
  preallocate_classes( model_dir ) if preallocate
75
-
78
+ Thread.abort_on_exception = true
76
79
  end
77
80
 
81
+ # thread safe method to allocate a resource
78
82
  def get_resource
83
+ logger.debug {"ALLOCATING NEW RESOURCE --> #{ ActiveOrient.db_pool.size }" }
79
84
  login = [ActiveOrient.default_server[:user] , ActiveOrient.default_server[:password]]
80
85
  server_adress = "http://#{ActiveOrient.default_server[:server]}:#{ActiveOrient.default_server[:port]}"
81
- RestClient::Resource.new(server_adress, *login)
86
+ RestClient::Resource.new(server_adress, *login)
82
87
  end
83
88
 
89
+
90
+
84
91
  # Used to connect to the database
85
92
 
86
- def connect
87
- first_tentative = true
88
- begin
93
+ def connect
94
+ first_tentative = true
95
+ begin
89
96
  database = ActiveOrient.database
90
- logger.progname = 'OrientDB#Connect'
91
- r = @res["/connect/#{database}"].get
92
- if r.code == 204
93
- logger.info{"Connected to database #{database}"}
94
- true
95
- else
96
- logger.error{"Connection to database #{database} could NOT be established"}
97
- nil
98
- end
99
- rescue RestClient::Unauthorized => e
100
- if first_tentative
101
- logger.info{"Database #{database} NOT present --> creating"}
102
- first_tentative = false
103
- create_database database: database
104
- retry
105
- else
106
- Kernel.exit
107
- end
108
- end
109
- end
110
-
111
- end
97
+ logger.progname = 'OrientDB#Connect'
98
+ r = ActiveOrient.db_pool.checkout do | conn |
99
+ r = conn["/connect/#{database}"].get
100
+ end
101
+ if r.code == 204
102
+ logger.info{"Connected to database #{database}"}
103
+ true
104
+ else
105
+ logger.error{"Connection to database #{database} could NOT be established"}
106
+ nil
107
+ end
108
+ rescue RestClient::Unauthorized => e
109
+ if first_tentative
110
+ logger.info{"Database #{database} NOT present --> creating"}
111
+ first_tentative = false
112
+ create_database database: database
113
+ retry
114
+ else
115
+ Kernel.exit
116
+ end
117
+ end
118
+ end
119
+ end
112
120
  end
@@ -0,0 +1,42 @@
1
+ =begin
2
+ Rails-specific stuff
3
+
4
+ Mimics ActiveModell::conversions
5
+ =end
6
+ module Conversions
7
+
8
+
9
+ =begin
10
+ Returns an Array of all key attributes if any is set, regardless if the object is persisted or not. Returns nil if there are no key attributes.
11
+ =end
12
+ def to_key
13
+ key = respond_to?(:rid) && rid
14
+ key ? [key] : nil
15
+ end
16
+
17
+ # Returns a +string+ representing the object's key suitable for use in URLs,
18
+ # # or +nil+ if <tt>persisted?</tt> is +false+.
19
+ def to_param
20
+ (persisted? && key = to_key) ? key.join('-') : nil
21
+ end
22
+
23
+
24
+ # Returns a +string+ identifying the path associated with the object.
25
+ # # ActionPack uses this to find a suitable partial to represent the object.
26
+ def to_partial_path
27
+ self.class._to_partial_path
28
+ end
29
+
30
+ # module ClassMethods #:nodoc:
31
+ # Provide a class level cache for #to_partial_path. This is an
32
+ # internal method and should not be accessed directly.
33
+
34
+ # def self._to_partial_path #:nodoc:
35
+ # @_to_partial_path ||= begin
36
+ # element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(name))
37
+ # collection = ActiveSupport::Inflector.tableize(name)
38
+ # "#{collection}/#{element}".freeze
39
+ # end
40
+ # end
41
+ #end
42
+ end
@@ -0,0 +1,7 @@
1
+ module OrientSupport
2
+ class DefaultFormatter < Logger::Formatter
3
+ def self.call(severity, time, program_name, msg)
4
+ "#{time.strftime("%d.%m.(%X)")}#{"%5s" % severity}->#{msg}"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,41 @@
1
+ module ActiveOrient
2
+ module Error
3
+ # Error handling
4
+ class Error < RuntimeError
5
+ end
6
+
7
+ class ArgumentError < ArgumentError
8
+ end
9
+
10
+ class SymbolError < ArgumentError
11
+ end
12
+
13
+ class LoadError < LoadError
14
+ end
15
+
16
+ class ServerError < RuntimeError
17
+ end
18
+ end # module IB
19
+ end
20
+ # Patching Object with universally accessible top level error method.
21
+ # The method is used throughout the lib instead of plainly raising exceptions.
22
+ # This allows lib user to easily inject user-specific error handling into the lib
23
+ # by just replacing Object#error method.
24
+ def error message, type=:standard, backtrace=nil
25
+ e = case type
26
+ when :standard
27
+ ActiveOrientOrient::Error.new message
28
+ when :args
29
+ ActiveOrient::ArgumentError.new message
30
+ when :symbol
31
+ ActiveOrient::SymbolError.new message
32
+ when :load
33
+ AcitveOrient::LoadError.new message
34
+ when :server
35
+ ActiveOrient::Error::ServerError.new message
36
+ end
37
+ e.set_backtrace(backtrace) if backtrace
38
+ raise e
39
+ end
40
+
41
+ # resued from https://github.com/ib-ruby/ib-ruby
@@ -6,8 +6,23 @@ module OrientSupport
6
6
  # of ActiveOrient
7
7
  #
8
8
  # The Database-Class is available through Array#record
9
-
10
-
9
+ #
10
+ # *caution:*
11
+ # Don't mix ActiveOrient::Array's with conventional ones
12
+ # > t= G21.first
13
+ # > t.ll
14
+ # => ["test", "test_2", 5, 8, 7988, "uzg"]
15
+ # > t.ll = [9,6,7] # This is an assignment of an Array to the variable »ll»
16
+ # # It does NOT call ActiveOrient::Array#=[].
17
+ # => [9, 6, 7] # Instead an Array is assigned to the variable »ll»
18
+ #
19
+ # it is only updated localy, as shown if we reload the document
20
+ # > t= G21.first.attributes
21
+ # => {:ll=>["test", "test_2", 5, 8, 7988, "uzg"]}
22
+ #
23
+ # Thus its imperativ to safe the changes made.
24
+
25
+
11
26
  class Array < Array
12
27
  include OrientSupport::Support
13
28
 
@@ -33,7 +48,7 @@ module OrientSupport
33
48
  @orient = work_on.class == Class ? work_on.new : work_on
34
49
  super work_with
35
50
  begin
36
- @name = @orient.attributes.key(self)
51
+ @name = block_given? ? yield : @orient.attributes.key(self)
37
52
  rescue TypeError => e # not defined
38
53
  ActiveOrient::Base.logger.debug{ "--------------------Type Error ----------------------------------" }
39
54
  ActiveOrient::Base.logger.debug("OrientSupport::Array"){ "Attributes #{@orient.attributes.inspect}" }
@@ -47,7 +62,6 @@ module OrientSupport
47
62
  ActiveOrient::Base.logger.debug{ "due to a bug in ActiveSupport DateTime Calculations" }
48
63
  # we just ignore the error
49
64
  end
50
- @name = yield if @name.nil? && block_given?
51
65
  end
52
66
  def as_json o=nil
53
67
  map{|x| x.rid? ? x.rid : x }
@@ -60,40 +74,115 @@ module OrientSupport
60
74
  def to_human
61
75
  map &:to_human
62
76
  end
63
- # returns the modified array and is chainable
77
+ =begin
78
+
79
+ Appends the arguments to the Array.
80
+
81
+ Returns the modified database-document (not the array !!)
82
+ =end
83
+ def append *arg
84
+
85
+ @orient.update { "set #{@name.to_s} = #{@name} || #{arg.to_or} "}[@name] if check_if_complete
86
+ @orient.reload!
87
+ end
88
+ =begin
89
+ Append the argument to the Array, changes the Array itself.
90
+
91
+ Returns the modified Array ( and is chainable )
64
92
  #
65
93
  # i= V.get( '89:0')
66
94
  # ii=i.zwoebelkuchen << 'z78' << 6 << [454, 787]
67
95
  # => [7, 5, 6, "z78", 78, 45, "z78", 6, 454, 787]
68
- =begin
69
- Append the argument to the Array, changes the Array itself.
70
96
 
71
97
  The change is immediately transmitted to the database.
72
98
 
99
+ The difference to `append`: that method accepts a komma separated list of arguments
100
+ and returns the modified database-document. `<<` accepts only one argument. An
101
+ Array is translated into multi-arguments of `append`
73
102
 
74
- =end
75
- def append *arg
103
+ > t = G21.create ll: ['test','test_2', 5, 8 , 7988, "uzg"]
104
+ INFO->CREATE VERTEX ml_g21 CONTENT {"ll":["test","test_2",5,8,7988,"uzg"]}
105
+ => #<ML::G21:0x0000000002622cb0 @metadata={:type=>"d", :class=>"ml_g21", :version=>1,
106
+ :fieldTypes=>nil, :cluster=>271, :record=>0},
107
+ @attributes={:ll=>["test", "test_2", 5, 8, 7988, "uzg"]}>
108
+ > t.ll << [9,10]
109
+ INFO->update #271:0 set ll = ll || [9, 10] return after @this
110
+ => ["test", "test_2", 5, 8, 7988, "uzg"]
111
+ > t.ll << [9,10] << 'u'
112
+ INFO->update #271:0 set ll = ll || [9, 10] return after @this
113
+ INFO->update #271:0 set ll = ll || ['u'] return after @this
114
+ => ["test", "test_2", 5, 8, 7988, "uzg", 9, 10]
115
+
116
+
117
+ The Array can be treated separately
118
+
119
+ > z = t.ll
120
+ => ["test", "test_2", 5, 8, 7988, "uzg"]
121
+ > z << 78
122
+ INFO->update #272:0 set ll = ll || [78] return after @this
123
+ => ["test", "test_2", 5, 8, 7988, "uzg", 78]
76
124
 
77
- @orient.update { "#{@name.to_s} = #{@name} || #{arg.to_or} "}[@name]
125
+ =end
126
+ def << arg
127
+ append( *arg).send @name
78
128
  end
79
129
 
80
- alias << append
130
+ =begin
131
+
132
+ Removes the specified list entries from the Array
133
+
134
+ Returns the modified Array (and is chainable).
81
135
 
136
+ > t= G21.first
137
+ > t.ll
138
+ => ["test", "test_2", 7988, "uzg", 6789, "xvy"]
139
+ > u= t.ll << 'xvz'
140
+ # INFO->update #272:0 set ll = ll || ['xvz'] return after @this
141
+ => ["test", "test_2", 7988, "uzg", 6789, "xvy", "xvz"]
142
+ > z= u.remove 'xvy'
143
+ # INFO->update #272:0 remove ll = 'xvy' return after @this
144
+ => ["test", "test_2", 7988, "uzg", 6789, "xvz"]
145
+
146
+ The ModelInstance is updated, too, as shown by calling
147
+
148
+ > t.ll
149
+ => ["test", "test_2", 7988, "uzg", 6789, "xvz"]
150
+
151
+
152
+ Thus
153
+
154
+ > t.ll.remove 7988
155
+ # INFO->update #272:0 remove ll = 7988 return after @this
156
+ => ["test", "test_2", "uzg", 6789, "xvz"]
157
+
158
+ returns thea modified Array
159
+ =end
82
160
  def remove *k
83
161
  # todo combine queries in a transaction
84
- puts "delete: #{@name} --< #{k.map(&:to_or).join( ' :: ' )}"
85
- k.each{|item| @orient.remove( " #{@name} = #{item.to_or}")[@name] }
162
+ ActiveOrient::Base.logger.debug { "delete: #{@name} --< #{k.map(&:to_or).join( ' :: ' )}"}
163
+ k.map{|l| @orient.update( {remove: { @name => l} } ) }
164
+ # @orient.reload!
165
+ # @orient.send @name
86
166
  end
87
167
 
168
+ def remove_by_index index
169
+ @orient.update( { remove: { @name => "#{@name[index]}" } } )
170
+ end
88
171
 
89
-
172
+ def check_if_complete
173
+ if @name.blank?
174
+ @orient.logger.warn{ "Database is uneffected. Operation is incomplete/ not allowed" }
175
+ false
176
+ else
177
+ true
178
+ end
179
+ end
90
180
  =begin
91
181
  Updating of single items
92
182
  =end
93
-
94
183
  def []= key, value
95
184
  super
96
- @orient.update set: {@name => self} if @name.present?
185
+ @orient.update set: {@name => self} if @name.present? if check_if_complete
97
186
  end
98
187
 
99
188
 
@@ -103,17 +192,23 @@ The change is immediately transmitted to the database.
103
192
  where_string = item.map{|m| where_string = compose_where( m ) }.join(' and ')
104
193
  subquery= OrientSupport::OrientQuery.new from: @orient, projection: "expand( #{@name})"
105
194
  q= OrientSupport::OrientQuery.new from: subquery, where: item
106
- @orient.query q.to_s
195
+ @orient.db.execute{ q.to_s } if check_if_complete
107
196
 
108
197
  end
109
198
 
110
- def method_missing *args
111
-
112
- self.map{|x| x.send *args }
199
+ def method_missing method, *args
200
+ return if empty?
201
+ if @orient.is_a? ActiveOrient::Model # IB::Model
202
+ # delegate to public methods
203
+ self.map{|x| x.public_send(method, *args)}
204
+ else
205
+ self.map{|x| x.send method, *args }
206
+ end
113
207
  rescue NoMethodError => e
114
- ActiveOrient::Base.logger.error("OrientSupport::Array"){ "MethodMissing -> Undefined method: #{args.first} -- Args: #{args[1..-1].inspect}"}
208
+ ActiveOrient::Base.logger.error("OrientSupport::Array"){ "#{self.inspect} MethodMissing -> Undefined method: #{args.first} -- Args: #{args[1..-1].inspect}"}
115
209
  ActiveOrient::Base.logger.error {" The Message #{e.message}"}
116
210
  ActiveOrient::Base.logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
211
+ raise
117
212
  end
118
213
 
119
214
  end #Class
@@ -125,49 +220,51 @@ The change is immediately transmitted to the database.
125
220
  include OrientSupport::Support
126
221
  def initialize modelinstance, args
127
222
  super()
128
- # puts "Hash.new args #{args}"
129
223
  @orient = modelinstance
130
224
  self.merge! args
131
- @name = modelinstance.attributes.key(self)
132
- @name = yield if @name.nil? && block_given?
133
- # puts "@name #{@name}"
225
+ @name = block_given? ? yield : modelinstance.attributes.key(self)
134
226
  self
135
227
  end
136
228
 
137
-
138
- def []= k, v
139
- @orient.update { "#{@name.to_s}.#{k.to_s} = #{v.to_or}" }
229
+ def store k, v
230
+ @orient.update { "set #{@name}[#{k.to_s.to_or}] = #{v.to_or} "}[@name] #if check_if_complete
231
+ @orient.reload!
140
232
  end
141
233
 
142
- # Inserts the provided Hash to the (possibly emty) list-property and returns a hash
143
- def append arg
144
- # the argument is simply provided as JSON-parameter to »update«
145
- # generated query: update {rrid} set { @name } = { arg.to_json } return after @this
146
- # todo : consider returning a OrientSuport::Hash
147
- @orient.update { "#{@name.to_s} = "+ arg.to_json }[@name]
234
+ alias []= store
235
+
236
+
237
+ # Inserts the provided Hash to the (possibly empty) list-property and updates the dataset
238
+ #
239
+ # Keys are translated to symbols
240
+ #
241
+ def merge **arg
242
+ @orient.update @name => super(**arg)
243
+ @orient.reload!
148
244
  end
149
245
 
150
- alias << append
246
+ alias << merge
151
247
 
152
248
  # removes a key-value entry from the hash.
153
249
  #
154
- # parameter: list of key's (duplicate values are removed)
250
+ # parameter: list of key's
251
+ #
252
+ # returns the modified OrientSupport::Hash
253
+ #
254
+ # ie, given
255
+ # b => <Base[51:0]: < Base: 51:0 >, a_set : {:warrant_value=>["8789", "HKD"], :what_if_pm_enabled=>["true", ""], :t_bill_value=>["0", "HKD"]}>
256
+ # c= b.a_set.remove :warrant_value
257
+ # INFO->update #51:0 remove a_set = 'warrant_value' return after $current
258
+ # c => {:what_if_pm_enabled=>["true", ""], :t_bill_value=>["0", "HKD"]}
259
+
260
+
155
261
  def remove *k
156
262
  # todo combine queries in a transaction
157
- k.map{ |key| @orient.update( remove: true ) { "#{@name.to_s}.#{key} " } }.last
263
+
264
+ r= k.map{|key| @orient.update{ "remove #{@name} = #{key.to_s.to_or} " } }
265
+ @orient.reload!.send @name
266
+
158
267
  end
159
- # def delete *key
160
- #
161
- # key.each do | k |
162
- # o = OrientSupport::OrientQuery.new from: @orient,
163
- # kind: 'update',
164
- # set: "#{@name}.#{k.to_s}",
165
- # return: "$current.#{@name}"
166
- # @orient.db.execute{ o.to_s.gsub( 'set ', 'remove ' ) }.first.send( @name ) # extracts the modified array (from DB) from the result
167
- # end
168
- # @orient.reload!
169
- # @orient.send @name # return value
170
- # end
171
268
 
172
269
  def delete_if &b
173
270
  super &b
@@ -175,7 +272,19 @@ The change is immediately transmitted to the database.
175
272
 
176
273
  end
177
274
 
178
-
275
+ # slice returns a subset of the hash
276
+ #
277
+ # excepts a regular expression as well
278
+ def slice arg
279
+ if arg.is_a? Regexp
280
+ find_all{ |key| key.to_s.match(arg) }.to_h
281
+ else
282
+ super arg.to_sym
283
+ end
284
+ end
285
+ def [] arg
286
+ super
287
+ end
179
288
  end
180
289
  end #Module
181
290
 
@@ -185,12 +294,12 @@ class Hash
185
294
  "{ " + self.map{ |k,v| [k.to_s,": ", v.to_orient].join }.join(', ') + " }"
186
295
  end
187
296
 
188
- def coerce arg
189
- if arg.is_a? DateTime
190
- nil
191
- else
192
- super
193
-
194
- end
195
- end
297
+ # def coerce arg
298
+ # if arg.is_a? DateTime
299
+ # nil
300
+ # else
301
+ # super
302
+ #
303
+ # end
304
+ # end
196
305
  end