The last few weeks I was learning ruby on rails and was especially impressed by the powerful simplicity of serializers. For my example, I build an app for tracking experiments and chemical inventory for a lab group. Users can log in and create experiments and attach or remove chemicals to an experiment. Chemicals can also be created in the DB, have their amount edited, and be removed. For example, I can create an experiment with the description "Baking soda and vinegar volcano". For this I can add the existing chemicals, baking soda, and vinegar. Important to note that these chemicals could be part of multiple experiments at once, so the models are connected with a join table like so:
#/experiment.rb
class Experiment < ApplicationRecord
has_many :chemical_experiments, dependent: :destroy
has_many :chemicals, through: :chemical_experiments
belongs_to :user
#/chemical.rb
class Chemical < ApplicationRecord
has_many :chemical_experiments, dependent: :destroy
has_many :experiments, through: :chemical_experiments
#/chemical_experiment.rb
class ChemicalExperiment < ApplicationRecord
belongs_to :chemical
belongs_to :experiment
#/user.rb
class User < ApplicationRecord
has_many :experiments
If I log out and then back in, I want to be able to fetch all of the user's data in one request, so that I can make use of React's responsive rendering with state. If set up the same :belongs_to and :has_many for user, experiments, and chemicals (the join table and through: is not needed in serializer if the models are set up correctly).
#/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :username
has_many :experiments
#/experiment_serializer.rb
class ExperimentSerializer < ActiveModel::Serializer
attributes :id, :description, :is_complete
has_many :chemicals
#/chemical_serializer.rb
class ChemicalSerializer < ActiveModel::Serializer
attributes :id, :name, :amount_in_grams, :location
So, our user#show method (GET /user/:id) should return a user object with id, username, and an array of experiments. Each experiment object should return it's attributes and an array of chemical objects.
#/users_controller.rb
def show
user = User.find(params[:id])
render json: current_user, status: 200
end
Fetching user with id of 1 would return something like this:
{
"id": 1,
"username": "Alex",
"experiments": [
{
"id": 1,
"description": "Baking soda and vinegar volcano",
"is_complete": false
}
]
}
The problem is that, by default, ActiveModel Serializer only will return one layer deep. In order to return the next layer, chemicals associated with the experiment, we need to make a slight change in our user_controller.rb.
#/users_controller.rb
def show
user = User.find(params[:id])
render json: current_user, include: ['experiments', 'experiments.chemicals'], status: 200
end
with this slight change we have just a few lines of code split between a few files that builds our tree of data. What the fetch request should now return is the user with all of the nested data that we built serializers for.
{
"id": 1,
"username": "Alex",
"experiments": [
{
"id": 1,
"description": "Baking soda and vinegar volcano",
"is_complete": false,
"chemicals": [
{
"id": 1,
"name": "Acetic Acid",
"amount_in_grams": 500,
"location": "Acids Cabinet, Lab B"
},
{
"id": 2,
"name": "Sodium Bicarbonate",
"amount_in_grams": 1000,
"location": "Chemical Storage, Shelf 2"
}
]
}
]
}
This nicely structured data will make building out the front end much easier, especially in a framework like react. I hope other developers taking their first delve into ruby on rails will find some of this information useful.
Top comments (0)