Wharel helps you write concise Arel queries with ActiveRecord using Virtual Rows inspired by Sequel.
Although similar in spirit to gems like
Squeel and
BabySqueel, which provide sophisticated
block-based interfaces for querying with Arel, Wharel is much much smaller. In
fact, the core of the gem is only 30 lines
long! It uses a
single BasicObject
as a clean
room to evaluate
the query block. That's really all there is to it.
For a more detailed explanation of the implementation, see this blog post.
Add this line to your application's Gemfile:
gem 'wharel', '~> 1.0.0'
And then execute:
$ bundle
Or install it yourself as:
$ gem install wharel
Suppose we have a model Post
:
class Post < ApplicationRecord
has_many :comments
end
And let's assume our Post
has columns title
and content
.
Now, if we wanted to find all the posts with a title which matched the string "foo" and content which matched the string "bar", we'd have to resort to something like this:
title = Post.arel_table[:title]
content = Post.arel_table[:content]
Post.where(title.matches("foo").and(content.matches("bar")))
With Wharel, you can drop the boilerplate and just use a block:
Post.where { title.matches("foo").and(content.matches("bar")) }
Wharel will map title
and content
in the block to the appropriate Arel
attribute for the column.
Wharel also supports most other query methods, e.g. not
:
Post.where.not { title.eq("foo") }
... and order
:
Post.order { title.lower }
Wharel also supports select
, having
, pluck
, pick
, group
and or
in the same way.
Now suppose we have another model Comment
with a column content
, and a
Post
has_many :comments
:
class Post < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :post
end
Now we want to find all comments which match the title of the comment's post. With standard Arel, you could do this with:
posts = Post.arel_table
comments = Comment.arel_table
Comment.joins(:post).where(comments[:content].matches(posts[:title]))
Using Wharel, you can pass an argument to blocks to handle this case:
Comment.joins(:post).where { |c| Post.where { |p| c.content.matches(p.title) } }
Much better!
Notice something wrong or a feature missing? Post an issue or create a pull request.
The gem is available as open source under the terms of the MIT License.