Database queries are very common when you develop web applications. ActiveRecord in Ruby on Rails allows you not to write tons of pre-compiled SQL code and as a result creates simple and powerful queries in Ruby in a simple way.
It is a fact that many of the great capabilities that Ruby and ActiveRecord offer are still not used. Often you will see a lot of scope in Ruby on Rails models, endless query strings in controllers and even bulky SQL blocks.
ActiveRecord query cases are not good
These bad code can create obstacles and become the reason for developers to headache with web applications in the real world.
Typical DB query applications
- Large query snippets in your controller / model / service mess up your code
- It is difficult to understand complex database requests
- SQL insertion is inconsistent and is often written with ActiveRecord queries
- Testing an isolated query separately is very difficult
- It is difficult to write, extend or inherit queries
- The Single Responsibility principle is often violated
These problems can be solved using the Query Object pattern – a common technique for isolating your complex queries.
The ideal query object is a separate class that contains a specific query that implements only one business logic rule.
In most cases, the PORO query object accepts relationships in the constructor and defines named queries like a common ActiveRecord method: Using the Query object in the controller
If your mirror relates to the current QueryObject, you can easily expand its relationship instead of cluttering your models.
The ActiveRecord :: QueryMethods.extending method will help you:
Extend scope for Query Object relationships
Write the Query Objects:
Query Objects should be created to support writing other Query Objects and ActiveRecord relationships. In the example below, two Query Objects are written to represent an SQL query:
Inheriting the Query Object:
If you have similar queries, you may want them to be inherited to reduce repetition and to follow the DRY principle:
Testing Query Objects:
Query Object is designed for easy testing. In most cases, you only need to check the core methods defined in the query to know their results:
- Follow the principle of Single Responsibility
- Can be easily independently tested
- Can be combined / expanded with another Query Object
- Can be easily reused in any other part of the application
- Returns ActiveRecord :: Relation, not an Array
- Only represent database queries, not logical or action bussiness
- Query Object methods are named like ActiveRecord (all, last, count, etc).
Use the Query Object when:
- You need to reuse a query in many places in the application
- You need to expand, edit, or inherit queries and their relationships
- You need to write lots of SQL statements, but don’t want to mess up your code
- Your query is too complex / broad for a method or scope
Do not use Query Object when:
- Your query is simple enough for just one method or scope
- You do not need to expand, edit or inherit your query
- Your query is unique and you do not want to make it reusable