class Kwipper::Model

Constants

DB_FILE_NAME
DB_NAME
ID_COLUMN
UnknownAttribute

Attributes

columns[R]
id[RW]

Public Class Methods

all(statement = "SELECT * FROM click to toggle source

Get records from a single table and instantiate them

# File lib/kwipper/model.rb, line 33
def all(statement = "SELECT * FROM #{table_name}")
  sql(statement).each_with_object [] do |attrs, models|
    models << new(attr_array_to_hash attrs)
  end
end
column(name, type) click to toggle source

Declare columns in the model subclass in the same order the columns were created in the table. This lets us instantiate model objects from arrays of field values from the db. ID columns is defaulted.

# File lib/kwipper/model.rb, line 17
def column(name, type)
  @columns ||= { ID_COLUMN => :to_i }
  @columns[name] = type
  attr_accessor name
end
count(statement = "SELECT COUNT(id) FROM click to toggle source
# File lib/kwipper/model.rb, line 74
def count(statement = "SELECT COUNT(id) FROM #{table_name}")
  sql(statement).first.first
end
create(attrs) click to toggle source
# File lib/kwipper/model.rb, line 47
def create(attrs)
  db_attrs = attrs.map { |k, v| normalize_value_for_db v, columns[k] }

  unless attrs.key? 'id'
    id = generate_id
    attrs['id'] = id
    db_attrs = [id, *db_attrs]
  end

  sql "INSERT INTO #{table_name} VALUES(#{db_attrs.join ', '})"
  new attrs
end
db() click to toggle source
# File lib/kwipper/model.rb, line 10
def db
  @db ||= SQLite3::Database.open File.join(Kwipper::ROOT, 'db', DB_FILE_NAME)
end
destroy(id) click to toggle source
# File lib/kwipper/model.rb, line 64
def destroy(id)
  sql "DELETE FROM #{table_name} WHERE id=#{id}"
end
exists?(id) click to toggle source
# File lib/kwipper/model.rb, line 68
def exists?(id)
  id = normalize_value_for_db id, columns['id']
  result = sql "SELECT id FROM #{table_name} WHERE id = #{id} LIMIT 1"
  result.first && result.first.any?
end
find(id) click to toggle source
# File lib/kwipper/model.rb, line 39
def find(id)
  where(id: id).first
end
new(attrs = {}) click to toggle source

Takes a hash of model attributes and sets them via accessors if they exists

# File lib/kwipper/model.rb, line 82
def initialize(attrs = {})
  attrs.keys.each do |name|
    if self.class.columns.keys.include? name
      type = self.class.columns[name]
      send "#{name}=", attrs[name].send(type)
    else
      raise UnknownAttribute, "#{name} for #{self}"
    end
  end
end
sql(cmd) click to toggle source

All SQL statements should be executed through this method

# File lib/kwipper/model.rb, line 25
def sql(cmd)
  start_time = Time.now.to_f
  db.execute(cmd).tap do
    log.debug "#{cmd.red} in #{sprintf '%.8f', Time.now.to_f - start_time}s"
  end
end
update(id, attrs) click to toggle source
# File lib/kwipper/model.rb, line 60
def update(id, attrs)
  sql "UPDATE #{table_name} SET #{hash_to_key_vals attrs} WHERE id=#{id}"
end
where(attrs) click to toggle source
# File lib/kwipper/model.rb, line 43
def where(attrs)
  all "SELECT * FROM #{table_name} WHERE #{hash_to_key_vals attrs}"
end

Private Class Methods

attr_array_to_hash(attrs) click to toggle source
# File lib/kwipper/model.rb, line 148
def attr_array_to_hash(attrs)
  attrs.each_with_index.inject({}) do |hash, (attr_val, i)|
    hash.merge! @columns.keys[i] => attr_val
  end
end
generate_id() click to toggle source
# File lib/kwipper/model.rb, line 142
def generate_id
  max_id_plus_1 = "SELECT (id + 1) as id FROM #{table_name} ORDER BY id DESC LIMIT 1"
  result = sql(max_id_plus_1).first
  result && result.first ? result.first : 1
end
hash_to_key_vals(hash) click to toggle source

Turn a hash of attributes into a comma separated string that's safe to use in a SQL statement (non int values are quoted). TODO: add SQL sanitation.

# File lib/kwipper/model.rb, line 157
def hash_to_key_vals(hash)
  hash.inject [] do |a, (k, v)|
    v = normalize_value_for_db v, columns[k]
    a << "#{k}=#{v}"
  end.join ', '
end
normalize_value_for_db(value, type) click to toggle source

Non int values should be quoted when putting in a SQL statement

# File lib/kwipper/model.rb, line 165
def normalize_value_for_db(value, type)
  case type when :to_i
    value.to_i
  else
    "\"#{value}\""
  end
end
table_name() click to toggle source
# File lib/kwipper/model.rb, line 138
def table_name
  Inflect.new(name).demodulize.pluralize.underscore
end

Public Instance Methods

destroy(id) click to toggle source
# File lib/kwipper/model.rb, line 115
def destroy(id)
  self.class.destroy id
end
save() click to toggle source

Saves model instance to the database

# File lib/kwipper/model.rb, line 94
def save
  if id
    self.class.update id, attrs_for_db
  else
    self.class.create a = attrs_for_db
    @id ||= a['id']
  end

  true
rescue SQLite3::SQLException => e
  log.warn e.message
  false
end
sql(statement) click to toggle source
# File lib/kwipper/model.rb, line 119
def sql(statement)
  self.class.sql statement
end
update(attrs) click to toggle source
# File lib/kwipper/model.rb, line 108
def update(attrs)
  self.class.update id, attrs
  true
rescue KeyError => e
  false
end

Private Instance Methods

attrs_for_db() click to toggle source
# File lib/kwipper/model.rb, line 125
def attrs_for_db
  self.class.columns.each_with_object({}) do |(name, _), attrs|
    value = send name
    value = generate_id if name == ID_COLUMN && value.nil?
    attrs[name] = value unless value.nil?
  end
end
generate_id() click to toggle source
# File lib/kwipper/model.rb, line 133
def generate_id
  self.class.generate_id
end