arel-helpers 2.8.0 → 2.12.0

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: e692ef35ca5dfd967971eeb47c87ef93b6ca21c142e03ecb29b608c1e8e533e0
4
- data.tar.gz: 719115c3d5f7a9c86d89d6bc720e390906eac6a9d5b60d29c19bebe751aa8c5b
3
+ metadata.gz: d792fc6a4945a79243f3e4b9720c9f10ce21f5a044328f56f2b381045183589a
4
+ data.tar.gz: becca530d9ecfd5a25594ce0363cbde8d792d564358d2394a36d7040e6341384
5
5
  SHA512:
6
- metadata.gz: 946f6a173a8a3da761ad79b9a416cc7b7871ba0ce879ecd0cb32b2348199c65109df1f7ebee1ec438bdf3cf443d840282935e97d56eed009ac54a31b79cb9d83
7
- data.tar.gz: 346d3fcbe5be8208734ca9a35f47870531524d008f8e67d307912eb3582d134b2b51c5a721ff31b5488efb519ade8c6c7830db509aa38450280e764147974527
6
+ metadata.gz: 2d825413e0dc2f3caff9ddcbc93a4eadbb1d8814196bc515fd13b3a759969db970a9fe2df911da86def2125d724d5c651e5a39eba2d9cf873d573c913b6a51f5
7
+ data.tar.gz: 70bea7795e9bcd75058dad5aaee877f6fb1d00e40f9e62b0db8b908d1a1f0656654fe67170a1024e1776b2a458ea11622fad399cda001ffd127c5f35f8197824
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
@@ -155,7 +155,7 @@ class PostQueryBuilder < ArelHelpers::QueryBuilder
155
155
  def with_comments_by(usernames)
156
156
  reflect(
157
157
  query
158
- .joins(:comments => :author)
158
+ .joins(comments: :author)
159
159
  .where(author[:username].in(usernames))
160
160
  )
161
161
  end
@@ -187,6 +187,32 @@ 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
218
  Requires ActiveRecord >= 3.1.0, < 6, tested against Ruby 2.2.4. Depends on SQLite for testing purposes.
@@ -1,24 +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
- s.has_rdoc = true
15
14
 
16
- if ENV["AR"]
17
- s.add_dependency 'activerecord', ENV["AR"]
18
- else
19
- s.add_dependency 'activerecord', '>= 3.1.0', '< 6'
20
- 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'
21
23
 
22
24
  s.require_path = 'lib'
23
- 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']
24
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|
22
+ if (value = super(*args))
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.8.0'
4
+ VERSION = '2.12.0'
5
5
  end
@@ -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
@@ -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