baby_squeel 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
[](https://codeclimate.com/github/rzane/baby_squeel)
|
5
5
|
[](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
|