I would like to introduce you to my new gem, the basic idea of which I have used in many commercial projects over the past 3 years. And, since the gem is now done, it's time to make it public and open-source.
tiny_filter provides an abstraction layer for filtering collections with a convenient helper that makes your ActiveRecord method chaining look like this:
Post.filter_by(tag_id: 1, from: 2.days.ago).order(:created_at)
All you have to do is implement a filter class with whatever logic you want. The gem provides you with a pretty DSL that makes your code look well:
# app/filters/post_filter.rb
PostFilter < ApplicationFilter
filters :tag_id do |scope, value|
scope.joins(:tags).where(tags: { id: value })
end
filters :from do |scope, value|
scope.where("created_at >= ?", value)
end
end
Common use cases:
1) Filtering collections with user input.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
private
def filter_params
return {} unless params.key?(:filter)
params.require(:filter).permit(*permitted_filters)
.to_h.symbolize_keys
end
def permitted_filters
[]
end
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Posts.filter_by(filter_params).order(:created_at)
end
private
def permitted_filters
%i[tag_id from]
end
end
2) Simplifying queries.
tiny_filter gives you a flexible abstraction where the interface doesn't depend on the DB structure, so you can use it anywhere instead of ActiveRecord scope
s and where
s.
# filter_args - a hash with filter params that you can collect dynamically.
posts = Post.filter_by(filter_args)
# versus where:
posts = Post.all
posts = posts.where("created_at >= ?", from) if from
posts = posts.joins(:tags).where(tags: { id: tag_id }) if tag_id
posts
# and versus scope:
posts = Post.all
posts = posts.from(from) if from
posts = posts.with_tag(tag_id) if tag_id
posts
More links:
Documentation
RubyGems
Top comments (0)