feat(orm): add model base with persistence and finders
This commit is contained in:
1 parent
b131acd7fe
commit
b301c51b3c
1 file changed
+204
@@ -0,0 +1,204 @@
|
||||
module Prism
|
||||
module Orm
|
||||
class RecordInvalid < StandardError; end
|
||||
class RecordNotFound < StandardError; end
|
||||
|
||||
class Model
|
||||
include Validatable
|
||||
include Associable
|
||||
|
||||
class << self
|
||||
def store
|
||||
@store || Orm.store
|
||||
end
|
||||
|
||||
def store=(instance)
|
||||
@store = instance
|
||||
end
|
||||
|
||||
def table_name(name = nil)
|
||||
@table_name = name.to_s if name
|
||||
@table_name || Inflector.tableize(self.name)
|
||||
end
|
||||
|
||||
def columns
|
||||
@columns ||= store.columns(table_name)
|
||||
end
|
||||
|
||||
def reset_schema!
|
||||
@columns = nil
|
||||
@schema_loaded = false
|
||||
end
|
||||
|
||||
def load_schema!
|
||||
return if @schema_loaded
|
||||
columns.each do |column|
|
||||
next if method_defined?(column) && method_defined?("#{column}=")
|
||||
define_method(column) { self[column] }
|
||||
define_method("#{column}=") { |value| self[column] = value }
|
||||
end
|
||||
@schema_loaded = true
|
||||
end
|
||||
|
||||
def instantiate(row)
|
||||
load_schema!
|
||||
allocate.tap { |record| record.send(:load_row, row) }
|
||||
end
|
||||
|
||||
def create(attributes = {})
|
||||
new(attributes).tap(&:save)
|
||||
end
|
||||
|
||||
def create!(attributes = {})
|
||||
record = new(attributes)
|
||||
record.save!
|
||||
record
|
||||
end
|
||||
|
||||
def find(id)
|
||||
row = store.find(table_name, id)
|
||||
raise RecordNotFound, "no #{name} with id #{id}" unless row
|
||||
instantiate(row)
|
||||
end
|
||||
|
||||
def all
|
||||
Relation.new(self)
|
||||
end
|
||||
|
||||
def where(conditions)
|
||||
all.where(conditions)
|
||||
end
|
||||
|
||||
def order(column, direction = :asc)
|
||||
all.order(column, direction)
|
||||
end
|
||||
|
||||
def limit(count)
|
||||
all.limit(count)
|
||||
end
|
||||
|
||||
def find_by(conditions)
|
||||
all.find_by(conditions)
|
||||
end
|
||||
|
||||
def first
|
||||
all.first
|
||||
end
|
||||
|
||||
def last
|
||||
all.last
|
||||
end
|
||||
|
||||
def count
|
||||
store.count(table_name)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(attributes = {})
|
||||
self.class.load_schema!
|
||||
@attributes = {}
|
||||
@new_record = true
|
||||
@destroyed = false
|
||||
assign(attributes)
|
||||
end
|
||||
|
||||
def [](key)
|
||||
@attributes[key.to_s]
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
@attributes[key.to_s] = value
|
||||
end
|
||||
|
||||
def id
|
||||
self['id']
|
||||
end
|
||||
|
||||
def new_record?
|
||||
@new_record
|
||||
end
|
||||
|
||||
def persisted?
|
||||
!@new_record && !@destroyed
|
||||
end
|
||||
|
||||
def destroyed?
|
||||
@destroyed
|
||||
end
|
||||
|
||||
def assign(attributes)
|
||||
attributes.each { |key, value| self[key] = value }
|
||||
self
|
||||
end
|
||||
|
||||
def save
|
||||
return false unless valid?
|
||||
touch_timestamps
|
||||
if new_record?
|
||||
@attributes['id'] = self.class.store.insert(self.class.table_name, @attributes)
|
||||
@new_record = false
|
||||
else
|
||||
self.class.store.update(self.class.table_name, id, @attributes)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def save!
|
||||
raise RecordInvalid, errors.full_messages.join(', ') unless save
|
||||
true
|
||||
end
|
||||
|
||||
def update(attributes)
|
||||
assign(attributes)
|
||||
save
|
||||
end
|
||||
|
||||
def update!(attributes)
|
||||
assign(attributes)
|
||||
save!
|
||||
end
|
||||
|
||||
def destroy
|
||||
self.class.store.delete(self.class.table_name, id) if persisted?
|
||||
@destroyed = true
|
||||
self
|
||||
end
|
||||
|
||||
def reload
|
||||
row = self.class.store.find(self.class.table_name, id)
|
||||
raise RecordNotFound, "no #{self.class.name} with id #{id}" unless row
|
||||
load_row(row)
|
||||
self
|
||||
end
|
||||
|
||||
def to_h
|
||||
@attributes.dup
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.is_a?(self.class) && !id.nil? && id == other.id
|
||||
end
|
||||
alias_method :eql?, :==
|
||||
|
||||
def hash
|
||||
[self.class, id].hash
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_row(row)
|
||||
@attributes = row.dup
|
||||
@new_record = false
|
||||
@destroyed = false
|
||||
self
|
||||
end
|
||||
|
||||
def touch_timestamps
|
||||
now = Time.now.utc
|
||||
columns = self.class.columns
|
||||
@attributes['created_at'] ||= now if columns.include?('created_at') && new_record?
|
||||
@attributes['updated_at'] = now if columns.include?('updated_at')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user