A novel implementation of join table relationships using JSON

Arguably one of the greatest things about the Rails stack is Active Record. When you’re using Active Record to manipulate your data you really do get the feeling that Rails is a magical tool; there’s just about always a way to express the relationship you want without diving into raw SQL. However as great as Active Record is there may be times when you find yourself working with your data outside of Rails. Given the explosive popularity of JavaScript sooner or later you can pretty much be assured you’ll be turning your relationships into JSON.

Generally working with your relationships in JSON is a breeze, it didn’t come to be the internet’s darling by accident. However every once in a while you may find your JSON relationships lacking. I’ve encountered this mostly in working with an objects that represented by join tables. While I was puzzled at first, there is a way to utilize Active Record to enrich your object making the JSON easier to manipulate.

Most recently I was asked to create a new feature in an existing app where I had to create a join table to represent a has many through relationship. Eventually I found myself querying this new object which I called “AssessmentContributor” via AJAX. I wanted to append the name of the contributor into my page but in JSON it wasn’t possible the same way it was in Active Record.

////Original JSON for AssessmentContributor////
{'id': 1, 'assessment_id': 1, 'user_id': 1}

Unfortunately there’s no way for JSON to see the value on the other side of the primary key when working with join tables. If you want to use that value, you must add it to JSON. With Active Record though it is possible to define a method to run every time you create a new object meaning with every entry into the join table, we can define the value we want. Using yet another built in Rails macro <after_initialize> you can create a do block to automate this. The simplest way to accomplish this looks like this:

class AssessmentContributor < ActiveRecord::Base
belongs_to :assessment
belongs_to :user
validates :assessment_id, presence: true
  validates :user_id, presence: true
validate :contributor_cannot_be_owner
  after_initialize do |ac|
ac.contributor_name = ac.user.name

By first adding a column to the model’s table used for the storing the string we want to receive with JSON we can then automatically define it using the Active Record relationship. To use this new method with JSON be sure to add it to the serializer like so:

class AssessmentContributerSerializer < ActiveModel::Serializer
attributes :id, :user_id, :assessment_id, :contributer_name

The idea behind this is similar to the Law of Demeter or the “one dot rule,” modify your object structure to create more simple relationship between information. This in theory could work on either side of the relationship if I decided to include an <assessment_name> method though its unlikely you’ll ever be querying an object without knowing at least one side.

after_initialize do |comment|
if comment.user
comment.by = comment.user.name
comment.by = "Anonymous"

This pattern can also be modified if you would like to present a default option for cases where validation isn’t required. Above I set the user name that I would be displaying for comments as default if the comment (another join model in the same project) is not associated to a user. Instead of having JSON with a nil value you can use a default identifier to add a level of abstraction to your app.

Admittedly, this is not likely to be something that you may use frequently. Edge cases however aren’t tricky because their frequency, but because they are so close to working all of the time. This is a solution that maintains the integrity of previous code without adding to much complexity to your app. Hopefully this implementation helps you if you ever find yourself working in a similar situation.