Ruby Serializer in Action
Ruby has been in use now for quite a while. Even though that is the case, there is still a need to address well-used issues. In this particular case, I am talking about active serializer.
One of the most important steps in developing a JSON API using Ruby on Rails is serializing the rendered JSON. That means structuring the data and selecting the attributes from the model.
Let’s look at an example of a basic route:/users
. This route is intended to serve an array of objects containing data for all users in the database. In the users_controller.rb
file you would find a method that looks like this:
def index
render json: @users
end
That short and simple method will serve a JSON array containing what we expect. However, it will contain every available attribute from the user's table: id, name, email, created_at, and updated_at.
[
{
"id": 3,
"email": "sigurd.cain@example.org",
"name": "Prak",
"created_at": "2020-04-20T02:45:00.906Z",
"updated_at": "2020-04-20T02:45:00.906Z"
},
{
"id": 4,
"email": "providenci@example.org",
"name": "Willam",
"created_at": "2020-04-20T02:45:00.909Z",
"updated_at": "2020-04-20T02:45:00.909Z"
}
]
Some of these attributes are fine, but some we don’t want to serve. We don’t actually want to include the user’s id in the response. And we may not want to include both the created_at and updated_at fields.
Serializer: to_json
To properly format the response, the serializer comes in to play.
def index
@users = User.all
render json: @users.to_json(only: [:email, :name])
end
Let’s take a look at the result:
[
{
"email": "sigurd.cain@example.org",
"name": "Prak"
},
{
"email": "providenci@example.org",
"name": "Willam"
}
]
Just like that, by employing the to_json serializer, we’ve cleaned up the data. The only
option specifies which attributes will actually be included.
Option: methods
Another important option to consider is methods
. When the class for the model in use contains methods whose returned values you want to be included in the rendered data, this is the option that gets it done.
Let’s say this user has ratings associated with a separate table. The average for those ratings is available as an instance method on the model.
def average_rating
if self.ratings.count > 0
self.ratings.map { |r| r.rating }.sum / self.ratings.count
else
1
end
end
To include this value in the rendered output, call on themethods
option.
render json: @users.to_json(only: [:email, :name], methods: :average_rating)
This will include the data and produce the desired output.
[
{
"email": "sigurd.cain@example.org",
"name": "Prak",
"average_rating": 3
},
{
"email": "providenci@example.org",
"name": "Willam",
"average_rating": 4
}
]
By utilizing these built-in simple techniques, the JSON output can be formatted as necessary without calling on external gems.
Happy coding!