arel-helpers 2.10.0 → 2.13.0
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 +4 -4
- data/Gemfile +3 -2
- data/README.md +29 -3
- data/arel-helpers.gemspec +12 -9
- data/lib/arel-helpers/join_association.rb +61 -34
- data/lib/arel-helpers/query_builder.rb +16 -1
- data/lib/arel-helpers/version.rb +1 -1
- data/spec/aliases_spec.rb +5 -7
- data/spec/arel_table_spec.rb +11 -13
- data/spec/env/models.rb +2 -6
- data/spec/internal/config/database.yml +3 -0
- data/spec/internal/db/combustion_test.sqlite +0 -0
- data/spec/internal/db/schema.rb +34 -0
- data/spec/internal/log/test.log +530 -0
- data/spec/join_association_spec.rb +101 -57
- data/spec/query_builder_spec.rb +39 -18
- data/spec/spec_helper.rb +10 -19
- metadata +57 -13
- data/spec/env/migrations.rb +0 -75
- data/spec/env.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afe6ddeb4e41b56b59f08d6b2ac09bfd832e66b995355169b756ed22d11a2136
|
4
|
+
data.tar.gz: 944d2e1730143abe055f2dad7efe9d01b9ddafabab2080811714d72405177446
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f03ab5b864494bcc5cfb2bc7558c315a18e6460cd815c34855b2b3f0ac9d2696a49b3bd3755543c9124e2ef0667e4d774ae4164cd0317705f74556696c6cfc3
|
7
|
+
data.tar.gz: e3f704bb25be566eb5797f81804d4446b9c3c96b5df84129afb5bdeec16b91fb420d2cc7ff901648e8dab03a818d2036a5288fa6a11c95d14231b6cd9ed74b97
|
data/Gemfile
CHANGED
@@ -1,2 +1,3 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
+

