Using Ruby's Active Record Gem Outside The Rails Web Framework Part 1

A practical guide to integrating clean data persistence into a ruby standalone app in development and Heroku production

ยท

8 min read

Using Ruby's Active Record Gem Outside The Rails Web Framework Part 1

At the heart of programming languages are libraries, a set of third-party tools that provide peculiar functionalities within a programming language. Libraries ensure that programmers can carry out complex operations without having to go through the pain of writing these programs themselves. They will typically have a unified publishing platform like rubygems for ruby, npm for nodeJS, packagist for PHP. Libraries provide ranging functionalities like database manipulation, image processing, time-formatting. There is a library for many complex operations you can think of.

In ruby, libraries are called Gems and in this article, we will be focusing on the ActiveRecord gem, a ruby library for interfacing with the database in an Object-Oriented way (specifically built for Rails).

Why Use ActiveRecord Outside Rails?

I was working on a telegram bot and at some point, I needed to persist data. On the one hand, I could have moved the project to Rails, since ActiveRecord is already integrated (for both development and production environments) into the Rails framework, but that would mean I have to include all the numerous gems, tools and files that come with rails which my bot will never need. I needed just the elegant data persistence feature which ActiveRecord provides.

Integrating ActiveRecord into the Ruby standalone app

NOTE: For every new code I add as this article progress, I will wrap it in two comments that say "# new code begins" and "# new code ends" respectively.

Without further ado, we'll dive into the code implementation of our subject matter. First, we'll create a new sample project, so create a new folder and call it activerecord-ruby. Inside it, we'll create a Gemfile (A ruby recognised file for describing gems that our program will need to work with). In the Gemfile, we will add two gems. The first is PostgreSQL, the relational database management system (RDBMS) we will be working with and the second is the ActiveRecord gem, the Object Relational Mapping (ORM) tool to interact with our database. For this, put the below lines in your Gemfile

# Gemfile

source 'https://rubygems.org'
gem 'pg'
gem 'activerecord'

Then run bundle install in your terminal to install these gems. Barring any hitch, you should now have PostgreSQL and Active record gems installed.

Setting Up ActiveRecord With PostgreSQL

Now that we have both gems installed, it's time to use them. Create a file called create_db.rb and require your the pg and activerecord gems in it. Like so:

# create_db.rb
require 'active_record'
require 'pg'

This lets us use the gems' power in our code. Next, add the line below.

db_connector = {
    host: 'localhost',
    adapter: 'postgresql',
    schema_search_path: 'public'
}

ActiveRecord::Base.establish_connection(db_connector)

What the above code does is establish a connection pool between our program and the PostgreSQL database system. The ActiveRecord::Base.establish_connection method takes a configuration hash to tell ActiveRecord the type of database system we need to use it with. In this case, that is postgresql, which we are passing as our adapter value. The adapter could be other database types like sqlite, nosql or mysql. If you run the above file in your terminal with ruby create_db.rb, the establish_connection method should output a ConnectionPool object like this ActiveRecord::ConnectionAdapters::ConnectionPool, which indicates that a connection has been successfully established to the database system.

Creating a Database

Every time we need to do something with our database, we will need the above connection process to take place because it automatically closes after every database interaction. So to create a database the ActiveRecord way, we'll add more code to our create_db file and it should now look like the code snippet below.

# create_db.rb

require 'active_record'
require 'pg'

db_connector = {
    host: 'localhost',
    adapter: 'postgresql',
    schema_search_path: 'public'
}

# Establish a new connection pool
ActiveRecord::Base.establish_connection(db_connector)

# New code begins
# 'Through the established connection pool to PostgreSQL, create a new database'
db_create = ActiveRecord::Base.connection.create_database('active_record', {
   template: 'template0',
   encoding: 'unicode',
})

puts "Database created" if db_create
# New code ends

In the code above, we:

  • require our pg and activerecord gem
  • open a connection pool to the database engine
  • asked the database engine to create a new database called active_record.
  • asked the program to print 'Database created' if successful

Let's give some explanation to what's happening in the create_database method. We called the method with ActiveRecord::Base.connection.create_database, and we pass it two arguments. The first argument is the name of the database we need to create, while the second argument is a hash configuration that holds a key-value pair. The key in our hash is the template. When creating a new PostgreSQL database, the database engine actually does this by copying an existing database. By default, it uses the inbuilt template1 database to spin new instances. For us, we are using the template0 template database, which allows us to use the unicode encoding.

The unicode encoding supports a large character set than the default sql_ascii encoding which only supports 128 characters. You can read more about the differences here.

However, if you'll like to use the default template1 database to create your new database, then your the hash configuration you'll pass into your create_database method should look like this:

{
   encoding: 'SQL_ASCII'     
}
# NOTE that we are not passing in a `template` property and that's because
# the create_database method will automatically use the template1 db

