baby_squeel 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/Gemfile +0 -1
- data/README.md +55 -40
- data/Rakefile +19 -11
- data/lib/baby_squeel/nodes.rb +9 -0
- data/lib/baby_squeel/version.rb +1 -1
- metadata +3 -4
- data/.rubocop.yml +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 630e302720d471b1ea0e6f618ef01e8592c0b9ab
|
4
|
+
data.tar.gz: 621e74ac8ad305e143e856e1cee8a8c023b3ac9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c91ddbf39dc8e7a153f9be6df8197ac7223d91d4bf003eb55049bb3c0d9f671e7aa4a810ddb122973e609f70e302db90011b1f82a9794133cd1d2f4f688d8d6c
|
7
|
+
data.tar.gz: e71c24fb4bf300ae9806e4664e07c0e07e07cf74997f1259b2e6068fcb304f5d171bea6db2095d1e44a1329fe019f4a381be5e9686c6b9ec33d60b70b7787118
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,13 +4,13 @@
|
|
4
4
|
[![Code Climate](https://codeclimate.com/github/rzane/baby_squeel/badges/gpa.svg)](https://codeclimate.com/github/rzane/baby_squeel)
|
5
5
|
[![Coverage Status](https://coveralls.io/repos/github/rzane/baby_squeel/badge.svg?branch=master)](https://coveralls.io/github/rzane/baby_squeel?branch=master)
|
6
6
|
|
7
|
-
|
7
|
+
<img align="right" src="http://static.thefrisky.com/uploads/2010/07/01/pig_in_boots_070110_m.jpg" alt="biddy piggy">
|
8
8
|
|
9
9
|
Have you ever used the [squeel](https://github.com/activerecord-hackery/squeel) gem? It's a really nice way to build complex queries. However, squeel monkeypatches ActiveRecord internals, so it has a tendency to break every time a new ActiveRecord version comes out.
|
10
10
|
|
11
|
-
For me, that's a deal breaker. BabySqueel provides a query DSL for ActiveRecord without all of the evil :heart
|
11
|
+
For me, that's a deal breaker. BabySqueel provides a query DSL for ActiveRecord without all of the evil. :heart:
|
12
12
|
|
13
|
-
It's
|
13
|
+
It's really just a layer of sugar on top of Arel.
|
14
14
|
|
15
15
|
## Installation
|
16
16
|
|
@@ -30,54 +30,58 @@ Or install it yourself as:
|
|
30
30
|
|
31
31
|
## Usage
|
32
32
|
|
33
|
-
Okay, so we have
|
33
|
+
Okay, so we have some models:
|
34
34
|
|
35
35
|
```ruby
|
36
36
|
class Post < ActiveRecord::Base
|
37
37
|
belongs_to :author
|
38
|
+
has_many :comments
|
39
|
+
end
|
40
|
+
|
41
|
+
class Author < ActiveRecord::Base
|
42
|
+
has_many :posts
|
43
|
+
has_many :comments, through: :posts
|
44
|
+
end
|
45
|
+
|
46
|
+
class Comment < ActiveRecord::Base
|
47
|
+
belongs_to :post
|
38
48
|
end
|
39
49
|
```
|
40
50
|
|
41
|
-
|
51
|
+
##### Selects
|
42
52
|
|
43
53
|
```ruby
|
44
54
|
Post.selecting { (id + 5).as('id_plus_five') }
|
45
|
-
# SELECT "posts"."id" + 5 AS id_plus_five FROM "posts"
|
55
|
+
# SELECT ("posts"."id" + 5) AS id_plus_five FROM "posts"
|
46
56
|
|
47
57
|
Post.selecting { id.sum }
|
48
58
|
# SELECT SUM("posts"."id") FROM "posts"
|
49
59
|
|
50
60
|
Post.joins(:author).selecting { [id, author.id] }
|
51
|
-
# SELECT "posts"."id", "
|
52
|
-
#
|
53
|
-
# INNER JOIN "authors" ON "posts"."author_id" = "authors"."id"
|
61
|
+
# SELECT "posts"."id", "authors"."id" FROM "posts"
|
62
|
+
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
54
63
|
```
|
55
64
|
|
56
|
-
|
65
|
+
##### Wheres
|
57
66
|
|
58
67
|
```ruby
|
59
68
|
Post.where.has { title == 'My Post' }
|
60
|
-
# SELECT "posts".* FROM "posts"
|
69
|
+
# SELECT "posts".* FROM "posts"
|
70
|
+
# WHERE "posts"."title" = 'My Post'
|
61
71
|
|
62
72
|
Post.where.has { title =~ 'My P%' }
|
63
|
-
# SELECT "posts".* FROM "posts"
|
73
|
+
# SELECT "posts".* FROM "posts"
|
74
|
+
# WHERE ("posts"."title" LIKE 'My P%')
|
64
75
|
|
65
76
|
Author.where.has { (name =~ 'Ray%') & (id < 5) | (name.lower =~ 'zane%') & (id > 100) }
|
66
77
|
# SELECT "authors".* FROM "authors"
|
67
|
-
# WHERE (
|
68
|
-
# "authors"."name" LIKE 'Ray%' AND "authors"."id" < 5 OR
|
69
|
-
# LOWER("authors"."name") LIKE 'zane%' AND "authors"."id" > 100
|
70
|
-
# )
|
78
|
+
# WHERE ("authors"."name" LIKE 'Ray%' AND "authors"."id" < 5 OR LOWER("authors"."name") LIKE 'zane%' AND "authors"."id" > 100)
|
71
79
|
|
72
80
|
Post.joins(:author).where.has { author.name == 'Ray' }
|
73
81
|
# SELECT "posts".* FROM "posts"
|
74
82
|
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
75
83
|
# WHERE "authors"."name" = 'Ray'
|
76
|
-
```
|
77
|
-
|
78
|
-
Here's the best part. Where conditions will always reference the correct table alias for a given association:
|
79
84
|
|
80
|
-
```ruby
|
81
85
|
Post.joins(author: :posts).where.has { author.posts.title =~ '%fun%' }
|
82
86
|
# SELECT "posts".* FROM "posts"
|
83
87
|
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
@@ -85,18 +89,20 @@ Post.joins(author: :posts).where.has { author.posts.title =~ '%fun%' }
|
|
85
89
|
# WHERE ("posts_authors"."title" LIKE '%fun%')
|
86
90
|
```
|
87
91
|
|
88
|
-
|
92
|
+
##### Orders
|
89
93
|
|
90
94
|
```ruby
|
91
95
|
Post.ordering { [id.desc, title.asc] }
|
92
|
-
# SELECT "posts".* FROM "posts"
|
96
|
+
# SELECT "posts".* FROM "posts"
|
97
|
+
# ORDER BY "posts"."id" DESC, "posts"."title" ASC
|
93
98
|
|
94
99
|
Post.ordering { (id * 5).desc }
|
95
|
-
# SELECT "posts".* FROM "posts"
|
100
|
+
# SELECT "posts".* FROM "posts"
|
101
|
+
# ORDER BY "posts"."id" * 5 DESC
|
96
102
|
|
97
103
|
Post.select(:author_id).group(:author_id).ordering { id.count.desc }
|
98
|
-
# SELECT "posts"."author_id"
|
99
|
-
#
|
104
|
+
# SELECT "posts"."author_id" FROM "posts"
|
105
|
+
# GROUP BY "posts"."author_id"
|
100
106
|
# ORDER BY COUNT("posts"."id") DESC
|
101
107
|
|
102
108
|
Post.joins(:author).ordering { author.id.desc }
|
@@ -105,8 +111,7 @@ Post.joins(:author).ordering { author.id.desc }
|
|
105
111
|
# ORDER BY "authors"."id" DESC
|
106
112
|
```
|
107
113
|
|
108
|
-
|
109
|
-
#### Joins
|
114
|
+
##### Joins
|
110
115
|
|
111
116
|
```ruby
|
112
117
|
Post.joining { author }
|
@@ -121,17 +126,20 @@ Post.joining { [author.outer, comments] }
|
|
121
126
|
Post.joining { author.comments }
|
122
127
|
# SELECT "posts".* FROM "posts"
|
123
128
|
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
124
|
-
# INNER JOIN "
|
129
|
+
# INNER JOIN "posts" "posts_authors_join" ON "posts_authors_join"."author_id" = "authors"."id"
|
130
|
+
# INNER JOIN "comments" ON "comments"."post_id" = "posts_authors_join"."id"
|
125
131
|
|
126
132
|
Post.joining { author.outer.comments.outer }
|
127
133
|
# SELECT "posts".* FROM "posts"
|
128
|
-
#
|
129
|
-
# LEFT OUTER JOIN "
|
134
|
+
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
135
|
+
# LEFT OUTER JOIN "posts" "posts_authors_join" ON "posts_authors_join"."author_id" = "authors"."id"
|
136
|
+
# LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts_authors_join"."id"
|
130
137
|
|
131
138
|
Post.joining { author.comments.outer }
|
132
139
|
# SELECT "posts".* FROM "posts"
|
133
140
|
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
134
|
-
# LEFT OUTER JOIN "
|
141
|
+
# LEFT OUTER JOIN "posts" "posts_authors_join" ON "posts_authors_join"."author_id" = "authors"."id"
|
142
|
+
# LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts_authors_join"."id"
|
135
143
|
|
136
144
|
Post.joining { author.outer.posts }
|
137
145
|
# SELECT "posts".* FROM "posts"
|
@@ -140,19 +148,30 @@ Post.joining { author.outer.posts }
|
|
140
148
|
|
141
149
|
Post.joining { author.alias('a').on((author.id == author_id) | (author.name == title)) }
|
142
150
|
# SELECT "posts".* FROM "posts"
|
143
|
-
# INNER JOIN "authors" "a" ON (
|
144
|
-
# "authors"."id" = "posts"."author_id" OR
|
145
|
-
# "authors"."name" = "posts"."title"
|
146
|
-
# )
|
151
|
+
# INNER JOIN "authors" "a" ON ("authors"."id" = "posts"."author_id" OR "authors"."name" = "posts"."title")
|
147
152
|
```
|
148
153
|
|
149
|
-
|
154
|
+
##### Functions
|
150
155
|
|
151
156
|
```ruby
|
152
157
|
Post.selecting { coalesce(author_id, 5).as('author_id_with_default') }
|
153
158
|
# SELECT coalesce("posts"."author_id", 5) AS author_id_with_default FROM "posts"
|
154
159
|
```
|
155
160
|
|
161
|
+
##### Subqueries
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
Post.joins(:author).where.has {
|
165
|
+
author.id.in Author.select(:id).where(name: 'Ray')
|
166
|
+
}
|
167
|
+
# SELECT "posts".* FROM "posts"
|
168
|
+
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
169
|
+
# WHERE "authors"."id" IN (
|
170
|
+
# SELECT "authors"."id" FROM "authors"
|
171
|
+
# WHERE "authors"."name" = 'Ray'
|
172
|
+
# )
|
173
|
+
```
|
174
|
+
|
156
175
|
## Important Notes
|
157
176
|
|
158
177
|
While inside one of BabySqueel's blocks, `self` will be something totally different. You won't have access to your instance variables or methods.
|
@@ -172,10 +191,6 @@ Post.where.has { |table| table.title == 'Test' }
|
|
172
191
|
|
173
192
|
You can also run `bin/console` to open up a prompt where you'll have access to some models to experiment with.
|
174
193
|
|
175
|
-
## Todo
|
176
|
-
|
177
|
-
+ Subqueries
|
178
|
-
|
179
194
|
## Contributing
|
180
195
|
|
181
196
|
Bug reports and pull requests are welcome on GitHub at https://github.com/rzane/baby_squeel.
|
data/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'yaml'
|
2
|
+
require 'open3'
|
2
3
|
require 'bundler/gem_tasks'
|
3
4
|
require 'rspec/core/rake_task'
|
4
5
|
require 'coveralls/rake/task'
|
@@ -7,26 +8,33 @@ Coveralls::RakeTask.new
|
|
7
8
|
|
8
9
|
RSpec::Core::RakeTask.new(:spec)
|
9
10
|
|
10
|
-
def
|
11
|
-
display = "AR=#{version} #{cmd}"
|
12
|
-
puts display
|
13
|
-
|
11
|
+
def invoke(version, cmd)
|
14
12
|
Bundler.with_clean_env do
|
15
|
-
system({ 'AR' => version }, cmd)
|
13
|
+
system({ 'AR' => version, 'SKIPCOV' => '1' }, cmd)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Run against a specific ActiveRecord version'
|
18
|
+
task 'spec:version', [:version] do |_, args|
|
19
|
+
if args.version.nil? || args.version.empty?
|
20
|
+
abort 'No version given'
|
16
21
|
end
|
17
22
|
|
18
|
-
|
23
|
+
FileUtils.rm_rf 'Gemfile.lock'
|
24
|
+
invoke args.version, 'bundle install --quiet'
|
25
|
+
invoke args.version, 'bundle exec rspec -f progress' if $?.success?
|
26
|
+
$stderr.puts "#{args.version} failed." unless $?.success?
|
19
27
|
end
|
20
28
|
|
21
29
|
desc 'Run against all ActiveRecord versions'
|
22
30
|
task 'spec:matrix' do
|
23
31
|
travis = YAML.load_file '.travis.yml'
|
32
|
+
spec_task = Rake::Task['spec:version']
|
24
33
|
|
25
|
-
travis['env']['matrix'].
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
run_version version, 'rake spec'
|
34
|
+
travis['env']['matrix'].each do |matrix|
|
35
|
+
spec_task.invoke matrix[/\=(.+)$/, 1]
|
36
|
+
spec_task.reenable
|
37
|
+
puts "\n\n"
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
data/lib/baby_squeel/nodes.rb
CHANGED
@@ -60,6 +60,7 @@ module BabySqueel
|
|
60
60
|
# include necessary/applicable modules.
|
61
61
|
class Generic < Proxy
|
62
62
|
extend Operators::ArelAliasing
|
63
|
+
include Arel::AliasPredication
|
63
64
|
include Arel::OrderPredications
|
64
65
|
include Operators::Comparison
|
65
66
|
include Operators::Equality
|
@@ -75,6 +76,14 @@ module BabySqueel
|
|
75
76
|
super(parent._table[name])
|
76
77
|
end
|
77
78
|
|
79
|
+
def in(rel)
|
80
|
+
if rel.is_a? ::ActiveRecord::Relation
|
81
|
+
::Arel::Nodes::In.new(self, Arel.sql(rel.to_sql))
|
82
|
+
else
|
83
|
+
super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
78
87
|
def _arel
|
79
88
|
parent_arel = @parent._arel
|
80
89
|
|
data/lib/baby_squeel/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: baby_squeel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ray Zane
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -89,7 +89,6 @@ extra_rdoc_files: []
|
|
89
89
|
files:
|
90
90
|
- ".gitignore"
|
91
91
|
- ".rspec"
|
92
|
-
- ".rubocop.yml"
|
93
92
|
- ".travis.yml"
|
94
93
|
- Gemfile
|
95
94
|
- LICENSE.txt
|
@@ -127,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
126
|
version: '0'
|
128
127
|
requirements: []
|
129
128
|
rubyforge_project:
|
130
|
-
rubygems_version: 2.
|
129
|
+
rubygems_version: 2.5.1
|
131
130
|
signing_key:
|
132
131
|
specification_version: 4
|
133
132
|
summary: A tiny squeel implementation without all of the evil.
|
data/.rubocop.yml
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
Lint/AssignmentInCondition:
|
2
|
-
Enabled: false
|
3
|
-
|
4
|
-
Style/BlockDelimiters:
|
5
|
-
Enabled: false
|
6
|
-
|
7
|
-
Style/Documentation:
|
8
|
-
Enabled: false
|
9
|
-
|
10
|
-
Style/SpecialGlobalVars:
|
11
|
-
Enabled: false
|
12
|
-
|
13
|
-
Style/NilComparison:
|
14
|
-
Exclude:
|
15
|
-
- spec/**/*
|
16
|
-
|
17
|
-
Metrics/LineLength:
|
18
|
-
Max: 100
|
19
|
-
|
20
|
-
Metrics/AbcSize:
|
21
|
-
Enabled: false
|