Give Codeship a try

Want to learn more?

This article was originally published by Constantine Lebedev on Medium, and with his permission, we are sharing it here for Codeship readers.

Sometimes we need to build an application that has domain models that we don’t know all the attributes of. A good example of such application is a system for tracking business contacts. In the center of it is a Contact model that has attributes like name, email, phone number, etc.

But can we know beforehand all the attributes that our Contact model will need to have? If we want to create the application for a wide audience, it can be difficult to predict. Solution? Allow users to add more attributes to Contact model in the runtime! We’ll do exactly that in this tutorial.

You can find the complete source of the demo application here. I won’t be covering the basic setup for this application (which, indeed, is very basic), and instead, focus on interesting parts.

Configuring model

First thing first, let’s install active_dynamic gem. I wrote this gem while working on the application similar to the one we’ll build in this tutorial, and the gem will do most of the heavy-lifting for us. Start by adding it to your Gemfile and running:

rails generate active_dynamic rake db:migrate

This will copy migration and configuration files to your project and run the migration.

Next, we need to include active_dynamic into our Contact model so that the gem knows that Contact can have dynamically added attributes:

Now we need to tell active_dynamic where to find the information about attributes it has to add to Contact model. To do that, we create a provider class that has to comply with two rules:

it needs to accept a model class as the only constructor argument;

it needs to have a call method that returns an array of attribute definitions;

Attribute definition is just a helper class that allows us to specify a display name of an attribute that will be dynamically added to our model, and, optionally, its data type, a system name, presence validation, and a default value. Here’s a simple provider class that defines age and description attributes:

To make them truly dynamic, we can load attribute definitions from an external storage — a database, YAML file, XML config, or something else. In the demo application, I used a contact_attributes table to store and manipulate the list of attribute definitions, so my ContactAttributeProvider looks like this:

Configuring controller and view

Now that our model is ready, let’s create a view to edit it. In the demo application, I used simple_form gem to generate HTML forms, but the same approach can be applied using standard Rails form builder. Here is the helper method that maps an array of dynamic attributes to an array of input fields:

It iterates over dynamic attributes of a model and uses datatype property that we set earlier to create different input types for different types of attributes. So it will render a text area element for text data type, numeric input for integer data type, and a simple text input for any other data type. We then can use this helper method in our view like this:

Now when we create or update a Contact record, we can use contact_params hash that will include all of our dynamic attributes. That’s it! Now we have a complete solution that allows us to add new attributes for Contact model as well as edit their values without making changes in the source code.

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles. Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.

We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.