Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

🐷 An expressive query DSL for Active Record

License

NotificationsYou must be signed in to change notification settings

rzane/baby_squeel

Repository files navigation

BuildVersion

Have you ever used theSqueel 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. ❤️

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 useinstance_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:

classPost <ActiveRecord::Basebelongs_to:authorhas_many:commentsendclassAuthor <ActiveRecord::Basehas_many:postshas_many:comments,through::postsendclassComment <ActiveRecord::Basebelongs_to:postend
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" ASCPost.ordering{(id *5).desc}# SELECT "posts".* FROM "posts"# ORDER BY "posts"."id" * 5 DESCPost.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") DESCPost.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.inAuthor.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{existsPost.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.rbbelongs_to:imageable,polymorphic:true# app/models/post.rbhas_many:pictures,as::imageable

The query might look like this:

Picture.joining{imageable.of(Post)}.selecting{imageable.of(Post).id}
Helpers
# SQL LiteralsPost.select('1 as one').ordering{sql('one').desc}# QuotingPost.selecting{title.op('||',quoted('diddly'))}# FunctionsPost.selecting{func('coalesce',id,1)}

Sifters

Sifters are like little snippets of conditions that can take arguments.

classPost <ActiveRecord::Basesifter:funnydotitle =='rabies'endendclassAuthor <ActiveRecord::Basesifter:name_containsdo |string|name =~"%#{string}%"endendPost.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:

BabySqueelActive Record Equivalent
selectingselect
orderingorder
joiningjoins
groupinggroup
where.haswhere
when_havinghaving

Migrating from Squeel

Check out themigration guide.

Development

  1. Pick an Active Record version to develop against, then export it:export AR=6.1.4.
  2. Runbin/setup to install dependencies.
  3. Runrake to run the specs.

You can also runbin/console to open up a prompt where you'll have access to some models to experiment with.

Contributing

Bug reports and pull requests are welcome on GitHub athttps://github.com/rzane/baby_squeel.

License

The gem is available as open source under the terms of theMIT License.

About

🐷 An expressive query DSL for Active Record

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp