arcadedb 0.3.1 → 0.4
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/.gitignore +57 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +25 -0
- data/Gemfile.lock +186 -0
- data/Guardfile +30 -0
- data/LICENSE +21 -0
- data/README.md +242 -0
- data/Rakefile +11 -0
- data/arcade.yml +23 -0
- data/arcadedb.gemspec +32 -0
- data/bin/+ +106 -0
- data/bin/console +126 -0
- data/examples/books.rb +139 -0
- data/examples/relation_1__1.rb +149 -0
- data/examples/relation_1__n.rb +56 -0
- data/examples/relation_n_n.rb +194 -0
- data/lib/arcade/api/operations.rb +257 -0
- data/lib/arcade/api/primitives.rb +98 -0
- data/lib/arcade/base.rb +454 -0
- data/lib/arcade/database.rb +367 -0
- data/lib/arcade/errors.rb +71 -0
- data/lib/arcade/logging.rb +38 -0
- data/lib/arcade/version.rb +3 -0
- data/lib/arcade.rb +36 -0
- data/lib/config.rb +72 -0
- data/lib/init.rb +50 -0
- data/lib/match.rb +216 -0
- data/lib/model/basicdocument.rb +7 -0
- data/lib/model/basicedge.rb +6 -0
- data/lib/model/basicvertex.rb +6 -0
- data/lib/model/document.rb +10 -0
- data/lib/model/edge.rb +47 -0
- data/lib/model/vertex.rb +238 -0
- data/lib/models.rb +6 -0
- data/lib/query.rb +384 -0
- data/lib/support/class.rb +13 -0
- data/lib/support/conversions.rb +295 -0
- data/lib/support/model.rb +87 -0
- data/lib/support/object.rb +20 -0
- data/lib/support/sql.rb +74 -0
- data/lib/support/string.rb +116 -0
- data/rails/arcade.rb +20 -0
- data/rails/config.yml +10 -0
- data/rails/connect.yml +17 -0
- data/rails.md +147 -0
- metadata +64 -5
@@ -0,0 +1,367 @@
|
|
1
|
+
module Arcade
|
2
|
+
##
|
3
|
+
# Implements the Database-Adapter
|
4
|
+
#
|
5
|
+
# currently, only attributes of type String are supported
|
6
|
+
#
|
7
|
+
# {Database-instance}.database points to the connected Aradedb-database
|
8
|
+
# DB.hi
|
9
|
+
#
|
10
|
+
##
|
11
|
+
class Database
|
12
|
+
include Logging
|
13
|
+
extend Dry::Core::ClassAttributes
|
14
|
+
include Support::Model # provides allocate_model
|
15
|
+
|
16
|
+
defines :namespace
|
17
|
+
defines :environment
|
18
|
+
|
19
|
+
def initialize environment=:development
|
20
|
+
self.class.configure_logger( Config.logger )
|
21
|
+
@connection = connect environment
|
22
|
+
if self.class.environment.nil? # class attribute is set on the first call
|
23
|
+
# further instances of Database share the same environment
|
24
|
+
self.class.environment environment
|
25
|
+
end
|
26
|
+
self.class.namespace Object.const_get( Config.namespace )
|
27
|
+
end
|
28
|
+
|
29
|
+
def database
|
30
|
+
@database ||= Config.database[self.class.environment]
|
31
|
+
end
|
32
|
+
|
33
|
+
# ------------ types ------------...
|
34
|
+
# returns an Array of type-attributes
|
35
|
+
# [{:name=>"Account", :type=>"document"},
|
36
|
+
# {:name=>"test", :type=>"vertex"},
|
37
|
+
# {:name=>"test1", :type=>"vertex"},
|
38
|
+
# {:parentTypes=>["test1"], :name=>"test2", :type=>"vertex"}]
|
39
|
+
#
|
40
|
+
def types refresh=false
|
41
|
+
# uses API
|
42
|
+
if $types.nil? || refresh
|
43
|
+
$types = Api.query(database, "select from schema:types" )
|
44
|
+
.map{ |x| x.transform_keys &:to_sym } # symbolize keys
|
45
|
+
.map{ |y| y.delete_if{|_,b,| b.empty? } } # eliminate empty entries
|
46
|
+
end
|
47
|
+
$types
|
48
|
+
## upom startup, this is the first access to the database-server
|
49
|
+
rescue NoMethodError => e
|
50
|
+
logger.fatal "Could not read Database Types. \n Is the database running?"
|
51
|
+
Kernel.exit
|
52
|
+
end
|
53
|
+
|
54
|
+
def indexes
|
55
|
+
DB.types.find{|x| x.key? :indexes }[:indexes]
|
56
|
+
end
|
57
|
+
|
58
|
+
# ------------ hierarchy -------------
|
59
|
+
# returns an Array of types
|
60
|
+
#
|
61
|
+
# each entry is an Array
|
62
|
+
# => [["test"], ["test1", "test2"]] (using the example above)
|
63
|
+
#
|
64
|
+
# Parameter: type -- one of 'vertex', 'document', 'edge'
|
65
|
+
def hierarchy type: 'vertex'
|
66
|
+
# uses API
|
67
|
+
# gets all types depending on the parent-type
|
68
|
+
pt = ->( s ) { types.find_all{ |x| x[:parentTypes] &.include?(s) }.map{ |v| v[:name] } }
|
69
|
+
# takes an array of base-types. gets recursivly all childs
|
70
|
+
child_types = -> (base_types) do
|
71
|
+
base_types.map do | bt |
|
72
|
+
if pt[ bt ].empty?
|
73
|
+
[ bt ]
|
74
|
+
else
|
75
|
+
[bt, child_types[ pt[ bt ] ] ].flatten
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# gets child-types for all base-types
|
81
|
+
child_types[ types.find_all{ |x| !x[:parentTypes] && x[:type] == type.to_s }.map{ |v| v[:name] } ]
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# ------------ create type -----------
|
87
|
+
# returns an Array
|
88
|
+
# Example: > create_type :vertex, :my_vertex
|
89
|
+
# => [{"typeName"=>"my_vertex", "operation"=>"create vertex type"}]
|
90
|
+
#
|
91
|
+
# takes additional arguments: extends: '<a supertype>' (for inheritance)
|
92
|
+
# bucket: <a list of bucket-id's >
|
93
|
+
# buckets: <how many bukets to assign>
|
94
|
+
#
|
95
|
+
# additional arguments are just added to the command
|
96
|
+
#
|
97
|
+
# its aliased as `create_class`
|
98
|
+
#
|
99
|
+
def create_type kind, type, **args
|
100
|
+
|
101
|
+
exe = -> do
|
102
|
+
case kind.to_s.downcase
|
103
|
+
when /^v/
|
104
|
+
"create vertex type #{type} "
|
105
|
+
when /^d/
|
106
|
+
"create document type #{type} "
|
107
|
+
when /^e/
|
108
|
+
"create edge type #{type} "
|
109
|
+
end.concat( args.map{|x,y| "#{x} #{y} "}.join)
|
110
|
+
end
|
111
|
+
db= Api.execute database, &exe
|
112
|
+
types( true ) # update cached schema
|
113
|
+
db
|
114
|
+
|
115
|
+
rescue HTTPX::HTTPError => e
|
116
|
+
# puts "ERROR: #{e.message.to_s}"
|
117
|
+
if e.status == 500 && e.message.to_s =~ /already exists/
|
118
|
+
Arcade::Database.logger.warn "Database type #{type} already present"
|
119
|
+
else
|
120
|
+
raise
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
alias create_class create_type
|
125
|
+
|
126
|
+
# ------------ drop type -----------
|
127
|
+
# delete any record prior to the attempt to drop a type.
|
128
|
+
# The `unsafe` option is nit implemented.
|
129
|
+
def drop_type type
|
130
|
+
Api.execute database, "drop type #{type} if exists"
|
131
|
+
end
|
132
|
+
|
133
|
+
# ------------ create -----------
|
134
|
+
# returns an rid of the successfully created vertex or document
|
135
|
+
#
|
136
|
+
# Parameter: name of the vertex or document type
|
137
|
+
# Hash of attributes
|
138
|
+
#
|
139
|
+
# Example: > DB.create :my_vertex, a: 14, name: "Hugo"
|
140
|
+
# => "#177:0"
|
141
|
+
#
|
142
|
+
def create type, **params
|
143
|
+
# uses API
|
144
|
+
Api.create_document database, type, **params
|
145
|
+
end
|
146
|
+
|
147
|
+
def insert **params
|
148
|
+
|
149
|
+
content_params = params.except( :type, :bucket, :index, :from, :return )
|
150
|
+
target_params = params.slice( :type, :bucket, :index )
|
151
|
+
if target_params.empty?
|
152
|
+
logger.error "Could not insert: target mising (type:, bucket:, index:)"
|
153
|
+
elsif content_params.empty?
|
154
|
+
logger.error "Nothing to Insert"
|
155
|
+
else
|
156
|
+
content = "CONTENT #{ content_params.to_json }"
|
157
|
+
target = target_params.map{|y,z| y==:type ? z : "#{y.to_s} #{ z } "}.join
|
158
|
+
Api.execute( database, "INSERT INTO #{target} #{content} ") &.first.allocate_model(false)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# ------------------------------ get ------------------------------------------------------ #
|
163
|
+
# Get fetches the record associated with the rid given as parameter.
|
164
|
+
#
|
165
|
+
# The rid is accepted as
|
166
|
+
# DB.get "#123:123", DB.get "123:123" or DB.get 123, 123
|
167
|
+
#
|
168
|
+
# Links are autoloaded (can be suppressed by the optional Block (false))
|
169
|
+
#
|
170
|
+
# puts DB.get( 19,0 )
|
171
|
+
# <my_document[#19:0]: emb : ["<my_alist[#33:0]: name : record 1, number : 1>", "<my_alist[#34:0]: name : record 2, number : 2>"]>
|
172
|
+
# puts DB.get( 19,0 ){ false }
|
173
|
+
# <my_document[#19:0]: emb : ["#33:0", "#34:0"]>
|
174
|
+
#
|
175
|
+
#
|
176
|
+
def get *rid
|
177
|
+
autocomplete = block_given? ? yield : true
|
178
|
+
rid = rid.join(':')
|
179
|
+
rid = rid[1..-1] if rid[0]=="#"
|
180
|
+
if rid.rid?
|
181
|
+
Api.query( database, "select from #{rid}" ).first &.allocate_model(autocomplete)
|
182
|
+
else
|
183
|
+
raise Arcade::QueryError "Get requires a rid input", caller
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# ------------------------------ property ------------------------------------------------- #
|
188
|
+
# Adds properties to the type
|
189
|
+
#
|
190
|
+
# call via
|
191
|
+
# Api.property <database>, <type>, name1: a_format , name2: a_format
|
192
|
+
#
|
193
|
+
# Format is one of
|
194
|
+
# Boolean, Integer, Short, Long, Float, Double, String
|
195
|
+
# Datetime, Binary, Byte, Decimal, Link
|
196
|
+
# Embedded, EmbeddedList, EmbeddedMap
|
197
|
+
#
|
198
|
+
# In case of an Error, anything is rolled back and nil is returned
|
199
|
+
#
|
200
|
+
def self.property database, type, **args
|
201
|
+
|
202
|
+
begin_transaction database
|
203
|
+
success = args.map do | name, format |
|
204
|
+
r= execute(database) {" create property #{type.to_s}.#{name.to_s} #{format.to_s} " } &.first
|
205
|
+
if r.nil?
|
206
|
+
false
|
207
|
+
else
|
208
|
+
r.keys == [ :propertyName, :typeName, :operation ] && r[:operation] == 'create property'
|
209
|
+
end
|
210
|
+
end.uniq
|
211
|
+
if success == [true]
|
212
|
+
commit database
|
213
|
+
true
|
214
|
+
else
|
215
|
+
rollback database
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
# ------------------------------ index ------------------------------------------------- #
|
222
|
+
def self.index database, type, name , *properties
|
223
|
+
properties = properties.map( &:to_s )
|
224
|
+
unique_requested = "unique" if properties.delete("unique")
|
225
|
+
unique_requested = "notunique" if properties.delete("notunique" )
|
226
|
+
automatic = true if
|
227
|
+
properties << name if properties.empty?
|
228
|
+
end
|
229
|
+
|
230
|
+
def delete rid
|
231
|
+
r = Api.execute( database ){ "delete from #{rid}" }
|
232
|
+
success = r == [{ :count => 1 }]
|
233
|
+
end
|
234
|
+
|
235
|
+
# execute a command which modifies the database
|
236
|
+
#
|
237
|
+
# The operation is performed via Transaction/Commit
|
238
|
+
# If an Error occurs, its rolled back
|
239
|
+
#
|
240
|
+
def execute &block
|
241
|
+
s = Api.begin_transaction database
|
242
|
+
# begin
|
243
|
+
response = Api.execute database, nil, s, &block
|
244
|
+
# rescue HTTPX::HTTPError => e
|
245
|
+
# raise e.message
|
246
|
+
# puts e.methods
|
247
|
+
# puts e.status
|
248
|
+
# puts e.response
|
249
|
+
# puts e.message
|
250
|
+
# puts e.exception
|
251
|
+
# puts e.cause
|
252
|
+
# end
|
253
|
+
# puts response.inspect # debugging
|
254
|
+
r= if response.is_a? Hash
|
255
|
+
_allocate_model res
|
256
|
+
# elsif response.is_a? Array
|
257
|
+
# remove empty results
|
258
|
+
# response.delete_if{|y| y.empty?}
|
259
|
+
# response.map do | res |
|
260
|
+
# if res.key? :"@rid"
|
261
|
+
# allocate_model res
|
262
|
+
# else
|
263
|
+
# res
|
264
|
+
# end
|
265
|
+
# end
|
266
|
+
else
|
267
|
+
response
|
268
|
+
end
|
269
|
+
if Api.commit( database, s) == 204
|
270
|
+
r # return associated array of Arcade::Base-objects
|
271
|
+
else
|
272
|
+
[]
|
273
|
+
end
|
274
|
+
rescue Dry::Struct::Error, HTTPX::HTTPError, Arcade::QueryError => e
|
275
|
+
Api.rollback database, s
|
276
|
+
logger.error "Execution FAILED --> Status #{e.status}"
|
277
|
+
# logger.error "Execution FAILED --> #{e.exception.message}"
|
278
|
+
[] # return empty result
|
279
|
+
end
|
280
|
+
|
281
|
+
# returns an array of results
|
282
|
+
#
|
283
|
+
# detects database-records and allocates them as model-objects
|
284
|
+
#
|
285
|
+
def query query_object
|
286
|
+
Api.query database, query_object.to_s
|
287
|
+
end
|
288
|
+
|
289
|
+
# returns an array of rid's (same logic as create)
|
290
|
+
def create_edge edge_class, from:, to:, **attributes
|
291
|
+
|
292
|
+
content = attributes.empty? ? "" : "CONTENT #{attributes.to_json}"
|
293
|
+
cr = ->( f, t ) do
|
294
|
+
begin
|
295
|
+
edges = Api.execute( database, "create edge #{edge_class} from #{f.rid} to #{t.rid} #{content}").allocate_model(false)
|
296
|
+
rescue HTTPX::HTTPError => e
|
297
|
+
# if e.status == 503
|
298
|
+
# puts e.status
|
299
|
+
# puts e.message
|
300
|
+
# puts e.message.class
|
301
|
+
# end
|
302
|
+
raise unless e.message =~ /Found duplicate key/
|
303
|
+
puts "#"+e.message.split("#").last[0..-3]
|
304
|
+
end
|
305
|
+
#else
|
306
|
+
# logger.error "Could not create Edge #{edge_class} from #{f} to #{t}"
|
307
|
+
## logger.error edges.to_s
|
308
|
+
#end
|
309
|
+
end
|
310
|
+
from = [from] unless from.is_a? Array
|
311
|
+
to = [to] unless to.is_a? Array
|
312
|
+
|
313
|
+
from.map do | from_record |
|
314
|
+
to.map { | to_record | cr[ from_record, to_record ] if to_record.rid? } if from_record.rid?
|
315
|
+
end.flatten
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
|
320
|
+
# query all: select @rid, * from {database}
|
321
|
+
|
322
|
+
# not used
|
323
|
+
# def get_schema
|
324
|
+
# query( "select from schema:types" ).map do |a|
|
325
|
+
# puts "a: #{a}"
|
326
|
+
# class_name = a["name"]
|
327
|
+
# inherent_class = a["parentTypes"].empty? ? [Object,nil] : a["parentTypes"].map(&:camelcase_and_namespace)
|
328
|
+
# namespace, type_name = a["type"].camelcase_and_namespace
|
329
|
+
# namespace= Arcade if namespace.nil?
|
330
|
+
# klass= Dry::Core::ClassBuilder.new( name: type_name,
|
331
|
+
# parent: nil,
|
332
|
+
# namespace: namespace).call
|
333
|
+
# end
|
334
|
+
# rescue NameError
|
335
|
+
# logger.error "Dataset type #{e} not defined."
|
336
|
+
# raise
|
337
|
+
# end
|
338
|
+
# Postgres is not implemented
|
339
|
+
# connects to the database and initialises @connection
|
340
|
+
def connection
|
341
|
+
@connection
|
342
|
+
end
|
343
|
+
|
344
|
+
def connect environment=:development # environments: production devel test
|
345
|
+
if [:production, :development, :test].include? environment
|
346
|
+
|
347
|
+
# connect through the ruby postgres driver
|
348
|
+
# c= PG::Connection.new dbname: Config.database[environment],
|
349
|
+
# user: Config.username[environment],
|
350
|
+
# password: Config.password[environment],
|
351
|
+
# host: Config.pg[:host],
|
352
|
+
# port: Config.pg[:port]
|
353
|
+
#
|
354
|
+
end
|
355
|
+
rescue PG::ConnectionBad => e
|
356
|
+
if e.to_s =~ /Credentials/
|
357
|
+
logger.error "NOT CONNECTED ! Either Database is not present or credentials (#{ Config.username[environment]} / #{Config.password[environment]}) are wrong"
|
358
|
+
nil
|
359
|
+
else
|
360
|
+
raise
|
361
|
+
end
|
362
|
+
end # def
|
363
|
+
|
364
|
+
|
365
|
+
|
366
|
+
end # class
|
367
|
+
end # module
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Arcade
|
2
|
+
|
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 ImmutableError < RuntimeError
|
17
|
+
end
|
18
|
+
class IndexError < RuntimeError
|
19
|
+
end
|
20
|
+
|
21
|
+
class RollbackError < RuntimeError
|
22
|
+
end
|
23
|
+
|
24
|
+
class QueryError < RuntimeError
|
25
|
+
end
|
26
|
+
|
27
|
+
# used by Dry::Validation, not covered by "error"
|
28
|
+
class InvalidParamsError < StandardError
|
29
|
+
attr_reader :object
|
30
|
+
# @param [Hash] object that contains details about params errors.
|
31
|
+
# # @param [String] message of the error.
|
32
|
+
def initialize(object, message)
|
33
|
+
@object = object
|
34
|
+
super(message)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end # module Arcade
|
39
|
+
|
40
|
+
# Patching Object with universally accessible top level error method.
|
41
|
+
# The method is used throughout the lib instead of plainly raising exceptions.
|
42
|
+
# This allows lib user to easily inject user-specific error handling into the lib
|
43
|
+
# by just replacing Object#error method.
|
44
|
+
#def error message, type=:standard, backtrace=nil
|
45
|
+
# e = case type
|
46
|
+
# when :standard
|
47
|
+
# Arcade::Error.new message
|
48
|
+
# when :args
|
49
|
+
# Arcade::ArgumentError.new message
|
50
|
+
# when :symbol
|
51
|
+
# Arcade::SymbolError.new message
|
52
|
+
# when :load
|
53
|
+
# Arcade::LoadError.new message
|
54
|
+
# when :immutable
|
55
|
+
# Arcade::ImmutableError.new message
|
56
|
+
# when :commit
|
57
|
+
# Arcade::RollbackError.new message
|
58
|
+
# when :query
|
59
|
+
# Arcade::QueryError.new message
|
60
|
+
# when :args
|
61
|
+
# IB::ArgumentError.new message
|
62
|
+
# when :flex
|
63
|
+
# IB::FlexError.new message
|
64
|
+
# when :reader
|
65
|
+
# IB::TransmissionError.new message
|
66
|
+
# when :verify
|
67
|
+
# IB::VerifyError.new message
|
68
|
+
# end
|
69
|
+
# e.set_backtrace(backtrace) if backtrace
|
70
|
+
# raise e
|
71
|
+
#end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#require_relative 'default_formatter'
|
2
|
+
module Arcade
|
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= STDOUT)
|
21
|
+
if log.is_a? Logger
|
22
|
+
@logger = log
|
23
|
+
else
|
24
|
+
@logger = Logger.new log
|
25
|
+
end
|
26
|
+
@logger.level = Logger::INFO
|
27
|
+
@logger.formatter = DefaultFormatter
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class DefaultFormatter < Logger::Formatter
|
32
|
+
def self.call(severity, time, program_name, msg)
|
33
|
+
"#{time.strftime("%d.%m.(%X)")}#{"%5s" % severity}->#{msg}\n"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# source: https://github.com/jondot/sneakers/blob/master/lib/sneakers/concerns/logging.rb
|
data/lib/arcade.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Arcade
|
2
|
+
|
3
|
+
end
|
4
|
+
|
5
|
+
require "arcade/version"
|
6
|
+
require "dry/configurable"
|
7
|
+
require "dry/struct"
|
8
|
+
require "dry/core/class_builder"
|
9
|
+
require "dry/core/class_attributes"
|
10
|
+
require 'json'
|
11
|
+
|
12
|
+
module Types
|
13
|
+
include Dry.Types()
|
14
|
+
end
|
15
|
+
require 'yaml'
|
16
|
+
require 'securerandom'
|
17
|
+
require 'httpx'
|
18
|
+
require 'arcade/errors'
|
19
|
+
require_relative '../lib/support/object'
|
20
|
+
require_relative '../lib/support/string'
|
21
|
+
require_relative '../lib/support/class'
|
22
|
+
require_relative '../lib/support/sql'
|
23
|
+
require_relative '../lib/support/model'
|
24
|
+
require_relative '../lib/arcade/logging'
|
25
|
+
require_relative '../lib/config'
|
26
|
+
require_relative '../lib/support/conversions'
|
27
|
+
require_relative '../lib/arcade/api/primitives'
|
28
|
+
require_relative '../lib/arcade/api/operations'
|
29
|
+
require_relative '../lib/arcade/base'
|
30
|
+
require_relative '../lib/arcade/database'
|
31
|
+
require_relative '../lib/init'
|
32
|
+
require_relative "../lib/models"
|
33
|
+
require_relative '../lib/query'
|
34
|
+
require_relative '../lib/match'
|
35
|
+
require_relative '../lib/railtie' if defined? Rails::Railtie
|
36
|
+
|
data/lib/config.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
module Arcade
|
2
|
+
class Config
|
3
|
+
extend Dry::Configurable
|
4
|
+
# central place to initialize constants
|
5
|
+
#
|
6
|
+
# ProjectRoot has to be a Pathname-Object
|
7
|
+
#
|
8
|
+
#puts "expand: #{File.expand_path(__dir__)}"
|
9
|
+
unless Arcade.const_defined?( :ProjectRoot )
|
10
|
+
Arcade::ProjectRoot = if defined?( Rails.env )
|
11
|
+
Rails.root
|
12
|
+
else
|
13
|
+
STDERR.puts "Using default (arcadedb gem) database credentials and settings"
|
14
|
+
# logger is not present at this stage
|
15
|
+
Pathname.new( File.expand_path( "../../", __FILE__ ))
|
16
|
+
end
|
17
|
+
else
|
18
|
+
STDERR.puts "Using provided database credentials and settings fron #{Arcade::ProjectRoot}"
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# initialised a hash { environment => property }
|
23
|
+
setting :username, default: :user, reader: true,
|
24
|
+
constructor: ->(v) { yml(:environment).map{|x,y| [x , y[v.to_s]] }.to_h }
|
25
|
+
setting :password, default: :password, reader: true,
|
26
|
+
constructor: ->(v) { yml(:environment).map{|x,y| [x , y["pass"]] }.to_h }
|
27
|
+
setting :database, default: :database, reader: true,
|
28
|
+
constructor: ->(v) { yml(:environment).map{|x,y| [x , y["dbname"]] }.to_h }
|
29
|
+
setting(:base_uri, default: :host , reader: true,
|
30
|
+
constructor: ->(v) { "http://"+yml(:admin)[v]+':'+yml(:admin)[:port].to_s+"/api/v1/" })
|
31
|
+
setting :autoload, default: :autoload, reader: true , constructor: ->(v) { yml(v) }
|
32
|
+
setting :pg, default: :pg, reader: true , constructor: ->(v) { yml(v) }
|
33
|
+
setting :admin, default: :admin, reader: true , constructor: ->(v) { yml(v) }
|
34
|
+
setting :logger, default: :logger, reader: true ,
|
35
|
+
constructor: ->(v) do
|
36
|
+
if defined?( Rails.env )
|
37
|
+
Rails.logger
|
38
|
+
elsif Object.const_defined?(:Bridgetown)
|
39
|
+
else
|
40
|
+
output = yml(v)
|
41
|
+
if output.upcase == 'STDOUT'
|
42
|
+
Logger.new STDOUT
|
43
|
+
else
|
44
|
+
Logger.new File.open( output, 'a' )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
setting :namespace, default: :namespace, reader: true , constructor: ->(v) { yml(v) }
|
49
|
+
setting :secret, reader: true, default: 12, constructor: ->(v) { seed(v) }
|
50
|
+
private
|
51
|
+
# if a config dir exists, use it.
|
52
|
+
# Standard: ProjectRoot/config.yml
|
53
|
+
def self.config_file
|
54
|
+
|
55
|
+
configdir = -> do
|
56
|
+
pr = ProjectRoot.is_a?(Pathname)? ProjectRoot : Pathname.new( ProjectRoot )
|
57
|
+
( cd = pr + 'arcade.yml' ).exist? || ( cd = pr + 'config' + 'arcade.yml' ).exist? || ( cd = pr + 'config.yml' )
|
58
|
+
cd
|
59
|
+
end
|
60
|
+
|
61
|
+
@cd ||= configdir[]
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.yml key=nil
|
65
|
+
y= YAML::load_file( config_file )
|
66
|
+
key.nil? ? y : y[key]
|
67
|
+
end
|
68
|
+
def self.seed( key= nil )
|
69
|
+
SecureRandom.hex( 40 )
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/init.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Arcade
|
2
|
+
|
3
|
+
# Arcade::Init.connect environment
|
4
|
+
# --------------------------------
|
5
|
+
# initializes the database connection
|
6
|
+
# and returns the active database handle
|
7
|
+
#
|
8
|
+
# The database cannot switched later
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# Arcade::Init.db
|
12
|
+
# --------------
|
13
|
+
# returns an instance of the database handle
|
14
|
+
#
|
15
|
+
class Init
|
16
|
+
extend Dry::Core::ClassAttributes
|
17
|
+
defines :db # database handle
|
18
|
+
|
19
|
+
def self.connect e= :development
|
20
|
+
|
21
|
+
env = if e.to_s =~ /^p/
|
22
|
+
:production
|
23
|
+
elsif e.to_s =~ /^t/
|
24
|
+
:test
|
25
|
+
else
|
26
|
+
:development
|
27
|
+
end
|
28
|
+
# set the class attribute
|
29
|
+
|
30
|
+
db Database.new(env)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Provides method `db` to every Model class
|
35
|
+
class Base
|
36
|
+
def self.db
|
37
|
+
Init.db
|
38
|
+
end
|
39
|
+
# expose db to instance methods as well
|
40
|
+
private define_method :db, &method(:db)
|
41
|
+
private_class_method :db
|
42
|
+
end
|
43
|
+
# Provides method `db` to every Query-Object
|
44
|
+
class Query
|
45
|
+
def db
|
46
|
+
Init.db
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|