stORM is an Object-Relational Mapping library inspired by ActiveRecord. stORM provides a base class SQLObject
that, when inherited from, allows Ruby classes to easily query their related database table and return Ruby objects representing that data. These database-connected subclasses can be mapped to each other using associations.
- A simple interface for generating SQL queries
- Cross table referencing for object associations
- Infers table-names, primary-keys, and foreign-keys when making associations resulting in elegant method calls
Mapped classes inherit from SQLObject
:
class Cat < SQLObject
end
SQLObjects can query the database:
- Returns all objects from the respective table
Cat.all
# => [[#<Cat:0x007f @attributes={:id=>1, :name=>"Breakfast"}>,
#<Cat:0x007f @attributes={:id=>2, :name=>"Earl"}>,]
- Queries using any object attribute
Cat.where({name: "Earl"})
# => [#<Cat:0x007f @attributes={:id=>2, :name=>"Earl">]
Associations can be defined between SQLObjects:
class Cat < SQLObject
belongs_to :human, foreign_key: :owner_id
end
earl = Cat.where({name: "Earl"})
earl.human
# => #<Human:0x007f9ba @attributes={:id=>2, :fname=>"Matt", :lname=>"Rubens"}>
To try out stORM, run the following command from within this directory:
irb -r ./demo.rb
These are the defined associations:
class Turtle < SQLObject
belongs_to :owner, class_name: :human
has_one_through :house, :owner, :house
end
class Human < SQLObject
belongs_to :house
has_many :turtles, foreign_key: :owner_id
has_one_through :neighborhood, :house, :neighborhood
end
class House < SQLObject
has_many :inhabitants, class_name: "Human"
belongs_to :neighborhood
end
class Neighborhood < SQLObject
has_many :houses, class_name: "House"
end
...and the database schema:
column name | data type | details |
---|---|---|
id | integer | primary key |
name | string | |
owner_id | integer | foreign key |
column name | data type | details |
---|---|---|
id | integer | primary key |
fname | string | |
lname | string | |
house_id | integer | foreign key |
column name | data type | details |
---|---|---|
id | integer | primary key |
address | string | |
neighborhood_id | integer | foreign_key |
column name | data type | details |
---|---|---|
id | integer | primary key |
name | string |
Returns an array of the related table's column names
Human.columns
# => [:id, :fname, :lname, :house_id]
Returns the inferred name for the related table. Can be set with table_name=
if incorrect
House.table_name
# => "houses"
SpeedBoat.table_name
# => "speed_boats"
Human.table_name
# => "humen"
Human.table_name = "humans"
Human.table_name
# => "humans"
The all
method returns an array of all objects from the respective table
House.all
# => [#<House:0x007f9ba186de88 @attributes={:id=>1, :address=>"18 N.10th"}>,
#<House:0x007f9ba186dac8 @attributes={:id=>2, :address=>"765 State Rd"}>...]
The find
method returns an object whose id matches :id
House.find(2)
# => #<House:0x007f9ba147c220 @attributes={:id=>2, :address=>"123 Main St"}>
The find_by_:attribute
method returns an object whose :attribute
matches the given value
. This method is applicable to any valid database column name in the related table
House.find_by_address("400 Broadway")
# => #<House:0x007f9ba147c220 @attributes={:id=>5, :address=>"400 Broadway"}>
The where
method returns an array of all objects who fit the criteria given in { params }
Human.where({ fname: "Matt", house_id: 1 })
# => #<Human:0x007f9ba1645278 @attributes={:id=>2, :fname=>"Matt", :lname=>"Rubens", :house_id=>1}>
An association class method defines a method on the class that calls it. This new method represents an association with another table and returns one or more new objects.
*The :primary_key
, :foreign_key
, and :class_name
options are all inferred by SQLObject. These can be overridden if the inferences are incorrect.
Used for a class that holds the foreign_key in an association
class Laptop < SQLObject
belongs_to :owner,
class_name: :Human,
foreign_key: :owner_id,
primary_key: :id
end
laptop = Laptop.find(2)
owner = laptop.owner
# => #<Human:0x007f9ba1c861f0 @attributes={:id=>4, :fname=>"Hazel", :lname=>"Peters"}>
laptop.owner_id == owner
# => true
Used for a class whose primary key is stored as the foreign_key of another object
class Human < SQLObject
has_one :house
end
human = Human.find(1)
human.house
# => #<House:0x007f9ba @attributes={:id=>7, :address=>"999 North 5th"}>
Used for a class whose primary key is stored as the foreign_key of multiple other objects that share a class
class Human < SQLObject
has_many :cats, foreign_key: :owner_id
end
human = Human.find(1)
human.cats
# => [#<Cat:0x007f9ba1667710 @attributes={:id=>9, :name=>"Supper", :owner_id=>1}>,
#<Cat:0x007f9ba1667558 @attributes={:id=>14, :name=>"Grapejuice", :owner_id=>1}>]
Used for a class that has a secondary association to another class
class House < SQLObject
has_many :dwellers, class_name: :human
end
class Human < SQLObject
belongs_to :house
end
class Dog < SQLObject
belongs_to :owner, class_name: :Human
has_one_through :house, :owner, :house
end