|
1
2
|
|
2
|
-
|
3
|
-
## arel-helpers [](http://travis-ci.org/camertron/arel-helpers)
|
3
|
+
## arel-helpers
|
4
4
|
|
5
5
|
Useful tools to help construct database queries with ActiveRecord and Arel.
|
6
6
|
|
@@ -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, <
|
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
|
-
|
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 =
|
5
|
+
s.name = 'arel-helpers'
|
6
6
|
s.version = ::ArelHelpers::VERSION
|
7
|
-
s.authors = [
|
8
|
-
s.email = [
|
9
|
-
s.homepage =
|
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 =
|
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
15
|
s.add_dependency 'activerecord', '>= 3.1.0', '< 7'
|
16
16
|
|
17
|
+
s.add_development_dependency 'appraisal'
|
18
|
+
s.add_development_dependency 'combustion', '~> 1.3'
|
19
|
+
s.add_development_dependency 'database_cleaner', '~> 1.8'
|
17
20
|
s.add_development_dependency 'rake', '~> 10.0'
|
18
|
-
s.add_development_dependency 'rspec', '~>
|
19
|
-
s.add_development_dependency '
|
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[
|
25
|
+
s.files = Dir['{lib,spec}/**/*', 'Gemfile', 'History.txt', 'README.md', 'Rakefile', 'arel-helpers.gemspec']
|
23
26
|
end
|
@@ -18,7 +18,9 @@ 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 >= '6.
|
21
|
+
if version >= '6.1.0'
|
22
|
+
join_association_6_1_0(table, association, join_type, options, &block)
|
23
|
+
elsif version >= '6.0.0'
|
22
24
|
join_association_6_0_0(table, association, join_type, options, &block)
|
23
25
|
elsif version >= '5.2.1'
|
24
26
|
join_association_5_2_1(table, association, join_type, options, &block)
|
@@ -42,7 +44,7 @@ module ArelHelpers
|
|
42
44
|
end
|
43
45
|
|
44
46
|
def join_association_3_1(table, association, join_type, options = {})
|
45
|
-
aliases = options.fetch(:aliases,
|
47
|
+
aliases = options.fetch(:aliases, []).index_by(&:table_name)
|
46
48
|
associations = association.is_a?(Array) ? association : [association]
|
47
49
|
join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
|
48
50
|
manager = Arel::SelectManager.new(table)
|
@@ -53,12 +55,9 @@ module ArelHelpers
|
|
53
55
|
end
|
54
56
|
|
55
57
|
manager.join_sources.map do |assoc|
|
56
|
-
|
57
|
-
assoc.left.table_alias = found_alias.name
|
58
|
-
end
|
58
|
+
assoc.left.table_alias = aliases[assoc.left.name].name if aliases.key?(assoc.left.name)
|
59
59
|
|
60
60
|
if block_given?
|
61
|
-
# yield |assoc_name, join_conditions|
|
62
61
|
right = yield assoc.left.name.to_sym, assoc.right
|
63
62
|
assoc.class.new(assoc.left, right)
|
64
63
|
else
|
@@ -68,7 +67,7 @@ module ArelHelpers
|
|
68
67
|
end
|
69
68
|
|
70
69
|
def join_association_4_1(table, association, join_type, options = {})
|
71
|
-
aliases = options.fetch(:aliases,
|
70
|
+
aliases = options.fetch(:aliases, []).index_by(&:table_name)
|
72
71
|
associations = association.is_a?(Array) ? association : [association]
|
73
72
|
join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
|
74
73
|
|
@@ -79,9 +78,7 @@ module ArelHelpers
|
|
79
78
|
constraint.right
|
80
79
|
end
|
81
80
|
|
82
|
-
|
83
|
-
constraint.left.table_alias = found_alias.name
|
84
|
-
end
|
81
|
+
constraint.left.table_alias = aliases[constraint.left.name].name if aliases.key?(constraint.left.name)
|
85
82
|
|
86
83
|
join_type.new(constraint.left, right)
|
87
84
|
end
|
@@ -93,7 +90,7 @@ module ArelHelpers
|
|
93
90
|
# dynamically. To get around the problem, this method must return
|
94
91
|
# a string.
|
95
92
|
def join_association_4_2(table, association, join_type, options = {})
|
96
|
-
aliases = options.fetch(:aliases, [])
|
93
|
+
aliases = options.fetch(:aliases, []).index_by(&:table_name)
|
97
94
|
associations = association.is_a?(Array) ? association : [association]
|
98
95
|
join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
|
99
96
|
|
@@ -111,9 +108,7 @@ module ArelHelpers
|
|
111
108
|
join.right
|
112
109
|
end
|
113
110
|
|
114
|
-
|
115
|
-
join.left.table_alias = found_alias.name
|
116
|
-
end
|
111
|
+
join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
|
117
112
|
|
118
113
|
join_type.new(join.left, right)
|
119
114
|
end
|
@@ -127,7 +122,7 @@ module ArelHelpers
|
|
127
122
|
end
|
128
123
|
|
129
124
|
def join_association_5_0(table, association, join_type, options = {})
|
130
|
-
aliases = options.fetch(:aliases, [])
|
125
|
+
aliases = options.fetch(:aliases, []).index_by(&:table_name)
|
131
126
|
associations = association.is_a?(Array) ? association : [association]
|
132
127
|
join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
|
133
128
|
|
@@ -146,9 +141,7 @@ module ArelHelpers
|
|
146
141
|
join.right
|
147
142
|
end
|
148
143
|
|
149
|
-
|
150
|
-
join.left.table_alias = found_alias.name
|
151
|
-
end
|
144
|
+
join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
|
152
145
|
|
153
146
|
join_type.new(join.left, right)
|
154
147
|
end
|
@@ -162,7 +155,7 @@ module ArelHelpers
|
|
162
155
|
end
|
163
156
|
|
164
157
|
def join_association_5_2(table, association, join_type, options = {})
|
165
|
-
aliases = options.fetch(:aliases, [])
|
158
|
+
aliases = options.fetch(:aliases, []).index_by(&:table_name)
|
166
159
|
associations = association.is_a?(Array) ? association : [association]
|
167
160
|
|
168
161
|
alias_tracker = ActiveRecord::Associations::AliasTracker.create(
|
@@ -182,16 +175,14 @@ module ArelHelpers
|
|
182
175
|
join.right
|
183
176
|
end
|
184
177
|
|
185
|
-
|
186
|
-
join.left.table_alias = found_alias.name
|
187
|
-
end
|
178
|
+
join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
|
188
179
|
|
189
180
|
join_type.new(join.left, right)
|
190
181
|
end
|
191
182
|
end
|
192
183
|
|
193
184
|
def join_association_5_2_1(table, association, join_type, options = {})
|
194
|
-
aliases = options.fetch(:aliases, [])
|
185
|
+
aliases = options.fetch(:aliases, []).index_by(&:table_name)
|
195
186
|
associations = association.is_a?(Array) ? association : [association]
|
196
187
|
|
197
188
|
alias_tracker = ActiveRecord::Associations::AliasTracker.create(
|
@@ -211,16 +202,14 @@ module ArelHelpers
|
|
211
202
|
join.right
|
212
203
|
end
|
213
204
|
|
214
|
-
|
215
|
-
join.left.table_alias = found_alias.name
|
216
|
-
end
|
205
|
+
join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
|
217
206
|
|
218
207
|
join_type.new(join.left, right)
|
219
208
|
end
|
220
209
|
end
|
221
210
|
|
222
211
|
def join_association_6_0_0(table, association, join_type, options = {})
|
223
|
-
aliases = options.fetch(:aliases, [])
|
212
|
+
aliases = options.fetch(:aliases, []).index_by(&:table_name)
|
224
213
|
associations = association.is_a?(Array) ? association : [association]
|
225
214
|
|
226
215
|
alias_tracker = ActiveRecord::Associations::AliasTracker.create(
|
@@ -240,14 +229,56 @@ module ArelHelpers
|
|
240
229
|
join.right
|
241
230
|
end
|
242
231
|
|
243
|
-
|
244
|
-
join.left.table_alias = found_alias.name
|
245
|
-
end
|
232
|
+
join.left.table_alias = aliases[join.left.name].name if aliases.key?(join.left.name)
|
246
233
|
|
247
234
|
join_type.new(join.left, right)
|
248
235
|
end
|
249
236
|
end
|
250
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
|
+
|
261
|
+
join_type.new(join.left, right)
|
262
|
+
end
|
263
|
+
end
|
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
|
+
|
251
282
|
private
|
252
283
|
|
253
284
|
def to_sql(node, table, binds)
|
@@ -255,10 +286,6 @@ module ArelHelpers
|
|
255
286
|
collect = visitor.accept(node, Arel::Collectors::Bind.new)
|
256
287
|
collect.substitute_binds(binds).join
|
257
288
|
end
|
258
|
-
|
259
|
-
def find_alias(name, aliases)
|
260
|
-
aliases.find { |a| a.table_name == name }
|
261
|
-
end
|
262
289
|
end
|
263
290
|
end
|
264
291
|
end
|
@@ -9,13 +9,28 @@ module ArelHelpers
|
|
9
9
|
include Enumerable
|
10
10
|
|
11
11
|
attr_reader :query
|
12
|
-
def_delegators :@query, :to_a, :to_sql, :each
|
12
|
+
def_delegators :@query, :to_a, :to_sql, :each, :empty?, :size
|
13
13
|
|
14
14
|
TERMINAL_METHODS = [:count, :first, :last]
|
15
15
|
TERMINAL_METHODS << :pluck if ActiveRecord::VERSION::MAJOR >= 4
|
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
|
data/lib/arel-helpers/version.rb
CHANGED
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
|
7
|
-
it
|
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
|
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
|
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
|
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
|
|
data/spec/arel_table_spec.rb
CHANGED
@@ -1,30 +1,28 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
require 'spec_helper'
|
4
2
|
|
5
3
|
describe ArelHelpers::ArelTable do
|
6
|
-
it
|
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.
|
9
|
-
post_id.name.
|
10
|
-
post_id.relation.name.
|
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
|
12
|
+
it 'should not interfere with associations' do
|
15
13
|
post = Post.create(title: "I'm a little teapot")
|
16
|
-
post.comments[0].
|
14
|
+
expect(post.comments[0]).to be_nil
|
17
15
|
end
|
18
16
|
|
19
|
-
it
|
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.
|
20
|
+
expect(post.reload.comments[0].id).to eq comment.id
|
23
21
|
end
|
24
22
|
|
25
|
-
it
|
26
|
-
Post.all[0].
|
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.
|
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
|
49
|
+
has_many :card_locations, as: :card
|
54
50
|
has_many :locations, through: :card_locations
|
55
51
|
end
|
Binary file
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ActiveRecord::Schema.define do
|
4
|
+
create_table :posts do |t|
|
5
|
+
t.column :title, :string
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :comments do |t|
|
9
|
+
t.references :post
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :authors do |t|
|
13
|
+
t.references :comment
|
14
|
+
t.references :collab_posts
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :favorites do |t|
|
18
|
+
t.references :post
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table :collab_posts do |t|
|
22
|
+
t.references :authors
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table :cards
|
26
|
+
|
27
|
+
create_table :card_locations do |t|
|
28
|
+
t.references :location
|
29
|
+
t.references :card, polymorphic: true
|
30
|
+
end
|
31
|
+
|
32
|
+
create_table :locations
|
33
|
+
create_table :community_tickets
|
34
|
+
end
|