arel-helpers 2.9.1 → 2.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5daa252f2c9a256e38b204ab59498c59cb5848ff5250db46a9f35df110adac06
4
- data.tar.gz: cc955446d243cbfe1285188b435911876b884bdd989920596ec547df410f48eb
3
+ metadata.gz: 20ee0a4bd3c483dbe5dc58fa930700f36a09e593faf1982f0c578e5adb4736e4
4
+ data.tar.gz: 26195a43b12121bb2ef5d4bbee9ebe9e95ce70769e24206892a04cd04d76cc25
5
5
  SHA512:
6
- metadata.gz: 5198b3ec78bb0f2379bd59de580623d238ffc4b608796b45e1afc5effb2a749c5e7da8934947e7fa1675230ca69ea1ca14b016907c24b1e095a190419158258c
7
- data.tar.gz: 20ed995c0792e698f40fddb31ca8568828450eda52cb9b83b98d3a94b7379c6a0488aee19e82a4d12ea5960a470c0bb3806eb3813da27d16329057c6361e8852
6
+ metadata.gz: 6e40a55ba798ac3c094a44aaf0d9dce31cf6ce12adca9a5e2b45f225696f69dfcbc1cb8b517ea984113ad888392b519b4d98538fd904f3cb4bdbfe7772cac5c7
7
+ data.tar.gz: 409ebbdbaedef2216bc68579f562dcfa44dfb2dcf7b0b4275599fee2c11f3e165674237cf882c4aef31e651069079649a04e583ac15a06a9ebfc39c3b0b48b98
data/Gemfile CHANGED
@@ -1,16 +1,3 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
5
- group :development, :test do
6
- gem 'pry-byebug'
7
-
8
- # lock to 10.0 until rspec is upgraded
9
- gem 'rake', '~> 10.0'
10
- end
11
-
12
- group :test do
13
- gem 'rspec', '~> 2.11.0'
14
- gem 'rr', '~> 1.0.4'
15
- gem 'sqlite3'
16
- end
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
-
1
+ ![Unit Tests](https://github.com/camertron/arel-helpers/actions/workflows/unit_tests.yml/badge.svg?branch=master)
2
2
 
3
3
  ## arel-helpers [![Build Status](https://secure.travis-ci.org/camertron/arel-helpers.png?branch=master)](http://travis-ci.org/camertron/arel-helpers)
4
4
 
@@ -187,9 +187,35 @@ PostQueryBuilder.new
187
187
  .since_yesterday
188
188
  ```
189
189
 
190
+ #### Conditional reflections
191
+
192
+ If you have parts of a query that should only be added under certain conditions you can return `reflect(query)` from your method. E.g:
193
+
194
+ ```ruby
195
+ def with_comments_by(usernames)
196
+ if usernames
197
+ reflect(
198
+ query.where(post[:title].matches("%#{title}%"))
199
+ )
200
+ else
201
+ reflect(query)
202
+ end
203
+ end
204
+ ```
205
+
206
+ This can become repetitive, and as an alternative you can choose to prepend `not_nil` to your method definition:
207
+
208
+ ```ruby
209
+ class PostQueryBuilder < ArelHelpers::QueryBuilder
210
+ not_nil def with_comments_by(usernames)
211
+ reflect(query.where(post[:title].matches("%#{title}%"))) if usernames
212
+ end
213
+ end
214
+ ```
215
+
190
216
  ## Requirements
191
217
 
192
- Requires ActiveRecord >= 3.1.0, < 6, tested against Ruby 2.2.4. Depends on SQLite for testing purposes.
218
+ Requires ActiveRecord >= 3.1.0, < 7. Depends on SQLite for testing purposes.
193
219
 
194
220
  ## Running Tests
195
221
 
data/arel-helpers.gemspec CHANGED
@@ -1,23 +1,26 @@
1
- $:.unshift File.join(File.dirname(__FILE__), 'lib')
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
2
2
  require 'arel-helpers/version'
3
3
 
4
4
  Gem::Specification.new do |s|
5
- s.name = "arel-helpers"
5
+ s.name = 'arel-helpers'
6
6
  s.version = ::ArelHelpers::VERSION
7
- s.authors = ["Cameron Dutro"]
8
- s.email = ["camertron@gmail.com"]
9
- s.homepage = "https://github.com/camertron/arel-helpers"
7
+ s.authors = ['Cameron Dutro']
8
+ s.email = ['camertron@gmail.com']
9
+ s.homepage = 'https://github.com/camertron/arel-helpers'
10
10
  s.license = 'MIT'
11
- s.description = s.summary = "Useful tools to help construct database queries with ActiveRecord and Arel."
11
+ s.description = s.summary = 'Useful tools to help construct database queries with ActiveRecord and Arel.'
12
12
 
13
13
  s.platform = Gem::Platform::RUBY
14
14
 
15
- if ENV["AR"]
16
- s.add_dependency 'activerecord', ENV["AR"]
17
- else
18
- s.add_dependency 'activerecord', '>= 3.1.0', '< 7'
19
- end
15
+ s.add_dependency 'activerecord', '>= 3.1.0', '< 7'
16
+
17
+ s.add_development_dependency 'appraisal'
18
+ s.add_development_dependency 'combustion', '~> 1.3'
19
+ s.add_development_dependency 'database_cleaner', '~> 1.8'
20
+ s.add_development_dependency 'rake', '~> 10.0'
21
+ s.add_development_dependency 'rspec', '~> 3'
22
+ s.add_development_dependency 'sqlite3', '~> 1.4.0'
20
23
 
21
24
  s.require_path = 'lib'
22
- s.files = Dir["{lib,spec}/**/*", "Gemfile", "History.txt", "README.md", "Rakefile", "arel-helpers.gemspec"]
25
+ s.files = Dir['{lib,spec}/**/*', 'Gemfile', 'History.txt', 'README.md', 'Rakefile', 'arel-helpers.gemspec']
23
26
  end
@@ -18,7 +18,11 @@ module ArelHelpers
18
18
  # This method encapsulates that functionality and yields an intermediate object for chaining.
19
19
  # It also allows you to use an outer join instead of the default inner via the join_type arg.
20
20
  def join_association(table, association, join_type = Arel::Nodes::InnerJoin, options = {}, &block)
21
- if version >= '5.2.1'
21
+ if version >= '6.1.0'
22
+ join_association_6_1_0(table, association, join_type, options, &block)
23
+ elsif version >= '6.0.0'
24
+ join_association_6_0_0(table, association, join_type, options, &block)
25
+ elsif version >= '5.2.1'
22
26
  join_association_5_2_1(table, association, join_type, options, &block)
23
27
  elsif version >= '5.2.0'
24
28
  join_association_5_2(table, association, join_type, options, &block)
@@ -40,7 +44,7 @@ module ArelHelpers
40
44
  end
41
45
 
42
46
  def join_association_3_1(table, association, join_type, options = {})
43
- aliases = options.fetch(:aliases, {})
47
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
44
48
  associations = association.is_a?(Array) ? association : [association]
45
49
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
46
50
  manager = Arel::SelectManager.new(table)
@@ -51,12 +55,9 @@ module ArelHelpers
51
55
  end
52
56
 
53
57
  manager.join_sources.map do |assoc|
54
- if found_alias = find_alias(assoc.left.name, aliases)
55
- assoc.left.table_alias = found_alias.name
56
- end
58
+ assoc.left.table_alias = aliases[assoc.left.name].name if aliases.key?(assoc.left.name)
57
59
 
58
60
  if block_given?
59
- # yield |assoc_name, join_conditions|
60
61
  right = yield assoc.left.name.to_sym, assoc.right
61
62
  assoc.class.new(assoc.left, right)
62
63
  else
@@ -66,7 +67,7 @@ module ArelHelpers
66
67
  end
67
68
 
68
69
  def join_association_4_1(table, association, join_type, options = {})
69
- aliases = options.fetch(:aliases, {})
70
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
70
71
  associations = association.is_a?(Array) ? association : [association]
71
72
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
72
73
 
@@ -77,9 +78,7 @@ module ArelHelpers
77
78
  constraint.right
78
79
  end
79
80
 
80
- if found_alias = find_alias(constraint.left.name, aliases)
81
- constraint.left.table_alias = found_alias.name
82
- end
81
+ constraint.left.table_alias = aliases[constraint.left.name].name if aliases.key?(constraint.left.name)
83
82
 
84
83
  join_type.new(constraint.left, right)
85
84
  end
@@ -91,7 +90,7 @@ module ArelHelpers
91
90
  # dynamically. To get around the problem, this method must return
92
91
  # a string.
93
92
  def join_association_4_2(table, association, join_type, options = {})
94
- aliases = options.fetch(:aliases, [])
93
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
95
94
  associations = association.is_a?(Array) ? association : [association]
96
95
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
97
96
 
@@ -109,9 +108,7 @@ module ArelHelpers
109
108
  join.right
110
109
  end
111
110
 
112
- if found_alias = find_alias(join.left.name, aliases)
113
- join.left.table_alias = found_alias.name
114
- end
111
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
115
112
 
116
113
  join_type.new(join.left, right)
117
114
  end
@@ -125,7 +122,7 @@ module ArelHelpers
125
122
  end
126
123
 
127
124
  def join_association_5_0(table, association, join_type, options = {})
128
- aliases = options.fetch(:aliases, [])
125
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
129
126
  associations = association.is_a?(Array) ? association : [association]
130
127
  join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
131
128
 
@@ -144,9 +141,7 @@ module ArelHelpers
144
141
  join.right
145
142
  end
146
143
 
147
- if found_alias = find_alias(join.left.name, aliases)
148
- join.left.table_alias = found_alias.name
149
- end
144
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
150
145
 
151
146
  join_type.new(join.left, right)
152
147
  end
@@ -160,7 +155,7 @@ module ArelHelpers
160
155
  end
161
156
 
162
157
  def join_association_5_2(table, association, join_type, options = {})
163
- aliases = options.fetch(:aliases, [])
158
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
164
159
  associations = association.is_a?(Array) ? association : [association]
165
160
 
166
161
  alias_tracker = ActiveRecord::Associations::AliasTracker.create(
@@ -180,16 +175,14 @@ module ArelHelpers
180
175
  join.right
181
176
  end
182
177
 
183
- if found_alias = find_alias(join.left.name, aliases)
184
- join.left.table_alias = found_alias.name
185
- end
178
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
186
179
 
187
180
  join_type.new(join.left, right)
188
181
  end
189
182
  end
190
183
 
191
184
  def join_association_5_2_1(table, association, join_type, options = {})
192
- aliases = options.fetch(:aliases, [])
185
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
193
186
  associations = association.is_a?(Array) ? association : [association]
194
187
 
195
188
  alias_tracker = ActiveRecord::Associations::AliasTracker.create(
@@ -209,14 +202,83 @@ module ArelHelpers
209
202
  join.right
210
203
  end
211
204
 
212
- if found_alias = find_alias(join.left.name, aliases)
213
- join.left.table_alias = found_alias.name
205
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
206
+
207
+ join_type.new(join.left, right)
208
+ end
209
+ end
210
+
211
+ def join_association_6_0_0(table, association, join_type, options = {})
212
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
213
+ associations = association.is_a?(Array) ? association : [association]
214
+
215
+ alias_tracker = ActiveRecord::Associations::AliasTracker.create(
216
+ table.connection, table.name, {}
217
+ )
218
+
219
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(
220
+ table, table.arel_table, associations, join_type
221
+ )
222
+
223
+ constraints = join_dependency.join_constraints([], alias_tracker)
224
+
225
+ constraints.map do |join|
226
+ right = if block_given?
227
+ yield join.left.name.to_sym, join.right
228
+ else
229
+ join.right
214
230
  end
215
231
 
232
+ join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
233
+
234
+ join_type.new(join.left, right)
235
+ end
236
+ end
237
+
238
+ def join_association_6_1_0(table, association, join_type, options = {})
239
+ aliases = options.fetch(:aliases, []).index_by(&:table_name)
240
+ associations = association.is_a?(Array) ? association : [association]
241
+
242
+ alias_tracker = ActiveRecord::Associations::AliasTracker.create(
243
+ table.connection, table.name, {}
244
+ )
245
+
246
+ join_dependency = ActiveRecord::Associations::JoinDependency.new(
247
+ table, table.arel_table, associations, join_type
248
+ )
249
+
250
+ constraints = join_dependency.join_constraints([], alias_tracker, [])
251
+
252
+ constraints.map do |join|
253
+ apply_aliases(join, aliases)
254
+
255
+ right = if block_given?
256
+ yield join.left.name.to_sym, join.right
257
+ else
258
+ join.right
259
+ end
260
+
216
261
  join_type.new(join.left, right)
217
262
  end
218
263
  end
219
264
 
265
+ def apply_aliases(node, aliases)
266
+ case node
267
+ when Arel::Nodes::Join
268
+ node.left = aliases[node.left.name] || node.left
269
+ apply_aliases(node.right, aliases)
270
+ when Arel::Attributes::Attribute
271
+ node.relation = aliases[node.relation.name] || node.relation
272
+ when Arel::Nodes::And
273
+ node.children.each { |child| apply_aliases(child, aliases) }
274
+ when Arel::Nodes::Unary
275
+ apply_aliases(node.value, aliases)
276
+ when Arel::Nodes::Binary
277
+ apply_aliases(node.left, aliases)
278
+ apply_aliases(node.right, aliases)
279
+ end
280
+ end
281
+
220
282
  private
221
283
 
222
284
  def to_sql(node, table, binds)
@@ -224,10 +286,6 @@ module ArelHelpers
224
286
  collect = visitor.accept(node, Arel::Collectors::Bind.new)
225
287
  collect.substitute_binds(binds).join
226
288
  end
227
-
228
- def find_alias(name, aliases)
229
- aliases.find { |a| a.table_name == name }
230
- end
231
289
  end
232
290
  end
233
291
  end
@@ -16,6 +16,21 @@ module ArelHelpers
16
16
 
17
17
  def_delegators :@query, *TERMINAL_METHODS
18
18
 
19
+ def self.not_nil(name)
20
+ mod = Module.new do
21
+ define_method(name) do |*args, **kwargs|
22
+ if (value = super(*args, **kwargs))
23
+ value
24
+ else
25
+ reflect(query)
26
+ end
27
+ end
28
+ end
29
+
30
+ prepend mod
31
+ name
32
+ end
33
+
19
34
  def initialize(query)
20
35
  @query = query
21
36
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module ArelHelpers
4
- VERSION = '2.9.1'
4
+ VERSION = '2.12.1'
5
5
  end
data/spec/aliases_spec.rb CHANGED
@@ -1,17 +1,15 @@
1
- # encoding: UTF-8
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe ArelHelpers::Aliases do
6
- describe "#aliased_as" do
7
- it "yields an alias when passed a block" do
4
+ describe '#aliased_as' do
5
+ it 'yields an alias when passed a block' do
8
6
  Post.aliased_as('foo') do |foo_alias|
9
7
  expect(foo_alias).to be_a(Arel::Nodes::TableAlias)
10
8
  expect(foo_alias.name).to eq('foo')
11
9
  end
12
10
  end
13
11
 
14
- it "is capable of yielding multiple aliases" do
12
+ it 'is capable of yielding multiple aliases' do
15
13
  Post.aliased_as('foo', 'bar') do |foo_alias, bar_alias|
16
14
  expect(foo_alias).to be_a(Arel::Nodes::TableAlias)
17
15
  expect(foo_alias.name).to eq('foo')
@@ -21,14 +19,14 @@ describe ArelHelpers::Aliases do
21
19
  end
22
20
  end
23
21
 
24
- it "returns an alias when not passed a block" do
22
+ it 'returns an alias when not passed a block' do
25
23
  aliases = Post.aliased_as('foo')
26
24
  expect(aliases.size).to eq(1)
27
25
  expect(aliases[0]).to be_a(Arel::Nodes::TableAlias)
28
26
  expect(aliases[0].name).to eq('foo')
29
27
  end
30
28
 
31
- it "is capable of returning multiple aliases" do
29
+ it 'is capable of returning multiple aliases' do
32
30
  aliases = Post.aliased_as('foo', 'bar')
33
31
  expect(aliases.size).to eq(2)
34
32
 
@@ -1,30 +1,28 @@
1
- # encoding: UTF-8
2
-
3
1
  require 'spec_helper'
4
2
 
5
3
  describe ArelHelpers::ArelTable do
6
- it "should add the [] function to the model and allow attribute access" do
4
+ it 'should add the [] function to the model and allow attribute access' do
7
5
  Post[:id].tap do |post_id|
8
- post_id.should be_a(Arel::Attribute)
9
- post_id.name.should == :id
10
- post_id.relation.name.should == "posts"
6
+ expect(post_id).to be_a Arel::Attribute
7
+ expect(post_id.name.to_s).to eq 'id'
8
+ expect(post_id.relation.name).to eq 'posts'
11
9
  end
12
10
  end
13
11
 
14
- it "should not interfere with associations" do
12
+ it 'should not interfere with associations' do
15
13
  post = Post.create(title: "I'm a little teapot")
16
- post.comments[0].should be_nil
14
+ expect(post.comments[0]).to be_nil
17
15
  end
18
16
 
19
- it "should allow retrieving associated records" do
17
+ it 'should allow retrieving associated records' do
20
18
  post = Post.create(title: "I'm a little teapot")
21
19
  comment = post.comments.create
22
- post.reload.comments[0].id.should == comment.id
20
+ expect(post.reload.comments[0].id).to eq comment.id
23
21
  end
24
22
 
25
- it "does not interfere with ActiveRecord::Relation objects" do
26
- Post.all[0].should be_nil
23
+ it 'does not interfere with ActiveRecord::Relation objects' do
24
+ expect(Post.all[0]).to be_nil
27
25
  p = Post.create(title: 'foo')
28
- Post.all[0].id.should == p.id
26
+ expect(Post.all[0].id).to eq p.id
29
27
  end
30
28
  end
data/spec/env/models.rb CHANGED
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
-
3
1
  class Post < ActiveRecord::Base
4
2
  include ArelHelpers::ArelTable
5
3
  include ArelHelpers::Aliases
@@ -44,12 +42,10 @@ end
44
42
 
45
43
  class Location < ActiveRecord::Base
46
44
  has_many :card_locations
47
- has_many :community_tickets, {
48
- through: :card_locations, source: :card, source_type: 'CommunityTicket'
49
- }
45
+ has_many :community_tickets, through: :card_locations, source: :card, source_type: 'CommunityTicket'
50
46
  end
51
47
 
52
48
  class CommunityTicket < ActiveRecord::Base
53
- has_many :card_locations, as: :card, source_type: 'CommunityTicket'
49
+ has_many :card_locations, as: :card
54
50
  has_many :locations, through: :card_locations
55
51
  end