TinyFilter is created to provide a simple object-oriented abstraction layer for filtering collections. It is mainly purposed for ActiveRecord/Sequel models, but you can also use it with any enumerable.
Post.where(title: "Wow!").filter_by(from: 2.days.ago, to: 1.day.ago).order(:created_at)-
Install the gem and add to the application's Gemfile by executing:
bundle add tiny_filter
-
Generate an application filter as an entry point to all your future filters:
bin/rails g tiny_filter:install
This will generate ApplicationFilter inside app/filters directory.
This directory is intended to store all your filters.
To generate a filter class simply run tiny_filter:filter command.
For example, to create a filter class for Post with filters from and to, run:
bin/rails g tiny_filter:filter post from toThis will generate the PostFilter class inside the app/filters directory with from and to filters.
Each filter is defined by calling filters method inside class body.
filters accepts two arguments:
key- a filter name, used as identifier;block- a block with filter logic, that returns filtered collection and itself accepts two arguments:scope- a collection that should be filtered;value- a value for filtering.
When you perform filtering, provided key indicate filter key and provided value is passed to value param in corresponding filter block.
scopes receive collections in a pipeline manner:
first executed filter receives original collection,
second and further receive the return collection of the previous filter.
To execute filtering, simply call filter with the initial scope and options provided.
class UserFilter < ApplicationFilter
filters(:name) { |scope, value| scope.where(first_name: value) }
filters(:surname) { |scope, value| scope.where(last_name: value) }
end
UserFilter.filter(User, name: "John", surname: "Doe")
# Which is equivalent to:
# User.where(first_name: "John").where(last_name: "Doe")Notice, that your filters must return the same scope type as they accept. It guarantees that scope behaves the same way as in other filters in this class.
filters(:title) { |scope, value| scope.where("title ILIKE ?", value) }
# bad - scope is an ActiveRecord collection, but the return value is an array.
filters(:from) { |scope, value| scope.select { |e| e.created_at >= value } }
# good - scope and return value are both ActiveRecord collections.
filters(:from) { |scope, value| scope.where("created_at >= ?", value) }Thus if the initial scope for filtering is an ActiveRecord collection, it is a bad practice for filter to return not an ActiveRecord collection. Otherwise you can face errors depending on the provided options order.
TinyFilter provides a simple concern, that adds just one method filter_by,
that can be used in ActiveRecord method chaining.
Just include TinyFilter::Concern in your model and that's all!
class Post < ApplicationRecord
include TinyFilter::Concern
endNow you can use filtering everywhere in your model method chaining.
Post.where(title: "something interesting").filter_by(from: 2.days.ago, to: 1.day.ago).order(:title)
Post.filter_by(from: 1.year.ago)The previously mentioned filter concern can also be used in Sequel models.
class Artist < Sequel::Model
include TinyFilter::Concern
endQuerying examples:
Artist.where(name: "Kirill").filter_by(from: 2.days.ago, to: 1.day.ago).order(:name).all
Artist.filter_by(from: 1.year.ago).allBy default a filter class and a model are mapped by a model name with a suffix Filter.
For example, the model My::Class by default will use the My::ClassFilter as a filter class.
You can customize this behavior by implementing a filter_class class method
with an appropriate class as a return value.
class My::Class < ApplicationRecord
# ...
def self.filter_class
CustomFilter
end
# ...
endYou can use filters with Plain Old Ruby collections like so:
options # filter options, for example: `{ from: 2.days.ago, to: 1.day.ago }`
collection # can be any Enumerable: array, hash, your custom collection, etc etc
MyFilter.filter(collection, options)After checking out the repo, run bin/setup to install dependencies. Then, run bin/rspec to run the tests.
You can also run bin/rubocop to lint the source code
and bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bin/rake install.
To release a new version, update the version number in version.rb, and then run bin/rake release,
which will create a git tag for the version, push git commits and the created tag,
and push the .gem file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/lassoid/tiny_filter. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the TinyFilter project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.