PlainQuery
PlainQuery is a simple gem that helps you write clear and flexible query objects. Main task for this gem is performing complex querying on active record relation, make it intuitive.
It helps in decomposing your fat ActiveRecord models and keeping your code slim and readable by extracting complex SQL queries or scopes into the separated classes that are easy to write, read and use.
PlainQuery is useful when you need to build one or more queries based on incoming parameters in the request. It hides scope building logic inside query class and allows you to structure the building of the scope.
Installation
Add this line to your application's Gemfile:
gem 'plain_query'
And then execute:
$ bundle
Or install it yourself as:
$ gem install plain_query
Usage
Setting up a query object
For setting up a query object you need to inherit your query class from PlainQuery. Then you need to describe query steps by using query_step method. Query steps perform in writing order.
class UsersQuery < PlainQuery
model User
query_step :filter_by_activity, if: { options[:only_active] }
query_step :filter_by_phone_presence
query_step :order_by_name
def filter_by_activity
relation.where(active: true)
end
def filter_by_phone_presence
relation.where.not(phone: nil)
end
def order_by_name
relation.order(name: :asc)
end
end
Query calling
users = UsersQuery.call(User.all, only_active: true)
Query object implements #call method with two arguments:
relation - Base scope which will be mutated inside query object. (User.all in example).
If you dont pass it - will be used default scope from model declaration inside query object.
options - Any data which will be used inside query steps or execution conditions. (only_active: true in example).
Query object returns scope builded by query steps execution.
query_step
query_step is a main part of query building.
It declares which query change method will be executed, condition of execution and order of query change methods execution.
It has several arguments:
query_step STEP_NAME, CONDITION_OF_EXECUTION
STEP_NAME is a name of method which will be executed.
CONDITION_OF_EXECUTION is a method which allows or denieds execution of query step.
Type of condition can be if: or unless:. Condition definition can be proc (lambda) or some query object method name.
Using in Active Record model scope.
First of all you need to set correct model name inside query object.
It uses for correct base scope building without passing relation to query object.
model User
class User < ActiveRecord::Base
scope :active_clients, ActiveClientsQuery
end
class ActiveClientsQuery < PlainQuery
model User
query_step :filter_by_activity
query_step :filter_by_role
def filter_by_activity
relation.where(active: true)
end
def filter_by_role
relation.where(role: :client)
end
end
And then you can use scope from model:
User.active_clients
Also you can pass to Active Record scope some options:
class User < ActiveRecord::Base
scope :active_clients, -> { ActiveClientsQuery.call(self, option: true) }
end
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/plain_query. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the PlainQuery project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.