The Magic Behind Active Record Part 1.

Active Record is a framework that bridges the Ruby environment and the SQL environment through object relational mapping. Simply put, it provides a design pattern for creating database tables and for performing a wide variety of functions such as inserting new rows using just ruby code. The “magic” is in how active record is able to form a one to one relationship between a ruby model(implemented as a class) and an sql table. To understand how this is done, its important to first consider what an object relation map is, and why we would want one in the first place!

Consider the following example

we would like a way to model a micro-post for Twitter, a social media platform that allows users to share small messages to the world in 140 characters or less.

Lets create a simple model that saves a tweet with the name of the user and their message to the world.

  1. create a connection and create a tweets table
db_connection = SQLite3::Database.new('db/tweets/db')
db_connection.execute("CREATE TABLE IF NOT EXISTS tweets(id INTEGER PRIMARY KEY, name TEXT, message TEXT)")

2. save a tweet in the database table named tweets

db_connection.execute("INSERT INTO tweets(name, message) VALUES ('Duncan' , 'hello world my name is Duncan and I love to read')

This works! The application has now saved that tweet for all the world to see. However, if we want to add more tweets we have to repeat the entire query with new instances. This logic of saving tweets is cumbersome as you can see below. furthermore consider all the other functions we would need in order for our app to be useful, reading tweets, deleting tweets, etc. under this framework we would have to write a new query every time we needed to handle data or alter a table.

db_connection.execute("INSERT INTO tweets(name, message) VALUES ('Barry' , 'hello world my name is Barry and I love to play music')
db_connection.execute("INSERT INTO tweets(name, message) VALUES ('Lucy' , 'hello world my name is Lucy and I love to cook')
db_connection.execute("INSERT INTO tweets(name, message) VALUES ('Megan' , 'hello world my name is Megan and I love Dancing')
db_connection.execute("INSERT INTO tweets(name, message) VALUES ('Ian' , 'hello world my name is Ian and I love programming')

To solve this problem we will map a one to one relationship between the database table and a tweet as defined in ruby. We will define a single tweet as an instance of the class Tweet. we will define several methods that create functions out of our queries, so later we can input specific instances. In our Tweet class this is handled by the create and save methods.

class Tweet
attr_accessor :name , :message
def self.create(name, message)
tweet = Tweet.new
tweet.name = name
tweet.message = message
tweet.save
end
  def save
db_connection.execute("INSERT INTO tweets (name, message)
VALUES(?,?),self.name, self.message)
end

def self.all
db_connection.execute("SELECT * FROM tweets")
end
end

Now when a user makes a tweet our program creates a new Tweet instance and saves it to the DB. calling Tweet.create, thats it! no more writing sql queries every time a user tweets.

Tweet.create("duncan","hello world my name is Duncan and I love to read")

So whats going on here? Its simple, what we’ve done is abstracted the layer of communication between ruby and sql so we can more easily create tweets and save them in the DB using only ruby. Our ruby code is now writing sql for us! This abstraction or model is saved as the class Tweet. The “map” in object relation map, refers to the naming convention that creates a one to one relationship between class attributes and database columns. The following diagram illustrates this relationship.

But we’re not done! what if we need a new database table?, what if the number of features in our app change? what if we didn’t know what the database looked like ahead of time? under our current model we would have to go back and re-write the entire model in order to accommodate for new requirements in our app. What we need is a more general methodology that relates models in ruby to databases in sql. What we need is a dynamic object relation map. That’ll be covered next on The Magic Behind Active Record part 2.