class RnDB::Table

Attributes

id[R]

Public Class Methods

new(id) click to toggle source

Create a new record wit the given ID.

# File lib/rndb/table.rb, line 10
def initialize(id)
  _validate!
  @id = id
end

Private Class Methods

[](index) click to toggle source

Return a new record corresponding to the specified index.

# File lib/rndb/table.rb, line 79
def [](index)
  _validate!
  new(index) if index < count
end
_db() click to toggle source
# File lib/rndb/table.rb, line 224
def _db
  Thread.current[:rndb_database]
end
_get_state(id, attribute) click to toggle source
# File lib/rndb/table.rb, line 310
def _get_state(id, attribute)
  _schema[:state][id] && _schema[:state][id][attribute]
end
_migrate(size) click to toggle source
# File lib/rndb/table.rb, line 272
def _migrate(size)
  raise "table already migrated" unless _schema[:class].nil?
  ids = Thicket.new(0...size)
  _schema[:columns].each_value do |column|
    distribution = column[:distribution]
    next if distribution.nil?
    if distribution.is_a?(Array)
      distribution.each do |context|
        thicket = ids & _query(context[:where], size)
        _migrate_column(column, thicket, context[:stats])
      end
    else
      raise "distribution must sum to unity" unless distribution.values.sum == 1
      _migrate_column(column, ids, distribution)
    end
    ids =
      column[:mapping].values.reduce(Thicket.new) do |thicket, other|
        thicket | other
      end
  end
  _schema[:size] = size
  _schema[:class] = self
end
_migrate_column(column, ids, distribution) click to toggle source
# File lib/rndb/table.rb, line 262
def _migrate_column(column, ids, distribution)
  min = 0.0
  distribution.each do |value, probability|
    max = min + probability
    column[:mapping][value] ||= Thicket.new
    column[:mapping][value] |= ids * (min..max)
    min = max
  end
end
_query(constraints, size) click to toggle source
# File lib/rndb/table.rb, line 249
def _query(constraints, size)
  ids = Thicket.new(0...size)
  constraints.each do |attribute, values|
    column = _schema[:columns][attribute]
    raise "no mapping for column" if column[:mapping].empty?
    other = Array(values).reduce(Thicket.new) do |thicket, value|
      thicket | column[:mapping][value]
    end
    ids &= other
  end
  ids
end
_schema() click to toggle source
# File lib/rndb/table.rb, line 228
def _schema
  Thread.current[:rndb_tables] ||= Hash.new do |tables, name|
    tables[name] = {
      class: nil,
      size: 0,
      columns: Hash.new do |columns, key|
        columns[key] = {
          distribution: nil,
          mapping: {},
          generator: nil
        }
      end,
      associations: Hash.new do |associations, key|
        associations[key] = nil
      end,
      state: {}
    }
  end
  Thread.current[:rndb_tables][table_name]
end
_seed_prng(id, attribute) click to toggle source
# File lib/rndb/table.rb, line 296
def _seed_prng(id, attribute)
  tuple = [_db.seed, table_name, attribute, id].join('-')
  digest = Digest::SHA256.hexdigest(tuple)
  value = digest.to_i(16) % 18_446_744_073_709_551_616
  _db.prng.srand(value)
  Faker::Config.random = _db.prng
  value
end
_set_state(id, attribute, value) click to toggle source
# File lib/rndb/table.rb, line 314
def _set_state(id, attribute, value)
  _schema[:state][id] ||= {}
  _schema[:state][id][attribute] = value
end
_validate!() click to toggle source
# File lib/rndb/table.rb, line 305
def _validate!
  @valid ||= (self == _schema[:class])
  raise "table not added to database" unless @valid
end
all() click to toggle source

Return all records.

# File lib/rndb/table.rb, line 91
def all
  where
end
association(attribute, *args) click to toggle source

Add an association between two Table models.

# File lib/rndb/table.rb, line 150
def association(attribute, *args)
  args.each do |arg|
    _schema[:associations][attribute] = arg
  end
  define_method("#{attribute}_id".to_sym) do
    _generate_association_id(attribute)
  end
  define_method(attribute) do
    _generate_association(attribute)
  end
end
column(attribute, *args) click to toggle source

Add a new column to the Table model.

# File lib/rndb/table.rb, line 121
def column(attribute, *args)
  column = _schema[:columns][attribute]
  args.each do |arg|
    index =
      case arg
      when Hash, Array
        :distribution
      when Proc
        :generator
      else
        raise "unsupported column parameter"
      end
    column[index] = arg
  end
  if column[:generator] && column[:distribution]
    define_method("#{attribute}_key") do
      _generate_column_key(attribute)
    end
  end
  define_method(attribute) do
    _generate_column(attribute)
  end
  define_method("#{attribute}=".to_sym) do |value|
    @_attributes&.delete(attribute)
    self.class.send(:_set_state, id, attribute, value)
  end
end
count() click to toggle source

