Skip to content

Instantly share code, notes, and snippets.

@TGSmith
Last active December 19, 2015 14:29
Show Gist options
  • Select an option

  • Save TGSmith/87e4a7b45e938885acf2 to your computer and use it in GitHub Desktop.

Select an option

Save TGSmith/87e4a7b45e938885acf2 to your computer and use it in GitHub Desktop.

Revisions

  1. TGSmith revised this gist Jul 10, 2013. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions database_model.rb
    Original file line number Diff line number Diff line change
    @@ -116,7 +116,7 @@ def insert!
    values = self.attributes.values
    marks = Array.new(fields.length) { '?' }.join(',')

    insert_sql = "INSERT INTO students (#{fields.join(',')}) VALUES (#{marks})"
    insert_sql = "INSERT INTO #{self.class.to_s}s (#{fields.join(',')}) VALUES (#{marks})"

    results = Database::Model.execute(insert_sql, *values)

    @@ -132,7 +132,7 @@ def update!
    values = self.attributes.values

    update_clause = fields.map { |field| "#{field} = ?" }.join(',')
    update_sql = "UPDATE students SET #{update_clause} WHERE id = ?"
    update_sql = "UPDATE #{self.class.to_s}s 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])
  2. TGSmith revised this gist Jul 10, 2013. 4 changed files with 224 additions and 1 deletion.
    1 change: 0 additions & 1 deletion ActiveRecord,_Jr._2:_SQL_Be_Gone_solution.rb
    Original file line number Diff line number Diff line change
    @@ -1 +0,0 @@
    # Solution for Challenge: ActiveRecord, Jr. 2: SQL Be Gone. Started 2013-07-10T20:33:55+00:00
    59 changes: 59 additions & 0 deletions cohort.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,59 @@
    class Cohort < Database::Model


    self.attribute_names = [:id, :name, :created_at, :updated_at]

    attr_reader :attributes, :old_attributes

    # e.g., Cohort.new(:id => 1, :name => 'Alpha', :created_at => '2012-12-01 05:54:30')
    def initialize(attributes = {})
    attributes.symbolize_keys!
    raise_error_if_invalid_attribute!(attributes.keys)

    @attributes = {}

    Cohort.attribute_names.each do |name|
    @attributes[name] = attributes[name]
    end

    @old_attributes = @attributes.dup
    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 students
    Student.where('cohort_id = ?', self[:id])
    end

    def add_students(students)
    students.each do |student|
    student.cohort = self
    end

    students
    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

    end
    151 changes: 151 additions & 0 deletions database_model.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,151 @@
    require 'sqlite3'

    module Database
    class InvalidAttributeError < StandardError;end
    class NotConnectedError < StandardError;end

    class Model

    def self.find(pk)
    self.where('id = ?', pk).first # get rid of array
    end

    # WHERE always returns array !!!!!!

    def self.create(attributes)
    record = self.new(attributes)
    record.save

    record
    end

    def new_record?
    self[:id].nil?
    end

    def self.all
    Database::Model.execute("SELECT * FROM #{self.to_s.downcase}s").map do |row|
    self.new(row)
    end
    end

    def self.where(query, *args)
    Database::Model.execute("SELECT * FROM #{self.to_s.downcase}s WHERE #{query}", *args).map do |row|
    self.new(row)
    end
    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.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 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(',')

    insert_sql = "INSERT INTO students (#{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 students 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


    def self.prepare_value(value)
    case value
    when Time, DateTime, Date
    value.to_s
    else
    value
    end
    end
    end
    end
    14 changes: 14 additions & 0 deletions student.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    # Solution for Challenge: ActiveRecord, Jr. 2: SQL Be Gone. Started 2013-07-10T20:33:55+00:00

    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 students 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
  3. TGSmith created this gist Jul 10, 2013.
    1 change: 1 addition & 0 deletions ActiveRecord,_Jr._2:_SQL_Be_Gone_solution.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # Solution for Challenge: ActiveRecord, Jr. 2: SQL Be Gone. Started 2013-07-10T20:33:55+00:00