All Projects → rzane → Baby_squeel

rzane / Baby_squeel

Licence: mit
🐷 An expressive query DSL for Active Record 4 and 5

Programming Languages

ruby
36898 projects - #4 most used programming language
dsl
153 projects

Projects that are alternatives of or similar to Baby squeel

Objectivesql
ObjectiveSQL is an ORM framework in Java based on ActiveRecord pattern, which encourages rapid development and clean, codes with the least and convention over configuration.
Stars: ✭ 1,109 (+206.35%)
Mutual labels:  sql, orm, activerecord
Openrecord
Make ORMs great again!
Stars: ✭ 474 (+30.94%)
Mutual labels:  sql, orm, activerecord
Walkable
A Clojure(script) SQL library for building APIs: Datomic® (GraphQL-ish) pull syntax, data driven configuration, dynamic filtering with relations in mind
Stars: ✭ 384 (+6.08%)
Mutual labels:  sql, orm, query
Elasticsearch
Use SQL statements to query elasticsearch
Stars: ✭ 98 (-72.93%)
Mutual labels:  sql, orm, query
Roapi
Create full-fledged APIs for static datasets without writing a single line of code.
Stars: ✭ 253 (-30.11%)
Mutual labels:  sql, query
rest-query-parser
Query Parser for REST
Stars: ✭ 29 (-91.99%)
Mutual labels:  query, orm
Shiba
Catch bad SQL queries before they cause problems in production
Stars: ✭ 277 (-23.48%)
Mutual labels:  sql, activerecord
Preql
An interpreted relational query language that compiles to SQL.
Stars: ✭ 257 (-29.01%)
Mutual labels:  sql, query
Xo
Command line tool to generate idiomatic Go code for SQL databases supporting PostgreSQL, MySQL, SQLite, Oracle, and Microsoft SQL Server
Stars: ✭ 2,974 (+721.55%)
Mutual labels:  sql, orm
Sqlc
Generate type-safe code from SQL
Stars: ✭ 4,564 (+1160.77%)
Mutual labels:  sql, orm
Loukoum
A simple SQL Query Builder
Stars: ✭ 305 (-15.75%)
Mutual labels:  sql, query
monalisa-orm
Very Simple ORM
Stars: ✭ 70 (-80.66%)
Mutual labels:  activerecord, orm
active-persistence
Active Persistence is a implementation of Active Record Query Interface for JPA that makes it easy and fun.
Stars: ✭ 14 (-96.13%)
Mutual labels:  activerecord, query
Dapper Plus
Dapper Plus - High-Efficient Bulk Actions (Insert, Update, Delete, and Merge) for .NET
Stars: ✭ 265 (-26.8%)
Mutual labels:  sql, orm
activerecord-setops
Union, Intersect, and Difference set operations for ActiveRecord (also, SQL's UnionAll).
Stars: ✭ 21 (-94.2%)
Mutual labels:  activerecord, query
Securitydriven.tinyorm
.NET micro ORM done right.
Stars: ✭ 281 (-22.38%)
Mutual labels:  sql, orm
Scala Activerecord
ActiveRecord-like ORM library for Scala
Stars: ✭ 324 (-10.5%)
Mutual labels:  orm, activerecord
Rel
💎 Modern Database Access Layer for Golang - Testable, Extendable and Crafted Into a Clean and Elegant API
Stars: ✭ 317 (-12.43%)
Mutual labels:  sql, orm
Freezer
A simple & fluent Android ORM, how can it be easier ? RxJava2 compatible
Stars: ✭ 326 (-9.94%)
Mutual labels:  sql, orm
Datamappify
Compose, decouple and manage domain logic and data persistence separately. Works particularly great for composing form objects!
Stars: ✭ 338 (-6.63%)
Mutual labels:  orm, activerecord

BabySqueel 🐷

Build Status Code Climate Coverage Status

biddy piggy

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. ❤️

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

  1. Pick an Active Record version to develop against, then export it: export AR=4.2.6.
  2. Run bin/setup to install dependencies.
  3. Run rake to run the specs.

You can also run bin/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 at https://github.com/rzane/baby_squeel.

License

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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].