arcadedb 0.4 → 0.5.2
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 +4 -4
- data/CHANGELOG.md +32 -1
- data/Gemfile.lock +2 -36
- data/README.md +191 -41
- data/arcade.yml +1 -1
- data/bin/console +17 -16
- data/iruby/.ipynb_checkpoints/01-start-and-end-of-datarows-checkpoint.ipynb +258 -0
- data/iruby/01-start-and-end-of-datarows.ipynb +203 -0
- data/iruby/db-console.rb +73 -0
- data/lib/arcade/api/operations.rb +48 -78
- data/lib/arcade/api/primitives.rb +64 -29
- data/lib/arcade/base.rb +105 -51
- data/lib/{config.rb → arcade/config.rb} +5 -6
- data/lib/arcade/database.rb +107 -156
- data/lib/arcade/errors.rb +8 -0
- data/lib/{init.rb → arcade/init.rb} +5 -2
- data/lib/arcade/match.rb +210 -0
- data/lib/{query.rb → arcade/query.rb} +40 -29
- data/lib/{support → arcade/support}/conversions.rb +1 -0
- data/lib/{support → arcade/support}/model.rb +23 -22
- data/lib/{support → arcade/support}/sql.rb +1 -1
- data/lib/{support → arcade/support}/string.rb +11 -14
- data/lib/arcade/version.rb +1 -1
- data/lib/arcade.rb +16 -15
- data/lib/model/document.rb +22 -0
- data/lib/model/edge.rb +29 -0
- data/lib/model/revision.rb +41 -0
- data/lib/model/revision_record.rb +29 -0
- data/lib/model/vertex.rb +118 -48
- data/lib/models.rb +2 -0
- data/lib/types.rb +35 -0
- metadata +22 -16
- data/lib/match.rb +0 -216
- /data/lib/{support → arcade/support}/class.rb +0 -0
- /data/lib/{support → arcade/support}/object.rb +0 -0
@@ -9,7 +9,7 @@ module Arcade
|
|
9
9
|
$ Arcade::Api.drop_database <a string> # returns true if successfull
|
10
10
|
|
11
11
|
$ Arcade::Api.create_document <database>, <type>, attributes
|
12
|
-
$ Arcade::Api.execute( <database> ) { <query> }
|
12
|
+
$ Arcade::Api.execute( <database> [, session_id: some_session_id ]) { <query> }
|
13
13
|
$ Arcade::Api.query( <database> ) { <query> }
|
14
14
|
$ Arcade::Api.get_record <database>, rid # returns a hash
|
15
15
|
|
@@ -32,13 +32,20 @@ module Arcade
|
|
32
32
|
get_data 'databases'
|
33
33
|
end
|
34
34
|
|
35
|
+
# ------------------------------ database? ------------------------------------------------- #
|
36
|
+
# returns true if the database exists #
|
37
|
+
|
38
|
+
def self.database? name
|
39
|
+
get_data "exists/#{name}"
|
40
|
+
end
|
41
|
+
|
35
42
|
# ------------------------------ create database ------------------------------------------------- #
|
36
43
|
# creates a database if not present #
|
37
44
|
def self.create_database name
|
38
45
|
return if databases.include?( name.to_s )
|
39
46
|
payload = { "command" => "create database #{name}" }
|
40
47
|
post_data "server", payload
|
41
|
-
rescue
|
48
|
+
rescue Arcade::QueryError => e
|
42
49
|
logger.fatal "Create database #{name} through \"POST create/#{name}\" failed"
|
43
50
|
logger.fatal e
|
44
51
|
raise
|
@@ -50,29 +57,10 @@ module Arcade
|
|
50
57
|
return unless databases.include?( name.to_s )
|
51
58
|
payload = {"command" => "drop database #{name}" }
|
52
59
|
post_data "server", payload
|
53
|
-
rescue
|
60
|
+
rescue Arcade::QueryError => e
|
54
61
|
logger.fatal "Drop database #{name} through \"POST drop database/#{name}\" failed"
|
55
62
|
raise
|
56
63
|
end
|
57
|
-
# ------------------------------ create document ------------------------------------------------- #
|
58
|
-
# adds a document to the specified database table
|
59
|
-
#
|
60
|
-
# specify database-fields as hash-type parameters
|
61
|
-
#
|
62
|
-
# i.e Arcade::Api.create_document 'devel', 'documents', name: 'herta meyer', age: 56, sex: 'f'
|
63
|
-
#
|
64
|
-
# returns the rid of the inserted dataset
|
65
|
-
#
|
66
|
-
def self.create_document database, type, **attributes
|
67
|
-
payload = { "@type" => type }.merge( attributes )
|
68
|
-
logger.debug "C: #{payload}"
|
69
|
-
options = if session.nil?
|
70
|
-
payload
|
71
|
-
else
|
72
|
-
payload.merge headers: { "arcadedb-session-id" => session }
|
73
|
-
end
|
74
|
-
post_data "document/#{database}", options
|
75
|
-
end
|
76
64
|
|
77
65
|
# ------------------------------ execute ------------------------------------------------- #
|
78
66
|
# executes a sql-query in the specified database
|
@@ -84,21 +72,40 @@ module Arcade
|
|
84
72
|
# Arcade::Api.execute( "devel" ) { 'select from test ' }
|
85
73
|
# =y [{"@rid"=>"#57:0", "@type"=>"test", "name"=>"Hugo"}, {"@rid"=>"#60:0", "@type"=>"test", "name"=>"Hubert"}]
|
86
74
|
#
|
87
|
-
def self.execute database,
|
88
|
-
pl =
|
89
|
-
if session_id.nil?
|
75
|
+
def self.execute database, session_id: nil
|
76
|
+
pl = provide_payload(yield)
|
77
|
+
if session_id.nil?
|
90
78
|
post_data "command/#{database}" , pl
|
91
79
|
else
|
92
|
-
post_transaction "command/#{database}" , pl, session_id
|
80
|
+
post_transaction "command/#{database}" , pl, session_id: session_id
|
93
81
|
end
|
94
82
|
end
|
95
83
|
|
96
84
|
# ------------------------------ query ------------------------------------------------- #
|
97
85
|
# same for idempotent queries
|
98
|
-
def self.query database, query
|
99
|
-
|
86
|
+
def self.query database, query, session_id: nil
|
87
|
+
# if session_id.nil?
|
88
|
+
get_data "query/#{database}/sql/" + provide_payload(query, action: :get)[:query]
|
89
|
+
# else
|
90
|
+
# post_transaction "query/#{database}" , provide_payload(query), session_id: session_id
|
91
|
+
# end
|
100
92
|
end
|
101
93
|
|
94
|
+
# ------------------------------ create Document ------------------------------------------------- #
|
95
|
+
# adds a record ( document, vertex or edge ) to the specified database type
|
96
|
+
#
|
97
|
+
# specify database-fields as hash-type parameters
|
98
|
+
#
|
99
|
+
# i.e Arcade::Api.create_record 'devel', 'documents', name: 'herta meyer', age: 56, sex: 'f'
|
100
|
+
#
|
101
|
+
# returns the rid of the inserted dataset
|
102
|
+
#
|
103
|
+
def self.create_document database, type, session_id: nil, **attributes
|
104
|
+
content = "CONTENT #{ attributes.to_json }"
|
105
|
+
result = execute( database, session_id: session_id ){ "INSERT INTO #{type} #{content} "}
|
106
|
+
result.first[:@rid] # emulate the »old« behavior of the /post/create_document api endpoint
|
107
|
+
end
|
108
|
+
|
102
109
|
# ------------------------------ get_record ------------------------------------------------- #
|
103
110
|
# fetches a record by providing database and rid
|
104
111
|
# and returns the result as hash
|
@@ -112,7 +119,7 @@ module Arcade
|
|
112
119
|
rid = rid.join(':')
|
113
120
|
rid = rid[1..-1] if rid[0]=="#"
|
114
121
|
if rid.rid?
|
115
|
-
|
122
|
+
get_data( "query/#{database}/sql/" + URI.encode_uri_component("select from #{rid}")) &.first
|
116
123
|
else
|
117
124
|
raise Error "Get requires a rid input"
|
118
125
|
end
|
@@ -133,21 +140,17 @@ module Arcade
|
|
133
140
|
#
|
134
141
|
def self.property database, type, **args
|
135
142
|
|
136
|
-
begin_transaction database
|
143
|
+
s= begin_transaction database
|
137
144
|
success = args.map do | name, format |
|
138
|
-
r= execute(database) {" create property #{type.to_s}.#{name.to_s} #{format.to_s} " } &.first
|
139
|
-
|
140
|
-
if r.nil?
|
141
|
-
false
|
142
|
-
else
|
143
|
-
r[:operation] == 'create property'
|
144
|
-
end
|
145
|
+
r= execute(database, session_id: s) {" create property #{type.to_s}.#{name.to_s} #{format.to_s} " } &.first
|
146
|
+
r.nil? ? false : r[:operation] == 'create property'
|
145
147
|
end.uniq
|
146
148
|
if success == [true]
|
147
|
-
commit database
|
149
|
+
commit database, session_id: s
|
148
150
|
true
|
149
151
|
else
|
150
|
-
rollback database
|
152
|
+
rollback database log: false, session_id: s
|
153
|
+
false
|
151
154
|
end
|
152
155
|
|
153
156
|
|
@@ -160,9 +163,6 @@ module Arcade
|
|
160
163
|
unique_requested = "notunique" if properties.delete("notunique" )
|
161
164
|
automatic = true if
|
162
165
|
properties << name if properties.empty?
|
163
|
-
# puts " create index #{type.to_s}[#{name.to_s}] on #{type} ( #{properties.join(',')} ) #{unique_requested}"
|
164
|
-
# VV 22.10: providing an index-name raises an Error ( Encountered " "(" "( "" at line 1, column 44. Was expecting one of: <EOF> <SCHEMA> ... <NULL_STRATEGY> ... ";" ... "," ... )) )
|
165
|
-
# named indices droped for now
|
166
166
|
success = execute(database) {" create index IF NOT EXISTS on #{type} (#{properties.join(', ')}) #{unique_requested}" } &.first
|
167
167
|
# puts "success: #{success}"
|
168
168
|
success[:operation] == 'create index'
|
@@ -176,9 +176,6 @@ module Arcade
|
|
176
176
|
Database.logger
|
177
177
|
end
|
178
178
|
|
179
|
-
def self.session
|
180
|
-
@session_id
|
181
|
-
end
|
182
179
|
|
183
180
|
def self. provide_payload( the_yield, action: :post )
|
184
181
|
unless the_yield.is_a? Hash
|
@@ -189,7 +186,11 @@ module Arcade
|
|
189
186
|
the_yield.map do | key, value |
|
190
187
|
case key
|
191
188
|
when :query
|
192
|
-
|
189
|
+
if action == :post
|
190
|
+
[ :command, value ]
|
191
|
+
else
|
192
|
+
[ :query, URI.encode_uri_component(value )]
|
193
|
+
end
|
193
194
|
when :limit
|
194
195
|
[ :limit , value ]
|
195
196
|
when :params
|
@@ -207,7 +208,7 @@ module Arcade
|
|
207
208
|
[ :serializer, value.to_sym ]
|
208
209
|
end
|
209
210
|
when :language
|
210
|
-
if [:sql, :cypher, :gremlin, :neo4j ].include? value.to_sym
|
211
|
+
if [:sql, :cypher, :gremlin, :neo4j, :sqlscript, :graphql, :mongo ].include? value.to_sym
|
211
212
|
[ :language, value.to_sym ]
|
212
213
|
end
|
213
214
|
end # case
|
@@ -216,42 +217,11 @@ module Arcade
|
|
216
217
|
|
217
218
|
|
218
219
|
|
219
|
-
# returns the json-response ## retiered
|
220
|
-
def self.analyse_result r, command
|
221
|
-
if r.success?
|
222
|
-
return nil if r.status == 204 # no content
|
223
|
-
result = JSON.parse( r.response_body, symbolize_names: true )[:result]
|
224
|
-
if result == [{}]
|
225
|
-
[]
|
226
|
-
else
|
227
|
-
result
|
228
|
-
end
|
229
|
-
elsif r.timed_out?
|
230
|
-
raise Error "Timeout Error", caller
|
231
|
-
[]
|
232
|
-
elsif r.response_code > 0
|
233
|
-
logger.error "Execution Failure – Code: #{ r.response_code } – #{r.status_message} "
|
234
|
-
error_message = JSON.parse( r.response_body, symbolize_names: true )
|
235
|
-
logger.error "ErrorMessage: #{ error_message[:detail]} "
|
236
|
-
if error_message[:detail] =~ /Duplicated key/
|
237
|
-
raise IndexError, error_message[:detail]
|
238
|
-
else
|
239
|
-
# available fields: :detail, :exception, error
|
240
|
-
puts error_message[:detail]
|
241
|
-
#raise error_message[:detail], caller
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
220
|
def self.auth
|
246
221
|
@a ||= { httpauth: :basic,
|
247
222
|
username: Config.admin[:user],
|
248
223
|
password: Config.admin[:pass] }
|
249
224
|
end
|
250
225
|
|
251
|
-
# not tested
|
252
|
-
def self.delete_data command
|
253
|
-
result = HTTPX.delete Config.base_uri + command, auth
|
254
|
-
analyse_result(result, command)
|
255
|
-
end
|
256
226
|
end
|
257
227
|
end
|
@@ -8,7 +8,13 @@ module Arcade
|
|
8
8
|
# persistent http handle to the database
|
9
9
|
|
10
10
|
def http
|
11
|
-
|
11
|
+
# break_on = -> (response) { response.status == 500 }
|
12
|
+
# Version 23.12: Persistent connection are inactive after 3 sec.
|
13
|
+
# Implemented a walk around, that renews the connection after 2 sec. of inactivity
|
14
|
+
t = Time.now
|
15
|
+
@t ||= Time.now
|
16
|
+
@http = nil if t-@t > 2
|
17
|
+
@t = t
|
12
18
|
@http ||= HTTPX.plugin(:basic_auth).basic_auth(auth[:username], auth[:password])
|
13
19
|
.plugin(:persistent)
|
14
20
|
.plugin(:circuit_breaker)
|
@@ -17,8 +23,16 @@ module Arcade
|
|
17
23
|
|
18
24
|
# ------------------------------ get data -------------------------------------------------------- #
|
19
25
|
def get_data command
|
20
|
-
response = http.get( Config.base_uri + command )
|
21
|
-
|
26
|
+
case response = http.get( Config.base_uri + command )
|
27
|
+
in {status: 200..299}
|
28
|
+
# success
|
29
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
30
|
+
in {status: 400..}
|
31
|
+
raise Arcade::QueryError.new **response.json( symbolize_names: true )
|
32
|
+
else
|
33
|
+
# # http error
|
34
|
+
raise response
|
35
|
+
end
|
22
36
|
|
23
37
|
JSON.parse( response.body, symbolize_names: true )[:result]
|
24
38
|
# alternative to `raise for status `
|
@@ -41,56 +55,77 @@ module Arcade
|
|
41
55
|
|
42
56
|
# ------------------------------ post data -------------------------------------------------------- #
|
43
57
|
def post_data command, payload
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
58
|
+
case response = http.post( Config.base_uri + command, json: payload )
|
59
|
+
in {status: 200..299}
|
60
|
+
# success
|
61
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
62
|
+
in {status: 400..}
|
63
|
+
detail = response.json( symbolize_names: true )[:detail]
|
64
|
+
if detail =~ /Please retry the operation/
|
65
|
+
logger.error "--------------------------------"
|
66
|
+
logger.error " ----> Operation repeated <---- "
|
67
|
+
logger.error detail
|
68
|
+
logger.error "The query --> #{payload.inspect}"
|
69
|
+
logger.error "--------------------------------"
|
70
|
+
sleep 1
|
71
|
+
post_data command, payload
|
72
|
+
else
|
73
|
+
raise Arcade::QueryError.new **response.json( symbolize_names: true )
|
74
|
+
end
|
75
|
+
else
|
76
|
+
# # http error
|
77
|
+
raise response
|
78
|
+
end
|
79
|
+
# response.raise_for_status
|
49
80
|
end
|
50
81
|
|
51
82
|
# ------------------------------ transaction ------------------------------------------------- #
|
52
83
|
#
|
84
|
+
# The payload, optional as a JSON, accepts the following parameters:
|
85
|
+
# isolationLevel: READ_COMMITTED (default) or REPEATABLE_READ. (not implemented)
|
86
|
+
#
|
53
87
|
def begin_transaction database
|
54
88
|
result = http.post Config.base_uri + "begin/#{database}"
|
55
|
-
@session_id = result.headers["arcadedb-session-id"]
|
56
89
|
# returns the session-id
|
90
|
+
result.headers["arcadedb-session-id"]
|
57
91
|
end
|
58
92
|
|
59
93
|
# ------------------------------ post transaction ------------------------------------------------- #
|
60
|
-
def post_transaction command, params, session_id
|
61
|
-
# http = HTTPX.plugin(:basic_auth)
|
62
|
-
# .basic_auth(auth[:username], auth[:password])
|
63
|
-
# .with( headers: { "arcadedb-session-id"=>session }, debug_level: 1)
|
94
|
+
def post_transaction command, params, session_id:
|
64
95
|
http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
|
65
|
-
|
66
|
-
response.
|
67
|
-
|
68
|
-
|
96
|
+
puts "params #{params.inspect}"
|
97
|
+
case response = http_a.post( Config.base_uri + command, json: params )
|
98
|
+
in {status: 200..299}
|
99
|
+
# success
|
100
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
101
|
+
in {status: 400..}
|
102
|
+
## debug
|
103
|
+
# puts "Command: #{command}"
|
104
|
+
# puts "params: #{params}"
|
105
|
+
# puts response.json( symbolize_names: true )
|
106
|
+
raise Arcade::QueryError.new **response.json( symbolize_names: true )
|
107
|
+
else
|
108
|
+
# # http error
|
109
|
+
raise response
|
110
|
+
end
|
69
111
|
end
|
70
112
|
|
71
113
|
# ------------------------------ commit ------------------------------------------------- #
|
72
|
-
def commit database, session_id
|
114
|
+
def commit database, session_id:
|
73
115
|
http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
|
74
116
|
response = http_a.post( Config.base_uri + "commit/#{database}" )
|
75
|
-
response.raise_for_status
|
76
|
-
@session_id = nil
|
77
117
|
response.status # returns 204 --> success
|
78
|
-
# 403 -->
|
118
|
+
# 403 --> invalid credentials
|
79
119
|
# 500 --> Transaction not begun
|
80
120
|
|
81
121
|
end
|
82
122
|
|
83
123
|
# ------------------------------ rollback ------------------------------------------------- #
|
84
|
-
def rollback database, session_id
|
85
|
-
# http = HTTPX.plugin(:basic_auth)
|
86
|
-
# .basic_auth(auth[:username], auth[:password])
|
87
|
-
# .with( headers: { "arcadedb-session-id"=>session_id }, debug_level: 1)
|
124
|
+
def rollback database, session_id: , log: true
|
88
125
|
http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
|
89
126
|
response = http_a.post( Config.base_uri + "rollback/#{database}" )
|
90
|
-
|
91
|
-
|
92
|
-
logger.error "A Transaction has been rolled back" # if publish_error
|
93
|
-
response.status
|
127
|
+
logger.info "A Transaction has been rolled back" if log
|
128
|
+
response.status # returns 500 !
|
94
129
|
end
|
95
130
|
end
|
96
131
|
end
|
data/lib/arcade/base.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Arcade
|
2
|
+
|
2
3
|
class Base < Dry::Struct
|
3
4
|
|
4
5
|
extend Arcade::Support::Sql
|
@@ -20,6 +21,9 @@ module Arcade
|
|
20
21
|
## ----------------------------------------- Class Methods------------------------------------ ##
|
21
22
|
# #
|
22
23
|
class << self
|
24
|
+
def descendants
|
25
|
+
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
26
|
+
end
|
23
27
|
|
24
28
|
# this has to be implemented on class level
|
25
29
|
# otherwise it interfere with attributes
|
@@ -27,6 +31,16 @@ module Arcade
|
|
27
31
|
self.name.snake_case
|
28
32
|
end
|
29
33
|
|
34
|
+
def begin_transaction
|
35
|
+
db.begin_transaction
|
36
|
+
end
|
37
|
+
def commit
|
38
|
+
db.commit
|
39
|
+
end
|
40
|
+
def rollback
|
41
|
+
db.rollback
|
42
|
+
end
|
43
|
+
|
30
44
|
def create_type
|
31
45
|
the_class = nil # declare as local var
|
32
46
|
parent_present = ->(cl){ db.hierarchy.flatten.include? cl }
|
@@ -67,7 +81,7 @@ module Arcade
|
|
67
81
|
the_command = command[0 .. -2] # remove '\n'
|
68
82
|
next if the_command == ''
|
69
83
|
# db.logger.info "Custom Setup:: #{the_command}"
|
70
|
-
db.
|
84
|
+
db.transmit { the_command }
|
71
85
|
end unless custom_setup.nil?
|
72
86
|
|
73
87
|
rescue RollbackError => e
|
@@ -118,9 +132,11 @@ module Arcade
|
|
118
132
|
# (not supported (jet): [RETURN <expression>] [FROM <query>] )
|
119
133
|
|
120
134
|
def insert **attributes
|
121
|
-
db.insert type: database_name, **attributes
|
135
|
+
db.insert type: database_name, session_id: attributes.delete(:session_id), **attributes
|
122
136
|
end
|
123
137
|
|
138
|
+
alias create insert
|
139
|
+
|
124
140
|
## ----------------------------------------- create ---------------------------------- ##
|
125
141
|
#
|
126
142
|
# Adds a record to the database
|
@@ -128,21 +144,21 @@ module Arcade
|
|
128
144
|
# returns the model dataset
|
129
145
|
# ( depreciated )
|
130
146
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
147
|
+
# def create **attributes
|
148
|
+
# s = Api.begin_transaction db.database
|
149
|
+
# attributes.merge!( created: DateTime.now ) if timestamps
|
150
|
+
# record = insert **attributes
|
151
|
+
# Api.commit db.database, s
|
152
|
+
# record
|
153
|
+
# rescue HTTPX::HTTPError => e
|
154
|
+
# db.logger.error "Dataset NOT created"
|
155
|
+
# db.logger.error "Provided Attributes: #{ attributes.inspect }"
|
156
|
+
# Api.rollback db.database # ---> raises "transactgion not begun"
|
157
|
+
# rescue Dry::Struct::Error => e
|
158
|
+
# Api.rollback db.database
|
159
|
+
# db.logger.error "#{ rid } :: Validation failed, record deleted."
|
160
|
+
# db.logger.error e.message
|
161
|
+
# end
|
146
162
|
|
147
163
|
def count **args
|
148
164
|
command = "count(*)"
|
@@ -219,9 +235,9 @@ module Arcade
|
|
219
235
|
# is equivalent to
|
220
236
|
# Strategie.all.find{|y| y.symbol == 'Still' }
|
221
237
|
def find **args
|
222
|
-
|
223
|
-
f= where( "#{ args.keys.first } like #{ args.values.first.to_or }" ).first if f.nil? || f.empty?
|
224
|
-
f
|
238
|
+
where(**args).first
|
239
|
+
# f= where( "#{ args.keys.first } like #{ args.values.first.to_or }" ).first if f.nil? || f.empty?
|
240
|
+
# f
|
225
241
|
end
|
226
242
|
# update returns a list of updated records
|
227
243
|
#
|
@@ -282,16 +298,19 @@ module Arcade
|
|
282
298
|
Query.new( **{ from: self }.merge(args) )
|
283
299
|
end
|
284
300
|
|
285
|
-
#
|
286
|
-
#
|
301
|
+
# ## Immutable Support
|
302
|
+
#
|
303
|
+
# To make a database type immutable add
|
287
304
|
# `not_permitted :update, :upsert, :delete`
|
288
305
|
# to the model-specification
|
289
306
|
#
|
307
|
+
# Even after applying `not_permitted` the database-type can be modified via class-methods.
|
308
|
+
#
|
290
309
|
def not_permitted *m
|
291
310
|
m.each do | def_m |
|
292
|
-
define_method(
|
293
|
-
|
294
|
-
|
311
|
+
define_method( def_m ) do | v = nil |
|
312
|
+
raise Arcade::ImmutableError.new( "operation #{def_m} not permitted" )
|
313
|
+
end
|
295
314
|
end
|
296
315
|
end
|
297
316
|
|
@@ -354,8 +373,6 @@ module Arcade
|
|
354
373
|
end
|
355
374
|
|
356
375
|
def to_human
|
357
|
-
|
358
|
-
|
359
376
|
"<#{ self.class.to_s.snake_case }" + rid? ? "[#{ rid }]: " : " " + invariant_attributes.map do |attr, value|
|
360
377
|
v= case value
|
361
378
|
when Base
|
@@ -367,6 +384,9 @@ module Arcade
|
|
367
384
|
end
|
368
385
|
"%s : %s" % [ attr, v] unless v.nil?
|
369
386
|
end.compact.sort.join(', ') + ">".gsub('"' , ' ')
|
387
|
+
|
388
|
+
rescue TypeError => e
|
389
|
+
attributes
|
370
390
|
end
|
371
391
|
|
372
392
|
|
@@ -376,26 +396,44 @@ module Arcade
|
|
376
396
|
to_human
|
377
397
|
end
|
378
398
|
|
399
|
+
|
400
|
+
def html_attributes
|
401
|
+
invariant_attributes
|
402
|
+
end
|
403
|
+
|
404
|
+
def in_and_out_attributes
|
405
|
+
_modul, _class = self.class.to_s.split "::"
|
406
|
+
the_class = _modul == 'Arcade' ? _class : self.class.to_s
|
407
|
+
the_attributes = { :"CLASS" => the_class, :"IN" => self.in.count, :"OUT" => self.out.count, :"RID" => rid }
|
408
|
+
end
|
409
|
+
|
379
410
|
def to_html # iruby
|
411
|
+
in_and_out = ->(r) { "[#{r}] : {#{self.in.count}->}{->#{self.out.count }}" }
|
412
|
+
the_rid = rid? && rid != "0:0" ? in_and_out[rid] : ""
|
380
413
|
_modul, _class = self.class.to_s.split "::"
|
381
414
|
the_class = _modul == 'Arcade' ? _class : self.class.to_s
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
415
|
+
# the_attribute = ->(v) do
|
416
|
+
# case v
|
417
|
+
# when Base
|
418
|
+
# "< #{ self.class.to_s.snake_case }: #{ v.rid } >"
|
419
|
+
# when Array
|
420
|
+
# v.map{|x| x.to_s}
|
421
|
+
# else
|
422
|
+
# v.to_s
|
423
|
+
# end
|
424
|
+
# end
|
425
|
+
# last_part = invariant_attributes.map do |attr, value|
|
426
|
+
# [ attr, the_attribute[value] ].join(": ")
|
427
|
+
# end.join(', ')
|
428
|
+
|
429
|
+
# IRuby.display( [IRuby.html("<span style=\"color: #50953DFF\"><b>#{the_class}</b><#{ the_class }</b>#{the_rid}</span><br/> ") , IRuby.table(html_attributes) ] )
|
430
|
+
IRuby.display IRuby.html("<span style=\"color: #50953DFF\"><b>#{the_class}</b><#{ the_class }</b>#{the_rid}</span>< #{ html_attributes.map{|_,v| v }.join(', ') } >")
|
394
431
|
end
|
395
432
|
|
433
|
+
|
396
434
|
def update **args
|
397
435
|
Query.new( from: rid , kind: :update, set: args).execute
|
398
|
-
refresh
|
436
|
+
refresh # return the updated record (the object itself is untouched!)
|
399
437
|
end
|
400
438
|
|
401
439
|
# inserts or updates a embedded document
|
@@ -406,40 +444,56 @@ module Arcade
|
|
406
444
|
obj.to_or
|
407
445
|
end
|
408
446
|
# if send( name ).nil? || send( name ).empty?
|
409
|
-
db.
|
447
|
+
db.transmit { "update #{ rid } set #{ name } = #{ value }" }.first[:count]
|
410
448
|
# end
|
411
449
|
end
|
412
450
|
|
413
451
|
# updates a single property in an embedded document
|
414
452
|
def update_embedded embedded, embedded_property, value
|
415
|
-
db.
|
453
|
+
db.transmit { " update #{rid} set `#{embedded}`.`#{embedded_property}` = #{value.to_or}" }
|
416
454
|
end
|
417
455
|
|
418
|
-
|
419
|
-
|
456
|
+
# Adds List-Elements to embedded List
|
457
|
+
#
|
458
|
+
# Arguments:
|
459
|
+
# * list: A symbol of the list property
|
460
|
+
# * value: A embedded document or a hash
|
461
|
+
# * modus: :auto, :first, :append
|
462
|
+
#
|
463
|
+
# Prefered modus operandi
|
464
|
+
# * the-element.insert (...) , #{list}:[]
|
465
|
+
# * the_element.update_list list, value: :append
|
466
|
+
#
|
467
|
+
def update_list list, value, modus: :auto
|
468
|
+
value = if value.is_a? Document # embedded mode
|
420
469
|
value.to_json
|
421
470
|
else
|
422
471
|
value.to_or
|
423
472
|
end
|
424
|
-
if
|
425
|
-
db.
|
473
|
+
if modus == :auto
|
474
|
+
modus = db.query( "select #{list}.size() from #{rid}" ).select_result.first.zero? ? :first : :append
|
475
|
+
end
|
476
|
+
|
477
|
+
if modus == :first
|
478
|
+
db.transmit { "update #{ rid } set #{ list } = [#{ value }]" }
|
426
479
|
else
|
427
|
-
db.
|
480
|
+
db.transmit { "update #{ rid } set #{ list } += #{ value }" }
|
428
481
|
end
|
429
|
-
|
482
|
+
# refresh
|
430
483
|
end
|
431
484
|
|
432
485
|
# updates a map property , actually adds the key-value pair to the property
|
486
|
+
## does not work on reduced model records
|
433
487
|
def update_map m, key, value
|
434
488
|
if send( m ).nil?
|
435
|
-
db.
|
489
|
+
db.transmit { "update #{ rid } set #{ m } = MAP ( #{ key.to_s.to_or } , #{ value.to_or } ) " }
|
436
490
|
else
|
437
|
-
db.
|
491
|
+
db.transmit { "update #{ rid } set #{ m }.`#{ key.to_s }` = #{ value.to_or }" }
|
438
492
|
end
|
439
493
|
refresh
|
440
494
|
end
|
441
495
|
def delete
|
442
|
-
response = db.
|
496
|
+
response = db.transmit { "delete from #{ rid }" }
|
443
497
|
true if response == [{ count: 1 }]
|
444
498
|
end
|
445
499
|
def == arg
|
@@ -3,11 +3,11 @@ module Arcade
|
|
3
3
|
extend Dry::Configurable
|
4
4
|
# central place to initialize constants
|
5
5
|
#
|
6
|
-
# ProjectRoot
|
6
|
+
# ProjectRoot should to be a Pathname-Object and has to be defined before `arcadedb` is required
|
7
7
|
#
|
8
8
|
#puts "expand: #{File.expand_path(__dir__)}"
|
9
|
-
unless
|
10
|
-
|
9
|
+
unless Object.const_defined?( :ProjectRoot )
|
10
|
+
::ProjectRoot = if defined?( Rails.env )
|
11
11
|
Rails.root
|
12
12
|
else
|
13
13
|
STDERR.puts "Using default (arcadedb gem) database credentials and settings"
|
@@ -15,10 +15,9 @@ module Arcade
|
|
15
15
|
Pathname.new( File.expand_path( "../../", __FILE__ ))
|
16
16
|
end
|
17
17
|
else
|
18
|
-
STDERR.puts "Using provided database credentials and settings
|
18
|
+
STDERR.puts "Using provided database credentials and settings from #{::ProjectRoot}"
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
21
|
# initialised a hash { environment => property }
|
23
22
|
setting :username, default: :user, reader: true,
|
24
23
|
constructor: ->(v) { yml(:environment).map{|x,y| [x , y[v.to_s]] }.to_h }
|
@@ -53,7 +52,7 @@ module Arcade
|
|
53
52
|
def self.config_file
|
54
53
|
|
55
54
|
configdir = -> do
|
56
|
-
pr = ProjectRoot.is_a?(Pathname)? ProjectRoot : Pathname.new( ProjectRoot )
|
55
|
+
pr = ::ProjectRoot.is_a?(Pathname)? ::ProjectRoot : Pathname.new( ::ProjectRoot )
|
57
56
|
( cd = pr + 'arcade.yml' ).exist? || ( cd = pr + 'config' + 'arcade.yml' ).exist? || ( cd = pr + 'config.yml' )
|
58
57
|
cd
|
59
58
|
end
|