class WAB::Impl::Model

The Model class is used to store data when using the WAB::Impl::Shell. It is not intended for any other use. The get and query methods are the primary means of interacting with the model.

The Model is simple in that it stores data in a Hash references by ref numbers. Data is stores in a directory as separate JSON files that are named as the ref number as a 16 character hexidecimal.

Public Class Methods

new(dir, indent=0) click to toggle source

Create a new Model using the designated directory as the store.

dir

directory to store data in

# File lib/wab/impl/model.rb, line 20
def initialize(dir, indent=0)
  @dir = dir.nil? ? nil : File.expand_path(dir)
  @cnt = 0
  @indent = indent
  @map = {}
  @lock = Thread::Mutex.new
  FileUtils.mkdir_p(@dir) unless @dir.nil? || Dir.exist?(@dir)
  load_files unless @dir.nil?
end

Public Instance Methods

get(ref) click to toggle source

Get a single record in the database. A WAB::Impl::Data object is returned if not nil.

ref

references number of the object to retrieve.

# File lib/wab/impl/model.rb, line 34
def get(ref)
  @map[ref]
end
query(tql) click to toggle source

Execute a TQL query.

_Note that the current implementation does not support nested data retrieval.

tql

query to execute

# File lib/wab/impl/model.rb, line 44
def query(tql)
  rid = tql[:rid]
  where = nil
  filter = nil
  if tql.has_key?(:where)
    w = tql[:where]
    where = w.is_a?(Array) ? ExprParser.parse(w) : w
  end
  filter = ExprParser.parse(tql[:filter]) if tql.has_key?(:filter)

  if tql.has_key?(:insert)
    insert(tql[:insert], rid, where, filter)
  elsif tql.has_key?(:update)
    update(tql[:update], rid, where, filter)
  elsif tql.has_key?(:delete)
    delete(tql[:delete], rid, where, filter)
  else
    select(tql[:select], rid, where, filter)
  end
end

Private Instance Methods

delete(_del_opt, _rid, where, filter) click to toggle source
# File lib/wab/impl/model.rb, line 139
def delete(_del_opt, _rid, where, filter)
  deleted = []
  @lock.synchronize {
    if where.is_a?(Expr)
      @map.each { |ref,obj|
        if where.eval(obj) && (filter.nil? || filter.eval(obj))
          deleted << ref
          @map.delete(ref)
        end
      }
    # A reference.
    elsif !@map.delete(where).nil?
      deleted << where
      remove_file(where)
    end
  }
  { code: 0, deleted: deleted }
end
extract_matches(format, ref, rid, obj) click to toggle source
# File lib/wab/impl/model.rb, line 89
def extract_matches(format, ref, rid, obj)
  if format.nil?
    { id: ref, data: obj.native }
  else
    format_obj(format, ref, rid, obj)
  end
end
format_obj(format, ref, rid, obj) click to toggle source
# File lib/wab/impl/model.rb, line 158
def format_obj(format, ref, rid, obj)
  case format
  when Hash
    native = {}
    format.each { |k,v| native[k] = format_obj(v, ref, rid, obj) }
    native
  when Array
    format.map { |v| format_obj(v, ref, rid, obj) }
  when String
    if '$ref' == format
      ref
    elsif '$rid' == format
      rid
    elsif '$' == format || '$root' == format
      obj.native
    elsif !format.empty? && format.start_with?("'")
      format[1..-1]
    else
      obj.get(format)
    end
  else
    format
  end
end
insert(obj, rid, where, filter) click to toggle source
# File lib/wab/impl/model.rb, line 67
def insert(obj, rid, where, filter)
  ref = nil
  @lock.synchronize {
    unless where.nil?
      @map.each_value { |v|
        if where.eval(v) && (filter.nil? || filter.eval(v))
          result = { code: -1, error: 'Already exists.' }
          result[:rid] = rid unless rid.nil?
          return result
        end
      }
    end
    @cnt += 1
    ref = @cnt
    @map[ref] = Data.new(obj, true)
    write_to_file(ref, obj)
  }
  result = { code: 0, ref: ref }
  result[:rid] = rid unless rid.nil?
  result
end
load_files() click to toggle source
# File lib/wab/impl/model.rb, line 183
def load_files()
  Dir.foreach(@dir) { |fn|
    next if '.' == fn[0]
    ref = fn[0..-6]
    iref = ref.to_i(16)
    @cnt = iref if @cnt < iref
    @map[iref] = Data.new(Oj.load_file(File.join(@dir, fn), mode: :wab), true)
  }
end
remove_file(ref) click to toggle source
# File lib/wab/impl/model.rb, line 199
def remove_file(ref)
  File.delete(File.join(@dir, "%016x.json" % ref)) unless @dir.nil?
end
select(format, rid, where, filter) click to toggle source
# File lib/wab/impl/model.rb, line 97
def select(format, rid, where, filter)
  matches = []
  @lock.synchronize {
    if where.nil? && filter.nil?
      @map.each { |ref,obj|
        matches << extract_matches(format, ref, rid, obj)
      }
    else
      @map.each { |ref,obj|
        if where.eval(obj) && (filter.nil? || filter.eval(obj))
          matches << extract_matches(format, ref, rid, obj)
        end
      }
    end
  }
  result = { code: 0, results: matches }
  result[:rid] = rid unless rid.nil?
  result
end
update(obj, _rid, where, filter) click to toggle source
# File lib/wab/impl/model.rb, line 117
def update(obj, _rid, where, filter)
  updated = []
  @lock.synchronize {
    if where.is_a?(Expr)
      # TBD must be able to update portions of an object
      @map.each_pair { |ref, v|
        if where.eval(v) && (filter.nil? || filter.eval(v))
          @map[ref] = Data.new(obj, true)
          updated << ref
          write_to_file(ref, obj)
        end
      }
    else
      # A reference.
      @map[where] = Data.new(obj, true)
      updated << where
      write_to_file(where, obj)
    end
  }
  { code: 0, updated: updated }
end
write_to_file(ref, obj) click to toggle source
# File lib/wab/impl/model.rb, line 193
def write_to_file(ref, obj)
  return if @dir.nil?
  obj.native if obj.is_a?(WAB::Data)
  File.open(File.join(@dir, "%016x.json" % ref), 'wb') { |f| f.write(Oj.dump(obj, mode: :wab, indent: @indent)) }
end