Class: Baza::ModelHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/baza/model_handler.rb,
lib/baza/model_handler_sqlhelper.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ ModelHandler

Returns a new instance of ModelHandler.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
# File 'lib/baza/model_handler.rb', line 7

def initialize(args)
  require "array_enumerator" if args[:array_enum]
  require "event_handler"
  require "monitor"
  require "ostruct"

  @callbacks = {}
  @args = args
  @args[:col_id] = :id unless @args[:col_id]
  @args[:class_pre] = "class_" unless @args[:class_pre]
  @args[:module] = Kernel unless @args[:module]
  @args[:cache] = :weak unless @args.key?(:cache)
  @objects = {}
  @locks = {}
  @data = {}
  @lock_require = Monitor.new

  # Set up various events.
  @events = EventHandler.new
  @events.add_event(name: :no_html, connections_max: 1)
  @events.add_event(name: :no_name, connections_max: 1)
  @events.add_event(name: :no_date, connections_max: 1)
  @events.add_event(name: :missing_class, connections_max: 1)
  @events.add_event(name: :require_class, connections_max: 1)

  raise "No DB given." if !@args[:db] && !@args[:custom]
  raise "No class path given." if !@args[:class_path] && (@args[:require] || !@args.key?(:require))

  if args[:require_all]
    loads = []

    Dir.foreach(@args[:class_path]) do |file|
      next if file == "." || file == ".." || !file.match(/\.rb$/)
      file_parsed = file
      file_parsed.gsub!(@args[:class_pre], "") if @args.key?(:class_pre)
      file_parsed.gsub!(/\.rb$/, "")
      file_parsed = StringCases.snake_to_camel(file_parsed)

      loads << file_parsed
      requireclass(file_parsed, load: false)
    end

    loads.each do |load_class|
      self.load_class(load_class)
    end
  end

  # Set up ID-caching.
  @ids_cache_should = {}

  return unless @args[:models]

  @ids_cache = {}

  @args[:models].each do |classname, classargs|
    @ids_cache_should[classname] = true if classargs[:cache_ids]
    cache_ids(classname)
  end
end

Instance Attribute Details

#argsObject (readonly)

Returns the value of attribute args.



5
6
7
# File 'lib/baza/model_handler.rb', line 5

def args
  @args
end

#dataObject (readonly)

Returns the value of attribute data.



5
6
7
# File 'lib/baza/model_handler.rb', line 5

def data
  @data
end

#eventsObject (readonly)

Returns the value of attribute events.



5
6
7
# File 'lib/baza/model_handler.rb', line 5

def events
  @events
end

#ids_cacheObject (readonly)

Returns the value of attribute ids_cache.



5
6
7
# File 'lib/baza/model_handler.rb', line 5

def ids_cache
  @ids_cache
end

#ids_cache_shouldObject (readonly)

Returns the value of attribute ids_cache_should.



5
6
7
# File 'lib/baza/model_handler.rb', line 5

def ids_cache_should
  @ids_cache_should
end

Instance Method Details

#add(classname, data = {}, args = nil) ⇒ Object

Add a new object to the database and to the cache.

Examples

obj = ob.add(:User, => “User 1”)



637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
# File 'lib/baza/model_handler.rb', line 637

def add(classname, data = {}, args = nil)
  raise "data-variable was not a hash: '#{data.class.name}'." unless data.is_a?(Hash)
  classname = classname.to_sym
  requireclass(classname)

  if @args[:custom]
    classobj = @args[:module].const_get(classname)
    retob = classobj.add(OpenStruct.new(
                           ob: self,
                           data: data
    ))
  else
    classobj = @args[:module].const_get(classname)

    # Run the class 'add'-method to check various data.
    classobj.add(OpenStruct.new(ob: self, db: @args[:db], data: data)) if classobj.respond_to?(:add)

    # Check if various required data is given. If not then raise an error telling about it.
    required_data = classobj.required_data
    required_data.each do |req_data|
      raise "No '#{req_data[:class]}' given by the data '#{req_data[:col]}'." unless data.key?(req_data[:col])
      raise "The '#{req_data[:class]}' by ID '#{data[req_data[:col]]}' could not be found with the data '#{req_data[:col]}'." unless self.exists?(req_data[:class], data[req_data[:col]])
    end

    # If 'skip_ret' is given, then the ID wont be looked up and the object wont be spawned. Be aware the connected events wont be executed either. In return it will go a lot faster.
    if args && args[:skip_ret] && !@ids_cache_should.key?(classname)
      ins_args = nil
    else
      ins_args = {return_id: true}
    end

    # Insert and (maybe?) get ID.
    ins_id = @args[:db].insert(classobj.table, data, ins_args).to_i

    # Add ID to ID-cache if ID-cache is active for that classname.
    @ids_cache[classname][ins_id] = true if ins_id != 0 && @ids_cache_should.key?(classname)

    # Skip the rest if we are told not to return result.
    return nil if args && args[:skip_ret]

    # Spawn the object.
    retob = get(classname, ins_id, skip_reload: true)
  end

  call("object" => retob, "signal" => "add")
  retob.send(:add_after, {}) if retob.respond_to?(:add_after)

  retob
end

#adds(classname, datas) ⇒ Object

Adds several objects to the database at once. This is faster than adding every single object by itself, since this will do multi-inserts if supported by the database.

Examples

