active_record_extended_telescope 2.0.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 +7 -0
- data/README.md +870 -0
- data/lib/active_record_extended.rb +10 -0
- data/lib/active_record_extended/active_record.rb +25 -0
- data/lib/active_record_extended/active_record/relation_patch.rb +50 -0
- data/lib/active_record_extended/arel.rb +7 -0
- data/lib/active_record_extended/arel/aggregate_function_name.rb +40 -0
- data/lib/active_record_extended/arel/nodes.rb +49 -0
- data/lib/active_record_extended/arel/predications.rb +50 -0
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +122 -0
- data/lib/active_record_extended/patch/5_1/where_clause.rb +11 -0
- data/lib/active_record_extended/patch/5_2/where_clause.rb +11 -0
- data/lib/active_record_extended/predicate_builder/array_handler_decorator.rb +20 -0
- data/lib/active_record_extended/query_methods/any_of.rb +93 -0
- data/lib/active_record_extended/query_methods/either.rb +62 -0
- data/lib/active_record_extended/query_methods/inet.rb +88 -0
- data/lib/active_record_extended/query_methods/json.rb +329 -0
- data/lib/active_record_extended/query_methods/select.rb +118 -0
- data/lib/active_record_extended/query_methods/unionize.rb +249 -0
- data/lib/active_record_extended/query_methods/where_chain.rb +132 -0
- data/lib/active_record_extended/query_methods/window.rb +93 -0
- data/lib/active_record_extended/query_methods/with_cte.rb +150 -0
- data/lib/active_record_extended/utilities/order_by.rb +77 -0
- data/lib/active_record_extended/utilities/support.rb +178 -0
- data/lib/active_record_extended/version.rb +5 -0
- data/lib/active_record_extended_telescope.rb +4 -0
- data/spec/active_record_extended_spec.rb +7 -0
- data/spec/query_methods/any_of_spec.rb +131 -0
- data/spec/query_methods/array_query_spec.rb +64 -0
- data/spec/query_methods/either_spec.rb +59 -0
- data/spec/query_methods/hash_query_spec.rb +45 -0
- data/spec/query_methods/inet_query_spec.rb +112 -0
- data/spec/query_methods/json_spec.rb +157 -0
- data/spec/query_methods/select_spec.rb +115 -0
- data/spec/query_methods/unionize_spec.rb +165 -0
- data/spec/query_methods/window_spec.rb +51 -0
- data/spec/query_methods/with_cte_spec.rb +50 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/sql_inspections/any_of_sql_spec.rb +41 -0
- data/spec/sql_inspections/arel/aggregate_function_name_spec.rb +41 -0
- data/spec/sql_inspections/arel/array_spec.rb +63 -0
- data/spec/sql_inspections/arel/inet_spec.rb +66 -0
- data/spec/sql_inspections/contains_sql_queries_spec.rb +47 -0
- data/spec/sql_inspections/either_sql_spec.rb +55 -0
- data/spec/sql_inspections/json_sql_spec.rb +82 -0
- data/spec/sql_inspections/unionize_sql_spec.rb +124 -0
- data/spec/sql_inspections/window_sql_spec.rb +98 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +95 -0
- data/spec/support/database_cleaner.rb +15 -0
- data/spec/support/models.rb +68 -0
- metadata +245 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record Select Methods" do
|
6
|
+
let(:numbers) { (1..10).to_a }
|
7
|
+
|
8
|
+
describe ".foster_select" do
|
9
|
+
context "with an aggregate function" do
|
10
|
+
context "agg_array" do
|
11
|
+
let(:number_set) { numbers.sample(6).to_enum }
|
12
|
+
let!(:users) { Array.new(6) { User.create!(number: number_set.next, ip: "127.0.0.1") } }
|
13
|
+
let!(:tags) { users.flat_map { |u| Array.new(2) { Tag.create!(user: u, tag_number: numbers.sample) } } }
|
14
|
+
|
15
|
+
it "can accept a subquery" do
|
16
|
+
subquery = Tag.select("count(*)").joins("JOIN users u ON tags.user_id = u.id").where("u.ip = users.ip")
|
17
|
+
query =
|
18
|
+
User.foster_select(tag_count: [subquery, { cast_with: :array_agg, distinct: true }])
|
19
|
+
.joins(:hm_tags)
|
20
|
+
.group(:ip)
|
21
|
+
.take
|
22
|
+
|
23
|
+
expect(query.tag_count).to eq([tags.size])
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can be ordered" do
|
27
|
+
query = User.foster_select(
|
28
|
+
asc_ordered_numbers: [:number, { cast_with: :array_agg, order_by: { number: :asc } }],
|
29
|
+
desc_ordered_numbers: [:number, { cast_with: :array_agg, order_by: { number: :desc } }]
|
30
|
+
).take
|
31
|
+
|
32
|
+
expect(query.asc_ordered_numbers).to eq(number_set.to_a.sort)
|
33
|
+
expect(query.desc_ordered_numbers).to eq(number_set.to_a.sort.reverse)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "works with joined relations" do
|
37
|
+
query =
|
38
|
+
User.foster_select(tag_numbers: { tags: :tag_number, cast_with: :array_agg })
|
39
|
+
.joins(:hm_tags)
|
40
|
+
.take
|
41
|
+
expect(query.tag_numbers).to match_array(Tag.pluck(:tag_number))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "bool_[and|or]" do
|
46
|
+
let!(:users) do
|
47
|
+
enum_numbers = numbers.to_enum
|
48
|
+
Array.new(6) { User.create!(number: enum_numbers.next, ip: "127.0.0.1") }
|
49
|
+
end
|
50
|
+
|
51
|
+
it "will return a boolean expression" do
|
52
|
+
query = User.foster_select(
|
53
|
+
truthly_expr: ["users.number > 0", { cast_with: :bool_and }],
|
54
|
+
falsey_expr: ["users.number > 200", { cast_with: :bool_and }],
|
55
|
+
other_true_expr: ["users.number > 4", { cast_with: :bool_or }],
|
56
|
+
other_false_expr: ["users.number > 6", { cast_with: :bool_or }]
|
57
|
+
).take
|
58
|
+
|
59
|
+
expect(query.truthly_expr).to be_truthy
|
60
|
+
expect(query.falsey_expr).to be_falsey
|
61
|
+
expect(query.other_true_expr).to be_truthy
|
62
|
+
expect(query.other_false_expr).to be_falsey
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "with math functions: sum|max|min|avg" do
|
67
|
+
before { 2.times.flat_map { |i| Array.new(2) { |j| User.create!(number: (i + 1) * j + 3) } } }
|
68
|
+
|
69
|
+
it "max" do
|
70
|
+
query = User.foster_select(max_num: [:number, { cast_with: :max }]).take
|
71
|
+
expect(query.max_num).to eq(5)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "min" do
|
75
|
+
query = User.foster_select(max_num: [:number, { cast_with: :min }]).take
|
76
|
+
expect(query.max_num).to eq(3)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "sum" do
|
80
|
+
query = User.foster_select(
|
81
|
+
num_sum: [:number, { cast_with: :sum }],
|
82
|
+
distinct_sum: [:number, { cast_with: :sum, distinct: true }]
|
83
|
+
).take
|
84
|
+
|
85
|
+
expect(query.num_sum).to eq(15)
|
86
|
+
expect(query.distinct_sum).to eq(12)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "avg" do
|
90
|
+
query = User.foster_select(
|
91
|
+
num_avg: [:number, { cast_with: :avg }],
|
92
|
+
distinct_avg: [:number, { cast_with: :avg, distinct: true }]
|
93
|
+
).take
|
94
|
+
|
95
|
+
expect(query.num_avg).to eq(3.75)
|
96
|
+
expect(query.distinct_avg).to eq(4.0)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "with standard select items" do
|
102
|
+
let!(:user) { User.create!(name: "Test") }
|
103
|
+
|
104
|
+
it "works with no alias" do
|
105
|
+
query = User.foster_select(:name).take
|
106
|
+
expect(query.name).to eq(user.name)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "works with alias" do
|
110
|
+
query = User.foster_select(my_name: :name).take
|
111
|
+
expect(query.my_name).to eq(user.name)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record Union Methods" do
|
6
|
+
let!(:user_one) { User.create!(number: 8) }
|
7
|
+
let!(:user_two) { User.create!(number: 10) }
|
8
|
+
let!(:user_three) { User.create!(number: 1) }
|
9
|
+
let!(:user_one_pl) { ProfileL.create!(user: user_one, likes: 100) }
|
10
|
+
let!(:user_two_pl) { ProfileL.create!(user: user_two, likes: 200) }
|
11
|
+
|
12
|
+
shared_examples_for "standard set of errors" do
|
13
|
+
let(:user_one_query) { User.select(:id).where(id: user_one.id) }
|
14
|
+
let(:user_two_query) { User.select(:id, :tags).where(id: user_two.id) }
|
15
|
+
let(:misaligned_cmd) { raise("required to override this 'let' statement") }
|
16
|
+
let(:lacking_union_cmd) { raise("required to override this 'let' statement") }
|
17
|
+
|
18
|
+
it "should raise an error if the select statements do not align" do
|
19
|
+
expect { misaligned_cmd.to_a }.to(
|
20
|
+
raise_error(ActiveRecord::StatementInvalid, /each [[:alpha:]]+ query must have the same number of columns/)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise an argument error if there are less then two union statements" do
|
25
|
+
expect { lacking_union_cmd.to_a }.to(
|
26
|
+
raise_error(ArgumentError, "You are required to provide 2 or more unions to join!")
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe ".union" do
|
32
|
+
it_behaves_like "standard set of errors" do
|
33
|
+
let!(:misaligned_cmd) { User.union(user_one_query, user_two_query) }
|
34
|
+
let!(:lacking_union_cmd) { User.union(user_one_query) }
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return two users that match the where conditions" do
|
38
|
+
query = User.union(User.where(id: user_one.id), User.where(id: user_three.id))
|
39
|
+
expect(query).to match_array([user_one, user_three])
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should allow joins on union statements" do
|
43
|
+
query = User.union(User.where(id: user_one.id), User.joins(:profile_l).where.not(id: user_one.id))
|
44
|
+
expect(query).to match_array([user_one, user_two])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should eliminate duplicate results" do
|
48
|
+
expected_ids = User.pluck(:id)
|
49
|
+
query = User.union(User.select(:id), User.select(:id))
|
50
|
+
expect(query.pluck(:id)).to have_attributes(size: expected_ids.size).and(match_array(expected_ids))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe ".union.all" do
|
55
|
+
it_behaves_like "standard set of errors" do
|
56
|
+
let!(:misaligned_cmd) { User.union.all(user_one_query, user_two_query) }
|
57
|
+
let!(:lacking_union_cmd) { User.union.all(user_one_query) }
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should keep duplicate results from each union statement" do
|
61
|
+
expected_ids = User.pluck(:id) * 2
|
62
|
+
query = User.union.all(User.select(:id), User.select(:id))
|
63
|
+
expect(query.pluck(:id)).to have_attributes(size: expected_ids.size).and(match_array(expected_ids))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".union.except" do
|
68
|
+
it_behaves_like "standard set of errors" do
|
69
|
+
let!(:misaligned_cmd) { User.union.except(user_one_query, user_two_query) }
|
70
|
+
let!(:lacking_union_cmd) { User.union.except(user_one_query) }
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should eliminate records that match a given except statement" do
|
74
|
+
query = User.union.except(User.select(:id), User.select(:id).where(id: user_one.id))
|
75
|
+
expect(query).to match_array([user_two, user_three])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "union.intersect" do
|
80
|
+
it_behaves_like "standard set of errors" do
|
81
|
+
let!(:misaligned_cmd) { User.union.intersect(user_one_query, user_two_query) }
|
82
|
+
let!(:lacking_union_cmd) { User.union.intersect(user_one_query) }
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should find records with similar attributes" do
|
86
|
+
ProfileL.create!(user: user_three, likes: 120)
|
87
|
+
|
88
|
+
query =
|
89
|
+
User.union.intersect(
|
90
|
+
User.select(:id, "profile_ls.likes").joins(:profile_l).where(profile_ls: { likes: 100 }),
|
91
|
+
User.select(:id, "profile_ls.likes").joins(:profile_l).where("profile_ls.likes < 150")
|
92
|
+
)
|
93
|
+
|
94
|
+
expect(query.pluck(:id)).to have_attributes(size: 1).and(eq([user_one_pl.id]))
|
95
|
+
expect(query.first.likes).to eq(user_one_pl.likes)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "union.as" do
|
100
|
+
let(:query) do
|
101
|
+
User
|
102
|
+
.select("happy_users.id")
|
103
|
+
.union(User.where(id: user_one.id), User.where(id: user_three.id))
|
104
|
+
.union_as(:happy_users)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should return two users" do
|
108
|
+
expect(query.size).to eq(2)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should return two userss id's" do
|
112
|
+
expect(query.map(&:id)).to match_array([user_one.id, user_three.id])
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should alias the tables being union'd but still allow for accessing table methods" do
|
116
|
+
query.each do |happy_person|
|
117
|
+
expect(happy_person).to respond_to(:profile_l)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "union.order_union" do
|
123
|
+
it "should order the .union commands" do
|
124
|
+
query = User.union(User.where(id: user_one.id), User.where(id: user_three.id)).order_union(id: :desc)
|
125
|
+
expect(query).to eq([user_three, user_one])
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should order the .union.all commands" do
|
129
|
+
query =
|
130
|
+
User.union.all(
|
131
|
+
User.where(id: user_one.id),
|
132
|
+
User.where(id: user_three.id)
|
133
|
+
).order_union(id: :desc)
|
134
|
+
|
135
|
+
expect(query).to eq([user_three, user_one])
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should order the union.except commands" do
|
139
|
+
query = User.union.except(User.order(id: :asc), User.where(id: user_one.id)).order_union(id: :desc)
|
140
|
+
expect(query).to eq([user_three, user_two])
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should order the .union.intersect commands" do
|
144
|
+
query =
|
145
|
+
User.union.intersect(
|
146
|
+
User.where("id < ?", user_three.id),
|
147
|
+
User.where("id >= ?", user_one.id)
|
148
|
+
).order_union(id: :desc)
|
149
|
+
|
150
|
+
expect(query).to eq([user_two, user_one])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "union.reorder_union" do
|
155
|
+
it "should replace the ordering with the new parameters" do
|
156
|
+
user_a = User.create!(number: 1)
|
157
|
+
user_b = User.create!(number: 10)
|
158
|
+
initial_ordering = [user_b, user_a]
|
159
|
+
query = User.union(User.where(id: user_a.id), User.where(id: user_b.id)).order_union(id: :desc)
|
160
|
+
|
161
|
+
expect(query).to eq(initial_ordering)
|
162
|
+
expect(query.reorder_union(number: :asc)).to eq(initial_ordering.reverse)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record Window Function Query Methods" do
|
6
|
+
let!(:user_one) { User.create! }
|
7
|
+
let!(:user_two) { User.create! }
|
8
|
+
|
9
|
+
let!(:tag_one) { Tag.create!(user: user_one, tag_number: 1) }
|
10
|
+
let!(:tag_two) { Tag.create!(user: user_two, tag_number: 2) }
|
11
|
+
|
12
|
+
let!(:tag_three) { Tag.create!(user: user_one, tag_number: 3) }
|
13
|
+
let!(:tag_four) { Tag.create!(user: user_two, tag_number: 4) }
|
14
|
+
|
15
|
+
let(:tag_group1) { [tag_one, tag_three] }
|
16
|
+
let(:tag_group2) { [tag_two, tag_four] }
|
17
|
+
|
18
|
+
describe ".window_select" do
|
19
|
+
context "when using ROW_NUMBER() ordered in asc" do
|
20
|
+
let(:base_query) do
|
21
|
+
Tag.define_window(:w).partition_by(:user_id, order_by: :tag_number).select(:id)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return tag_one with r_id 1 and tag_three with r_id 2" do
|
25
|
+
results = base_query.select_window(:row_number, over: :w, as: :r_id).group_by(&:id)
|
26
|
+
tag_group1.each.with_index { |tag, idx| expect(results[tag.id].first.r_id).to eq(idx + 1) }
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return tag_two with r_id 1 and tag_four with r_id 2" do
|
30
|
+
results = base_query.select_window(:row_number, over: :w, as: :r_id).group_by(&:id)
|
31
|
+
tag_group2.each.with_index { |tag, idx| expect(results[tag.id].first.r_id).to eq(idx + 1) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when using ROW_NUMBER() ordered in desc" do
|
36
|
+
let(:base_query) do
|
37
|
+
Tag.define_window(:w).partition_by(:user_id, order_by: { tag_number: :desc }).select(:id)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should return tag_one with r_id 2 and tag_three with r_id 1" do
|
41
|
+
results = base_query.select_window(:row_number, over: :w, as: :r_id).group_by(&:id)
|
42
|
+
tag_group1.reverse_each.with_index { |tag, idx| expect(results[tag.id].first.r_id).to eq(idx + 1) }
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should return tag_two with r_id 2 and tag_four with r_id 1" do
|
46
|
+
results = base_query.select_window(:row_number, over: :w, as: :r_id).group_by(&:id)
|
47
|
+
tag_group2.reverse_each.with_index { |tag, idx| expect(results[tag.id].first.r_id).to eq(idx + 1) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record With CTE Query Methods" do
|
6
|
+
let!(:user_one) { User.create! }
|
7
|
+
let!(:user_two) { User.create! }
|
8
|
+
let!(:profile_one) { ProfileL.create!(user_id: user_one.id, likes: 200) }
|
9
|
+
let!(:profile_two) { ProfileL.create!(user_id: user_two.id, likes: 500) }
|
10
|
+
|
11
|
+
describe ".with/1" do
|
12
|
+
context "when using as a standalone query" do
|
13
|
+
it "should only return a person with less than 300 likes" do
|
14
|
+
query = User.with(profile: ProfileL.where("likes < 300"))
|
15
|
+
.joins("JOIN profile ON profile.id = users.id")
|
16
|
+
|
17
|
+
expect(query).to match_array([user_one])
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return anyone with likes greater than or equal to 200" do
|
21
|
+
query = User.with(profile: ProfileL.where("likes >= 200"))
|
22
|
+
.joins("JOIN profile ON profile.id = users.id")
|
23
|
+
|
24
|
+
expect(query).to match_array([user_one, user_two])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when merging in query" do
|
29
|
+
let!(:version_one) { VersionControl.create!(versionable: profile_one, source: { help: "me" }) }
|
30
|
+
let!(:version_two) { VersionControl.create!(versionable: profile_two, source: { help: "no one" }) }
|
31
|
+
|
32
|
+
it "will maintain the CTE table when merging into existing AR queries" do
|
33
|
+
sub_query = ProfileL.with(version_controls: VersionControl.where.contains(source: { help: "me" }))
|
34
|
+
query = User.joins(profile_l: :version).merge(sub_query)
|
35
|
+
|
36
|
+
expect(query).to match_array([user_one])
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should contain a unique list of ordered CTE keys when merging in multiple children" do
|
40
|
+
x = User.with(profile: ProfileL.where("likes < 300"))
|
41
|
+
y = User.with(profile: ProfileL.where("likes > 400"))
|
42
|
+
z = y.merge(x).joins("JOIN profile ON profile.id = users.id") # Y should reject X's CTE (FIFO)
|
43
|
+
query = User.with(my_profile: z).joins("JOIN my_profile ON my_profile.id = users.id")
|
44
|
+
|
45
|
+
expect(query.cte.with_keys).to eq([:profile, :my_profile])
|
46
|
+
expect(query).to match_array([user_two])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "simplecov"
|
4
|
+
SimpleCov.start
|
5
|
+
|
6
|
+
require "active_record_extended"
|
7
|
+
|
8
|
+
unless ENV["DATABASE_URL"]
|
9
|
+
require "dotenv"
|
10
|
+
Dotenv.load
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"])
|
14
|
+
|
15
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require File.expand_path(f) }
|
16
|
+
Dir["#{File.dirname(__FILE__)}/**/*examples.rb"].sort.each { |f| require f }
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
# Enable flags like --only-failures and --next-failure
|
20
|
+
config.example_status_persistence_file_path = ".rspec_status"
|
21
|
+
|
22
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
23
|
+
config.disable_monkey_patching!
|
24
|
+
|
25
|
+
config.expect_with :rspec do |c|
|
26
|
+
c.syntax = :expect
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Any / None of SQL Queries" do
|
6
|
+
let(:equal_query) { '"users"."personal_id" = 1' }
|
7
|
+
let(:or_query) { 'OR "users"."personal_id" = 2' }
|
8
|
+
let(:equal_or) { "#{equal_query} #{or_query}" }
|
9
|
+
let(:join_query) { /INNER JOIN "tags" ON "tags"."user_id" = "users"."id/ }
|
10
|
+
|
11
|
+
describe "where.any_of/1" do
|
12
|
+
it "should group different column arguments into nested or conditions" do
|
13
|
+
query = User.where.any_of({ personal_id: 1 }, { id: 2 }, { personal_id: 2 }).to_sql
|
14
|
+
expect(query).to match_regex(/WHERE \(\(.+ = 1 OR .+ = 2\) OR .+ = 2\)/)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "Should assign where clause predicates for standard queries" do
|
18
|
+
query = User.where.any_of({ personal_id: 1 }, { personal_id: 2 }).to_sql
|
19
|
+
expect(query).to include(equal_or)
|
20
|
+
|
21
|
+
personal_one = User.where(personal_id: 1)
|
22
|
+
personal_two = User.where(personal_id: 2)
|
23
|
+
query = User.where.any_of(personal_one, personal_two).to_sql
|
24
|
+
expect(query).to include(equal_or)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "Joining queries should be added to the select statement" do
|
28
|
+
user_two_tag = User.where(personal_id: 1).joins(:hm_tags)
|
29
|
+
query = User.where.any_of(user_two_tag).to_sql
|
30
|
+
expect(query).to match_regex(join_query)
|
31
|
+
expect(query).to include(equal_query)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "where.none_of/1" do
|
36
|
+
it "Should surround the query in a WHERE NOT clause" do
|
37
|
+
query = User.where.none_of({ personal_id: 1 }, { id: 2 }, { personal_id: 2 }).to_sql
|
38
|
+
expect(query).to match_regex(/WHERE.+NOT \(\(.+ = 1 OR .+ = 2\) OR .+ = 2\)/)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|