#-------------------------------------------------------------------------------
# Implementation of the Active Record Pattern
#-------------------------------------------------------------------------------

require 'sqlite3'

DataBase = SQLite3::Database.new("students.db")

class Student
  
  # Assumes that the database contains a table created the following way:
  #
  #   create table students (
  #       id integer primary key autoincrement,
  #       name text,
  #       gpa real
  #   );
  
  attr_reader :id
  attr_accessor :name, :gpa
  
  def initialize(name='', gpa=0)
    @name = name
    @gpa = gpa
    @id = -1
  end
  
  def save
    if @id == -1
      id = DataBase.get_first_value('select max(id) from students')
      if id
        @id = id + 1
      else
        @id = 1
      end          
    end
    DataBase.execute('insert or replace into students values (?, ?, ?)',
                     [@id, @name, @gpa])
  end
  
  def self.create(name, gpa)
    student = Student.new(name, gpa)
    student.save
    student
  end
  
  def remove
    if @id != -1
      DataBase.execute('delete from students where id = ?', [@id])
      @id = -1
    end    
  end
  
  def self.read(id)
    row = DataBase.get_first_row('select * from students where id = ?', [id])
    if row
      student = Student.new(row[1], row[2])
      student.instance_variable_set(:@id, row[0])
      student
    else
      nil
    end
  end
  
  def self.read_all
    DataBase.execute('select * from students').map do |row|
      student = Student.new(row[1], row[2])
      student.instance_variable_set(:@id, row[0])
      student
    end    
  end
  
  def inspect
    "Student(id: #{ @id}, name: '#{ @name }', gpa: #{ @gpa })"
  end
  
end

#--------------------------------------------------------------------
# Typical use cases

# Initialize and save students
s1 = Student.new
s1.name = 'John'
s1.gpa = 3.5
s1.save
s2 = Student.new('Mary', 2.7)
s2.save

# Create a student in one step
s3 = Student.create('Peter', 3.1)

# Update student with id = 1
s4 = Student.read(1)
s4.gpa = 3.9
s4.save

# Delete student with id = 2
s5 = Student.read(2)
s5.remove

# Retrieve all students in an array
a = Student.read_all
p a