The main principle of software development is to keep the code DRY (Don’t Repeat Yourself) to reduce the reduction in the code. So, Ruby On Rails follows some design patterns to achieve the DRY principle. Though we can use models, helpers, and concerns to make the code dry, if the code complexity is higher or we are using external API, in that case, we use rails Design Patterns.
Here are the ruby on rails design patterns:
(1) Service Objects
(2) View Objects
(3) Query Objects
(5) Form Objects
(6) Value Objects
(7) Policy Objects
Let’s Discuss the all above-mentioned ruby design patterns one by one with examples.
(1) Service Objects
- What is a Service Object?
- When to use service objects?
A Service Object is PORO – Plain Old Ruby Object, which is meant to encapsulate business logic and complex calculations into manageable classes and methods.
1. When we need to perform complex calculations or business logic, for, e.g. if we need to calculate employee’s salaries based on their attendance.
2. When we need to implement any other API, for, eg. we need to implement payment gateway such as Stripe.
3. When we want to import CSV that contains the bulk of data.
4. When we need to clear garbage/unused/old data from the database in an efficient manner that it won’t affect the existing data.
In this example, we perform stripe integration with the help of Service Object. Here the stripe service will create stripe customers based on their email address and token.
Have a look at PaymentsController; here, you can see that the controller is too skinny. Payment service has solved the problem of too much code inside the controller and made the controller skinny and readable.
Now create payment_service.rb inside your_project/app/services folder.
(2) View Objects (Presenter)
View Objects allow us to encapsulate all view related logic and keep both models and views neat. The View should not contain calculation logic.
To solve the calculation logic problem, we can use rails helper, but if the complexity of the code is high, in that case, we should use the Presenter.
Have a look on the below screenshot of view page:
Here we can see that we are concatenating the user’s first_name and last_name on the view. Which is not a good practice. Hence to solve this problem, we should use the presenter.
Let’s create a presenter class to solve this.
Save it under app/presenters/user_presenter.rb and create the presenter’s folder if you are not having it.
Now let’s change the view to make it more readable and without any calculations.
(3) Query Object
Query Object is a type of design pattern that lets us fetch query logic from Controllers and Models into reusable classes.
Let’s have a look at the below example:
We want to request a list of posts with the type “video” that has a view count greater than 1000 and that the current user can access.
The problems in the above code:
- This code isn’t reusable
- It’s hard to test the logic.
- Any changes to the post schema can break this logic/code.
To make the controller skinny, readable, and neat we can use scopes:
So, the controller will look like that:
But still, it is not an appropriate solution; here we need to create scopes for every query condition we want to add, we are also increasing the code in the Model with various combinations of scope for diverse use cases.
To solve this kind of problem, we use the Query Object:
Now, it’s reusable! We can use this class to query any other models that have a similar scheme.
The decorator is a design pattern that allows the behavior to be added to an object, dynamically, without disturbing the behavior of other objects of the same class. Decorators can be useful for cleaning up logic/code written inside the view and controller in a Rails application.
(1) Create an app/decorator folder.
(2) Add decorate helper in ApplicationHelper.
(3) Add base_decorator.rb in app/decorators folder.
(4) Add user_decorator.rb in app/decorators folder.
(5) Initialize @user_decorator in your user_controller.
(6) Let’s use this in our view(show.html.erb).
(5) Form Objects
The form object is a design pattern that is used to encapsulate the code related to validation and persisting data into a single unit.
Let’s have a look at the example of the Form Objects.
Let’s assume that we have a rails post model and a controller (posts_controller) action for creating the new post.
Let’s discuss the problem, Here Post Model contains all validation logic, so it’s not reusable for other entities, e.g., Admin.
The better solution is to move the validation logic to a separate singular responsibility class that we might call PostForm:
Now, We can use it inside our posts_controller like that:
(6) Value Object
Value Object is a Design pattern. It represents value but not something unique in your system like a user object. Value Objects always return only values.
Let’s have a look at the example for better understanding:
We are doing the following thing with the email:
- We are not changing the email value.
- We return only values.
Now, let’s create the value object:
Now, we just need to use Email value object link this:
(7) Policy Object
Policy object is similar to the Service object; the only difference is policy object is responsible for the read operations, and the service object is responsible for write operations. In rails, we use cancan or pundit gem for the authorization, but these gems are suitable if the application complexity is medium, If the application complexity is high (in terms of authorization), then, in that case, we use policy object for better efficiency. It returns a boolean value (true or false).
Let’s have a look at the below example for better understanding.
Let’s create a policy (app/policies/user_policy.rb):
As we can see from the above example that user_policy is authorizing and returning a boolean value.