rzane/baby_squeel
rzane/baby_squeel has 508 GitHub stars and 53 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-11 20:25 UTC. Last push 2025-11-30.
:pig: An expressive query DSL for Active Record
측정된 스타 증가
- in 24h
- 0
- in 7 days
- 업데이트 지연
- in 15 days
- 업데이트 지연
- in 1 month
- 업데이트 지연
서로 겹치는 기간의 순증가량이며 일별 합계가 아닙니다. 측정값이 없으면 비워 둡니다. 스타는 관심도를 나타내며 품질을 보장하지 않습니다.
README.md
OriginalBabySqueel 🐷
Have you ever used the Squeel gem? It's a really nice way to build complex queries. However, Squeel monkeypatches Active Record internals, because it was aimed at enhancing the existing API with the aim of inclusion into Rails. However, that inclusion never happened, and it left Squeel susceptible to breakage from arbitrary changes in Active Record, eventually burning out the maintainer.
BabySqueel provides a Squeel-like query DSL for Active Record while hopefully avoiding the majority of the version upgrade difficulties via a minimum of monkeypatching. :heart:
Installation
Add this line to your application's Gemfile:
gem 'baby_squeel'
And then execute:
$ bundle
Or install it yourself as:
$ gem install baby_squeel
Introduction
With Active Record, you might write something like this:
Post.where('created_at >= ?', 2.weeks.ago)
But then someone tells you, "Hey, you should use Arel!". So you convert your query to use Arel:
Post.where(Post.arel_table[:created_at].gteq(2.weeks.ago))
Well, that's great, but it's also pretty verbose. Why don't you give BabySqueel a try:
Post.where.has { created_at >= 2.weeks.ago }
Quick note
BabySqueel's blocks use instance_eval, which means you won't have access to your instance variables or methods. Don't worry, there's a really easy solution. Just give arity to the block:
Post.where.has { |post| post.created_at >= 2.weeks.ago }
Usage
Okay, so we have some models:
class Post < ActiveRecord::Base
belongs_to :author
has_many :comments
end
class Author < ActiveRecord::Base
has_many :posts
has_many :comments, through: :posts
end
class Comment < ActiveRecord::Base
belongs_to :post
end
Selects
Post.selecting { (id + 5).as('id_plus_five') }
# SELECT ("posts"."id" + 5) AS id_plus_five FROM "posts"
Post.selecting { id.sum }
# SELECT SUM("posts"."id") FROM "posts"
Post.joins(:author).selecting { [id, author.id] }
# SELECT "posts"."id", "authors"."id" FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
Wheres
Post.where.has { title == 'My Post' }
# SELECT "posts".* FROM "posts"
# WHERE "posts"."title" = 'My Post'
Post.where.has { title =~ 'My P%' }
# SELECT "posts".* FROM "posts"
# WHERE ("posts"."title" LIKE 'My P%')
Author.where.has { (name =~ 'Ray%') & (id < 5) | (name.lower =~ 'zane%') & (id > 100) }
# SELECT "authors".* FROM "authors"
# WHERE ("authors"."name" LIKE 'Ray%' AND "authors"."id" < 5 OR LOWER("authors"."name") LIKE 'zane%' AND "authors"."id" > 100)
Post.joins(:author).where.has { author.name == 'Ray' }
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."name" = 'Ray'
Post.joins(author: :posts).where.has { author.posts.title =~ '%fun%' }
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# INNER JOIN "posts" "posts_authors" ON "posts_authors"."author_id" = "authors"."id"
# WHERE ("posts_authors"."title" LIKE '%fun%')
Orders
Post.ordering { [id.desc, title.asc] }
# SELECT "posts".* FROM "posts"
# ORDER BY "posts"."id" DESC, "posts"."title" ASC
Post.ordering { (id * 5).desc }
# SELECT "posts".* FROM "posts"
# ORDER BY "posts"."id" * 5 DESC
Post.select(:author_id).group(:author_id).ordering { id.count.desc }
# SELECT "posts"."author_id" FROM "posts"
# GROUP BY "posts"."author_id"
# ORDER BY COUNT("posts"."id") DESC
Post.joins(:author).ordering { author.id.desc }
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# ORDER BY "authors"."id" DESC
Joins
Post.joining { author }
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
Post.joining { [author.outer, comments] }
# SELECT "posts".* FROM "posts"
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
Post.joining { author.comments }
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# INNER JOIN "posts" "posts_authors_join" ON "posts_authors_join"."author_id" = "authors"."id"
# INNER JOIN "comments" ON "comments"."post_id" = "posts_authors_join"."id"
Post.joining { author.outer.comments.outer }
# SELECT "posts".* FROM "posts"
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# LEFT OUTER JOIN "posts" "posts_authors_join" ON "posts_authors_join"."author_id" = "authors"."id"
# LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts_authors_join"."id"
Post.joining { author.comments.outer }
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# LEFT OUTER JOIN "posts" "posts_authors_join" ON "posts_authors_join"."author_id" = "authors"."id"
# LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts_authors_join"."id"
Post.joining { author.outer.posts }
# SELECT "posts".* FROM "posts"
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# INNER JOIN "posts" "posts_authors" ON "posts_authors"."author_id" = "authors"."id"
Post.joining { author.on((author.id == author_id) | (author.name == title)) }
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON ("authors"."id" = "posts"."author_id" OR "authors"."name" = "posts"."title")
Post.joining { |post| post.author.as('a').on { (id == post.author_id) | (name == post.title) } }
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" "a" ON ("a"."id" = "posts"."author_id" OR "a"."name" = "posts"."title")
Picture.joining { imageable.of(Post) }
# SELECT "pictures".* FROM "pictures"
# INNER JOIN "posts" ON "posts"."id" = "pictures"."imageable_id" AND "pictures"."imageable_type" = 'Post'
Picture.joining { imageable.of(Post).outer }
# SELECT "pictures".* FROM "pictures"
# LEFT OUTER JOIN "posts" ON "posts"."id" = "pictures"."imageable_id" AND "pictures"."imageable_type" = 'Post'
Grouping
Post.selecting { id.count }.grouping { author_id }.when_having { id.count > 5 }
# SELECT COUNT("posts"."id") FROM "posts"
# GROUP BY "posts"."author_id"
# HAVING (COUNT("posts"."id") > 5)
Functions
Post.selecting { coalesce(author_id, 5).as('author_id_with_default') }
# SELECT coalesce("posts"."author_id", 5) AS author_id_with_default FROM "posts"
Subqueries
Post.joins(:author).where.has {
author.id.in Author.select(:id).where(name: 'Ray')
}
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."id" IN (
# SELECT "authors"."id" FROM "authors"
# WHERE "authors"."name" = 'Ray'
# )
Exists
Post.where.has {
exists Post.where.has { author_id == 1 }
}
# SELECT "posts".* FROM "posts"
# WHERE (
# EXISTS(
# SELECT "posts".* FROM "posts"
# WHERE "posts"."author_id" = 1
# )
# )
Custom SQL Operators
authors = Author.selecting { name.op('||', quoted('-dizzle')).as('swag') }
# SELECT "authors"."name" || '-dizzle' AS swag FROM "authors"
authors.first.swag #=> 'Ray Zane-dizzle'
Querying tables without Active Record models
table = BabySqueel[:some_table]
Post.joining {
table.on(table.post_id == id)
}.where.has {
table.some_column == 1
}
Polymorphism
Given this polymorphism:
# app/models/picture.rb
belongs_to :imageable, polymorphic: true
# app/models/post.rb
has_many :pictures, as: :imageable
The query might look like this:
Picture.
joining { imageable.of(Post) }.
selecting { imageable.of(Post).id }
Helpers
# SQL Literals
Post.select('1 as one').ordering { sql('one').desc }
# Quoting
Post.selecting { title.op('||', quoted('diddly')) }
# Functions
Post.selecting { func('coalesce', id, 1) }
Sifters
Sifters are like little snippets of conditions that can take arguments.
class Post < ActiveRecord::Base
sifter :funny do
title == 'rabies'
end
end
class Author < ActiveRecord::Base
sifter :name_contains do |string|
name =~ "%#{string}%"
end
end
Post.joins(:author).where.has {
sift(:funny) | author.sift(:name_contains, 'blergh')
}
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE ("posts"."title" = 'rabies' OR "authors"."name" LIKE '%blergh%')
What's what?
The following methods give you access to BabySqueel's DSL:
| BabySqueel | Active Record Equivalent |
|---|---|
selecting |
select |
ordering |
order |
joining |
joins |
grouping |
group |
where.has |
where |
when_having |
having |
Migrating from Squeel
Check out the migration guide.
Development
- Pick an Active Record version to develop against, then export it:
export AR=7.2.2. - Run
bin/setupto install dependencies. - Run
raketo run the specs.
One-liner to run the specs with different rails versions:
export AR='~> 7.1.6'; rm Gemfile.lock; bin/setup; rake
export AR='~> 7.2.3'; rm Gemfile.lock; bin/setup; rake
export AR='~> 8.0.4'; rm Gemfile.lock; bin/setup; rake
export AR='~> 8.1.1'; rm Gemfile.lock; bin/setup; rake
You can also run bin/console to open up a prompt where you'll have access to some models to experiment with.
Rails update
- Update baby_squeel.gemspec
- Add the version to test matrix build.yml
- Update development section in the README.md
- If you need to change the code consider to add a version check methode in version_helper.rb
- Run the specs with all supported versions
- Add comment to the unreleased section in CHANGELOG.md
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/rzane/baby_squeel.
License
The gem is available as open source under the terms of the MIT License.
관련 프로젝트 살펴보기
Asabeneh/30-Days-Of-Python
30 Days of Python 프로그래밍 챌린지는 Python 프로그래밍 언어를 30일 동안 배우기 위한 단계별 가이드입니다. 이 챌린지는 100일 이상 걸릴 수 있습니다. 자신의 속도에 맞춰 진행하세요. 다음 동영상도 도움이 될 수 있습니다: https://www.youtube.com/channel/UC7PNRuno1rzYPb1xLa4yktw
78,560 스타grafana/grafana
개방적이고 조합 가능한 옵저버빌리티 및 데이터 시각화 플랫폼입니다. Prometheus, Loki, Elasticsearch, InfluxDB, Postgres 등 여러 소스의 메트릭, 로그, 트레이스를 시각화합니다.
77,830 스타redis/redis
실시간 데이터 기반 애플리케이션을 구축하는 개발자에게 Redis는 가장 선호되고, 가장 빠르며, 기능이 가장 풍부한 캐시, 데이터 구조 서버이자 문서 및 벡터 쿼리 엔진입니다.
77,395 스타apache/superset
Apache Superset는 데이터 시각화 및 데이터 탐색 플랫폼입니다.
75,473 스타