Class: Kwipper::Model

Inherits:
Object
  • Object
show all
Defined in:
lib/kwipper/model.rb

Direct Known Subclasses

Comment, Post, PostFavorite, Session, User

Constant Summary collapse

DB_NAME =
'kwipper'
DB_FILE_NAME =
"#{DB_NAME}.db"
ID_COLUMN =
'id'
UnknownAttribute =
Class.new ArgumentError

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attrs = {}) ⇒ Model

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



82
83
84
85
86
87
88
89
90
91
# 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

Class Attribute Details

.columnsObject (readonly)

Returns the value of attribute columns.



22
23
24
# File 'lib/kwipper/model.rb', line 22

def columns
  @columns
end

Instance Attribute Details

#idObject

Returns the value of attribute id.



79
80
81
# File 'lib/kwipper/model.rb', line 79

def id
  @id
end

Class Method Details

.all(statement = "SELECT * FROM #{table_name}") ⇒ Object

Get records from a single table and instantiate them



33
34
35
36
37
# 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

.attr_array_to_hash(attrs) ⇒ Object



148
149
150
151
152
# 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

.column(name, type) ⇒ Object

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.



17
18
19
20
21
# 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 #{table_name}") ⇒ Object



74
75
76
# File 'lib/kwipper/model.rb', line 74

def count(statement = "SELECT COUNT(id) FROM #{table_name}")
  sql(statement).first.first
end

.create(attrs) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
# 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

.dbObject



10
11
12
# File 'lib/kwipper/model.rb', line 10

def db
  @db ||= SQLite3::Database.open File.join(Kwipper::ROOT, 'db', DB_FILE_NAME)
end

.destroy(id) ⇒ Object



64
65
66
# File 'lib/kwipper/model.rb', line 64

def destroy(id)
  sql "DELETE FROM #{table_name} WHERE id=#{id}"
end

.exists?(id) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
71
72
# 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) ⇒ Object



39
40
41
# File 'lib/kwipper/model.rb', line 39

def find(id)
  where(id: id).first
end

.generate_idObject



142
143
144
145
146
# 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) ⇒ Object

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.



157
158
159
160
161
162
# 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) ⇒ Object

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



165
166
167
168
169
170
171
# 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

.sql(cmd) ⇒ Object

All SQL statements should be executed through this method



25
26
27
28
29
30
# 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

.table_nameObject



138
139
140
# File 'lib/kwipper/model.rb', line 138

def table_name
  Inflect.new(name).demodulize.pluralize.underscore
end

.update(id, attrs) ⇒ Object



60
61
62
# 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) ⇒ Object



43
44
45
# File 'lib/kwipper/model.rb', line 43

def where(attrs)
  all "SELECT * FROM #{table_name} WHERE #{hash_to_key_vals attrs}"
end

Instance Method Details

#destroy(id) ⇒ Object



115
116
117
# File 'lib/kwipper/model.rb', line 115

def destroy(id)
  self.class.destroy id
end

#saveObject

Saves model instance to the database



94
95
96
97
98
99
100
101
102
103
104
105
106
# 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) ⇒ Object



119
120
121
# File 'lib/kwipper/model.rb', line 119

def sql(statement)
  self.class.sql statement
end

#update(attrs) ⇒ Object



108
109
110
111
112
113
# File 'lib/kwipper/model.rb', line 108

def update(attrs)
  self.class.update id, attrs
  true
rescue KeyError => e
  false
end