Now, when we run ruby create_db.rb, we should now have a new database instance called active_record in our Database Management System (DBMS). To check this, run psql in your terminal to open the PostgreSQL console interface. Then run \l and you should get a list of databases (including the newly created active_record db) in your DBMS. Screen Shot 2022-03-20 at 8.03.03 PM.png

Dropping a Database

Dropping a database is fairly straightforward and need little explanation. Create a file called drop_db.rb and put the code below in it:

# drop_db.rb

require 'active_record'
require 'pg'

db_connector = {
    host: 'localhost',
    adapter: 'postgresql',
    schema_search_path: 'public'
}

# Establish a new connection pool
ActiveRecord::Base.establish_connection(db_connector)

# 'Through the established connection pool to PostgreSQL, delete this database'
db_drop = ActiveRecord::Base.connection.drop_database('active_record')

puts 'Database dropped' if db_drop

Now, run ruby drop_db.rb to drop/delete database.

Converting our database actions to rake tasks

Before we start writing codes for our migration, I think it's necessary that we first do something important. We need to write our database commands into reusable tasks. Tasks are single or set of actions that we need our computer to run often and in ruby, the rake gem is a task runner that helps us achieve this. So, instead of having separate files for tasks like creating, deleting, changing and migrating databases, we will bring them under one Rakefile and assign commands to them as is standard in Rails.

In Rails, ActiveRecord have some db commands like:

  • rails db:create - for creating a database
  • rails db:migrate - for database migrations
  • rails db:drop - for deleting a database.

These are rake commands that hold a set of database interactions. The rails command you see above is powered by rake and can be used interchangeably as rake. That means rails db:create can be rewritten as rake db:create.

First, add the rake gem to your Gemfile

# Gemfile
gem 'rake'

Then run bundle install to simply install it. Next, create a file called Rakefile in the root of your project folder. In this Rakefile, we will put all our rake tasks and assign commands to them.

Require your pg and activerecord gems and create a namespace called db in the `Rakefile

# Rakefile

require 'active_record'
require 'pg'

namespace :db do
end

The namespace construct in the above code is used to group tasks together in our Rakefile. All our database related tasks will go in the db namespace, and that helps us structure our rake commands in a syntax that is easy to understand.

Next, we will declare our earlier database connection configuration within the db namespace.

# Rakefile

require 'active_record'
require 'pg'

namespace :db do
  # new code begins
  db_connector = {
    host: 'localhost',
    adapter: 'postgresql'
  }
  # new code ends
end

Database Creation Task

Now, we will create a task/command within the namespace for creating a new database like so:

# Rakefile

require 'active_record'
require 'pg'

namespace :db do
  db_connector = {
    host: 'localhost',
    adapter: 'postgresql'
  }

  # new code begin
  desc "Create the database"
  task :create do
    ActiveRecord::Base.establish_connection(db_connector)
    db_create = ActiveRecord::Base.connection.create_database('active_record', {
      template: 'template0',
      encoding: 'unicode'     
    })

    puts "Database created." if db_create
  end
  # new code end
end

Let's explain the newly added bit of code in the above snippet. First, we added a description to our task with the desc method - desc "Create the database". Then we declare our task named create. So, we are saying when this task is called, run everything inside its do block.

In the create task, we simply tell it to open a new connection pool to the database and run the create_database method which we already explained how it works in an earlier section of this article. Then we tell it to print "database created" when successful.

To run this task, simply run rake db:create in your terminal. We are simply telling the computer to go into the Rakefile, then into the db namespace and run the create task. All things being equal, your database should be successfully created ๐Ÿ˜‰. Screen Shot 2022-03-25 at 6.54.57 PM.png

Database Deletion Task

In a very similar fashion to the create task, we will create a rake task for dropping a database in our db namespace. We'll call it drop.

# Rakefile

require 'active_record'
require 'pg'

namespace :db do
  db_connector = {
    host: 'localhost',
    adapter: 'postgresql'
  }

  desc "Create the database"
  task :create do
    ActiveRecord::Base.establish_connection(db_connector)
    db_create = ActiveRecord::Base.connection.create_database('active_record', {
      template: 'template0',
      encoding: 'unicode'     
    })

    puts "Database created." if db_create
  end

  # new code begins
  desc "Drop the database"
  task :drop do
    ActiveRecord::Base.establish_connection(db_connector)
    db_drop = ActiveRecord::Base.connection.drop_database('active_record')
    puts "Database deleted." if db_drop
  end
  # new code end
end

Now, if you run the drop task with rake db:drop, the active_record database should be deleted. Then we can now delete our create_db.rb and drop_db.rb files as we do not need them anymore.

With our new Rakefile structure, you can see that our project now works like a standard Rails structured ActiveRecord DB interaction.

Next, we will talk about setting up database migrations in part two of this article.