CSC/ECE 517 Fall 2014/ch1a 24 sa: Difference between revisions
No edit summary |
|||
(77 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
[[File:ClientServer. | =Active Model Serializers= | ||
[http://en.wikipedia.org/wiki/Ruby_on_Rails Ruby on Rails] offers strong features to write API based server applications, which expose data through a well designed API and serve data in various formats as requested. One of these features is '''[http://github.com/rails-api/active_model_serializers Active Model Serializers]''' which helps the API designers to have fine control over the response generated by the API to a data request. '''Active Model Serializers''' help programmers do this very easily and cleanly without breaking separation of concerns in a rails application. They promote [http://en.wikipedia.org/wiki/Convention_over_configuration convention over configuration], thus reducing code bloat and increasing code scalibility and readability. | |||
<br><br> | |||
[[File:ClientServer.png|right|]] | |||
__TOC__ | |||
=Background= | |||
The modern web has seen more and more web applications that are written as web services or web APIs. These applications do not return a web page to the client browser, but return just the bare bones data to the client in the format requested. This gives the clients the liberty to develop customized User Interfaces for the same web application. A common example is the Rotten Tomatoes API which offers movie data in JSON, XML and various other formats. The clients request the Rotten Tomatoes web server for the data in the desired format, and the web server is able to respond with the correct data in the required format. | |||
Active Model Serializers are a way for Web API designers who use Ruby on Rails to have full control over the formatting and the content of data that is returned to clients. For eg. An employee data model returned as JSON to a data request might look like this: | |||
<pre> | |||
{Employee: { | |||
name: emp_name | |||
age: 26 | |||
salary: 35000 | |||
weight: 90kg | |||
height: 170 | |||
} | |||
} | |||
</pre> | |||
But the API designer may want to improve this formatting and exclude certain fields so that the response is just the data without clubbing it under the "Employee" root token: | |||
<pre> | |||
{ | |||
EmpConciseDetails: emp_name 26 | |||
} | |||
</pre> | |||
In a Ruby On Rails application there could be a couple of ways to achieve this. | |||
===Option 1 : Customizing response in Controller Layer=== | |||
The first one is shown below where we can specify the JSON formatting option in the action method which handles the data request. The below method fetches the desired Employee and returns its JSON representation to the client: | |||
<pre> | |||
def show | |||
@emp = Employee.find(params[:id]) | |||
respond_to do |format| | |||
format.json { render :json => @emp, root:false} | |||
end | |||
end | |||
</pre> | |||
Specifying '''root: false''' above will remove the unwanted Employee token from the resultant JSON. However this method is not recommended as we are littering our controller layer with data formatting code. If we want to maintain the MVC separation of concerns, formatting the response data does not belong in the controller layer. | |||
===Option 2: Customizing response in Model Layer=== | |||
Another option is to override the '''as_json''' method of the Employee data model which is typically an Active Record in a Rails application: | |||
<pre> | |||
class Employee < ActiveRecord | |||
def as_json(options) | |||
super(:only => [:name, :age]) | |||
end | |||
end | |||
</pre> | |||
The above '''as_json''' method is called whenever Rails has to convert the Employee object to its JSON format to send to the client. Our custom '''as_json''' method will help us return only the name and the age as desired. However we again have a problem as we are littering our Model layer with custom formatting tasks which it is not intended to handle. | |||
Through '''Active model serializers''' we are able to place these custom formatting tasks to a separate class namely an EmployeeSerializer which inherits from ActiveModel::Serializer class. The Rails application is able to discover this serializer when Employee data model has to be serialized without any configuration from the programmer's side. The advantages of this scheme are dual. It helps keep separation of concerns in the Rails application, and its done with a convention over configuration approach. Specifics are discussed in following sections. | |||
= Introduction = | |||
Knowledge of the following terms would help before getting to the usage of '''Active Model Serializers'''. | |||
===Active Model=== | |||
'''[http://railscasts.com/episodes/219-active-model Active Model]''' is closely related to the widely used concept of '''[http://en.wikibooks.org/wiki/Ruby_on_Rails/ActiveRecord Active Record]''' in the Rails world. '''Active Record''' is an object that maps directly to the data in our database. An '''Active Record''' takes care of fetching and saving data to the database. For example, in an employee records application an Employee Active Record helps to persist a newly created/edited Employee. So how is an '''Active Model''' different? | |||
It may not always be the case that the Employee model has to be backed by a database and the programmer may decide not to implement an Employee model as an Active Record. However the programmer might want some of the model attribute validation functionality that an Active Record provides. For eg: Imagine an employee records system that handles an create employee form by reading the POST data into an Employee object. The system does not persist this Employee model to any database though. It simply emails the admin with all the captured data present in the Employee model. However before emailing the programmer might want to validate if certain required attributes about the Employee are present. | |||
The solution to this problem is the Active Model class. An '''Active Model''' is an Active Record devoid of all the database persistence functionality. Infact, the rails developers segregated all the non database functionality out of an '''Active Record''' into an '''Active Model'''. | |||
===Serializer=== | |||
Serializer is a small program that takes an object residing in program memory (for example: could be a C# object in an ASP.NET application, or an ActiveModel object in a Rails application) and converts this information into format that is transferrable over the wire, for example, JSON and XML. This is required because the client may be written in Java and there is no way for it to read an Active Model Ruby Object that exists in memory on the server side. | |||
===API Server Application=== | |||
API Server application is an application which serves data requests coming from a variety of clients (for example: mobile, desktop) in various formats such as JSON, XML. For example, An employee data web service which returns employee age and name in both JSON and XML | |||
=Why Active Model Serializers?= | |||
===Custom response attributes=== | |||
It is not always the case that an API application serves a request by responding with data as it is stored on the server. The API designer has a task of exposing just the right data to the clients and hence needs fine control over exactly what part of the data on the server is returned in a response. An Active Model Serializer could help return just the Employee age and name to the client as JSON and ignore all the other attributes set on an Employee Model. | |||
<br> | |||
===Separation of Concerns=== | |||
The response data could also be customized in the respond_to method of the rails controller action or for example, by implementing a custom as_json method on the model class. However this breaks the separation of concerns principle as we let something like formatting the response data venture into the controller and model layer. With Active Model Serializers we can keep this functionality segregated from the application. | |||
<br> | |||
===Maintainability=== | |||
Client needs keep changing. Let's say a Client requests some changes to the API response data, only the Active Model Serializer would get affected and there are no side effects on the Controller or the Model itself. | |||
<br> | |||
=How Active Model Serializer Works?= | |||
[[File:Active-Model-Serializer2.png|right]] | |||
Active Model Serializers work with the help of two components namely '''Serializer''' and '''Adapter'''. Let's say we wanted to write an Active Model Serializer for customizing an API that returns Employee data as json. We would have to write an '''EmployeeSerializer''' which is configured to use a '''JsonApiAdapter'''. The '''Serializer''' Component would specify which attributes and relationships of Employee to other models would be captured in the response data. For example, it might choose to return the Employee name, age and the data about the employee's family members which are different models. The '''Adapter''' component specifies how to format this selected Employee data in the correct format as required by the client. A JsonApiAdapter would format it as JSON and is the default adapter used by an Active Model Serializer. However you can always supply your own Adapter for the serializer to use. | |||
Assume the Employee data API scenario cited above and Refer to the figure. | |||
===Step 1 : Controller=== | |||
The Controller receives the request for json data and routes it to a specific action method | |||
===Step 2 : Action Method=== | |||
The action method has a respond_to method that returns Employee model as JSON. When Rails encounters this line, it looks for an EmployeeSerializer in the project specifically in a file employee_serializer to get the exact data to be put into the response. | |||
Serializer is a | ===Step 3 : Active Model Serializer=== | ||
The EmployeeSerializer is by default configured to use a JsonApiAdapter and formats the specified attributes and relationships in the JSON format. This forms the final customized response. | |||
Instead of returning the default JSON conversion of the Employee model from the respond_to method, that Rails does automatically for us, we were able to customize our API response data through the use of Active Model Serializers in a non-intrusive way maintaining separation from the controller and the model layers. | |||
=Using Active Model Serializer= | |||
Time to write some code now. | |||
Step1 : Add to the gem file the following line : | ===Step1 : Install gem === | ||
Active Model Serializer is available as a gem. Add to the gem file the following line : | |||
<pre> | |||
gem 'active_model_serializers' | gem 'active_model_serializers' | ||
</pre> | |||
Step2 : Run the | ===Step2 : Update bundle=== | ||
Run the following command at the terminal window to install the gem. | |||
Step3 : This gem provides a serializer generator for the selected Active Model. For | <pre> | ||
bundle update | |||
</pre> | |||
===Step3 : Generate Serializer Class=== | |||
This gem provides a serializer generator for the selected Active Model. For example, if employee was your Active Model run : | |||
<pre> | |||
rails g serializer employee | rails g serializer employee | ||
</pre> | |||
This would create a file employee_serializer.rb containing | This would create a file employee_serializer.rb containing the following code in the newly created app/serializers directory of your project: | ||
<pre> | <pre> | ||
class EmployeeSerializer < ActiveModel::Serializer | class EmployeeSerializer < ActiveModel::Serializer | ||
Line 41: | Line 150: | ||
end | end | ||
</pre> | </pre> | ||
===Step4 : Customize Serializer=== | |||
Add your custom attributes and any other customizations to this file. For example: We are adding a url attribute to get details of this employee, although the Employee Active Model does not have any such attribute. We also add a custom attribute '''EmpConciseDetails''' that appends two attributes of an Employee. We add it because the client might be interested in it. That is the power of Active Model Serializer. It does not require us to define another property on the Employee Active Model to return its Url or EmpConciseDetails. | |||
<pre> | <pre> | ||
class EmployeeSerializer < ActiveModel::Serializer | class EmployeeSerializer < ActiveModel::Serializer | ||
attributes : | attributes :url, :EmpConciseDetails | ||
def url | def url | ||
employee_url(object) | employee_url(object) | ||
end | end | ||
def EmpConciseDetails | |||
:name + " " + :age | |||
end | |||
end | end | ||
</pre> | </pre> | ||
===Step5 : Get Custom object formatting=== | |||
That's it! Whenever an Employee is requested the custom EmployeeSerializer does the task of serializing the employee to json and will now include a url token to view the Employee and Employee name and age in concise form: | |||
<pre> | |||
{ | |||
url: /employees/1.json | |||
EmpConciseDetails: emp_name 26 | |||
} | |||
</pre> | |||
=References= | |||
#[http://railscasts.com/episodes/409-active-model-serializers Railcast on Active Model Serializers] | |||
#[https://github.com/rails-api/active_model_serializers Active Model Serializer module on github] | |||
#[http://asciicasts.com/episodes/409-active-model-serializers Active Model Serializer Asciicast] | |||
#[https://www.youtube.com/watch?v=G6ipU6AiEXY Active Model Serializer Video Tutorial] | |||
#[http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/ Active Model] | |||
#[http://en.wikipedia.org/wiki/Serialization Serialization wiki] | |||
#[http://en.wikipedia.org/wiki/JSON JSON wiki] | |||
#[http://en.wikipedia.org/wiki/RubyGems RubyGems wiki] |
Latest revision as of 03:44, 26 September 2014
Active Model Serializers
Ruby on Rails offers strong features to write API based server applications, which expose data through a well designed API and serve data in various formats as requested. One of these features is Active Model Serializers which helps the API designers to have fine control over the response generated by the API to a data request. Active Model Serializers help programmers do this very easily and cleanly without breaking separation of concerns in a rails application. They promote convention over configuration, thus reducing code bloat and increasing code scalibility and readability.
Background
The modern web has seen more and more web applications that are written as web services or web APIs. These applications do not return a web page to the client browser, but return just the bare bones data to the client in the format requested. This gives the clients the liberty to develop customized User Interfaces for the same web application. A common example is the Rotten Tomatoes API which offers movie data in JSON, XML and various other formats. The clients request the Rotten Tomatoes web server for the data in the desired format, and the web server is able to respond with the correct data in the required format.
Active Model Serializers are a way for Web API designers who use Ruby on Rails to have full control over the formatting and the content of data that is returned to clients. For eg. An employee data model returned as JSON to a data request might look like this:
{Employee: { name: emp_name age: 26 salary: 35000 weight: 90kg height: 170 } }
But the API designer may want to improve this formatting and exclude certain fields so that the response is just the data without clubbing it under the "Employee" root token:
{ EmpConciseDetails: emp_name 26 }
In a Ruby On Rails application there could be a couple of ways to achieve this.
Option 1 : Customizing response in Controller Layer
The first one is shown below where we can specify the JSON formatting option in the action method which handles the data request. The below method fetches the desired Employee and returns its JSON representation to the client:
def show @emp = Employee.find(params[:id]) respond_to do |format| format.json { render :json => @emp, root:false} end end
Specifying root: false above will remove the unwanted Employee token from the resultant JSON. However this method is not recommended as we are littering our controller layer with data formatting code. If we want to maintain the MVC separation of concerns, formatting the response data does not belong in the controller layer.
Option 2: Customizing response in Model Layer
Another option is to override the as_json method of the Employee data model which is typically an Active Record in a Rails application:
class Employee < ActiveRecord def as_json(options) super(:only => [:name, :age]) end end
The above as_json method is called whenever Rails has to convert the Employee object to its JSON format to send to the client. Our custom as_json method will help us return only the name and the age as desired. However we again have a problem as we are littering our Model layer with custom formatting tasks which it is not intended to handle.
Through Active model serializers we are able to place these custom formatting tasks to a separate class namely an EmployeeSerializer which inherits from ActiveModel::Serializer class. The Rails application is able to discover this serializer when Employee data model has to be serialized without any configuration from the programmer's side. The advantages of this scheme are dual. It helps keep separation of concerns in the Rails application, and its done with a convention over configuration approach. Specifics are discussed in following sections.
Introduction
Knowledge of the following terms would help before getting to the usage of Active Model Serializers.
Active Model
Active Model is closely related to the widely used concept of Active Record in the Rails world. Active Record is an object that maps directly to the data in our database. An Active Record takes care of fetching and saving data to the database. For example, in an employee records application an Employee Active Record helps to persist a newly created/edited Employee. So how is an Active Model different?
It may not always be the case that the Employee model has to be backed by a database and the programmer may decide not to implement an Employee model as an Active Record. However the programmer might want some of the model attribute validation functionality that an Active Record provides. For eg: Imagine an employee records system that handles an create employee form by reading the POST data into an Employee object. The system does not persist this Employee model to any database though. It simply emails the admin with all the captured data present in the Employee model. However before emailing the programmer might want to validate if certain required attributes about the Employee are present.
The solution to this problem is the Active Model class. An Active Model is an Active Record devoid of all the database persistence functionality. Infact, the rails developers segregated all the non database functionality out of an Active Record into an Active Model.
Serializer
Serializer is a small program that takes an object residing in program memory (for example: could be a C# object in an ASP.NET application, or an ActiveModel object in a Rails application) and converts this information into format that is transferrable over the wire, for example, JSON and XML. This is required because the client may be written in Java and there is no way for it to read an Active Model Ruby Object that exists in memory on the server side.
API Server Application
API Server application is an application which serves data requests coming from a variety of clients (for example: mobile, desktop) in various formats such as JSON, XML. For example, An employee data web service which returns employee age and name in both JSON and XML
Why Active Model Serializers?
Custom response attributes
It is not always the case that an API application serves a request by responding with data as it is stored on the server. The API designer has a task of exposing just the right data to the clients and hence needs fine control over exactly what part of the data on the server is returned in a response. An Active Model Serializer could help return just the Employee age and name to the client as JSON and ignore all the other attributes set on an Employee Model.
Separation of Concerns
The response data could also be customized in the respond_to method of the rails controller action or for example, by implementing a custom as_json method on the model class. However this breaks the separation of concerns principle as we let something like formatting the response data venture into the controller and model layer. With Active Model Serializers we can keep this functionality segregated from the application.
Maintainability
Client needs keep changing. Let's say a Client requests some changes to the API response data, only the Active Model Serializer would get affected and there are no side effects on the Controller or the Model itself.
How Active Model Serializer Works?
Active Model Serializers work with the help of two components namely Serializer and Adapter. Let's say we wanted to write an Active Model Serializer for customizing an API that returns Employee data as json. We would have to write an EmployeeSerializer which is configured to use a JsonApiAdapter. The Serializer Component would specify which attributes and relationships of Employee to other models would be captured in the response data. For example, it might choose to return the Employee name, age and the data about the employee's family members which are different models. The Adapter component specifies how to format this selected Employee data in the correct format as required by the client. A JsonApiAdapter would format it as JSON and is the default adapter used by an Active Model Serializer. However you can always supply your own Adapter for the serializer to use.
Assume the Employee data API scenario cited above and Refer to the figure.
Step 1 : Controller
The Controller receives the request for json data and routes it to a specific action method
Step 2 : Action Method
The action method has a respond_to method that returns Employee model as JSON. When Rails encounters this line, it looks for an EmployeeSerializer in the project specifically in a file employee_serializer to get the exact data to be put into the response.
Step 3 : Active Model Serializer
The EmployeeSerializer is by default configured to use a JsonApiAdapter and formats the specified attributes and relationships in the JSON format. This forms the final customized response.
Instead of returning the default JSON conversion of the Employee model from the respond_to method, that Rails does automatically for us, we were able to customize our API response data through the use of Active Model Serializers in a non-intrusive way maintaining separation from the controller and the model layers.
Using Active Model Serializer
Time to write some code now.
Step1 : Install gem
Active Model Serializer is available as a gem. Add to the gem file the following line :
gem 'active_model_serializers'
Step2 : Update bundle
Run the following command at the terminal window to install the gem.
bundle update
Step3 : Generate Serializer Class
This gem provides a serializer generator for the selected Active Model. For example, if employee was your Active Model run :
rails g serializer employee
This would create a file employee_serializer.rb containing the following code in the newly created app/serializers directory of your project:
class EmployeeSerializer < ActiveModel::Serializer attributes :id end
Step4 : Customize Serializer
Add your custom attributes and any other customizations to this file. For example: We are adding a url attribute to get details of this employee, although the Employee Active Model does not have any such attribute. We also add a custom attribute EmpConciseDetails that appends two attributes of an Employee. We add it because the client might be interested in it. That is the power of Active Model Serializer. It does not require us to define another property on the Employee Active Model to return its Url or EmpConciseDetails.
class EmployeeSerializer < ActiveModel::Serializer attributes :url, :EmpConciseDetails def url employee_url(object) end def EmpConciseDetails :name + " " + :age end end
Step5 : Get Custom object formatting
That's it! Whenever an Employee is requested the custom EmployeeSerializer does the task of serializing the employee to json and will now include a url token to view the Employee and Employee name and age in concise form:
{ url: /employees/1.json EmpConciseDetails: emp_name 26 }