Class: Arcade::Base

Inherits:
Dry::Struct
  • Object
show all
Extended by:
Support::Sql
Defined in:
lib/init.rb,
lib/arcade/base.rb

Overview

Provides method ‘db` to every Model class

Direct Known Subclasses

Document, Edge, Vertex

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Support::Sql

compose_where, generate_sql_list

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *key) ⇒ Object

enables to display values keys like methods



322
323
324
325
326
# File 'lib/arcade/base.rb', line 322

def method_missing method, *key
  if attributes[:values] &.keys &.include?  method
    return values.fetch(method)
  end
end

Class Method Details

.all(a = true, autoload: true, **args) ⇒ Object

Lists all records of a type

Accepts any parameter supported by Arcade::Query

Model.all false –> suppresses the autoload mechanism

Example

My::Names.all order: ‘name’, autoload: false



162
163
164
165
# File 'lib/arcade/base.rb', line 162

def all  a= true, autoload: true, **args
  autoload =  false if a != autoload
  query(**args).query.allocate_model( autoload )
end

.count(**args) ⇒ Object



147
148
149
150
# File 'lib/arcade/base.rb', line 147

def count  **args
  command = "count(*)"
  query( **( { projection:  command  }.merge args  ) ).query.first[command.to_sym] rescue 0
end

.create(**attributes) ⇒ Object

—————————————– create ———————————- ##

Adds a record to the database

returns the model dataset
( depreciated )


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/arcade/base.rb', line 131

def  create **attributes
  s = Api.begin_transaction db.database
  attributes.merge!( created: DateTime.now ) if timestamps
  record = insert **attributes
  Api.commit db.database, s
  record
rescue QueryError => e
  db.logger.error "Dataset NOT created"
  db.logger.error "Provided Attributes: #{ attributes.inspect }"
 #  Api.rollback db.database   --->  raises "transactgion not begun"
rescue  Dry::Struct::Error => e
  Api.rollback db.database
  db.logger.error "#{ rid } :: Validation failed, record deleted."
  db.logger.error e.message
end

.create_typeObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/arcade/base.rb', line 30

def create_type
  the_class = nil   # declare as local var
  parent_present = ->(cl){ db.hierarchy.flatten.include? cl }
  e = ancestors.each
  myselfclass = e.next  # start with the actual class(self)
  loop do
    superclass = the_class  = e.next
    break if the_class.is_a? Class
  end
  begin
  loop do
    if the_class.respond_to?(:demodulize)
      if [ 'Document','Vertex', 'Edge'].include?(the_class.demodulize)
        if  the_class == superclass  # no inheritance
            db.create_type the_class.demodulize, to_s.snake_case
        else
          if superclass.is_a? Class  #  maybe its a module.
             extended = superclass.to_s.snake_case
           else
             extended = superclass.superclass.to_s.snake_case
           end
          if !parent_present[extended]
            superclass.create_type
          end
          db.create_type the_class.demodulize, to_s.snake_case, extends:  extended
        end
        break  # stop iteration
      end
    end
    the_class = e.next  # iterate through the enumerator
  end
  # todo
  # include `created`` and `updated` properties to the aradedb-database schema if timestamps are set
  # (it works without declaring them explicitly, its thus omitted for now )
  # Integration is easy: just execute two commands
  custom_setup = db_init rescue ""
  custom_setup.each_line do |  command |
    the_command =  command[0 .. -2]  #  remove '\n'
    next if the_command == ''
  #  db.logger.info "Custom Setup:: #{the_command}"
    db.execute { the_command }
  end unless custom_setup.nil?

  rescue RollbackError => e
    db.logger.warn e
  rescue RuntimeError => e
    db.logger.warn e
  end
end

.database_nameObject

this has to be implemented on class level otherwise it interfere with attributes



26
27
28
# File 'lib/arcade/base.rb', line 26

def database_name
  self.name.snake_case
end

.drop_typeObject



81
82
83
# File 'lib/arcade/base.rb', line 81

def drop_type
  db.drop_type to_s.snake_case
end

.find(**args) ⇒ Object

