baby_squeel 1.1.5 → 1.4.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +28 -2
- data/README.md +45 -9
- data/baby_squeel.gemspec +7 -6
- data/lib/baby_squeel.rb +6 -1
- data/lib/baby_squeel/active_record/base.rb +13 -4
- data/lib/baby_squeel/active_record/calculations.rb +3 -1
- data/lib/baby_squeel/active_record/query_methods.rb +20 -9
- data/lib/baby_squeel/association.rb +2 -2
- data/lib/baby_squeel/compat.rb +75 -0
- data/lib/baby_squeel/dsl.rb +0 -10
- data/lib/baby_squeel/{join_expression.rb → join.rb} +2 -2
- data/lib/baby_squeel/join_dependency.rb +64 -0
- data/lib/baby_squeel/nodes/attribute.rb +1 -1
- data/lib/baby_squeel/nodes/node.rb +5 -0
- data/lib/baby_squeel/pluck.rb +2 -2
- data/lib/baby_squeel/table.rb +28 -10
- data/lib/baby_squeel/version.rb +1 -1
- metadata +41 -29
- data/lib/baby_squeel/join_dependency/builder.rb +0 -121
- data/lib/baby_squeel/join_dependency/injector.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2df3535278f1724c6db1656ab9c5bbe0e9097e80ee6f2c1fdaeb3491bc838ae9
|
4
|
+
data.tar.gz: bddd464023f9f53f0ff32cc1fbaa88928f5736b3e6541418b8c36f26b01b8d34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44f8ecb2269017a1e141f0f90d5c9a7eee7d5b877500897aba0a60646f38de0cdae4b992f505d022740c03e73d6293990d9f509d5b3188b5be8051819e168069
|
7
|
+
data.tar.gz: a63f7b474504ef598508879d8ad702396f194c663cc46ececdb9c8dd31aabb10752323332bc26b3cec5cfb2df93e5328e11d2ce1983efe23793c618ccce9294d
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,27 @@
|
|
2
2
|
|
3
3
|
Nothing to see here.
|
4
4
|
|
5
|
+
## [1.3.1] - 2018-05-15
|
6
|
+
## Fixed
|
7
|
+
- Upgraded `join_dependency` requirement, which fixes [issue #1](https://github.com/rzane/join_dependency/issues/1).
|
8
|
+
|
9
|
+
## [1.3.0] - 2018-05-04
|
10
|
+
## Added
|
11
|
+
- The ability to use `plucking` with an array of nodes. For example, `User.plucking { [id, name] }`.
|
12
|
+
|
13
|
+
## [1.2.1] - 2018-04-25
|
14
|
+
## Fixed
|
15
|
+
- Added support for Active Record 5.2
|
16
|
+
|
17
|
+
## [1.2.0] - 2017-10-20
|
18
|
+
### Added
|
19
|
+
- `reordering`, which is just a BabySqueel version of Active Record's `reorder`.
|
20
|
+
- `on` expressions can now be given a block that will yield the current node (#77).
|
21
|
+
|
22
|
+
## [1.1.5] - 2017-05-26
|
23
|
+
### Fixed
|
24
|
+
- Returning an empty hash from a `where.has {}` block would generate invalid SQL (#69).
|
25
|
+
|
5
26
|
## [1.1.4] - 2017-04-13
|
6
27
|
### Fixed
|
7
28
|
- Nodes::Attribute#in and #not_in generate valid SQL when given ActiveRecord::NullRelations.
|
@@ -118,8 +139,13 @@ Nothing to see here.
|
|
118
139
|
### Added
|
119
140
|
- Initial support for selects, orders, wheres, and joins.
|
120
141
|
|
121
|
-
[Unreleased]: https://github.com/rzane/baby_squeel/compare/v1.1
|
122
|
-
[1.1
|
142
|
+
[Unreleased]: https://github.com/rzane/baby_squeel/compare/v1.3.1...HEAD
|
143
|
+
[1.3.1]: https://github.com/rzane/baby_squeel/compare/v1.3.0...v1.3.1
|
144
|
+
[1.3.0]: https://github.com/rzane/baby_squeel/compare/v1.2.1...v1.3.0
|
145
|
+
[1.2.1]: https://github.com/rzane/baby_squeel/compare/v1.2.0...v1.2.1
|
146
|
+
[1.2.0]: https://github.com/rzane/baby_squeel/compare/v1.1.5...v1.2.0
|
147
|
+
[1.1.5]: https://github.com/rzane/baby_squeel/compare/v1.1.4...v1.1.5
|
148
|
+
[1.1.4]: https://github.com/rzane/baby_squeel/compare/v1.1.3...v1.1.4
|
123
149
|
[1.1.3]: https://github.com/rzane/baby_squeel/compare/v1.1.2...v1.1.3
|
124
150
|
[1.1.2]: https://github.com/rzane/baby_squeel/compare/v1.1.1...v1.1.2
|
125
151
|
[1.1.1]: https://github.com/rzane/baby_squeel/compare/v1.1.0...v1.1.1
|
data/README.md
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
# BabySqueel
|
1
|
+
# BabySqueel 🐷
|
2
2
|
|
3
|
-
|
4
|
-
|
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
|
-
|
7
|
-
<img align="right" src="http://static.thefrisky.com/uploads/2010/07/01/pig_in_boots_070110_m.jpg" alt="biddy piggy">
|
3
|
+
![Build](https://github.com/rzane/baby_squeel/workflows/Build/badge.svg)
|
4
|
+
![Version](https://img.shields.io/gem/v/baby_squeel)
|
8
5
|
|
9
6
|
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 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.
|
10
7
|
|
@@ -172,9 +169,13 @@ Post.joining { author.outer.posts }
|
|
172
169
|
# LEFT OUTER JOIN "authors" ON "authors"."id" = "posts"."author_id"
|
173
170
|
# INNER JOIN "posts" "posts_authors" ON "posts_authors"."author_id" = "authors"."id"
|
174
171
|
|
175
|
-
Post.joining { author.
|
172
|
+
Post.joining { author.on((author.id == author_id) | (author.name == title)) }
|
173
|
+
# SELECT "posts".* FROM "posts"
|
174
|
+
# INNER JOIN "authors" ON ("authors"."id" = "posts"."author_id" OR "authors"."name" = "posts"."title")
|
175
|
+
|
176
|
+
Post.joining { |post| post.author.as('a').on { (id == post.author_id) | (name == post.title) } }
|
176
177
|
# SELECT "posts".* FROM "posts"
|
177
|
-
# INNER JOIN "authors" "a" ON ("
|
178
|
+
# INNER JOIN "authors" "a" ON ("a"."id" = "posts"."author_id" OR "a"."name" = "posts"."title")
|
178
179
|
|
179
180
|
Picture.joining { imageable.of(Post) }
|
180
181
|
# SELECT "pictures".* FROM "pictures"
|
@@ -215,6 +216,21 @@ Post.joins(:author).where.has {
|
|
215
216
|
# )
|
216
217
|
```
|
217
218
|
|
219
|
+
##### Exists
|
220
|
+
|
221
|
+
```ruby
|
222
|
+
Post.where.has {
|
223
|
+
exists Post.where.has { author_id == 1 }
|
224
|
+
}
|
225
|
+
# SELECT "posts".* FROM "posts"
|
226
|
+
# WHERE (
|
227
|
+
# EXISTS(
|
228
|
+
# SELECT "posts".* FROM "posts"
|
229
|
+
# WHERE "posts"."author_id" = 1
|
230
|
+
# )
|
231
|
+
# )
|
232
|
+
```
|
233
|
+
|
218
234
|
##### Custom SQL Operators
|
219
235
|
|
220
236
|
```ruby
|
@@ -236,6 +252,26 @@ Post.joining {
|
|
236
252
|
}
|
237
253
|
```
|
238
254
|
|
255
|
+
##### Polymorphism
|
256
|
+
|
257
|
+
Given this polymorphism:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
# app/models/picture.rb
|
261
|
+
belongs_to :imageable, polymorphic: true
|
262
|
+
|
263
|
+
# app/models/post.rb
|
264
|
+
has_many :pictures, as: :imageable
|
265
|
+
```
|
266
|
+
|
267
|
+
The query might look like this:
|
268
|
+
|
269
|
+
```ruby
|
270
|
+
Picture.
|
271
|
+
joining { imageable.of(Post) }.
|
272
|
+
selecting { imageable.of(Post).id }
|
273
|
+
```
|
274
|
+
|
239
275
|
##### Helpers
|
240
276
|
|
241
277
|
```ruby
|
@@ -279,7 +315,7 @@ Post.joins(:author).where.has {
|
|
279
315
|
The following methods give you access to BabySqueel's DSL:
|
280
316
|
|
281
317
|
| BabySqueel | Active Record Equivalent |
|
282
|
-
|
318
|
+
| ------------- | ------------------------ |
|
283
319
|
| `selecting` | `select` |
|
284
320
|
| `ordering` | `order` |
|
285
321
|
| `joining` | `joins` |
|
data/baby_squeel.gemspec
CHANGED
@@ -19,11 +19,12 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.files = Dir.glob('{lib/**/*,*.{md,txt,gemspec}}')
|
21
21
|
|
22
|
-
spec.add_dependency 'activerecord', '
|
23
|
-
spec.add_dependency '
|
22
|
+
spec.add_dependency 'activerecord', '~> 5.2'
|
23
|
+
spec.add_dependency 'ransack', '~> 2.3'
|
24
|
+
spec.add_dependency 'join_dependency', '~> 0.1.4'
|
24
25
|
|
25
|
-
spec.add_development_dependency 'bundler', '~>
|
26
|
-
spec.add_development_dependency 'rake', '~>
|
27
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
28
|
-
spec.add_development_dependency 'sqlite3'
|
26
|
+
spec.add_development_dependency 'bundler', '~> 2'
|
27
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.10'
|
29
|
+
spec.add_development_dependency 'sqlite3', '~> 1.3.6'
|
29
30
|
end
|
data/lib/baby_squeel.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'active_record/relation'
|
3
|
-
|
3
|
+
begin
|
4
|
+
require 'polyamorous'
|
5
|
+
rescue LoadError
|
6
|
+
# Trying loading from 'ransack' as of commit c9cc20de9 (post v2.3.2)
|
7
|
+
require 'polyamorous/polyamorous'
|
8
|
+
end
|
4
9
|
require 'baby_squeel/version'
|
5
10
|
require 'baby_squeel/errors'
|
6
11
|
require 'baby_squeel/active_record/base'
|
@@ -3,10 +3,19 @@ require 'baby_squeel/dsl'
|
|
3
3
|
module BabySqueel
|
4
4
|
module ActiveRecord
|
5
5
|
module Base
|
6
|
-
delegate :joining,
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
6
|
+
delegate :joining,
|
7
|
+
:selecting,
|
8
|
+
:ordering,
|
9
|
+
:reordering,
|
10
|
+
:grouping,
|
11
|
+
:when_having,
|
12
|
+
:plucking,
|
13
|
+
:averaging,
|
14
|
+
:counting,
|
15
|
+
:maximizing,
|
16
|
+
:minimizing,
|
17
|
+
:summing,
|
18
|
+
to: :all
|
10
19
|
|
11
20
|
# Define a sifter that can be used within DSL blocks.
|
12
21
|
#
|
@@ -5,7 +5,9 @@ module BabySqueel
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Calculations
|
7
7
|
def plucking(&block)
|
8
|
-
|
8
|
+
nodes = Array.wrap(DSL.evaluate(self, &block))
|
9
|
+
nodes = nodes.map { |node| Pluck.decorate(node) }
|
10
|
+
pluck(*nodes)
|
9
11
|
end
|
10
12
|
|
11
13
|
def counting(&block)
|
@@ -1,30 +1,35 @@
|
|
1
1
|
require 'baby_squeel/dsl'
|
2
|
-
require 'baby_squeel/join_dependency
|
2
|
+
require 'baby_squeel/join_dependency'
|
3
3
|
|
4
4
|
module BabySqueel
|
5
5
|
module ActiveRecord
|
6
6
|
module QueryMethods
|
7
|
-
# Constructs Arel for ActiveRecord::
|
7
|
+
# Constructs Arel for ActiveRecord::QueryMethods#joins using the DSL.
|
8
8
|
def joining(&block)
|
9
9
|
joins DSL.evaluate(self, &block)
|
10
10
|
end
|
11
11
|
|
12
|
-
# Constructs Arel for ActiveRecord::
|
12
|
+
# Constructs Arel for ActiveRecord::QueryMethods#select using the DSL.
|
13
13
|
def selecting(&block)
|
14
14
|
select DSL.evaluate(self, &block)
|
15
15
|
end
|
16
16
|
|
17
|
-
# Constructs Arel for ActiveRecord::
|
17
|
+
# Constructs Arel for ActiveRecord::QueryMethods#order using the DSL.
|
18
18
|
def ordering(&block)
|
19
19
|
order DSL.evaluate(self, &block)
|
20
20
|
end
|
21
21
|
|
22
|
-
# Constructs Arel for ActiveRecord::
|
22
|
+
# Constructs Arel for ActiveRecord::QueryMethods#reorder using the DSL.
|
23
|
+
def reordering(&block)
|
24
|
+
reorder DSL.evaluate(self, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Constructs Arel for ActiveRecord::QueryMethods#group using the DSL.
|
23
28
|
def grouping(&block)
|
24
29
|
group DSL.evaluate(self, &block)
|
25
30
|
end
|
26
31
|
|
27
|
-
# Constructs Arel for ActiveRecord::
|
32
|
+
# Constructs Arel for ActiveRecord::QueryMethods#having using the DSL.
|
28
33
|
def when_having(&block)
|
29
34
|
having DSL.evaluate(self, &block)
|
30
35
|
end
|
@@ -34,9 +39,15 @@ module BabySqueel
|
|
34
39
|
# This is a monkey patch, and I'm not happy about it.
|
35
40
|
# Active Record will call `group_by` on the `joins`. The
|
36
41
|
# Injector has a custom `group_by` method that handles
|
37
|
-
# BabySqueel::
|
38
|
-
|
39
|
-
|
42
|
+
# BabySqueel::Join nodes.
|
43
|
+
if ::ActiveRecord::VERSION::MAJOR >= 5 && ::ActiveRecord::VERSION::MINOR >= 2
|
44
|
+
def build_joins(manager, joins, aliases)
|
45
|
+
super manager, BabySqueel::JoinDependency::Injector.new(joins), aliases
|
46
|
+
end
|
47
|
+
else
|
48
|
+
def build_joins(manager, joins)
|
49
|
+
super manager, BabySqueel::JoinDependency::Injector.new(joins)
|
50
|
+
end
|
40
51
|
end
|
41
52
|
end
|
42
53
|
end
|
@@ -47,7 +47,7 @@ module BabySqueel
|
|
47
47
|
_join == Arel::Nodes::OuterJoin || _reflection.polymorphic?
|
48
48
|
end
|
49
49
|
|
50
|
-
# See
|
50
|
+
# See Join#add_to_tree.
|
51
51
|
def add_to_tree(hash)
|
52
52
|
polyamorous = Polyamorous::Join.new(
|
53
53
|
_reflection.name,
|
@@ -85,7 +85,7 @@ module BabySqueel
|
|
85
85
|
def _arel(associations = [])
|
86
86
|
if _on
|
87
87
|
super
|
88
|
-
elsif
|
88
|
+
elsif alias?
|
89
89
|
raise AssociationAliasingError.new(_reflection.name, _table.right)
|
90
90
|
elsif _reflection.polymorphic? && _polymorphic_klass.nil?
|
91
91
|
raise PolymorphicNotSpecifiedError.new(_reflection.name)
|
data/lib/baby_squeel/compat.rb
CHANGED
@@ -8,6 +8,46 @@ module BabySqueel
|
|
8
8
|
::ActiveRecord::Relation.prepend QueryMethods
|
9
9
|
end
|
10
10
|
|
11
|
+
class KeyPath
|
12
|
+
def self.evaluate(&block)
|
13
|
+
if block.arity.zero?
|
14
|
+
unwrap new.instance_eval(&block)
|
15
|
+
else
|
16
|
+
unwrap yield(new)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.unwrap(path)
|
21
|
+
if path.kind_of? self
|
22
|
+
path.value
|
23
|
+
elsif path.respond_to? :map
|
24
|
+
path.map(&method(:unwrap))
|
25
|
+
else
|
26
|
+
[]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(*path)
|
31
|
+
@path = path
|
32
|
+
end
|
33
|
+
|
34
|
+
def value
|
35
|
+
@path.reverse.reduce({}) do |acc, name|
|
36
|
+
{ name => acc }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def respond_to_missing?(*)
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
def method_missing(name, *)
|
47
|
+
self.class.new(*@path, name)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
11
51
|
module DSL
|
12
52
|
# An alias for BabySqueel::DSL#sql
|
13
53
|
def `(str)
|
@@ -36,6 +76,33 @@ module BabySqueel
|
|
36
76
|
end
|
37
77
|
end
|
38
78
|
|
79
|
+
# Overrides ActiveRecord::QueryMethods#includes
|
80
|
+
def includes(*args, &block)
|
81
|
+
if block_given? && args.empty?
|
82
|
+
super KeyPath.evaluate(&block)
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Overrides ActiveRecord::QueryMethods#eager_load
|
89
|
+
def eager_load(*args, &block)
|
90
|
+
if block_given? && args.empty?
|
91
|
+
super KeyPath.evaluate(&block)
|
92
|
+
else
|
93
|
+
super
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Overrides ActiveRecord::QueryMethods#preload
|
98
|
+
def preload(*args, &block)
|
99
|
+
if block_given? && args.empty?
|
100
|
+
super KeyPath.evaluate(&block)
|
101
|
+
else
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
39
106
|
# Heads up, Array#select conflicts with
|
40
107
|
# ActiveRecord::QueryMethods#select. So, if arity
|
41
108
|
# is given to the block, we'll use Array#select.
|
@@ -61,6 +128,14 @@ module BabySqueel
|
|
61
128
|
end
|
62
129
|
end
|
63
130
|
|
131
|
+
def reorder(*args, &block)
|
132
|
+
if block_given? && args.empty?
|
133
|
+
reordering(&block)
|
134
|
+
else
|
135
|
+
super
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
64
139
|
# Overrides ActiveRecord::QueryMethods#group
|
65
140
|
def group(*args, &block)
|
66
141
|
if block_given? && args.empty?
|
data/lib/baby_squeel/dsl.rb
CHANGED
@@ -85,16 +85,6 @@ module BabySqueel
|
|
85
85
|
sql _scope.connection.quote(value)
|
86
86
|
end
|
87
87
|
|
88
|
-
# Evaluates a DSL block. If arity is given, this method
|
89
|
-
# `yield` itself, rather than `instance_eval`.
|
90
|
-
def evaluate(&block)
|
91
|
-
if block.arity.zero?
|
92
|
-
instance_eval(&block)
|
93
|
-
else
|
94
|
-
yield(self)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
88
|
private
|
99
89
|
|
100
90
|
def resolver
|
@@ -2,8 +2,8 @@ module BabySqueel
|
|
2
2
|
# This is the thing that gets added to Active Record's joins_values.
|
3
3
|
# By including Polyamorous::TreeNode, when this instance is found when
|
4
4
|
# traversing joins in ActiveRecord::Associations::JoinDependency::walk_tree,
|
5
|
-
#
|
6
|
-
class
|
5
|
+
# Join#add_to_tree will be called.
|
6
|
+
class Join
|
7
7
|
include Polyamorous::TreeNode
|
8
8
|
|
9
9
|
def initialize(associations)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'join_dependency'
|
2
|
+
|
3
|
+
module BabySqueel
|
4
|
+
module JoinDependency
|
5
|
+
# This class allows BabySqueel to slip custom
|
6
|
+
# joins_values into Active Record's JoinDependency
|
7
|
+
class Injector < Array # :nodoc:
|
8
|
+
# Active Record will call group_by on this object
|
9
|
+
# in ActiveRecord::QueryMethods#build_joins. This
|
10
|
+
# allows BabySqueel::Joins to be treated
|
11
|
+
# like typical join hashes until Polyamorous can
|
12
|
+
# deal with them.
|
13
|
+
def group_by
|
14
|
+
super do |join|
|
15
|
+
case join
|
16
|
+
when BabySqueel::Join
|
17
|
+
:association_join
|
18
|
+
else
|
19
|
+
yield join
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Builder # :nodoc:
|
26
|
+
attr_reader :join_dependency
|
27
|
+
|
28
|
+
def initialize(relation)
|
29
|
+
@join_dependency = ::JoinDependency.from_relation(relation) do |join|
|
30
|
+
:association_join if join.kind_of? BabySqueel::Join
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Find the alias of a BabySqueel::Association, by passing
|
35
|
+
# a list (in order of chaining) of associations and finding
|
36
|
+
# the respective JoinAssociation at each level.
|
37
|
+
def find_alias(associations)
|
38
|
+
# If we tell join_dependency to construct its tables, Active Record
|
39
|
+
# handles building the correct aliases and attaching them to its
|
40
|
+
# JoinDepenencies.
|
41
|
+
if ::ActiveRecord::VERSION::STRING >= '5.2.3'
|
42
|
+
join_dependency.send(:construct_tables!, join_dependency.send(:join_root))
|
43
|
+
end
|
44
|
+
|
45
|
+
join_association = find_join_association(associations)
|
46
|
+
join_association.table
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def find_join_association(associations)
|
52
|
+
current = join_dependency.send(:join_root)
|
53
|
+
|
54
|
+
associations.each do |association|
|
55
|
+
name = association._reflection.name
|
56
|
+
current = current.children.find { |c| c.reflection.name == name }
|
57
|
+
break if current.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
current
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -7,6 +7,11 @@ module BabySqueel
|
|
7
7
|
# In the future, these proxy classes should be more specific and only
|
8
8
|
# include necessary/applicable modules.
|
9
9
|
class Node < Proxy
|
10
|
+
def initialize(node)
|
11
|
+
super
|
12
|
+
node.extend Arel::Math
|
13
|
+
end
|
14
|
+
|
10
15
|
extend Operators::ArelAliasing
|
11
16
|
include Operators::Comparison
|
12
17
|
include Operators::Equality
|
data/lib/baby_squeel/pluck.rb
CHANGED
@@ -9,9 +9,9 @@ module BabySqueel
|
|
9
9
|
# bother to use this there.
|
10
10
|
|
11
11
|
if ::ActiveRecord::VERSION::MAJOR >= 5
|
12
|
-
def self.
|
12
|
+
def self.decorate(node); node; end
|
13
13
|
else
|
14
|
-
def self.
|
14
|
+
def self.decorate(node); new(node); end
|
15
15
|
end
|
16
16
|
|
17
17
|
def initialize(node)
|
data/lib/baby_squeel/table.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'baby_squeel/resolver'
|
2
|
-
require 'baby_squeel/
|
3
|
-
require 'baby_squeel/join_dependency
|
2
|
+
require 'baby_squeel/join'
|
3
|
+
require 'baby_squeel/join_dependency'
|
4
4
|
|
5
5
|
module BabySqueel
|
6
6
|
class Table
|
@@ -20,6 +20,10 @@ module BabySqueel
|
|
20
20
|
@_join ||= Arel::Nodes::InnerJoin
|
21
21
|
end
|
22
22
|
|
23
|
+
def as(alias_name)
|
24
|
+
self.alias(alias_name)
|
25
|
+
end
|
26
|
+
|
23
27
|
# Alias a table. This is only possible when joining
|
24
28
|
# an association explicitly.
|
25
29
|
def alias(alias_name)
|
@@ -31,6 +35,10 @@ module BabySqueel
|
|
31
35
|
self
|
32
36
|
end
|
33
37
|
|
38
|
+
def alias?
|
39
|
+
_table.kind_of? Arel::Nodes::TableAlias
|
40
|
+
end
|
41
|
+
|
34
42
|
# Instruct the table to be joined with a LEFT OUTER JOIN.
|
35
43
|
def outer
|
36
44
|
clone.outer!
|
@@ -52,22 +60,32 @@ module BabySqueel
|
|
52
60
|
end
|
53
61
|
|
54
62
|
# Specify an explicit join.
|
55
|
-
def on(node)
|
56
|
-
clone.on!
|
63
|
+
def on(node = nil, &block)
|
64
|
+
clone.on!(node, &block)
|
57
65
|
end
|
58
66
|
|
59
|
-
def on!(node) # :nodoc:
|
60
|
-
self._on = node
|
67
|
+
def on!(node = nil, &block) # :nodoc:
|
68
|
+
self._on = node || evaluate(&block)
|
61
69
|
self
|
62
70
|
end
|
63
71
|
|
72
|
+
# Evaluates a DSL block. If arity is given, this method
|
73
|
+
# `yield` itself, rather than `instance_eval`.
|
74
|
+
def evaluate(&block)
|
75
|
+
if block.arity.zero?
|
76
|
+
instance_eval(&block)
|
77
|
+
else
|
78
|
+
yield(self)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
64
82
|
# When referencing a joined table, the tables that
|
65
83
|
# attributes reference can change (due to aliasing).
|
66
84
|
# This method allows BabySqueel::Nodes::Attribute
|
67
85
|
# instances to find what their alias will be.
|
68
86
|
def find_alias(associations = [])
|
69
|
-
|
70
|
-
builder.
|
87
|
+
rel = _scope.joins _arel(associations)
|
88
|
+
builder = JoinDependency::Builder.new(rel)
|
71
89
|
builder.find_alias(associations)
|
72
90
|
end
|
73
91
|
|
@@ -78,13 +96,13 @@ module BabySqueel
|
|
78
96
|
# 2. Implicit join without using an outer join. In this case, we'll just
|
79
97
|
# give a hash to Active Record, and join the normal way.
|
80
98
|
# 3. Implicit join using an outer join. In this case, we need to use
|
81
|
-
# Polyamorous to build the join. We'll return a
|
99
|
+
# Polyamorous to build the join. We'll return a Join.
|
82
100
|
#
|
83
101
|
def _arel(associations = [])
|
84
102
|
if _on
|
85
103
|
_join.new(_table, Arel::Nodes::On.new(_on))
|
86
104
|
elsif associations.any?(&:needs_polyamorous?)
|
87
|
-
|
105
|
+
Join.new(associations)
|
88
106
|
elsif associations.any?
|
89
107
|
associations.reverse.inject({}) do |names, assoc|
|
90
108
|
{ assoc._reflection.name => names }
|
data/lib/baby_squeel/version.rb
CHANGED
metadata
CHANGED
@@ -1,99 +1,113 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: baby_squeel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ray Zane
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '5.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '5.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: ransack
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '2.3'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '2.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: join_dependency
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.1.4
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.1.4
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '2'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '2'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rake
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
75
|
+
version: '13.0'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
82
|
+
version: '13.0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rspec
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: 3.
|
89
|
+
version: '3.10'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: 3.
|
96
|
+
version: '3.10'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: sqlite3
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
|
-
- - "
|
101
|
+
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
103
|
+
version: 1.3.6
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
|
-
- - "
|
108
|
+
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
110
|
+
version: 1.3.6
|
97
111
|
description: An expressive query DSL for Active Record 4 and 5.
|
98
112
|
email:
|
99
113
|
- ray@promptworks.com
|
@@ -116,9 +130,8 @@ files:
|
|
116
130
|
- lib/baby_squeel/compat.rb
|
117
131
|
- lib/baby_squeel/dsl.rb
|
118
132
|
- lib/baby_squeel/errors.rb
|
119
|
-
- lib/baby_squeel/
|
120
|
-
- lib/baby_squeel/join_dependency
|
121
|
-
- lib/baby_squeel/join_expression.rb
|
133
|
+
- lib/baby_squeel/join.rb
|
134
|
+
- lib/baby_squeel/join_dependency.rb
|
122
135
|
- lib/baby_squeel/nodes.rb
|
123
136
|
- lib/baby_squeel/nodes/attribute.rb
|
124
137
|
- lib/baby_squeel/nodes/binary.rb
|
@@ -136,7 +149,7 @@ homepage: https://github.com/rzane/baby_squeel
|
|
136
149
|
licenses:
|
137
150
|
- MIT
|
138
151
|
metadata: {}
|
139
|
-
post_install_message:
|
152
|
+
post_install_message:
|
140
153
|
rdoc_options: []
|
141
154
|
require_paths:
|
142
155
|
- lib
|
@@ -147,13 +160,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
147
160
|
version: '0'
|
148
161
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
162
|
requirements:
|
150
|
-
- - "
|
163
|
+
- - ">"
|
151
164
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
165
|
+
version: 1.3.1
|
153
166
|
requirements: []
|
154
|
-
|
155
|
-
|
156
|
-
signing_key:
|
167
|
+
rubygems_version: 3.0.3
|
168
|
+
signing_key:
|
157
169
|
specification_version: 4
|
158
170
|
summary: An expressive query DSL for Active Record 4 and 5.
|
159
171
|
test_files: []
|
@@ -1,121 +0,0 @@
|
|
1
|
-
require 'baby_squeel/join_dependency/injector'
|
2
|
-
|
3
|
-
module BabySqueel
|
4
|
-
module JoinDependency
|
5
|
-
# Unfortunately, this is mostly all duplication of
|
6
|
-
# ActiveRecord::QueryMethods#build_joins
|
7
|
-
class Builder # :nodoc:
|
8
|
-
attr_reader :relation
|
9
|
-
|
10
|
-
def initialize(relation)
|
11
|
-
@relation = relation
|
12
|
-
@joins_values = relation.joins_values.dup
|
13
|
-
end
|
14
|
-
|
15
|
-
def ensure_associated(*values)
|
16
|
-
@joins_values += values
|
17
|
-
end
|
18
|
-
|
19
|
-
def join_dependency
|
20
|
-
::ActiveRecord::Associations::JoinDependency.new(
|
21
|
-
relation.model,
|
22
|
-
association_joins,
|
23
|
-
join_list
|
24
|
-
)
|
25
|
-
end
|
26
|
-
delegate :join_root, to: :join_dependency
|
27
|
-
|
28
|
-
def find_join_association(associations)
|
29
|
-
associations.inject(join_root) do |parent, assoc|
|
30
|
-
parent.children.find do |join_association|
|
31
|
-
reflections_equal?(
|
32
|
-
assoc._reflection,
|
33
|
-
join_association.reflection
|
34
|
-
)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# Find the alias of a BabySqueel::Association, by passing
|
40
|
-
# a list (in order of chaining) of associations and finding
|
41
|
-
# the respective JoinAssociation at each level.
|
42
|
-
def find_alias(associations)
|
43
|
-
table = find_join_association(associations).table
|
44
|
-
reconstruct_with_type_caster(table, associations)
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
# Compare two reflections and see if they're the same.
|
50
|
-
def reflections_equal?(a, b)
|
51
|
-
comparable_reflection(a) == comparable_reflection(b)
|
52
|
-
end
|
53
|
-
|
54
|
-
# Get the parent of the reflection if it has one.
|
55
|
-
# In AR4, #parent_reflection returns [name, reflection]
|
56
|
-
# In AR5, #parent_reflection returns just a reflection
|
57
|
-
def comparable_reflection(reflection)
|
58
|
-
[*reflection.parent_reflection].last || reflection
|
59
|
-
end
|
60
|
-
|
61
|
-
# Active Record 5's AliasTracker initializes Arel tables
|
62
|
-
# with the type_caster belonging to the wrong model.
|
63
|
-
#
|
64
|
-
# See: https://github.com/rails/rails/pull/27994
|
65
|
-
def reconstruct_with_type_caster(table, associations)
|
66
|
-
return table if ::ActiveRecord::VERSION::MAJOR < 5
|
67
|
-
type_caster = associations.last._scope.type_caster
|
68
|
-
::Arel::Table.new(table.name, type_caster: type_caster)
|
69
|
-
end
|
70
|
-
|
71
|
-
def join_list
|
72
|
-
join_nodes + join_strings_as_ast
|
73
|
-
end
|
74
|
-
|
75
|
-
def association_joins
|
76
|
-
buckets[:association_join] || []
|
77
|
-
end
|
78
|
-
|
79
|
-
def stashed_association_joins
|
80
|
-
buckets[:stashed_join] || []
|
81
|
-
end
|
82
|
-
|
83
|
-
def join_nodes
|
84
|
-
(buckets[:join_node] || []).uniq
|
85
|
-
end
|
86
|
-
|
87
|
-
def string_joins
|
88
|
-
(buckets[:string_join] || []).map(&:strip).uniq
|
89
|
-
end
|
90
|
-
|
91
|
-
if Arel::VERSION >= '7.0.0'
|
92
|
-
def join_strings_as_ast
|
93
|
-
manager = Arel::SelectManager.new(relation.table)
|
94
|
-
relation.send(:convert_join_strings_to_ast, manager, string_joins)
|
95
|
-
end
|
96
|
-
else
|
97
|
-
def join_strings_as_ast
|
98
|
-
manager = Arel::SelectManager.new(relation.table.engine, relation.table)
|
99
|
-
relation.send(:custom_join_ast, manager, string_joins)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def buckets
|
104
|
-
@buckets ||= Injector.new(@joins_values).group_by do |join|
|
105
|
-
case join
|
106
|
-
when String
|
107
|
-
:string_join
|
108
|
-
when Hash, Symbol, Array
|
109
|
-
:association_join
|
110
|
-
when ::ActiveRecord::Associations::JoinDependency
|
111
|
-
:stashed_join
|
112
|
-
when ::Arel::Nodes::Join
|
113
|
-
:join_node
|
114
|
-
else
|
115
|
-
raise 'unknown class: %s' % join.class.name
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module BabySqueel
|
2
|
-
module JoinDependency
|
3
|
-
# This class allows BabySqueel to slip custom
|
4
|
-
# joins_values into Active Record's JoinDependency
|
5
|
-
class Injector < Array # :nodoc:
|
6
|
-
# Active Record will call group_by on this object
|
7
|
-
# in ActiveRecord::QueryMethods#build_joins. This
|
8
|
-
# allows BabySqueel::JoinExpressions to be treated
|
9
|
-
# like typical join hashes until Polyamorous can
|
10
|
-
# deal with them.
|
11
|
-
def group_by
|
12
|
-
super do |join|
|
13
|
-
case join
|
14
|
-
when BabySqueel::JoinExpression
|
15
|
-
:association_join
|
16
|
-
else
|
17
|
-
yield join
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|