arel-helpers 2.9.1 → 2.12.1

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
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