Finds the first matching record providing the parameters of a ‘where` query

Strategie.find symbol: 'Still'
is equivalent to
Strategie.all.find{|y| y.symbol == 'Still' }


221
222
223
224
225
# File 'lib/arcade/base.rb', line 221

def find **args
  f= where(**args).first
  f= where( "#{ args.keys.first } like #{ args.values.first.to_or }" ).first if f.nil? || f.empty?
  f
end

.first(a = true, autoload: true, **args) ⇒ Object

Lists the first record of a type or a query

Accepts any parameter supported by Arcade::Query

Model.first false –> suppresses the autoload mechanism

Example

My::Names.first where: ‘age < 50’, autoload: false



177
178
179
180
# File 'lib/arcade/base.rb', line 177

def first a= true, autoload: true, **args
  autoload =  false if a != autoload
  query( **( { order: "@rid"  , limit: 1  }.merge args ) ).query.allocate_model( autoload ) &.first
end

.insert(**attributes) ⇒ Object

—————————————– insert ———————————- ##

Adds a record to the database

returns the inserted record

Bucket and Index are supported

fired Database-command
INSERT INTO  <type>  BUCKET<bucket> INDEX <index> [CONTENT {<attributes>}]
      (not supported (jet): [RETURN <expression>] [FROM <query>] )


120
121
122
# File 'lib/arcade/base.rb', line 120

def insert **attributes
  db.insert type: database_name, **attributes
end

.last(a = true, autoload: true, **args) ⇒ Object

Lists the last record of a type or a query

Accepts any parameter supported by Arcade::Query

Model.last false –> suppresses the autoload mechanism

Example

My::Names.last where: ‘age > 50’, autoload: false



193
194
195
196
# File 'lib/arcade/base.rb', line 193

def last  a= true, autoload: true, **args
  autoload =  false if a != autoload
  query( **( { order: {"@rid" => 'desc'} , limit: 1  }.merge args ) ).query.allocate_model( autoload )&.first
end

.not_permitted(*m) ⇒ Object

immutable support to make a database type immutable add

`not_permitted :update, :upsert, :delete`

to the model-specification



290
291
292
293
294
295
296
# File 'lib/arcade/base.rb', line 290

def not_permitted *m
  m.each do | def_m |
    define_method(  def_m ) do | v = nil |
    raise ArcadeImmutableError "operation not permitted", caller
  end
  end
end

.propertiesObject



85
86
87
# File 'lib/arcade/base.rb', line 85

def properties

end

.query(**args) ⇒ Object



281
282
283
# File 'lib/arcade/base.rb', line 281

def query **args
  Query.new( **{ from: self }.merge(args) )
end

.timestamps(set = nil) ⇒ Object

add timestamp attributes to the model

updated is optional

timestamps are included in create and update statements



97
98
99
100
101
102
103
104
# File 'lib/arcade/base.rb', line 97

def timestamps set=nil
  if set && @stamps.nil?
    @stamps = true
    attribute :created, Types::JSON::DateTime
    attribute :updated?, Types::JSON::DateTime
  end
  @stamps
end

.update(**args) ⇒ Object

update returns a list of updated records

It fires a query update <type> set <property> = <new value > upsert return after $current where < condition >

which returns a list of modified rid’s

required parameter:  set:
                   where:

todo refacture required parameters notification



237
238
239
240
241
242
243
244
245
246
# File 'lib/arcade/base.rb', line 237

def update **args
  if args.keys.include?(:set) && args.keys.include?(:where)
    args.merge!( updated: DateTime.now ) if timestamps
    query( **( { kind: :update }.merge args ) ).execute do |r|
      r[:"$current"] &.allocate_model(false)  #  do not autoload modelfiles
    end
  else
    raise "at least set: and where: are required to perform this operation"
  end
end

.update!(**args) ⇒ Object

update! returns the count of affected records

required parameter:  set:
                   where:


253
254
255
256
257
258
259
260
# File 'lib/arcade/base.rb', line 253

def update! **args
  if args.keys.include?(:set) && args.keys.include?(:where)
    args.merge!( updated: DateTime.now ) if timestamps
    query( **( { kind: :update! }.merge args ) ).execute{|y| y[:count] } &.first
  else
    raise "at least set: and where: are required to perform this operation"
  end
end

.upsert(**args) ⇒ Object

returns a list of updated records



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/arcade/base.rb', line 264

def upsert **args
  set_statement = args.delete :set
  args.merge!( updated: DateTime.now ) if timestamps
  where_statement = args[:where] || args
  statement = if set_statement
                { set: set_statement, where: where_statement }
              else
                { where: where_statement }
              end
  result= query( **( { kind: :upsert  }.merge statement ) ).execute do | answer|
    z=  answer[:"$current"] &.allocate_model(false)  #  do not autoload modelfiles
    raise LoadError "Upsert failed"   unless z.is_a?  Base
    z  #  return record
  end
end

.where(a = true, autoload: true, **args) ⇒ Object

Selects records of a type or a query

Accepts only parameters to restrict the query (apart from autoload).

Use ‘Model.query where: args“to use the full range of supported parameters

Model.where false –> suppresses the autoload mechanism

Example

My::Names.last where: ‘age > 50’, autoload: false



210
211
212
213
214
215
# File 'lib/arcade/base.rb', line 210

def where a= true, autoload: true, **args
  autoload =  false if a != autoload
   args = a if a.is_a?(String)
   ##  the result is always an array
   query( where: args ).query.allocate_model(autoload)
end

Instance Method Details

#==(arg) ⇒ Object



445
446
447
448
# File 'lib/arcade/base.rb', line 445

def == arg
 # self.attributes == arg.attributes
  self.rid == arg.rid
end

#accepted_methodsObject



16
17
18
# File 'lib/arcade/base.rb', line 16

def accepted_methods
  [ :rid, :to_human, :delete ]
end

#deleteObject



441
442
443
444
# File 'lib/arcade/base.rb', line 441

def delete
  response = db.execute { "delete from #{ rid }" }
  true if response == [{ count: 1 }]
end

#insert_document(name, obj) ⇒ Object

inserts or updates a embedded document



402
403
404
405
406
407
408
409
410
411
# File 'lib/arcade/base.rb', line 402

def insert_document name, obj
  value = if obj.is_a? Document
            obj.to_json
          else
            obj.to_or
          end
#      if send( name ).nil? || send( name ).empty?
    db.execute { "update #{ rid } set  #{ name } =  #{ value }" }.first[:count]
#      end
end

#inspectObject

configure irb-output to to_human for all Arcade::Base-Objects



375
376
377
# File 'lib/arcade/base.rb', line 375

def inspect
  to_human
end

#invariant_attributesObject



311
312
313
314
315
316
317
318
# File 'lib/arcade/base.rb', line 311

def invariant_attributes
  result= attributes.except :rid, :in, :out, :values, :created_at,  :updated_at
  if  attributes.keys.include?(:values)
    result.merge values
  else
    result
  end
end

#query(**args) ⇒ Object



328
329
330
# File 'lib/arcade/base.rb', line 328

def query **args
  Query.new( **{ from: rid }.merge(args) )
end

#refreshObject



450
451
452
# File 'lib/arcade/base.rb', line 450

def refresh
  db.get(rid)
end

#rid?Boolean

Returns:

  • (Boolean)


343
344
345
# File 'lib/arcade/base.rb', line 343

def rid?
  true unless ["#0:0", "#-1:-1"].include?  rid
end

#to_htmlObject

iruby



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/arcade/base.rb', line 379

def to_html  # iruby
  _modul, _class =  self.class.to_s.split "::"
  the_class =  _modul == 'Arcade' ? _class : self.class.to_s
  IRuby.display IRuby.html "<b style=\"color: #50953DFF\"><#{ the_class}</b>"
  + rid? ? "[#{ rid }]: " : " " + invariant_attributes.map do |attr, value|
    v= case value
       when Base
         "< #{ self.class.to_s.snake_case }: #{ value.rid } >"
       when Array
         value.map{|x| x.to_s}
       else
         value.from_db
       end
    "%s : %s" % [ attr, v]  unless v.nil?
  end.compact.sort.join(', ') + ">".gsub('"' , ' ')
end

#to_humanObject



356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/arcade/base.rb', line 356

def to_human


		"<#{ self.class.to_s.snake_case }" + rid? ? "[#{ rid }]: " : " " + invariant_attributes.map do |attr, value|
			v= case value
 when Base
	 "< #{ self.class.to_s.snake_case }: #{ value.rid } >"
 when Array
       value.map{|x| x.to_s}
 else
	 value.from_db
 end
			"%s : %s" % [ attr, v]  unless v.nil?
		end.compact.sort.join(', ') + ">".gsub('"' , ' ')
end

#to_json(*args) ⇒ Object

to JSON controlls the serialisation of Arcade::Base Objects for the HTTP-JSON API

ensure, that only the rid is transmitted to the database



336
337
338
339
340
341
342
# File 'lib/arcade/base.rb', line 336

def to_json *args
  unless ["#0:0", "#-1:-1"].include?  rid   #  '#-1:-1' is the null-rid
    rid
  else
    invariant_attributes.merge( :'@type' =>  self.class.database_name  ).to_json
  end
end

#to_orObject

enables usage of Base-Objects in queries



348
349
350
351
352
353
354
# File 'lib/arcade/base.rb', line 348

def to_or
  if rid?
    rid
  else
    to_json
  end
end

#update(**args) ⇒ Object



396
397
398
399
# File 'lib/arcade/base.rb', line 396

def update **args
  Query.new( from: rid , kind: :update, set: args).execute
  refresh
end

#update_embedded(embedded, embedded_property, value) ⇒ Object

updates a single property in an embedded document



414
415
416
# File 'lib/arcade/base.rb', line 414

def update_embedded embedded, embedded_property, value
  db.execute{ " update #{rid} set `#{embedded}`.`#{embedded_property}` =  #{value.to_or}" }
end

#update_list(list, value) ⇒ Object



418
419
420
421
422
423
424
425
426
427
428
429
430
# File 'lib/arcade/base.rb', line 418

def update_list list, value
  value = if value.is_a? Document
            value.to_json
          else
            value.to_or
          end
  if send( list ).nil? || send( list ).empty?
    db.execute { "update #{ rid } set  #{ list } =  [#{ value }]" }
  else
    db.execute { "update #{ rid } set  #{ list } += #{ value }" }
  end
  refresh
end

#update_map(m, key, value) ⇒ Object

updates a map property , actually adds the key-value pair to the property



433
434
435
436
437
438
439
440
# File 'lib/arcade/base.rb', line 433

def update_map m, key, value
  if send( m ).nil?
    db.execute { "update #{ rid } set #{ m } = MAP ( #{ key.to_s.to_or } , #{ value.to_or } ) "  }
  else
    db.execute { "update #{ rid } set #{ m }.`#{ key.to_s }` = #{ value.to_or }" }
  end
  refresh
end