Last active
December 15, 2015 12:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Cohort < Database::Model | |
self.table_name = 'cohorts' | |
attr_reader :attributes, :old_attributes | |
# e.g., Cohort.new(:id => 1, :name => 'Alpha', :created_at => '2012-12-01 05:54:30') | |
def [](attribute) | |
raise_error_if_invalid_attribute!(attribute) | |
@attributes[attribute] | |
end | |
def students | |
Student.where('cohort_id = ?', self[:id]) | |
end | |
def add_students(students) | |
students.each do |student| | |
student.cohort = self | |
end | |
students | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'sqlite3' | |
module Database | |
class InvalidAttributeError < StandardError;end | |
class NotConnectedError < StandardError;end | |
class Model | |
def initialize(attributes = {}) | |
attributes.symbolize_keys! | |
raise_error_if_invalid_attribute!(attributes.keys) | |
@attributes = {} | |
attributes_names = [] | |
#I couldn't get the inherited method to work so I just used PRAGMA in the initialization. | |
raw_attr_data = Database::Model.execute("PRAGMA table_info(#{self.class.table_name})") | |
attributes_syms_array = raw_attr_data.map {|column| column["name"].to_sym } | |
attributes_syms_array.each do |name| | |
@attributes[name] = attributes[name] | |
end | |
@old_attributes = @attributes.dup | |
p attributes_syms_array | |
end | |
def self.inherited(subclass) | |
p "yup" | |
p Database::Model.execute("PRAGMA table_info(#{self.table_name})") | |
end | |
def save | |
if new_record? | |
results = insert! | |
else | |
results = update! | |
end | |
# When we save, remove changes between new and old attributes | |
@old_attributes = @attributes.dup | |
results | |
end | |
def self.create(attributes) | |
record = self.new(attributes) | |
record.save | |
record | |
end | |
def self.where(query, *args) | |
Database::Model.execute("SELECT * FROM #{self.table_name} WHERE #{query}", *args).map do |row| | |
self.new(row) | |
end | |
end | |
def self.all | |
Database::Model.execute("SELECT * FROM #{self.table_name}").map do |row| | |
self.new(row) | |
# self.class.new(row) | |
end | |
end | |
def self.find(pk) | |
self.where('id = ?', pk).first | |
end | |
def new_record? | |
self[:id].nil? | |
end | |
def [](attribute) | |
raise_error_if_invalid_attribute!(attribute) | |
@attributes[attribute] | |
end | |
def []=(attribute, value) | |
raise_error_if_invalid_attribute!(attribute) | |
@attributes[attribute] = value | |
end | |
def self.inherited(klass) | |
end | |
def self.connection | |
@connection | |
end | |
def self.filename | |
@filename | |
end | |
def self.database=(filename) | |
@filename = filename.to_s | |
@connection = SQLite3::Database.new(@filename) | |
# Return the results as a Hash of field/value pairs | |
# instead of an Array of values | |
@connection.results_as_hash = true | |
# Automatically translate data from database into | |
# reasonably appropriate Ruby objects | |
@connection.type_translation = true | |
end | |
def self.table_name=(table_name) | |
@table_name = table_name | |
end | |
def self.table_name | |
@table_name | |
end | |
def self.attribute_names | |
@attribute_names | |
end | |
def self.attribute_names=(attribute_names) | |
@attribute_names = attribute_names | |
end | |
# Input looks like, e.g., | |
# execute("SELECT * FROM students WHERE id = ?", 1) | |
# Returns an Array of Hashes (key/value pairs) | |
def self.execute(query, *args) | |
raise NotConnectedError, "You are not connected to a database." unless connected? | |
prepared_args = args.map { |arg| prepare_value(arg) } | |
Database::Model.connection.execute(query, *prepared_args) | |
end | |
def self.last_insert_row_id | |
Database::Model.connection.last_insert_row_id | |
end | |
def self.connected? | |
!self.connection.nil? | |
end | |
def raise_error_if_invalid_attribute!(attributes) | |
# This guarantees that attributes is an array, so we can call both: | |
# raise_error_if_invalid_attribute!("id") | |
# and | |
# raise_error_if_invalid_attribute!(["id", "name"]) | |
Array(attributes).each do |attribute| | |
unless valid_attribute?(attribute) | |
raise InvalidAttributeError, "Invalid attribute for #{self.class}: #{attribute}" | |
end | |
end | |
end | |
def to_s | |
attribute_str = self.attributes.map { |key, val| "#{key}: #{val.inspect}" }.join(', ') | |
"#<#{self.class} #{attribute_str}>" | |
end | |
def valid_attribute?(attribute) | |
self.class.attribute_names.include? attribute | |
end | |
private | |
def self.prepare_value(value) | |
case value | |
when Time, DateTime, Date | |
value.to_s | |
else | |
value | |
end | |
end | |
def insert! | |
self[:created_at] = DateTime.now | |
self[:updated_at] = DateTime.now | |
fields = self.attributes.keys | |
values = self.attributes.values | |
marks = Array.new(fields.length) { '?' }.join(',') | |
# puts self.class.table_name | |
insert_sql = "INSERT INTO #{self.class.table_name} (#{fields.join(',')}) VALUES (#{marks})" | |
results = Database::Model.execute(insert_sql, *values) | |
# This fetches the new primary key and updates this instance | |
self[:id] = Database::Model.last_insert_row_id | |
results | |
end | |
def update! | |
self[:updated_at] = DateTime.now | |
fields = self.attributes.keys | |
values = self.attributes.values | |
update_clause = fields.map { |field| "#{field} = ?" }.join(',') | |
update_sql = "UPDATE #{self.class.table_name} SET #{update_clause} WHERE id = ?" | |
# We have to use the (potentially) old ID attribute in case the user has re-set it. | |
Database::Model.execute(update_sql, *values, self.old_attributes[:id]) | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Student < Database::Model | |
self.table_name = 'students' | |
attr_reader :attributes, :old_attributes | |
# e.g., student['first_name'] #=> 'Steve' | |
def [](attribute) | |
raise_error_if_invalid_attribute!(attribute) | |
@attributes[attribute] | |
end | |
def cohort | |
Cohort.where('id = ?', self[:cohort_id]).first | |
end | |
def cohort=(cohort) | |
self[:cohort_id] = cohort[:id] | |
self.save | |
cohort | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment