active-orient 0.4 → 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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.graphs.txt.swp +0 -0
- data/Gemfile +9 -5
- data/Guardfile +12 -4
- data/README.md +70 -281
- data/VERSION +1 -1
- data/active-orient.gemspec +9 -7
- data/bin/active-orient-0.6.gem +0 -0
- data/bin/active-orient-console +97 -0
- data/changelog.md +60 -0
- data/config/boot.rb +70 -17
- data/config/config.yml +10 -0
- data/config/connect.yml +11 -6
- data/examples/books.rb +154 -65
- data/examples/streets.rb +89 -85
- data/graphs.txt +70 -0
- data/lib/active-orient.rb +78 -6
- data/lib/base.rb +266 -168
- data/lib/base_properties.rb +76 -65
- data/lib/class_utils.rb +187 -0
- data/lib/database_utils.rb +99 -0
- data/lib/init.rb +80 -0
- data/lib/java-api.rb +442 -0
- data/lib/jdbc.rb +211 -0
- data/lib/model/custom.rb +29 -0
- data/lib/model/e.rb +6 -0
- data/lib/model/edge.rb +114 -0
- data/lib/model/model.rb +134 -0
- data/lib/model/the_class.rb +657 -0
- data/lib/model/the_record.rb +313 -0
- data/lib/model/vertex.rb +371 -0
- data/lib/orientdb_private.rb +48 -0
- data/lib/other.rb +423 -0
- data/lib/railtie.rb +68 -0
- data/lib/rest/change.rb +150 -0
- data/lib/rest/create.rb +287 -0
- data/lib/rest/delete.rb +150 -0
- data/lib/rest/operations.rb +222 -0
- data/lib/rest/read.rb +189 -0
- data/lib/rest/rest.rb +120 -0
- data/lib/rest_disabled.rb +24 -0
- data/lib/support/conversions.rb +42 -0
- data/lib/support/default_formatter.rb +7 -0
- data/lib/support/errors.rb +41 -0
- data/lib/support/logging.rb +38 -0
- data/lib/support/orient.rb +305 -0
- data/lib/support/orientquery.rb +647 -0
- data/lib/support/query.rb +92 -0
- data/rails.md +154 -0
- data/rails/activeorient.rb +32 -0
- data/rails/config.yml +10 -0
- data/rails/connect.yml +17 -0
- metadata +89 -30
- data/lib/model.rb +0 -461
- data/lib/orient.rb +0 -98
- data/lib/query.rb +0 -88
- data/lib/rest.rb +0 -1036
- data/lib/support.rb +0 -347
- data/test.rb +0 -4
- data/usecase.md +0 -91
data/lib/rest/rest.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require_relative "read.rb" # manage get
|
2
|
+
require_relative "create.rb" # manage create
|
3
|
+
require_relative "change.rb" # manage update
|
4
|
+
require_relative "operations.rb" # manage count, functions and execute
|
5
|
+
require_relative "delete.rb" # manage delete
|
6
|
+
require_relative "../support/logging"
|
7
|
+
#require 'cgi'
|
8
|
+
require 'rest-client'
|
9
|
+
require 'pond'
|
10
|
+
|
11
|
+
module ActiveOrient
|
12
|
+
|
13
|
+
=begin
|
14
|
+
OrientDB points to an OrientDB-Database.
|
15
|
+
The communication is based on the OrientDB-REST-API.
|
16
|
+
|
17
|
+
Its usually initialised through ActiveOrient::Init.connect
|
18
|
+
|
19
|
+
=end
|
20
|
+
|
21
|
+
class OrientDB
|
22
|
+
include OrientSupport::Support
|
23
|
+
include OrientSupport::Logging
|
24
|
+
include OrientDbPrivate
|
25
|
+
include DatabaseUtils
|
26
|
+
include ClassUtils
|
27
|
+
include RestRead
|
28
|
+
include RestCreate
|
29
|
+
include RestChange
|
30
|
+
include RestOperations
|
31
|
+
include RestDelete
|
32
|
+
|
33
|
+
|
34
|
+
#### INITIALIZATION ####
|
35
|
+
|
36
|
+
=begin
|
37
|
+
OrientDB is conventionally initialized.
|
38
|
+
|
39
|
+
|
40
|
+
The first call initialises database-name and -classes, server-adress and user-credentials.
|
41
|
+
|
42
|
+
Subsequent initialisations are made to initialise namespaced database classes, ie.
|
43
|
+
|
44
|
+
ORD = ActiveOrient.init.connect database: 'temp'
|
45
|
+
server: 'localhost',
|
46
|
+
port: 2480,
|
47
|
+
user: root,
|
48
|
+
password: root
|
49
|
+
module HC; end
|
50
|
+
ActiveOrient::Init.define_namespace { HC }
|
51
|
+
ActiveOrient::OrientDB.new preallocate: true
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
=end
|
58
|
+
|
59
|
+
def initialize database: nil, preallocate: true, model_dir: nil, **defaults
|
60
|
+
ActiveOrient.database ||= database || 'temp'
|
61
|
+
ActiveOrient.database_classes ||= Hash.new
|
62
|
+
|
63
|
+
ActiveOrient.default_server ||= { :server => defaults[:server] || 'localhost' ,
|
64
|
+
:port => defaults[:port] ||= 2480,
|
65
|
+
:user => defaults[:user].to_s ,
|
66
|
+
:password => defaults[:password].to_s }
|
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
|
71
|
+
connect()
|
72
|
+
database_classes # initialize @classes-array and ActiveOrient.database_classes
|
73
|
+
ActiveOrient::Base.logger = logger
|
74
|
+
ActiveOrient::Model.orientdb = self
|
75
|
+
ActiveOrient::Model.db = self
|
76
|
+
ActiveOrient::Model.keep_models_without_file ||= nil
|
77
|
+
preallocate_classes( model_dir ) if preallocate
|
78
|
+
Thread.abort_on_exception = true
|
79
|
+
end
|
80
|
+
|
81
|
+
# thread safe method to allocate a resource
|
82
|
+
def get_resource
|
83
|
+
logger.debug {"ALLOCATING NEW RESOURCE --> #{ ActiveOrient.db_pool.size }" }
|
84
|
+
login = [ActiveOrient.default_server[:user] , ActiveOrient.default_server[:password]]
|
85
|
+
server_adress = "http://#{ActiveOrient.default_server[:server]}:#{ActiveOrient.default_server[:port]}"
|
86
|
+
RestClient::Resource.new(server_adress, *login)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
# Used to connect to the database
|
92
|
+
|
93
|
+
def connect
|
94
|
+
first_tentative = true
|
95
|
+
begin
|
96
|
+
database = ActiveOrient.database
|
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
|
120
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
=begin #nodoc#
|
2
|
+
If properties are allocated on class-level, they can be preinitialized using
|
3
|
+
this method.
|
4
|
+
This is disabled for now, because it does not seem nessesary
|
5
|
+
=end
|
6
|
+
|
7
|
+
def preallocate_class_properties o_class # :nodoc:
|
8
|
+
p= get_class_properties( o_class )['properties']
|
9
|
+
unless p.nil? || p.blank?
|
10
|
+
predefined_attributes = p.map do | property |
|
11
|
+
[ property['name'] ,
|
12
|
+
case property['type']
|
13
|
+
when 'LINKMAP'
|
14
|
+
Array.new
|
15
|
+
when 'STRING'
|
16
|
+
''
|
17
|
+
else
|
18
|
+
nil
|
19
|
+
end ]
|
20
|
+
end.to_h
|
21
|
+
else
|
22
|
+
{}
|
23
|
+
end
|
24
|
+
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,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
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#require_relative 'default_formatter'
|
2
|
+
module OrientSupport
|
3
|
+
module Logging
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
base.send :define_method, :logger do
|
7
|
+
base.logger
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def logger
|
13
|
+
@logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def logger=(logger)
|
17
|
+
@logger = logger
|
18
|
+
end
|
19
|
+
|
20
|
+
def configure_logger(log= nil)
|
21
|
+
if log
|
22
|
+
@logger = log
|
23
|
+
else
|
24
|
+
@logger = Logger.new(STDOUT)
|
25
|
+
@logger.level = Logger::INFO
|
26
|
+
@logger.formatter = DefaultFormatter
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class DefaultFormatter < Logger::Formatter
|
33
|
+
def self.call(severity, time, program_name, msg)
|
34
|
+
"#{time.strftime("%d.%m.(%X)")}#{"%5s" % severity}->#{msg}\n"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# source: https://github.com/jondot/sneakers/blob/master/lib/sneakers/concerns/logging.rb
|
@@ -0,0 +1,305 @@
|
|
1
|
+
module OrientSupport
|
2
|
+
|
3
|
+
# This Module fences specialized Ruby objects
|
4
|
+
|
5
|
+
# The Array _knows_ its database-class. This enables database-transactions outside the scope
|
6
|
+
# of ActiveOrient
|
7
|
+
#
|
8
|
+
# The Database-Class is available through Array#record
|
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
|
+
|
26
|
+
class Array < Array
|
27
|
+
include OrientSupport::Support
|
28
|
+
|
29
|
+
=begin
|
30
|
+
During initialisation the model-instance to work on is stored in @orient.
|
31
|
+
|
32
|
+
The keyword_parameter »work_on« holds the record to work on.
|
33
|
+
The second argument holds the array to work with
|
34
|
+
|
35
|
+
If instead of a model-instance the model-class is provided, a new model-instance is created and returned
|
36
|
+
Its up to the caller to save the new instance in the database
|
37
|
+
|
38
|
+
Further a list of array-elements is expected, which are forwarded (as Array) to Array
|
39
|
+
|
40
|
+
Its used to initialize Objects comming from the database (i.e. /lib/base.rb)
|
41
|
+
|
42
|
+
elsif iv.is_a? Array
|
43
|
+
OrientSupport::Array.new( work_on: self, work_with: iv.from_orient){ key.to_sym }
|
44
|
+
|
45
|
+
=end
|
46
|
+
|
47
|
+
def initialize( work_on:, work_with: )
|
48
|
+
@orient = work_on.class == Class ? work_on.new : work_on
|
49
|
+
super work_with
|
50
|
+
begin
|
51
|
+
@name = block_given? ? yield : @orient.attributes.key(self)
|
52
|
+
rescue TypeError => e # not defined
|
53
|
+
ActiveOrient::Base.logger.debug{ "--------------------Type Error ----------------------------------" }
|
54
|
+
ActiveOrient::Base.logger.debug("OrientSupport::Array"){ "Attributes #{@orient.attributes.inspect}" }
|
55
|
+
ActiveOrient::Base.logger.debug("OrientSupport::Array"){ e.inspect
|
56
|
+
ActiveOrient::Base.logger.debug{ "indicates a try to access a non existing array element" }}
|
57
|
+
nil
|
58
|
+
rescue NameError =>e
|
59
|
+
ActiveOrient::Base.logger.debug{ "--------------------Name Error ------------" }
|
60
|
+
ActiveOrient::Base.logger.debug ("OrientSupport::Array"){ e.inspect }
|
61
|
+
#ActiveOrient::Base.logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
|
62
|
+
ActiveOrient::Base.logger.debug{ "due to a bug in ActiveSupport DateTime Calculations" }
|
63
|
+
# we just ignore the error
|
64
|
+
end
|
65
|
+
end
|
66
|
+
def as_json o=nil
|
67
|
+
map{|x| x.rid? ? x.rid : x }
|
68
|
+
end
|
69
|
+
|
70
|
+
def record
|
71
|
+
@orient
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_human
|
75
|
+
map &:to_human
|
76
|
+
end
|
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 )
|
92
|
+
#
|
93
|
+
# i= V.get( '89:0')
|
94
|
+
# ii=i.zwoebelkuchen << 'z78' << 6 << [454, 787]
|
95
|
+
# => [7, 5, 6, "z78", 78, 45, "z78", 6, 454, 787]
|
96
|
+
|
97
|
+
The change is immediately transmitted to the database.
|
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`
|
102
|
+
|
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]
|
124
|
+
|
125
|
+
=end
|
126
|
+
def << arg
|
127
|
+
append( *arg).send @name
|
128
|
+
end
|
129
|
+
|
130
|
+
=begin
|
131
|
+
|
132
|
+
Removes the specified list entries from the Array
|
133
|
+
|
134
|
+
Returns the modified Array (and is chainable).
|
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
|
160
|
+
def remove *k
|
161
|
+
# todo combine queries in a transaction
|
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
|
166
|
+
end
|
167
|
+
|
168
|
+
def remove_by_index index
|
169
|
+
@orient.update( { remove: { @name => "#{@name[index]}" } } )
|
170
|
+
end
|
171
|
+
|
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
|
180
|
+
=begin
|
181
|
+
Updating of single items
|
182
|
+
=end
|
183
|
+
def []= key, value
|
184
|
+
super
|
185
|
+
@orient.update set: {@name => self} if @name.present? if check_if_complete
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
###
|
190
|
+
## just works with Hashes as parameters
|
191
|
+
def where *item
|
192
|
+
where_string = item.map{|m| where_string = compose_where( m ) }.join(' and ')
|
193
|
+
subquery= OrientSupport::OrientQuery.new from: @orient, projection: "expand( #{@name})"
|
194
|
+
q= OrientSupport::OrientQuery.new from: subquery, where: item
|
195
|
+
@orient.db.execute{ q.to_s } if check_if_complete
|
196
|
+
|
197
|
+
end
|
198
|
+
|
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
|
207
|
+
rescue NoMethodError => e
|
208
|
+
ActiveOrient::Base.logger.error("OrientSupport::Array"){ "#{self.inspect} MethodMissing -> Undefined method: #{args.first} -- Args: #{args[1..-1].inspect}"}
|
209
|
+
ActiveOrient::Base.logger.error {" The Message #{e.message}"}
|
210
|
+
ActiveOrient::Base.logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
|
211
|
+
raise
|
212
|
+
end
|
213
|
+
|
214
|
+
end #Class
|
215
|
+
|
216
|
+
|
217
|
+
|
218
|
+
|
219
|
+
class Hash < Hash # WithIndifferentAccess
|
220
|
+
include OrientSupport::Support
|
221
|
+
def initialize modelinstance, args
|
222
|
+
super()
|
223
|
+
@orient = modelinstance
|
224
|
+
self.merge! args
|
225
|
+
@name = block_given? ? yield : modelinstance.attributes.key(self)
|
226
|
+
self
|
227
|
+
end
|
228
|
+
|
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!
|
232
|
+
end
|
233
|
+
|
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!
|
244
|
+
end
|
245
|
+
|
246
|
+
alias << merge
|
247
|
+
|
248
|
+
# removes a key-value entry from the hash.
|
249
|
+
#
|
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
|
+
|
261
|
+
def remove *k
|
262
|
+
# todo combine queries in a transaction
|
263
|
+
|
264
|
+
r= k.map{|key| @orient.update{ "remove #{@name} = #{key.to_s.to_or} " } }
|
265
|
+
@orient.reload!.send @name
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
def delete_if &b
|
270
|
+
super &b
|
271
|
+
@orient.update set:{ @name => self}
|
272
|
+
|
273
|
+
end
|
274
|
+
|
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
|
288
|
+
end
|
289
|
+
end #Module
|
290
|
+
|
291
|
+
class Hash
|
292
|
+
|
293
|
+
def to_human
|
294
|
+
"{ " + self.map{ |k,v| [k.to_s,": ", v.to_orient].join }.join(', ') + " }"
|
295
|
+
end
|
296
|
+
|
297
|
+
# def coerce arg
|
298
|
+
# if arg.is_a? DateTime
|
299
|
+
# nil
|
300
|
+
# else
|
301
|
+
# super
|
302
|
+
#
|
303
|
+
# end
|
304
|
+
# end
|
305
|
+
end
|