active-orient 0.79 → 0.80

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