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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: '01931ce5553f1202e28f1698e03ac5edf72a0166'
4
- data.tar.gz: 6c3a49f9b7a5cb8c9d72f280d05cf0e165ddf3fc
2
+ SHA256:
3
+ metadata.gz: 2df3535278f1724c6db1656ab9c5bbe0e9097e80ee6f2c1fdaeb3491bc838ae9
4
+ data.tar.gz: bddd464023f9f53f0ff32cc1fbaa88928f5736b3e6541418b8c36f26b01b8d34
5
5
  SHA512:
6
- metadata.gz: 335dd7d2f9e2f8f3d263339441069e68eca7a632b7296f4ed523f91be621ae727e8e63a8725c8f8b216db43a7a7d8b0f70b2fe407a861104885ae032d7ba88b5
7
- data.tar.gz: d25f185e663890ecf277045612eb71206de192d4e79883046a2deaa981378987e872a9516cc654e910ffe5cfb45113bccf3d2880fde90e6129b8e784a9fa14f1
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.4...HEAD
122
- [1.1.3]: https://github.com/rzane/baby_squeel/compare/v1.1.3...v1.1.4
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
- [![Build Status](https://travis-ci.org/rzane/baby_squeel.svg?branch=master)](https://travis-ci.org/rzane/baby_squeel)
4
- [![Code Climate](https://codeclimate.com/github/rzane/baby_squeel/badges/gpa.svg)](https://codeclimate.com/github/rzane/baby_squeel)
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.alias('a').on((author.id == author_id) | (author.name == title)) }
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 ("authors"."id" = "posts"."author_id" OR "authors"."name" = "posts"."title")
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', '>= 4.2.0'
23
- spec.add_dependency 'polyamorous', '~> 1.3'
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', '~> 1.11'
26
- spec.add_development_dependency 'rake', '~> 10.0'
27
- spec.add_development_dependency 'rspec', '~> 3.4.0'
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
- require 'polyamorous'
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, :joining!, :selecting, :ordering,
7
- :grouping, :when_having, :plucking,
8
- :averaging, :counting, :maximizing,
9
- :minimizing, :summing, to: :all
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
- pluck Pluck.wrap(DSL.evaluate(self, &block))
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/injector'
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::Base#joins using the DSL.
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::Base#select using the DSL.
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::Base#order using the DSL.
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::Base#group using the DSL.
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::Base#having using the DSL.
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::JoinExpression nodes.
38
- def build_joins(manager, joins)
39
- super manager, BabySqueel::JoinDependency::Injector.new(joins)
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 JoinExpression#add_to_tree.
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 _table.is_a? Arel::Nodes::TableAlias
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)
@@ -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?
@@ -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
- # JoinExpression#add_to_tree will be called.
6
- class JoinExpression
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
@@ -26,7 +26,7 @@ module BabySqueel
26
26
  end
27
27
 
28
28
  def _arel
29
- if @parent.kind_of? BabySqueel::Association
29
+ if @parent.kind_of?(BabySqueel::Association) && !@parent.alias?
30
30
  @parent.find_alias[@name]
31
31
  else
32
32
  super
@@ -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
@@ -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.wrap(node); node; end
12
+ def self.decorate(node); node; end
13
13
  else
14
- def self.wrap(node); new(node); end
14
+ def self.decorate(node); new(node); end
15
15
  end
16
16
 
17
17
  def initialize(node)
@@ -1,6 +1,6 @@
1
1
  require 'baby_squeel/resolver'
2
- require 'baby_squeel/join_expression'
3
- require 'baby_squeel/join_dependency/builder'
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! node
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
- builder = JoinDependency::Builder.new(_scope.all)
70
- builder.ensure_associated _arel(associations)
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 JoinExpression.
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
- JoinExpression.new(associations)
105
+ Join.new(associations)
88
106
  elsif associations.any?
89
107
  associations.reverse.inject({}) do |names, assoc|
90
108
  { assoc._reflection.name => names }
@@ -1,3 +1,3 @@
1
1
  module BabySqueel
2
- VERSION = '1.1.5'.freeze
2
+ VERSION = '1.4.0.beta1'.freeze
3
3
  end
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.1.5
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: 2017-05-26 00:00:00.000000000 Z
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: 4.2.0
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: 4.2.0
26
+ version: '5.2'
27
27
  - !ruby/object:Gem::Dependency
28
- name: polyamorous
28
+ name: ransack
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.3'
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: '1.3'
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: '1.11'
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: '1.11'
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: '10.0'
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: '10.0'
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.4.0
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.4.0
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: '0'
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: '0'
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/join_dependency/builder.rb
120
- - lib/baby_squeel/join_dependency/injector.rb
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: '0'
165
+ version: 1.3.1
153
166
  requirements: []
154
- rubyforge_project:
155
- rubygems_version: 2.6.10
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