ob.adds(:User, [=> “User 1”, => “User 2”)



690
691
692
693
694
695
696
697
698
699
700
701
702
703
# File 'lib/baza/model_handler.rb', line 690

def adds(classname, datas)
  if @args[:module].const_get(classname).respond_to?(:add)
    datas.each do |data|
      @args[:module].const_get(classname).add(OpenStruct.new(
                                                ob: self,
                                                db: db,
                                                data: data
      ))
    end
  end

  db.insert_multi(classname, datas)
  cache_ids(classname)
end

#cache_ids(classname) ⇒ Object

Caches all IDs for a specific classname.



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/baza/model_handler.rb', line 68

def cache_ids(classname)
  classname = classname.to_sym
  return nil if !@ids_cache_should || !@ids_cache_should[classname]

  newcache = {}
  @args[:db].q("SELECT `#{@args[:col_id]}` FROM `#{classname}` ORDER BY `#{@args[:col_id]}`") do |data|
    newcache[data[@args[:col_id]].to_i] = true
  end

  @ids_cache[classname] = newcache
end

#call(args, &_block) ⇒ Object

This method is used to call the connected callbacks for an event.



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/baza/model_handler.rb', line 174

def call(args, &_block)
  classstr = args["object"].class.classname.to_sym

  return unless @callbacks.key?(classstr)

  @callbacks[classstr].clone.each do |_callback_key, callback|
    docall = false

    if callback.key?("signal") && args.key?("signal") && callback["signal"].to_s == args["signal"].to_s
      docall = true
    elsif callback["signals"] && args["signal"] && (callback["signals"].include?(args["signal"].to_s) || callback["signals"].include?(args["signal"].to_sym))
      docall = true
    end

    next unless docall

    if callback["block"]
      callargs = []
      arity = callback["block"].arity
      if arity <= 0
        # do nothing
      elsif arity == 1
        callargs << args["object"]
      else
        raise "Unknown number of arguments: #{arity}"
      end

      callback["block"].call(*callargs)
    else
      raise "No valid callback given."
    end
  end
end

#classes_loadedObject



907
908
909
# File 'lib/baza/model_handler.rb', line 907

def classes_loaded
  @objects.keys
end

#clean(classn) ⇒ Object

Try to clean up objects by unsetting everything, start the garbagecollector, get all the remaining objects via ObjectSpace and set them again. Some (if not all) should be cleaned up and our cache should still be safe… dirty but works.



865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
# File 'lib/baza/model_handler.rb', line 865

def clean(classn)
  if classn.is_a?(Array)
    classn.each do |realclassn|
      clean(realclassn)
    end

    return nil
  end

  if @args[:cache] == :weak
    @objects[classn].clean
  elsif @args[:cache] == :none
    return false
  else
    return false unless @objects.key?(classn)
    @objects[classn] = {}
    GC.start

    @objects.keys.each do |classname|
      data = @objects[classname]
      classobj = @args[:module].const_get(classname)
      ObjectSpace.each_object(classobj) do |obj|
        begin
          data[obj.id.to_i] = obj
        rescue => e
          if e.message == "No data on object."
            # Object has been unset - skip it.
            next
          end

          raise e
        end
      end
    end
  end
end

#clean_allObject

Erases the whole cache and regenerates it from ObjectSpace if not running weak-link-caching. If running weaklink-caching then it will only removes the dead links.



903
904
905
# File 'lib/baza/model_handler.rb', line 903

def clean_all
  clean(@objects.keys)
end

#connect(args, &block) ⇒ Object

This connects a block to an event. When the event is called the block will be executed.



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/baza/model_handler.rb', line 125

def connect(args, &block)
  raise "No object given." unless args["object"]
  raise "No signals given." if !args.key?("signal") && !args.key?("signals")
  args["block"] = block if block_given?
  object = args["object"].to_sym

  @callbacks[object] = {} unless @callbacks[object]
  conn_id = @callbacks[object].length.to_s
  @callbacks[object][conn_id] = args
  conn_id
end

#connected?(args) ⇒ Boolean

Returns true if the given signal is connected to the given object.

Returns:

  • (Boolean)


138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/baza/model_handler.rb', line 138

def connected?(args)
  raise "No object given." unless args["object"]
  raise "No signal given." unless args.key?("signal")
  object = args["object"].to_sym

  if @callbacks.key?(object)
    @callbacks[object].clone.each do |_ckey, callback|
      return true if callback.key?("signal") && callback["signal"].to_s == args["signal"].to_s
      return true if callback.key?("signals") && (callback["signals"].include?(args["signal"].to_s) || callback["signals"].include?(args["signal"].to_sym))
    end
  end

  false
end

#count_objectsObject

Returns the total count of objects currently held by this instance.



115
116
117
118
119
120
121
122
# File 'lib/baza/model_handler.rb', line 115

def count_objects
  count = 0
  @objects.keys.each do |key|
    count += @objects[key].length
  end

  count
end

#datarow_from_datarow_argument(datarow_argument) ⇒ Object



467
468
469
470
471
472
473
474
475
476
477
# File 'lib/baza/model_handler_sqlhelper.rb', line 467

def datarow_from_datarow_argument(datarow_argument)
  if datarow_argument.is_a?(String)
    const = Knj::Strings.const_get_full(datarow_argument)
  else
    const = datarow_argument
  end

  load_class(datarow_argument.to_s.split("::").last) unless const.initialized? # Make sure the class is initialized.

  const
end

#datarow_obj_from_args(args, _list_args, class_name) ⇒ Object

Used by sqlhelper-method to look up datarow-classes and automatically load them if they arent loaded already.



449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'lib/baza/model_handler_sqlhelper.rb', line 449

def datarow_obj_from_args(args, _list_args, class_name)
  class_name = class_name.to_sym

  unless args.key?(:joined_tables)
    raise "No joined tables on '#{args[:table]}' to find datarow for: '#{class_name}'."
  end

  args[:joined_tables].each do |table_name, table_data|
    next if table_name.to_sym != class_name
    return datarow_from_datarow_argument(table_data[:datarow]) if table_data[:datarow]

    requireclass(class_name) if @objects.key?(class_name)
    return @args[:module].const_get(class_name)
  end

  raise "Could not figure out datarow for: '#{class_name}'."
end

#dbObject

Returns the database-connection used by this instance of Objects.



110
111
112
# File 'lib/baza/model_handler.rb', line 110

def db
  @args[:db]
end

#delete(object, args = nil) ⇒ Object

Delete an object. Both from the database and from the cache.

Examples

user = ob.get(:User, 1) ob.delete(user)



761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
# File 'lib/baza/model_handler.rb', line 761

def delete(object, args = nil)
  # Return false if the object has already been deleted.
  return false if object.deleted?
  classname = object.class.classname.to_sym

  call("object" => object, "signal" => "delete_before")
  unset(object)
  obj_id = object.id
  object.delete if object.respond_to?(:delete)

  # If autodelete is set by 'has_many'-method, go through it and delete the various objects first.
  if autodelete_data = object.class.autodelete_data
    autodelete_data.each do |adel_data|
      list(adel_data[:classname], adel_data[:colname].to_s => object.id) do |obj_del|
        delete(obj_del, args)
      end
    end
  end

  # If depend is set by 'has_many'-method, check if any objects exists and raise error if so.
  if dep_datas = object.class.depending_data
    dep_datas.each do |dep_data|
      if obj = get_by(dep_data[:classname], dep_data[:colname].to_s => object.id)
        raise "Cannot delete <#{object.class.name}:#{object.id}> because <#{obj.class.name}:#{obj.id}> depends on it."
      end
    end
  end

  # If autozero is set by 'has_many'-method, check if any objects exists and set the ID to zero.
  if autozero_datas = object.class.autozero_data
    autozero_datas.each do |zero_data|
      list(zero_data[:classname], zero_data[:colname].to_s => object.id) do |obj_zero|
        obj_zero[zero_data[:colname].to_sym] = 0
      end
    end
  end

  # Delete any translations that has been set on the object by 'has_translation'-method.
  if object.class.translations
    begin
      _hb.trans_del(object)
    rescue NameError
      _kas.trans_del(object)
    end
  end


  # If a buffer is given in arguments, then use that to delete the object.
  if args && buffer = args[:db_buffer]
    buffer.delete(object.table, id: obj_id)
  else
    @args[:db].delete(object.table, id: obj_id)
  end

  @ids_cache[classname].delete(obj_id.to_i) if @ids_cache_should.key?(classname)
  call("object" => object, "signal" => "delete")
  object.destroy
  nil
end

#delete_ids(args) ⇒ Object

Deletes all objects with the given IDs 500 at a time to prevent memory exhaustion or timeout.

Examples

ob.delete_ids(:class => :Person, :ids => [1, 3, 5, 6, 7, 8, 9])


855
856
857
858
859
860
861
862
# File 'lib/baza/model_handler.rb', line 855

def delete_ids(args)
  while !args[:ids].empty? && (ids = args[:ids].shift(500))
    objs = list(args[:class], "id" => ids)
    deletes(objs)
  end

  nil
end

#deletes(objs) ⇒ Object

Deletes several objects as one. If running datarow-mode it checks all objects before it starts to actually delete them. Its faster than deleting every single object by itself…



822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
# File 'lib/baza/model_handler.rb', line 822

def deletes(objs)
  tables = {}

  begin
    objs.each do |obj|
      next if obj.deleted?
      tablen = obj.table

      tables[tablen] = [] unless tables.key?(tablen)

      tables[tablen] << obj.id
      obj.delete if obj.respond_to?(:delete)

      # Remove from ID-cache.
      classname = obj.class.classname.to_sym
      @ids_cache[classname].delete(obj.id.to_i) if @ids_cache_should.key?(classname)

      # Unset any data on the object, so it seems deleted.
      obj.destroy
    end
  ensure
    # An exception may occur, and we should make sure, that objects that has gotten 'delete' called also are deleted from their tables.
    tables.each do |table, ids|
      ids.each_slice(1000) do |ids_slice|
        @args[:db].delete(table, id: ids_slice)
      end
    end
  end
end

#exists?(classname, id) ⇒ Boolean

Returns true if a row of the given classname and the ID exists. Will use ID-cache if set in arguments and spawned otherwise it will do an actual lookup.

Examples

print “User 5 exists.” if ob.exists?(:User, 5)

Returns:

  • (Boolean)


289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/baza/model_handler.rb', line 289

def exists?(classname, id)
  # Make sure the given data are in the correct types.
  classname = classname.to_sym
  id = id.to_i

  # Check if ID-cache is enabled for that classname. Avoid SQL-lookup by using that.
  if @ids_cache_should.key?(classname)
    if @ids_cache[classname].key?(id)
      return true
    else
      return false
    end
  end

  # If the object currently exists in cache, we dont have to do a lookup either.
  return true if @objects.key?(classname) && (obj = @objects[classname].get(id)) && !obj.deleted?

  # Okay - no other options than to actually do a real lookup.
  begin
    table = @args[:module].const_get(classname).table
    row = @args[:db].single(table, @args[:col_id] => id)

    if row
      return true
    else
      return false
    end
  rescue Errno::ENOENT
    return false
  end
end

#get(classname, data, args = nil) ⇒ Object

Gets an object from the ID or the full data-hash in the database.

Examples

inst = ob.get(:User, 5)



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/baza/model_handler.rb', line 324

def get(classname, data, args = nil)
  classname = classname.to_sym

  if data.is_a?(Integer) || data.is_a?(String) || data.is_a?(Fixnum)
    id = data.to_i
  elsif data.is_a?(Hash) && data.key?(@args[:col_id].to_sym)
    id = data[@args[:col_id].to_sym].to_i
  elsif data.is_a?(Hash) && data.key?(@args[:col_id].to_s)
    id = data[@args[:col_id].to_s].to_i
  else
    raise ArgumentError, "Unknown data for class '#{classname}': '#{data.class}' (#{data})."
  end

  if @objects.key?(classname)
    case @args[:cache]
    when :weak
      if (obj = @objects[classname].get(id)) && obj.id.to_i == id
        return obj
      end
    else
      return @objects[classname][id] if @objects[classname].key?(id)
    end
  end

  requireclass(classname) unless @objects.key?(classname)

  @locks[classname].synchronize do
    # Maybe the object got spawned while we waited for the lock? If so we shouldnt spawn another instance.
    if @args[:cache] == :weak && obj = @objects[classname].get(id) && obj.id.to_i == id
      return obj
    end

    # Spawn object.
    obj = @args[:module].const_get(classname).new(data, args)

    # Save object in cache.
    case @args[:cache]
    when :none
      return obj
    else
      @objects[classname][id] = obj
      return obj
    end
  end

  raise "Unexpected run?"
end

#get!(*args, &block) ⇒ Object

Same as normal get but returns false if not found instead of raising error.



373
374
375
376
377
# File 'lib/baza/model_handler.rb', line 373

def get!(*args, &block)
  return get(*args, &block)
rescue Errno::ENOENT
  return false
end

#get_by(classname, args = {}) ⇒ Object

Returns the first object found from the given arguments. Also automatically limits the results to 1.



387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/baza/model_handler.rb', line 387

def get_by(classname, args = {})
  classname = classname.to_sym
  requireclass(classname)
  classob = @args[:module].const_get(classname)

  raise "list-function has not been implemented for '#{classname}'." unless classob.respond_to?(:list)

  args["limit"] = 1
  list(classname, args) do |obj|
    return obj
  end

  false
end

#get_if_cached(classname, id) ⇒ Object

Returns the instance of classname, but only if it already exists.



275
276
277
278
279
280
281
282
283
284
# File 'lib/baza/model_handler.rb', line 275

def get_if_cached(classname, id)
  classname = classname.to_sym
  id = id.to_i

  if wref_map = @objects[classname] && obj = wref_map.get(id)
    return obj
  end

  nil
end

#get_or_add(classname, data, _args = nil) ⇒ Object

Searches for an object with the given data. If not found it creates it. Returns the found or created object in the end.



403
404
405
406
407
# File 'lib/baza/model_handler.rb', line 403

def get_or_add(classname, data, _args = nil)
  obj = get_by(classname, data.clone)
  obj = add(classname, data) unless obj
  obj
end

#get_try(obj, col_name, obj_name = nil) ⇒ Object



409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/baza/model_handler.rb', line 409

def get_try(obj, col_name, obj_name = nil)
  unless obj_name
    if match = col_name.to_s.match(/^(.+)_id$/)
      obj_name = Php4r.ucwords(match[1]).to_sym
    else
      raise "Could not figure out objectname for: #{col_name}."
    end
  end

  id_data = obj[col_name].to_i
  return false if id_data.to_i <= 0

  begin
    return get(obj_name, id_data)
  rescue Errno::ENOENT
    return false
  end
end

#init_class(classname) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/baza/model_handler.rb', line 80

def init_class(classname)
  classname = classname.to_sym
  return false if @objects.key?(classname)

  if @args[:cache] == :weak
    @objects[classname] = Wref::Map.new
  else
    @objects[classname] = {}
  end

  @locks[classname] = Monitor.new
end

#list(classname, args = {}, &block) ⇒ Object

Returns an array-list of objects. If given a block the block will be called for each element and memory will be spared if running weak-link-mode.

Examples

ob.list(:User) do |user|

print "Username: #{user.name}\n"

end



433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/baza/model_handler.rb', line 433

def list(classname, args = {}, &block)
  args = {} if args == nil
  classname = classname.to_sym
  requireclass(classname)
  classob = @args[:module].const_get(classname)

  raise "list-function has not been implemented for '#{classname}'." unless classob.respond_to?("list")
  ret = classob.list(OpenStruct.new(args: args, ob: self, db: @args[:db]), &block)

  # If 'ret' is an array and a block is given then the list-method didnt return blocks. We emulate it instead with the following code.
  if block && ret.is_a?(Array)
    ret.each do |obj|
      block.call(obj)
    end
    return nil
  elsif block && !ret.nil?
    raise "Return should return nil because of block but didnt. It wasnt an array either..."
  elsif block
    return nil
  else
    return ret
  end
end

#list_bysql(classname, sql, args = nil, &block) ⇒ Object

Returns a list of a specific object by running specific SQL against the database.



588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
# File 'lib/baza/model_handler.rb', line 588

def list_bysql(classname, sql, args = nil, &block)
  classname = classname.to_sym
  ret = [] unless block
  qargs = nil

  if args
    args.each do |key, _val|
      case key
      when :cloned_ubuf
        qargs = {cloned_ubuf: true}
      else
        raise "Invalid key: '#{key}'."
      end
    end
  end

  if @args[:array_enum]
    enum = Enumerator.new do |yielder|
      @args[:db].q(sql, qargs) do |d_obs|
        yielder << get(classname, d_obs)
      end
    end

    if block
      enum.each(&block)
      return nil
    else
      return ArrayEnumerator.new(enum)
    end
  else
    @args[:db].q(sql, qargs) do |d_obs|
      if block
        block.call(get(classname, d_obs))
      else
        ret << get(classname, d_obs)
      end
    end

    if !block
      return ret
    else
      return nil
    end
  end
end

#list_invalid_required(args, &block) ⇒ Object

Yields every object that is missing certain required objects (based on ‘has_many’ required-argument).



458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/baza/model_handler.rb', line 458

def list_invalid_required(args, &block)
  enum = Enumerator.new do |yielder|
    classname = args[:class]
    classob = @args[:module].const_get(classname)
    required_data = classob.required_data

    if required_data && !required_data.empty?
      required_data.each do |req_data|
        list(args[:class]) do |obj|
          puts "Checking #{obj.classname}(#{obj.id}) for required #{req_data[:class]}." if args[:debug]
          id = obj[req_data[:col]]

          begin
            raise Errno::ENOENT unless id
            obj_req = get(req_data[:class], id)
          rescue Errno::ENOENT
            yielder << {obj: obj, type: :required, id: id, data: req_data}
          end
        end
      end
    end
  end

  if block
    enum.each(&block)
  else
    return ArrayEnumerator.new(enum)
  end
end

#list_opts(classname, args = {}) ⇒ Object

Returns select-options-HTML for inserting into a HTML-select-element.



489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
# File 'lib/baza/model_handler.rb', line 489

def list_opts(classname, args = {})
  Knj::ArrayExt.hash_sym(args)
  classname = classname.to_sym

  if args[:list_args].is_a?(Hash)
    list_args = args[:list_args]
  else
    list_args = {}
  end

  html = ""

  if args[:addnew] || args[:add]
    html << "<option"
    html << " selected=\"selected\"" unless args[:selected]
    html << " value=\"\">#{_("Add new")}</option>"
  elsif args[:none]
    html << "<option"
    html << " selected=\"selected\"" unless args[:selected]
    html << " value=\"\">#{_("None")}</option>"
  end

  list(classname, args[:list_args]) do |object|
    html << "<option value=\"#{object.id.html}\""

    selected = false
    if args[:selected].is_a?(Array) && !args[:selected].index(object).nil?
      selected = true
    elsif args[:selected] && args[:selected].respond_to?("is_knj?") && args[:selected].id.to_s == object.id.to_s
      selected = true
    end

    html << " selected=\"selected\"" if selected

    obj_methods = object.class.instance_methods(false)

    begin
      if !obj_methods.index("name").nil? || !obj_methods.index(:name).nil?
        objhtml = object.name.html
      elsif !obj_methods.index("title").nil? || !obj_methods.index(:title).nil?
        objhtml = object.title.html
      elsif object.respond_to?(:data)
        obj_data = object.data

        if obj_data.key?(:name)
          objhtml = obj_data[:name]
        elsif obj_data.key?(:title)
          objhtml = obj_data[:title]
        end
      else
        objhtml = ""
      end

      raise "Could not figure out which name-method to call?" unless objhtml
      html << ">#{objhtml}</option>"
    rescue => e
      html << ">[#{object.class.name}: #{e.message}]</option>"
    end
  end

  html
end

#list_optshash(classname, args = {}) ⇒ Object

Returns a hash which can be used to generate HTML-select-elements.



553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
# File 'lib/baza/model_handler.rb', line 553

def list_optshash(classname, args = {})
  classname = classname.to_sym

  if args[:list_args].is_a?(Hash)
    list_args = args[:list_args]
  else
    list_args = {}
  end

  list = {}

  if args[:addnew] || args[:add]
    list["0"] = _("Add new")
  elsif args[:choose]
    list["0"] = _("Choose") + ":"
  elsif args[:all]
    list["0"] = _("All")
  elsif args[:none]
    list["0"] = _("None")
  end

  self.list(classname, args[:list_args]) do |object|
    if object.respond_to?(:name)
      list[object.id] = object.name
    elsif object.respond_to?(:title)
      list[object.id] = object.title
    else
      raise "Object of class '#{object.class.name}' doesnt support 'name' or 'title."
    end
  end

  list
end

#load_class(classname, args = {}) ⇒ Object

Loads a Datarow-class by calling various static methods.



262
263
264
265
266
267
268
269
270
271
272
# File 'lib/baza/model_handler.rb', line 262

def load_class(classname, args = {})
  if args[:class]
    classob = args[:class]
  else
    classob = @args[:module].const_get(classname)
  end

  pass_arg = OpenStruct.new(ob: self, db: @args[:db])
  classob.load_columns(pass_arg) if classob.respond_to?(:load_columns)
  classob.datarow_init(pass_arg) if classob.respond_to?(:datarow_init)
end

#not(not_v, val) ⇒ Object



479
480
481
482
483
# File 'lib/baza/model_handler_sqlhelper.rb', line 479

def not(not_v, val)
  return val if not_v == "not" || not_v == "not_"

  ""
end

#object_finalizer(id) ⇒ Object



379
380
381
382
383
384
# File 'lib/baza/model_handler.rb', line 379

def object_finalizer(id)
  classname = @objects_idclass[id]
  return unless classname
  @objects[classname].delete(id)
  @objects_idclass.delete(id)
end

#objectsObject

Returns a cloned version of the @objects variable. Cloned because iteration on it may crash some of the other methods in Ruby 1.9+



99
100
101
102
103
104
105
106
107
# File 'lib/baza/model_handler.rb', line 99

def objects
  objs_cloned = {}

  @objects.keys.each do |key|
    objs_cloned[key] = @objects[key].clone
  end

  objs_cloned
end

#requireclass(classname, args = {}) ⇒ Object



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/baza/model_handler.rb', line 208

def requireclass(classname, args = {})
  classname = classname.to_sym
  return false if @objects.key?(classname)
  classname_snake = StringCases.camel_to_snake(classname)

  @lock_require.synchronize do
    # Maybe the classname got required meanwhile the synchronized wait - check again.
    return false if @objects.key?(classname)

    if @events.connected?(:require_class)
      @events.call(:require_class, class: classname)
    else
      doreq = false

      if args[:require]
        doreq = true
      elsif args.key?(:require) && !args[:require]
        doreq = false
      elsif @args[:require] || !@args.key?(:require)
        doreq = true
      end

      if doreq
        filename = "#{@args[:class_path]}/#{@args[:class_pre]}#{classname_snake}.rb"
        filename_req = "#{@args[:class_path]}/#{@args[:class_pre]}#{classname_snake}"
        raise "Class file could not be found: #{filename}." unless File.exist?(filename)
        require filename_req
      end
    end

    if args[:class]
      classob = args[:class]
    else
      begin
        classob = @args[:module].const_get(classname)
      rescue NameError => e
        if @events.connected?(:missing_class)
          @events.call(:missing_class, class: classname)
          classob = @args[:module].const_get(classname)
        else
          raise e
        end
      end
    end

    if (classob.respond_to?(:load_columns) || classob.respond_to?(:datarow_init)) && (!args.key?(:load) || args[:load])
      load_class(classname, args)
    end

    init_class(classname)
  end
end

#sqlhelper(list_args, args_def) ⇒ Object

This method helps build SQL from Objects-instances list-method. It should not be called directly but only through Objects.list.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/baza/model_handler_sqlhelper.rb', line 5

def sqlhelper(list_args, args_def)
  args = args_def

  if args[:db]
    db = args[:db]
  else
    db = @args[:db]
  end

  if args[:table]
    table_def = "`#{db.escape_table(args[:table])}`."
  else
    table_def = ""
  end

  sql_joins = ""
  sql_where = ""
  sql_order = ""
  sql_limit = ""
  sql_groupby = ""

  do_joins = {}

  limit_from = nil
  limit_to = nil

  if list_args.key?("orderby")
    orders = []
    orderstr = list_args["orderby"]
    list_args["orderby"] = [list_args["orderby"]] if list_args["orderby"].is_a?(Hash)

    if list_args["orderby"].is_a?(String)
      found = false
      found = true if args[:cols].key?(orderstr)

      if found
        sql_order << " ORDER BY "
        ordermode = " ASC"
        if list_args.key?("ordermode")
          if list_args["ordermode"] == "desc"
            ordermode = " DESC"
          elsif list_args["ordermode"] == "asc"
            ordermode = " ASC"
            raise "Unknown ordermode: #{list_args["ordermode"]}"
          end

          list_args.delete("ordermode")
        end

        sql_order << "#{table_def}`#{db.escape_column(list_args["orderby"])}`#{ordermode}"
        list_args.delete("orderby")
      end
    elsif list_args["orderby"].is_a?(Array)
      sql_order << " ORDER BY "

      list_args["orderby"].each do |val|
        ordermode = nil
        orderstr = nil
        found = false

        if val.is_a?(Array)
          if val[1] == "asc"
            ordermode = " ASC"
          elsif val[1] == "desc"
            ordermode = " DESC"
          end

          if val[0].is_a?(Array)
            if args[:joined_tables]
              args[:joined_tables].each do |table_name, _table_data|
                next if table_name.to_s != val[0][0].to_s
                do_joins[table_name] = true
                orders << "`#{db.escape_table(table_name)}`.`#{db.escape_column(val[0][1])}`#{ordermode}"
                found = true
                break
              end
            end

            raise "Could not find joined table for ordering: '#{val[0][0]}'." unless found
          else
            orderstr = val[0]
          end
        elsif val.is_a?(String)
          orderstr = val
          ordermode = " ASC"
        elsif val.is_a?(Hash) && val[:type] == :sql
          orders << val[:sql]
          found = true
        elsif val.is_a?(Hash) && val[:type] == :case
          caseorder = " CASE"

          val[:case].each do |key, caseval|
            col = key.first
            isval = key.last
            col_str = nil

            if col.is_a?(Array)
              raise "No joined tables for '#{args[:table]}'." unless args[:joined_tables]

              found = false
              args[:joined_tables].each do |table_name, _table_data|
                next unless table_name == col.first
                do_joins[table_name] = true
                col_str = "`#{db.escape_table(table_name)}`.`#{db.escape_column(col.last)}`"
                found = true
                break
              end

              raise "No such joined table on '#{args[:table]}': '#{col.first}' (#{col.first.class.name}) with the following joined table:\n#{Php4r.print_r(args[:joined_tables], true)}" unless found
            elsif col.is_a?(String) || col.is_a?(Symbol)
              col_str = "#{table_def}`#{col}`"
              found = true
            else
              raise "Unknown type for case-ordering: '#{col.class.name}'."
            end

            raise "'colstr' was not set." unless col_str
            caseorder << " WHEN #{col_str} = '#{db.esc(isval)}' THEN '#{db.esc(caseval)}'"
          end

          caseorder << " ELSE '#{db.esc(val[:else])}'" if val[:else]

          caseorder << " END"
          orders << caseorder
        elsif val.is_a?(Hash)
          raise "No joined tables." unless args.key?(:joined_tables)

          if val[:mode] == "asc"
            ordermode = " ASC"
          elsif val[:mode] == "desc"
            ordermode = " DESC"
          end

          if args[:joined_tables]
            args[:joined_tables].each do |table_name, table_data|
              if table_data[:parent_table]
                table_name_real = table_name
              elsif table_data[:datarow]
                table_name_real = datarow_from_datarow_argument(table_data[:datarow]).classname
              else
                table_name_real = @args[:module].const_get(table_name).classname
              end

              next unless table_name.to_s == val[:table].to_s
              do_joins[table_name] = true

              if val[:sql]
                orders << val[:sql]
              elsif val[:col]
                orders << "`#{db.escape_table(table_name_real)}`.`#{db.escape_column(val[:col])}`#{ordermode}"
              else
                raise "Couldnt figure out how to order based on keys: '#{val.keys.sort}'."
              end

              found = true
              break
            end
          end
        else
          raise "Unknown object: #{val.class.name}"
        end

        found = true if args[:cols].key?(orderstr)

        raise "Column not found for ordering: #{orderstr}." unless found

        orders << "#{table_def}`#{db.escape_column(orderstr)}`#{ordermode}" if orderstr
      end

      sql_order << orders.join(", ")
      list_args.delete("orderby")
    else
      raise "Unknown orderby object: #{list_args["orderby"].class.name}."
    end
  end

  list_args.each do |realkey, val|
    found = false

    if realkey.is_a?(Array)
      if !args[:joins_skip]
        datarow_obj = datarow_obj_from_args(args_def, list_args, realkey[0])
        args = datarow_obj.columns_sqlhelper_args
        raise "Couldnt get arguments from SQLHelper." unless args
      else
        datarow_obj = @args[:module].const_get(realkey[0])
        args = args_def
      end

      table_sym = realkey[0].to_sym
      do_joins[table_sym] = true
      list_table_name_real = table_sym
      table = "`#{db.escape_table(list_table_name_real)}`."
      key = realkey[1]
    else
      table = table_def
      args = args_def
      key = realkey
    end

    if args.key?(:cols_bools) && !args[:cols_bools].index(key).nil?
      val_s = val.to_s

      if val_s == "1" || val_s == "true"
        realval = "1"
      elsif val_s == "0" || val_s == "false"
        realval = "0"
      else
        raise "Could not make real value out of class: #{val.class.name} => #{val}."
      end

      sql_where << " AND #{table}`#{db.escape_column(key)}` = '#{db.esc(realval)}'"
      found = true
    elsif args[:cols].key?(key.to_s)
      if val.is_a?(Array)
        if val.empty? && db.opts[:type].to_s == "mysql"
          sql_where << " AND false"
        else
          escape_sql = val.map { |v| "'#{db.escape(v)}'" }.join(",")
          sql_where << " AND #{table}`#{db.escape_column(key)}` IN (#{escape_sql})"
        end
      elsif val.is_a?(Hash) && val[:type].to_sym == :col
        raise "No table was given for join: '#{val}', key: '#{key}' on table #{table}." unless val.key?(:table)
        do_joins[val[:table].to_sym] = true
        sql_where << " AND #{table}`#{db.escape_column(key)}` = `#{db.escape_table(val[:table])}`.`#{db.escape_column(val[:name])}`"
      elsif val.is_a?(Hash) && val[:type] == :sqlval && val[:val] == :null
        sql_where << " AND #{table}`#{db.escape_column(key)}` IS NULL"
      elsif val.is_a?(Proc)
        call_args = OpenStruct.new(ob: self, db: db)
        sql_where << " AND #{table}`#{db.escape_column(key)}` = '#{db.esc(val.call(call_args))}'"
      else
        sql_where << " AND #{table}`#{db.escape_column(key)}` = '#{db.esc(val)}'"
      end

      found = true
    elsif key.to_s == "limit_from"
      limit_from = val.to_i
      found = true
    elsif key.to_s == "limit_to"
      limit_to = val.to_i
      found = true
    elsif key.to_s == "limit"
      limit_from = 0
      limit_to = val.to_i
      found = true
    elsif args.key?(:cols_dbrows) && !args[:cols_dbrows].index("#{key}_id").nil?
      if val == false
        sql_where << " AND #{table}`#{db.escape_column(key.to_s + "_id")}` = '0'"
      elsif val.is_a?(Array)
        if val.empty?
          sql_where << " AND false"
        else
          sql_where << " AND #{table}`#{db.escape_column("#{key}_id")}` IN (#{Knj::ArrayExt.join(arr: val, sep: ",", surr: "'", callback: proc { |obj| obj.id.sql })})"
        end
      else
        sql_where << " AND #{table}`#{db.escape_column(key.to_s + "_id")}` = '#{db.esc(val.id)}'"
      end

      found = true
    elsif match = key.match(/^([A-z_\d]+)_(search|has)$/) && !args[:cols].key?(match[1]).nil?
      if match[2] == "search"
        Knj::Strings.searchstring(val).each do |str|
          sql_where << " AND #{table}`#{db.escape_column(match[1])}` LIKE '%#{db.esc(str)}%'"
        end
      elsif match[2] == "has"
        if val
          sql_where << " AND #{table}`#{db.escape_column(match[1])}` != ''"
        else
          sql_where << " AND #{table}`#{db.escape_column(match[1])}` = ''"
        end
      end

      found = true
    elsif match = key.match(/^([A-z_\d]+)_(not|lower)$/) && args[:cols].key?(match[1])
      if match[2] == "not"
        if val.is_a?(Array)
          if val.empty?
            # ignore.
          else
            escape_sql = Knj::ArrayExt.join(
              arr: val,
              callback: proc do|value|
                db.escape(value)
              end,
              sep: ",",
              surr: "'"
            )
            sql_where << " AND #{table}`#{db.escape_column(match[1])}` NOT IN (#{escape_sql})"
          end
        else
          sql_where << " AND #{table}`#{db.escape_column(match[1])}` != '#{db.esc(val)}'"
        end
      elsif match[2] == "lower"
        sql_where << " AND LOWER(#{table}`#{db.escape_column(match[1])}`) = LOWER('#{db.esc(val)}')"
      else
        raise "Unknown mode: '#{match[2]}'."
      end

      found = true
    elsif args.key?(:cols_date) && (match = key.match(/^(.+)_(day|week|month|year|from|to|below|above)(|_(not))$/)) && !args[:cols_date].index(match[1]).nil?
      not_v = match[4]
      val = Datet.in(val) if val.is_a?(Time)

      if match[2] == "day"
        if val.is_a?(Array)
          sql_where << " AND ("
          first = true

          val.each do |dayval|
            if first
              first = false
            else
              sql_where << " OR "
            end

            sql_where << "#{db.sqlspecs.strftime("%d %m %Y", "#{table}`#{db.escape_column(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%d %m %Y", "'#{db.esc(dayval.dbstr)}'")}"
          end

          sql_where << ")"
        else
          sql_where << " AND #{db.sqlspecs.strftime("%d %m %Y", "#{table}`#{db.escape_column(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%d %m %Y", "'#{db.esc(val.dbstr)}'")}"
        end
      elsif match[2] == "week"
        sql_where << " AND #{db.sqlspecs.strftime("%W %Y", "#{table}`#{db.escape_column(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%W %Y", "'#{db.esc(val.dbstr)}'")}"
      elsif match[2] == "month"
        sql_where << " AND #{db.sqlspecs.strftime("%m %Y", "#{table}`#{db.escape_column(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%m %Y", "'#{db.esc(val.dbstr)}'")}"
      elsif match[2] == "year"
        sql_where << " AND #{db.sqlspecs.strftime("%Y", "#{table}`#{db.escape_column(match[1])}`")} #{self.not(not_v, "!")}= #{db.sqlspecs.strftime("%Y", "'#{db.esc(val.dbstr)}'")}"
      elsif match[2] == "from" || match[2] == "above"
        sql_where << " AND #{table}`#{db.escape_column(match[1])}` >= '#{db.esc(val.dbstr)}'"
      elsif match[2] == "to" || match[2] == "below"
        sql_where << " AND #{table}`#{db.escape_column(match[1])}` <= '#{db.esc(val.dbstr)}'"
      else
        raise "Unknown date-key: #{match[2]}."
      end

      found = true
    elsif args.key?(:cols_num) && match = key.match(/^(.+)_(from|to|above|below|numeric)$/) && !args[:cols_num].index(match[1]).nil?
      if match[2] == "from"
        sql_where << " AND #{table}`#{db.escape_column(match[1])}` >= '#{db.esc(val)}'"
      elsif match[2] == "to"
        sql_where << " AND #{table}`#{db.escape_column(match[1])}` <= '#{db.esc(val)}'"
      elsif match[2] == "above"
        sql_where << " AND #{table}`#{db.escape_column(match[1])}` > '#{db.esc(val)}'"
      elsif match[2] == "below"
        sql_where << " AND #{table}`#{db.escape_column(match[1])}` < '#{db.esc(val)}'"
      else
        raise "Unknown method of treating cols-num-argument: #{match[2]}."
      end

      found = true
    elsif match = key.match(/^(.+)_lookup$/) && args[:cols].key?("#{match[1]}_id") && args[:cols].key?("#{match[1]}_class")
      sql_where << " AND #{table}`#{db.escape_column("#{match[1]}_class")}` = '#{db.esc(val.table)}'"
      sql_where << " AND #{table}`#{db.escape_column("#{match[1]}_id")}` = '#{db.esc(val.id)}'"
      found = true
    elsif realkey == "groupby"
      found = true

      if val.is_a?(Array)
        val.each do |col_name|
          raise "Column '#{val}' not found on table '#{table}'." unless args[:cols].key?(col_name)
          sql_groupby << ", " if sql_groupby.length > 0
          sql_groupby << "#{table}`#{db.escape_column(col_name)}`"
        end
      elsif val.is_a?(String)
        sql_groupby << ", " if sql_groupby.length > 0
        sql_groupby << "#{table}`#{db.escape_column(val)}`"
      else
        raise "Unknown class given for 'groupby': '#{val.class.name}'."
      end
    end

    list_args.delete(realkey) if found
  end

  args = args_def

  unless args[:joins_skip]
    raise "No joins defined on '#{args[:table]}' for: '#{args[:table]}'." if !do_joins.empty? && !args[:joined_tables]

    do_joins.each do |table_name, _temp_val|
      raise "No join defined on table '#{args[:table]}' for table '#{table_name}'." unless args[:joined_tables].key?(table_name)
      table_data = args[:joined_tables][table_name]

      if table_data.key?(:parent_table)
        join_table_name_real = table_name
        sql_joins << " LEFT JOIN `#{table_data[:parent_table]}` AS `#{table_name}` ON 1=1"
      else
        const = @args[:module].const_get(table_name)
        join_table_name_real = const.classname
        sql_joins << " LEFT JOIN `#{const.table}` AS `#{const.classname}` ON 1=1"
      end

      if table_data[:ob]
        ob = table_data[:ob]
      else
        ob = self
      end

      class_name = args[:table].to_sym

      if table_data[:datarow]
        datarow = datarow_from_datarow_argument(table_data[:datarow])
      else
        requireclass(class_name) if @objects.key?(class_name)
        datarow = @args[:module].const_get(class_name)
      end

      unless datarow.columns_sqlhelper_args
        ob.requireclass(datarow.table.to_sym)
        raise "No SQL-helper-args on class '#{datarow.table}' ???" unless datarow.columns_sqlhelper_args
      end

      newargs = datarow.columns_sqlhelper_args.clone
      newargs[:table] = join_table_name_real
      newargs[:joins_skip] = true

      # Clone the where-arguments and run them against another sqlhelper to sub-join.
      join_args = table_data[:where].clone
      ret = sqlhelper(join_args, newargs)
      sql_joins << ret[:sql_where]

      # If any of the join-arguments are left, then we should throw an error.
      join_args.each_key do |key|
        raise "Invalid key '#{key}' when trying to join table '#{table_name}' on table '#{args_def[:table]}'."
      end
    end
  end

  # If limit arguments has been given then add them.
  sql_limit = " LIMIT #{limit_from}, #{limit_to}" if limit_from && limit_to

  sql_groupby = nil if sql_groupby.empty?

  {
    sql_joins: sql_joins,
    sql_where: sql_where,
    sql_limit: sql_limit,
    sql_order: sql_order,
    sql_groupby: sql_groupby
  }
end

#static(class_name, method_name, *args, &block) ⇒ Object

Calls a static method on a class. Passes the d-variable which contains the Objects-object, database-reference and more…



706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
# File 'lib/baza/model_handler.rb', line 706

def static(class_name, method_name, *args, &block)
  class_name = class_name
  method_name = method_name

  requireclass(class_name)
  class_obj = @args[:module].const_get(class_name)

  # Sometimes this raises the exception but actually responds to the class? Therefore commented out. - knj
  # raise "The class '#{class_obj.name}' has no such method: '#{method_name}' (#{class_obj.methods.sort.join(", ")})." if !class_obj.respond_to?(method_name)

  pass_args = [OpenStruct.new(ob: self, db: db)]

  args.each do |arg|
    pass_args << arg
  end

  class_obj.send(method_name, *pass_args, &block)
end

#unconnect(args) ⇒ Object

Unconnects a connect by ‘object’ and ‘conn_id’.

Raises:

  • (ArgumentError)


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/baza/model_handler.rb', line 154

def unconnect(args)
  raise ArgumentError, "No object given." unless args["object"]
  object = args["object"].to_sym
  raise ArgumentError, "Object doesnt exist: '#{object}'." unless @callbacks.key?(object)

  if args["conn_id"]
    conn_ids = [args["conn_id"]]
  elsif args["conn_ids"]
    conn_ids = args["conn_ids"]
  else
    raise ArgumentError, "Could not figure out connection IDs."
  end

  conn_ids.each do |conn_id|
    raise Errno::ENOENT, "Conn ID doest exist: '#{conn_id}' (#{args})." unless @callbacks[object].key?(conn_id)
    @callbacks[object].delete(conn_id)
  end
end

#uninit_class(classname) ⇒ Object



93
94
95
96
# File 'lib/baza/model_handler.rb', line 93

def uninit_class(classname)
  @objects.delete(classname)
  @locks.delete(classname)
end

#unset(object) ⇒ Object

Unset object. Do this if you are sure, that there are no more references left. This will be done automatically when deleting it.



726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
# File 'lib/baza/model_handler.rb', line 726

def unset(object)
  if object.is_a?(Array)
    object.each do |obj|
      unset(obj)
    end
    return nil
  end

  classname = object.class.name

  classname = classname.gsub(@args[:module].name + "::", "") if @args[:module]

  classname = classname.to_sym
  @objects[classname].delete(object.id.to_i)
end

#unset_class(classname) ⇒ Object



742
743
744
745
746
747
748
749
750
751
752
753
754
755
# File 'lib/baza/model_handler.rb', line 742

def unset_class(classname)
  if classname.is_a?(Array)
    classname.each do |classn|
      unset_class(classn)
    end

    return false
  end

  classname = classname.to_sym

  return false unless @objects.key?(classname)
  @objects.delete(classname)
end