Ruby Gem – ExternalFields


We recently released a rails gem called ExternalFields (Github). As the name suggests, this gem allows you to create the illusion that an object has specific attributes when those attributes actually belong to an associated object. This is particularly useful for different classes within a single-table inheritance table to have access to separate fields in class-specific associations. In addition, this gem gives us more power than standard Ruby/Rails built-in delegation methods by automatically creating necessary objects and associating them.

Let’s try to understand what this gem does:

Basic Usage

Let’s say you have a class StudentData:

and it is associated with another class Student:

Now, to access the attributes age, credits, etc of students, you’ll need to get to StudentData via Student each time

And this can soon get really annoying and if you have lengthy class names:

There is no need to explain how lazy programmers are and how much we hate repetition. This is where ExternalFields comes in – this gem allows you to access attributes of an associated model directly from your main model so you do not have to access them via the association every time. So if you define the attributes of the associated class using the external_field method:

you can directly call the read/write accessors on the Student object:

and this can remove a lot of repetition in your codebase!

ActiveRecord Concerns

This gem is especially useful when working with ActiveRecord concerns. One example of such usage is an Addressable concern which we use for models where address information is needed. It uses this gem to add a list of fields which can be directly accessed from the parent model. Each of these fields is actually part of the Address model:

Why not use Rails’ #delegate or Ruby’s #def_delegator ?

You might be wondering why we cannot just use the delegate method of Rails or the Forwardable module of Ruby to forward messages to other objects. Both of these just send the message to the specified object, and so if the target object is nil and/or does not respond to the delegated method a NoMethodError is raised. They also provide the option to return nil instead of raising an error if the target object is nil and does not respond to the delegated method.

ExternalFields, by default, automatically creates a new target object if it doesn’t exist already and this removes the hassle of checking if an object exists. For example, if we use the Addressable concern described above for a Student class:

addresses would be created automatically when we use accessors or mutators. By virtue of lazy initialization, ExternalFields creates these objects only when necessary so we do not have lots of empty objects in our database.

In this case, a new address with address_line1 = “20 Main St” was automatically created and associated with the student.
At Panorama, we deal with a lot of SIS (Student Information System) data and many of our models need to interact with Student, Teacher and Guardian objects along with their associations. This would typically create a lot of repetition when the associated attributes are accessed but using the ExternalFields gem we have been able to avoid that.

For detailed installation and usage instructions of this gem visit the Github repository. If you want look at other open-source repos created and maintained by engineers at Panorama, you can find them here.


Sounds like fun? We’re hiring!

Related Posts
Panorama Engineering Goes Civic Hacking!
Toward a Swankier Rails Console
Implementing priority lanes for jobs of the same type in Sidekiq
A Homemade Distributed Computing Project