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
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
andactiverecord
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.
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 databaserails db:migrate
- for database migrationsrails 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 ๐.
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.