Count all records, delegating this to the all Query.

# File lib/rndb/table.rb, line 96
def count
  all.count
end
each(&block) click to toggle source

Iterate over all records, delegating this to the all Query

# File lib/rndb/table.rb, line 106
def each(&block)
  all.each(&block)
end
get(attribute) click to toggle source
# File lib/rndb/table.rb, line 211
def get(attribute)
  raise unless @current
  if _schema[:columns].key?(attribute)
    value(@current, attribute)
  elsif _schema[:associations].key?(attribute)
    join(@current, attribute)
  else
    raise "no such attribute"
  end
end
join(id, name) click to toggle source

Return the instance joined to the current ID.

# File lib/rndb/table.rb, line 202
def join(id, name)
  @current = id
  _schema[:associations][name].each do |context|
    next unless (index = where(context[:where]).index(id))
    return where(context[:joins])[index]
  end
  nil
end
key(id, attribute) click to toggle source

Retrieve the key that can be queried on for generated attributes.

# File lib/rndb/table.rb, line 170
def key(id, attribute)
  @current = id
  _validate!
  column = _schema[:columns][attribute]
  return if column[:distribution].nil?
  column[:mapping].find do |_, ids|
    ids.include?(id)
  end&.first
end
last() click to toggle source

Return the last record, to be consistent with first, which we get by magic.

# File lib/rndb/table.rb, line 101
def last
  all.last
end
pluck(*args) click to toggle source

Pluck specified attributes from all records, delegating this to the all query.

# File lib/rndb/table.rb, line 111
def pluck(*args)
  all.pluck(args)
end
rand(*args) click to toggle source

Generate a random number, intended to be used in lambdas. The number will have been seeded appropriately to ensure determinism.

# File lib/rndb/table.rb, line 164
def rand(*args)
  _validate!
  _db.prng.rand(*args)
end
sample(limit=1) click to toggle source

Return a Querty that contains a random sampling of records.

# File lib/rndb/table.rb, line 116
def sample(limit=1)
  all.sample(limit)
end
table_name() click to toggle source

Return the name of the table, which is derived from the class name.

# File lib/rndb/table.rb, line 74
def table_name
  name.downcase.to_sym
end
value(id, attribute) click to toggle source

Retrieve the value of the given attribute for the given ID.

# File lib/rndb/table.rb, line 181
def value(id, attribute)
  @current = id
  _validate!
  return id if attribute == :id
  override = _get_state(id, attribute)
  return override unless override.nil?
  column = _schema[:columns][attribute]
  value = key(id, attribute)
  unless column[:generator].nil?
    _seed_prng(id, attribute)
    value =
      if column[:distribution].nil?
        column[:generator].call
      else
        column[:generator].call(value)
      end
  end
  value
end
where(constraints={}) click to toggle source

Return a Query that matches the supplied constraints

# File lib/rndb/table.rb, line 85
def where(constraints={})
  _validate!
  Query.new(self, _query(constraints, _schema[:size]))
end

Public Instance Methods

attributes() click to toggle source

Generate all attributes, which may be expensive.

# File lib/rndb/table.rb, line 16
def attributes
  _generate_all
end
to_h() click to toggle source

Return the attributes as a hash.

# File lib/rndb/table.rb, line 21
def to_h
  attributes
end
to_s() click to toggle source

Return a stringified version of the attributes hash.

# File lib/rndb/table.rb, line 26
def to_s
  to_h.to_s
end

Private Instance Methods

_generate_all() click to toggle source
# File lib/rndb/table.rb, line 32
def _generate_all
  _schema[:columns].each do |name, column|
    _generate_column_key(name) if column[:generator] && column[:distribution]
    _generate_column(name)
  end
  _schema[:associations].each_key do |name|
    _generate_association_id(name)
  end
  @_attributes
end
_generate_association(name) click to toggle source
# File lib/rndb/table.rb, line 58
def _generate_association(name)
  self.class.join(@id, name)
end
_generate_association_id(name) click to toggle source
# File lib/rndb/table.rb, line 53
def _generate_association_id(name)
  @_attributes ||= { id: @id }
  @_attributes["#{name}_id".to_sym] ||= _generate_association(name)&.id
end
_generate_column(name) click to toggle source
# File lib/rndb/table.rb, line 48
def _generate_column(name)
  @_attributes ||= { id: @id }
  @_attributes[name] ||= self.class.value(@id, name)
end
_generate_column_key(name) click to toggle source
# File lib/rndb/table.rb, line 43
def _generate_column_key(name)
  @_attributes ||= { id: @id }
  @_attributes["#{name}_key".to_sym] ||= self.class.key(@id, name)
end
_schema() click to toggle source
# File lib/rndb/table.rb, line 66
def _schema
  self.class.send(:_schema)
end
_validate!() click to toggle source
# File lib/rndb/table.rb, line 62
def _validate!
  self.class.send(:_validate!)
end