Getting the most out of ActiveRecord

Robert Shilcof
5 min readDec 13, 2020

Housing Development UK, a Sinatra based web application

Housing Development UK is a web application that demonstrates features of Sinatra and ActiveRecord. It allows two different types of users to create accounts and sign in;

  • firstly, housing development companies who can display their latest developments, and
  • secondly, users who can follow development companies and leave feedback on their proposals.

The foundations of my application could easily be altered to cater for many different scenarios where there are users with different requirements. The content creator and the reviewer. Another example being budding authors submitting short stories for peer review.

With current COVID restrictions placed on face to face communication, it is evident to me that the need to create accessible online consultation platforms has never been more relevant.

Whilst this project introduced me to numerous different aspects of web applications - from the fundamentals of handling requests and application layouts to final touches like CSS styling - in this post I have chosen to detail how to get the most out of the ActiveRecord features I found particularly useful.

But first, a brief introduction of ActiveRecord

Web applications consist of models, views and controllers, which are all content to communicate using Ruby. They also need a database to store information. However databases are not fluent in Ruby.

Structured Query Language (SQL) is the means by which databases can communicate with the application’s models.

To implement SQL I used the Object-Relational Mapper (ORM) ActiveRecord. This is a Ruby gem that provides models with executable methods that send SQL queries to the database… and a whole lot more…

Class associations

The first feature I want to highlight that comes with the ActiveRecord library is the ability to associate classes with one another.

This is achieved by adding specific ActiveRecord’s meta-programming methods when defining a class, where meta-programming methods are pieces of code that write code themselves. The code these methods write consists of additional methods for the class they are included in.

For example, by adding has_many :developments, the User class will have access to additional methods. These include .developments which will use ActiveRecord’s ability to communicate with the database in order to list the developments that have a foreign key user_id that matches a user’s id.

class User    has_many :developmentsend

This functionality is excellent. It saves the software developer the task of having to create appropriate SQL statements and write the methods that use them.

Aliasing

In the context of my application, a developer is a member of the user class, and only a developer can have many developments.

Therefore it seems inaccurate to record the relationship in the development table with a user_id, but if we were to simply change this column to developer_id, ActiveRecord’s has_many method would not be able to find it.

However, ActiveRecord allows columns to be given aliases, requiring the has_many method to be called with the corresponding foreign key. As demonstrated below.

class User    has_many :developments, foreign_key: :developer_idend

The code is now better resembling what is actually happening, and the methods provided by ActiveRecord’s meta-programming are easily understood.

Complex class associations

Now let’s consider the functionality of a user following many developers and a developer having many followers. As we have already established, a developer is fundamentally a user, if we were to write out this relationship in the same fashion as the first example we would have.

class User    has_many :users    has_many :usersend

This should ring alarm bells! But again, ActiveRecord’s methods come prepared to deal with these scenarios for you.

Whenever implementing a has many to has many relationship, an additional join table is required to store the foreign keys for each of the classes.

Following convention this join table is named follower_developers. As we are working with ActiveRecord we must create an associated class, which again by convention would be named FollowerDeveloper.

This class will belong to each of the tables that it is joining, and as we are aliasing, we must specify the class associated with the aliased ids.

class FollowerDeveloper < ActiveRecord::Base    belongs_to :follower, class_name: :User    belongs_to :developer, class_name: :Userend

Now when we say that a follower has many developers, and a developer has many followers, they do so through the join class. Once again these relationships must be aliased as follows.

class User < ActiveRecord::Base    has_many :follower_associations, 
class_name: :FollowerDeveloper,
foreign_key: :developer_id
has_many :following_associations,
class_name: :FollowerDeveloper,
foreign_key: :follower_id
has_many :followers,
through: :follower_associations
has_many :followed_developers,
through: :following_associations,
source: :developer
end

The amount of methods, dynamic SQL statements, and classes that are required to be written to achieve this level of functionality is vast. But when you know how to utilise ActiveRecord’s library, the time and effort needed to set up a web application is substantially reduced.

Validations

Another feature of ActiveRecord I found particularly useful is the ability to validate data provided when attempting to persist an instance of a class to the database.

A basic example is that a user should not be able to be saved without an email, and the following code ensures that this will not be possible.

class User < ActiveRecord::Base    validates :email, presence: trueend

Any attempt now made to save a user without an email will result in an error message being added to the user instance that reads: Email can’t be blank, instead of it being persisted to the database.

Using flash messages the message can be fed back to a user that has incorrectly completed a sign up form.

Expanding on this validation, certain fields such as email require the input to be unique. The validation required for this can be chained onto the earlier validation as shown.

class User < ActiveRecord::Base    validates :email, presence: true, uniqueness: trueend

In order to protect this application from malicious users, I found it possible to write appropriate validations as shown in the following example.

class User < ActiveRecord::Base    validates_each :first_name, :last_name do |record, attr, value|        if value && value.match(/[<>]/)            record.errors.add(attr, "must not contain \< or \>")        end
end
end

This validation will add an error to an instance if either of its name attributes contain a < or >, sanitising the fields to ensure that they cannot be used to attack the application with HTML or Script inserts.

Additionally, as we can get a falsy value from ActiveRecord’s .save method when there are errors present, the application’s controllers needs no additional logic to be added for validating user inputs.

A simple if .save statement will be sufficient.

Let me know what you think!

Check out the concept video below, and view the source code here.

I thoroughly enjoyed developing this application, and increasing my knowledge of ActiveRecord.

Having learned the fundamentals of associations, I now appreciate the complexities of developing web applications more, and will no longer be able to interact with them without considering how they operate.

If you found this project interesting or would like further information as to how I put it together, I would love to hear from you! Please feel free to leave a comment and I will get in touch.

--

--