/stORM

Primary LanguageRuby

stORM

stORM is a light object-relational mapping (ORM) framework. stORM was written in Ruby and heavily inspired by Ruby on Rails. It can use Ruby methods and objects to manipulate the database through a very simple interface. Objects are a record in the database and relationships between the objects can be referred through associations.

Demo

The dog.rb file sets up three models (Dog, Human and a House). The file also sets up the necessary associations. The database has been seeded using the dogs.sql file. Some setup is required to test out the demo.

Setup

  1. Download/clone the library
  2. Navigate to the root directory of the file in the terminal.
  3. Open pry and load 'dog.rb'.
  4. Start testing!

Database Manipulation and Querying

::all

Returns an array of Ruby objects that belong to that specific class.

ie Dog.all

def self.all
  all = DBConnection.execute(<<-SQL)
  SELECT
    #{self.table_name}.*
  FROM
    #{self.table_name}
  SQL
  parse_all(all)
end

::find(id)

Returns a Ruby object with the corresponding id. Returns nil if record does not exist.

ie Human.find(1)

def self.find(id)
  one_dog = DBConnection.execute(<<-SQL, id)
  SELECT
    #{self.table_name}.*
  FROM
    #{self.table_name}
  WHERE
    id = ?
  SQL

  parse_all(one_dog).first
end

::first

Returns the first Ruby object.

ie House.first

def self.first
  first = DBConnection.execute(<<-SQL)
  SELECT
    #{self.table_name}.*
  FROM
    #{self.table_name}
  LIMIT
    1
  SQL
  parse_all(first).first
end

::last

Returns the last Ruby object.

ie Dog.last

def self.last
  last = DBConnection.execute(<<-SQL)
  SELECT
    #{self.table_name}.*
  FROM
    #{self.table_name}
  ORDER BY
    id DESC
  LIMIT
    1
  SQL
  parse_all(last).first
end

::table_name

Returns an instance variable of the table name or creates a table name.

def self.table_name
  @table_name || self.name.to_s.pluralize.tableize
end

::table_name=(table_name)

Takes the argument and sets the table name to the argument.

def self.table_name=(table_name)
  @table_name = table_name
end

#initialize(params = {})

Return a Ruby object with the params provided. The params should be provided in a key-value pair the key is a column name and the value is the value for the new object under that specific column name. ie. {name: "Martin"}, :name is the column name and "Martin" is the value. If a column name is provided and does not exist on the table, then an error will be raised.

def initialize(params = {})
  params.each do |name, value|
    name = name.to_sym
    if self.class.columns.include?(name)
      self.send("#{name}=", value)
    else
      raise "unknown attribute '#{name}'"
    end
  end
end

insert

Inserts an object into the corresponding table.

ie new_dog = Dog.new(name: "new_name", owner_id: id)

new_dog.insert

def insert
  col_names = self.class.columns[1..-1].join(", ")
  question_marks = (["?"] * attribute_values.length).join(", ")
  DBConnection.execute(<<-SQL, *attribute_values)
  INSERT INTO
    #{self.class.table_name} (#{col_names})
  VALUES
    (#{question_marks})
  SQL

  self.id = DBConnection.last_insert_row_id
end

update

Updates objects in the database

ie dog = Dog.last

dog.name = "new_name"

dog.update

def update
  id = self.attributes[:id]
  attr_name = self.class.columns.map do |column|
    "#{column} = ?"
  end
  attr_name = attr_name.join(", ")
  DBConnection.execute(<<-SQL, *attribute_values, id)
  UPDATE
    #{self.class.table_name}
  SET
    #{attr_name}
  WHERE
    id = ?
  SQL
end

#save

Saves or updates the new record to the database.

ie new_dog = Dog.first

new_dog.name = "new_name"

new_dog.save

def save
  if self.attributes[:id].nil?
    self.insert
  else
    self.update
  end
end

belongs_to(name, options = {})

The class with the belongs_to association usually contains a foreign key that references another Ruby object. This is a one-to-one relation and this method defines an instance method on the name that is passed in through the params. The associated model is returned.

ie Dog.first.owner

class Dog < SQLObject
  belongs_to :owner,
    class_name: 'Human'

  has_one_through :home, :owner, :house

  finalize!
end

Options

:class_name

The class name option should either be a symbol or string. The name provided in the parameter is used and converted to CamelCase and singularized. If the name provided was dog, then the class name would be Dog.

:foreign_key

The foreign key option should either be a symbol or string. The name provided in the parameter is used and converted to snake_case and _id is appended to the end. If the name provided was dog, then the foreign key would be dog_id.

:primary_key

The primary key option should either be a symbol or string. The primary key will most likely always be set to id.

has_many(name, options = {})

The class with the has_many association usually does not contain a foreign key. This is a one-to-many relation and this method defines an instance method on the name that is passed in through the params. The associated models are returned in an array.

ie Human.find(3).dogs

class Human  < SQLObject
  self.table_name = 'humans'

  belongs_to :house

  has_many :dogs,
    foreign_key: :owner_id

  finalize!
end

:class_name

The class name option should either be a symbol or string. The name provided in the parameter is used and converted to CamelCase and singularized. If the name provided was dog, then the class name would be Dog.

:foreign_key

The foreign key option should either be a symbol or string. The name provided in the parameter is used and converted to snake_case and _id is appended to the end. If the name provided was dog, then the foreign key would be dog_id.

:primary_key

The primary key option should either be a symbol or string. The primary key will most likely always be set to id.

has_one_through(name, through_name, source_name)

This association is a one-to-one relationship where the current model reaches through an already existing association to create a new association.

ie Dog.first.home

class Dog < SQLObject
  belongs_to :owner,
    class_name: 'Human'

  has_one_through :home, :owner, :house

  finalize!
end