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,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record Any / None of Methods" do
|
6
|
+
let!(:one) { User.create!(personal_id: 1) }
|
7
|
+
let!(:two) { User.create!(personal_id: 2) }
|
8
|
+
let!(:three) { User.create!(personal_id: 3) }
|
9
|
+
|
10
|
+
let!(:tag_one) { Tag.create!(user_id: one.id) }
|
11
|
+
let!(:tag_two) { Tag.create!(user_id: two.id) }
|
12
|
+
let!(:tag_three) { Tag.create!(user_id: three.id) }
|
13
|
+
|
14
|
+
describe "where.any_of/1" do
|
15
|
+
it "Should return queries that match any of the outlined queries" do
|
16
|
+
query = User.where.any_of({ personal_id: 1 }, { personal_id: 2 })
|
17
|
+
expect(query).to include(one, two)
|
18
|
+
expect(query).to_not include(three)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "Should accept where query predicates" do
|
22
|
+
personal_one = User.where(personal_id: 1)
|
23
|
+
personal_two = User.where(personal_id: 2)
|
24
|
+
query = User.where.any_of(personal_one, personal_two)
|
25
|
+
|
26
|
+
expect(query).to include(one, two)
|
27
|
+
expect(query).to_not include(three)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "Should accept query strings" do
|
31
|
+
personal_one = User.where(personal_id: 1)
|
32
|
+
|
33
|
+
query = User.where.any_of(personal_one, "personal_id > 2")
|
34
|
+
expect(query).to include(one, three)
|
35
|
+
expect(query).to_not include(two)
|
36
|
+
|
37
|
+
query = User.where.any_of(["personal_id >= ?", 2])
|
38
|
+
expect(query).to include(two, three)
|
39
|
+
expect(query).to_not include(one)
|
40
|
+
end
|
41
|
+
|
42
|
+
context "Relationship queries" do
|
43
|
+
it "Finds records that are queried from two or more has_many associations" do
|
44
|
+
user_one_tag = Tag.create!(user_id: one.id)
|
45
|
+
user_two_tag = Tag.create!(user_id: two.id)
|
46
|
+
query = Tag.where.any_of(one.hm_tags, two.hm_tags)
|
47
|
+
|
48
|
+
expect(query).to include(tag_one, tag_two, user_one_tag, user_two_tag)
|
49
|
+
expect(query).to_not include(tag_three)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "Finds records that are dynamically joined" do
|
53
|
+
user_one_tag = Tag.where(users: { id: one.id }).includes(:user).references(:user)
|
54
|
+
user_two_tag = Tag.where(users: { id: two.id }).joins(:user)
|
55
|
+
query = Tag.where.any_of(user_one_tag, user_two_tag)
|
56
|
+
|
57
|
+
expect(query).to include(tag_one, tag_two)
|
58
|
+
expect(query).to_not include(tag_three)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "Return matched records of a joined table on the parent level" do
|
62
|
+
query = Tag.joins(:user).where.any_of(
|
63
|
+
{ users: { personal_id: 1 } },
|
64
|
+
{ users: { personal_id: 3 } }
|
65
|
+
)
|
66
|
+
|
67
|
+
expect(query).to include(tag_one, tag_three)
|
68
|
+
expect(query).to_not include(tag_two)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "where.none_of/1" do
|
74
|
+
it "Should return queries that match none of the outlined queries" do
|
75
|
+
query = User.where.none_of({ personal_id: 1 }, { personal_id: 2 })
|
76
|
+
expect(query).to include(three)
|
77
|
+
expect(query).to_not include(one, two)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "Should accept where query predicates" do
|
81
|
+
personal_one = User.where(personal_id: 1)
|
82
|
+
personal_two = User.where(personal_id: 2)
|
83
|
+
query = User.where.none_of(personal_one, personal_two)
|
84
|
+
|
85
|
+
expect(query).to include(three)
|
86
|
+
expect(query).to_not include(one, two)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "Should accept query strings" do
|
90
|
+
personal_one = User.where(personal_id: 1)
|
91
|
+
|
92
|
+
query = User.where.none_of(personal_one, "personal_id > 2")
|
93
|
+
expect(query).to include(two)
|
94
|
+
expect(query).to_not include(one, three)
|
95
|
+
|
96
|
+
query = User.where.none_of(["personal_id >= ?", 2])
|
97
|
+
expect(query).to include(one)
|
98
|
+
expect(query).to_not include(two, three)
|
99
|
+
end
|
100
|
+
|
101
|
+
context "Relationship queries" do
|
102
|
+
it "Finds records that are queried from two or more has_many associations" do
|
103
|
+
user_one_tag = Tag.create!(user_id: one.id)
|
104
|
+
user_two_tag = Tag.create!(user_id: two.id)
|
105
|
+
query = Tag.where.none_of(one.hm_tags, two.hm_tags)
|
106
|
+
|
107
|
+
expect(query).to include(tag_three)
|
108
|
+
expect(query).to_not include(tag_one, tag_two, user_one_tag, user_two_tag)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "Finds records that are dynamically joined" do
|
112
|
+
user_one_tag = Tag.where(users: { id: one.id }).includes(:user).references(:user)
|
113
|
+
user_two_tag = Tag.where(users: { id: two.id }).joins(:user)
|
114
|
+
query = Tag.where.none_of(user_one_tag, user_two_tag)
|
115
|
+
|
116
|
+
expect(query).to include(tag_three)
|
117
|
+
expect(query).to_not include(tag_one, tag_two)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "Return matched records of a joined table on the parent level" do
|
121
|
+
query = Tag.joins(:user).where.none_of(
|
122
|
+
{ users: { personal_id: 1 } },
|
123
|
+
{ users: { personal_id: 3 } }
|
124
|
+
)
|
125
|
+
|
126
|
+
expect(query).to include(tag_two)
|
127
|
+
expect(query).to_not include(tag_one, tag_three)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record Array Query Methods" do
|
6
|
+
let!(:one) { User.create!(tags: [1, 2, 3], personal_id: 33) }
|
7
|
+
let!(:two) { User.create!(tags: [3, 1, 5], personal_id: 88) }
|
8
|
+
let!(:three) { User.create!(tags: [2, 8, 20], personal_id: 33) }
|
9
|
+
|
10
|
+
describe "#overlap" do
|
11
|
+
it "Should return matched records" do
|
12
|
+
query = User.where.overlap(tags: [1])
|
13
|
+
expect(query).to include(one, two)
|
14
|
+
expect(query).to_not include(three)
|
15
|
+
|
16
|
+
query = User.where.overlap(tags: [2, 3])
|
17
|
+
expect(query).to include(one, two, three)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#contains" do
|
22
|
+
it "returns records that contain elements in an array" do
|
23
|
+
query = User.where.contains(tags: [1, 3])
|
24
|
+
expect(query).to include(one, two)
|
25
|
+
expect(query).to_not include(three)
|
26
|
+
|
27
|
+
query = User.where.contains(tags: [8, 2])
|
28
|
+
expect(query).to include(three)
|
29
|
+
expect(query).to_not include(one, two)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#any" do
|
34
|
+
it "should return any records that match" do
|
35
|
+
query = User.where.any(tags: 3)
|
36
|
+
expect(query).to include(one, two)
|
37
|
+
expect(query).to_not include(three)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "allows chaining" do
|
41
|
+
query = User.where.any(tags: 3).where(personal_id: 33)
|
42
|
+
expect(query).to include(one)
|
43
|
+
expect(query).to_not include(two, three)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#all" do
|
48
|
+
let!(:contains_all) { User.create!(tags: [1], personal_id: 1) }
|
49
|
+
let!(:contains_all_two) { User.create!(tags: [1], personal_id: 2) }
|
50
|
+
let!(:contains_some) { User.create!(tags: [1, 2], personal_id: 2) }
|
51
|
+
|
52
|
+
it "should return any records that match" do
|
53
|
+
query = User.where.all(tags: 1)
|
54
|
+
expect(query).to include(contains_all, contains_all_two)
|
55
|
+
expect(query).to_not include(contains_some)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "allows chaining" do
|
59
|
+
query = User.where.all(tags: 1).where(personal_id: 1)
|
60
|
+
expect(query).to include(contains_all)
|
61
|
+
expect(query).to_not include(contains_all_two, contains_some)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record Either Methods" do
|
6
|
+
let!(:one) { User.create! }
|
7
|
+
let!(:two) { User.create! }
|
8
|
+
let!(:three) { User.create! }
|
9
|
+
let!(:profile_l) { ProfileL.create!(user_id: one.id, likes: 100) }
|
10
|
+
let!(:profile_r) { ProfileR.create!(user_id: two.id, dislikes: 50) }
|
11
|
+
|
12
|
+
describe ".either_join/2" do
|
13
|
+
it "Should only only return records that belong to profile L or profile R" do
|
14
|
+
query = User.either_join(:profile_l, :profile_r)
|
15
|
+
expect(query).to include(one, two)
|
16
|
+
expect(query).to_not include(three)
|
17
|
+
end
|
18
|
+
|
19
|
+
context "Alias .either_joins/2" do
|
20
|
+
it "Should only only return records that belong to profile L or profile R" do
|
21
|
+
query = User.either_joins(:profile_l, :profile_r)
|
22
|
+
expect(query).to include(one, two)
|
23
|
+
expect(query).to_not include(three)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ".either_order/2" do
|
29
|
+
it "Should not exclude anyone who does not have a relationship" do
|
30
|
+
query = User.either_order(:asc, profile_l: :likes, profile_r: :dislikes)
|
31
|
+
expect(query.count).to eq(3)
|
32
|
+
expect(query[0]).to eq(two)
|
33
|
+
expect(query[1]).to eq(one)
|
34
|
+
expect(query[2]).to eq(three)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "Should order users based on their likes and dislikes in ascended order" do
|
38
|
+
query = User.either_order(:asc, profile_l: :likes, profile_r: :dislikes).where(id: [one.id, two.id])
|
39
|
+
expect(query.count).to eq(2)
|
40
|
+
expect(query.first).to eq(two)
|
41
|
+
expect(query.last).to eq(one)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "Should order users based on their likes and dislikes in descending order" do
|
45
|
+
query = User.either_order(:desc, profile_l: :likes, profile_r: :dislikes).where(id: [one.id, two.id])
|
46
|
+
expect(query.first).to eq(one)
|
47
|
+
expect(query.last).to eq(two)
|
48
|
+
end
|
49
|
+
|
50
|
+
context "Alias .either_order/2" do
|
51
|
+
it "Should order users based on their likes and dislikes in ascended order" do
|
52
|
+
query = User.either_orders(:asc, profile_l: :likes, profile_r: :dislikes).where(id: [one.id, two.id])
|
53
|
+
expect(query.count).to eq(2)
|
54
|
+
expect(query.first).to eq(two)
|
55
|
+
expect(query.last).to eq(one)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record Hash Related Query Methods" do
|
6
|
+
let!(:one) { User.create!(data: { nickname: "george" }, jsonb_data: { payment: "zip" }) }
|
7
|
+
let!(:two) { User.create!(data: { nickname: "dan" }, jsonb_data: { payment: "zipper" }) }
|
8
|
+
let!(:three) { User.create!(data: { nickname: "georgey" }) }
|
9
|
+
|
10
|
+
describe "#contains" do
|
11
|
+
context "HStore Column Type" do
|
12
|
+
it "returns records that contain hash elements in joined tables" do
|
13
|
+
tag_one = Tag.create!(user_id: one.id)
|
14
|
+
tag_two = Tag.create!(user_id: two.id)
|
15
|
+
|
16
|
+
query = Tag.joins(:user).where.contains(users: { data: { nickname: "george" } })
|
17
|
+
expect(query).to include(tag_one)
|
18
|
+
expect(query).to_not include(tag_two)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns records that contain hash value" do
|
22
|
+
query = User.where.contains(data: { nickname: "george" })
|
23
|
+
expect(query).to include(one)
|
24
|
+
expect(query).to_not include(two, three)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "JSONB Column Type" do
|
29
|
+
it "returns records that contains a json hashed value" do
|
30
|
+
query = User.where.contains(jsonb_data: { payment: "zip" })
|
31
|
+
expect(query).to include(one)
|
32
|
+
expect(query).to_not include(two, three)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns records that contain jsonb elements in joined tables" do
|
36
|
+
tag_one = Tag.create!(user_id: one.id)
|
37
|
+
tag_two = Tag.create!(user_id: two.id)
|
38
|
+
|
39
|
+
query = Tag.joins(:user).where.contains(users: { jsonb_data: { payment: "zip" } })
|
40
|
+
expect(query).to include(tag_one)
|
41
|
+
expect(query).to_not include(tag_two)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record Inet Query Methods" do
|
6
|
+
before { stub_const("User", Namespaced::Record) }
|
7
|
+
|
8
|
+
describe "#inet_contained_within" do
|
9
|
+
let!(:local_1) { User.create!(ip: "127.0.0.1") }
|
10
|
+
let!(:local_44) { User.create!(ip: "127.0.0.44") }
|
11
|
+
let!(:local_99_1) { User.create!(ip: "127.0.99.1") }
|
12
|
+
let!(:local_range) { User.create!(ip: "127.0.0.1/10") }
|
13
|
+
|
14
|
+
it "Should return users who have an IP within the 127.0.0.1/24 range" do
|
15
|
+
query = User.where.inet_contained_within(ip: "127.0.0.1/24")
|
16
|
+
expect(query).to include(local_1, local_44)
|
17
|
+
expect(query).to_not include(local_99_1, local_range)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "Should return all users who have an IP within the 127.0.0.1/16 range" do
|
21
|
+
query = User.where.inet_contained_within(ip: "127.0.0.1/16")
|
22
|
+
expect(query).to include(local_1, local_44, local_99_1)
|
23
|
+
expect(query).to_not include(local_range)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "inet_contained_within_or_equals" do
|
28
|
+
let!(:local_1) { User.create!(ip: "127.0.0.1/10") }
|
29
|
+
let!(:local_44) { User.create!(ip: "127.0.0.44/32") }
|
30
|
+
let!(:local_99_1) { User.create!(ip: "127.0.99.1") }
|
31
|
+
|
32
|
+
it "Should find records that contain a matching submask" do
|
33
|
+
query = User.where.inet_contained_within_or_equals(ip: "127.0.0.44/32")
|
34
|
+
expect(query).to include(local_44)
|
35
|
+
expect(query).to_not include(local_1, local_99_1)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "Should find records that are within range of a given submask" do
|
39
|
+
query = User.where.inet_contained_within_or_equals(ip: "127.0.0.1/16")
|
40
|
+
expect(query).to include(local_44, local_99_1)
|
41
|
+
expect(query).to_not include(local_1)
|
42
|
+
|
43
|
+
query = User.where.inet_contained_within_or_equals(ip: "127.0.0.1/8")
|
44
|
+
expect(query).to include(local_1, local_44, local_99_1)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#inet_contains_or_equals" do
|
49
|
+
let!(:local_1) { User.create!(ip: "127.0.0.1/10") }
|
50
|
+
let!(:local_44) { User.create!(ip: "127.0.0.44/24") }
|
51
|
+
let!(:local_99_1) { User.create!(ip: "127.0.99.1") }
|
52
|
+
|
53
|
+
it "Should find records with submask ranges that contain a given IP" do
|
54
|
+
query = User.where.inet_contains_or_equals(ip: "127.0.255.255")
|
55
|
+
expect(query).to include(local_1)
|
56
|
+
expect(query).to_not include(local_44, local_99_1)
|
57
|
+
|
58
|
+
query = User.where.inet_contains_or_equals(ip: "127.0.0.255")
|
59
|
+
expect(query).to include(local_1, local_44)
|
60
|
+
expect(query).to_not include(local_99_1)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "Finds records when querying with a submasked value" do
|
64
|
+
query = User.where.inet_contains_or_equals(ip: "127.0.0.1/10")
|
65
|
+
expect(query).to include(local_1)
|
66
|
+
expect(query).to_not include(local_44, local_99_1)
|
67
|
+
|
68
|
+
query = User.where.inet_contains_or_equals(ip: "127.0.0.1/32")
|
69
|
+
expect(query).to include(local_1, local_44)
|
70
|
+
expect(query).to_not include(local_99_1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#inet_contains" do
|
75
|
+
let!(:local_1) { User.create!(ip: "127.0.0.1/10") }
|
76
|
+
let!(:local_44) { User.create!(ip: "127.0.0.44/24") }
|
77
|
+
|
78
|
+
it "Should find records that the given IP falls within" do
|
79
|
+
query = User.where.inet_contains(ip: "127.0.0.1")
|
80
|
+
expect(query).to include(local_1, local_44)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "Should not find records when querying with a submasked value" do
|
84
|
+
query = User.where.inet_contains(ip: "127.0.0.0/8")
|
85
|
+
expect(query).to be_empty
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#inet_contains_or_is_contained_within" do
|
90
|
+
let!(:local_1) { User.create!(ip: "127.0.0.1/24") }
|
91
|
+
let!(:local_44) { User.create!(ip: "127.0.22.44/8") }
|
92
|
+
let!(:local_99_1) { User.create!(ip: "127.0.99.1") }
|
93
|
+
|
94
|
+
it "should find records where the records contain the given IP" do
|
95
|
+
query = User.where.inet_contains_or_is_contained_within(ip: "127.0.255.80")
|
96
|
+
expect(query).to include(local_44)
|
97
|
+
expect(query).to_not include(local_1, local_99_1)
|
98
|
+
|
99
|
+
query = User.where.inet_contains_or_is_contained_within(ip: "127.0.0.80")
|
100
|
+
expect(query).to include(local_1, local_44)
|
101
|
+
expect(query).to_not include(local_99_1)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "Should find records that the where query contains a valid range" do
|
105
|
+
query = User.where.inet_contains_or_is_contained_within(ip: "127.0.0.80/8")
|
106
|
+
expect(query).to include(local_1, local_44, local_99_1)
|
107
|
+
|
108
|
+
query = User.where.inet_contains_or_is_contained_within(ip: "127.0.0.80/16")
|
109
|
+
expect(query).to include(local_1, local_44, local_99_1)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe "Active Record JSON methods" do
|
4
|
+
let!(:user_one) { User.create! }
|
5
|
+
let!(:user_two) { User.create! }
|
6
|
+
|
7
|
+
describe ".select_row_to_json" do
|
8
|
+
let!(:tag_one) { Tag.create!(user: user_one, tag_number: 2) }
|
9
|
+
let!(:tag_two) { Tag.create!(user: user_two, tag_number: 5) }
|
10
|
+
let(:sub_query) { Tag.select(:tag_number).where("tags.user_id = users.id") }
|
11
|
+
|
12
|
+
it "should nest a json object in the query results" do
|
13
|
+
query = User.select(:id).select_row_to_json(sub_query, as: :results).where(id: user_one.id)
|
14
|
+
expect(query.size).to eq(1)
|
15
|
+
expect(query.take.results).to be_a(Hash).and(match("tag_number" => 2))
|
16
|
+
end
|
17
|
+
|
18
|
+
# ugh wording here sucks, brain is fried.
|
19
|
+
it "accepts a block for appending additional scopes to the middle-top level" do
|
20
|
+
query = User.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results) do |scope|
|
21
|
+
scope.where("tag_row.tag_number = 5")
|
22
|
+
end
|
23
|
+
|
24
|
+
expect(query.size).to eq(2)
|
25
|
+
query.each do |result|
|
26
|
+
if result.id == user_one.id
|
27
|
+
expect(result.results).to be_blank
|
28
|
+
else
|
29
|
+
expect(result.results).to be_present.and(match("tag_number" => 5))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "accepts a scope-block without arguments" do
|
35
|
+
query = User.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results) do
|
36
|
+
where("tag_row.tag_number = 5")
|
37
|
+
end
|
38
|
+
|
39
|
+
expect(query.size).to eq(2)
|
40
|
+
query.each do |result|
|
41
|
+
if result.id == user_one.id
|
42
|
+
expect(result.results).to be_blank
|
43
|
+
else
|
44
|
+
expect(result.results).to be_present.and(match("tag_number" => 5))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "allows for casting results in an aggregate-able Array function" do
|
50
|
+
query = User.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results, cast_with: :array)
|
51
|
+
expect(query.take.results).to be_a(Array).and(be_present)
|
52
|
+
expect(query.take.results.first).to be_a(Hash)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "raises an error if a from clause key is missing" do
|
56
|
+
expect do
|
57
|
+
User.select(:id).select_row_to_json(key: :tag_row, as: :results)
|
58
|
+
end.to raise_error(ArgumentError)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe ".json_build_object" do
|
63
|
+
let(:sub_query) do
|
64
|
+
User.select_row_to_json(from: User.select(:id), cast_with: :array, as: :ids).where(id: user_one.id)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "defaults the column alias if one is not provided" do
|
68
|
+
query = User.json_build_object(:personal, sub_query)
|
69
|
+
expect(query.size).to eq(1)
|
70
|
+
expect(query.take.results).to match(
|
71
|
+
"personal" => match("ids" => match_array([{ "id" => user_one.id }, { "id" => user_two.id }]))
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "allows for re-aliasing the default 'results' column" do
|
76
|
+
query = User.json_build_object(:personal, sub_query, as: :cool_dudes)
|
77
|
+
expect(query.take).to respond_to(:cool_dudes)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe ".jsonb_build_object" do
|
82
|
+
let(:sub_query) { User.select(:id, :number).where(id: user_one.id) }
|
83
|
+
|
84
|
+
it "defaults the column alias if one is not provided" do
|
85
|
+
query = User.jsonb_build_object(:personal, sub_query)
|
86
|
+
expect(query.size).to eq(1)
|
87
|
+
expect(query.take.results).to be_a(Hash).and(be_present)
|
88
|
+
expect(query.take.results).to match("personal" => match("id" => user_one.id, "number" => user_one.number))
|
89
|
+
end
|
90
|
+
|
91
|
+
it "allows for re-aliasing the default 'results' column" do
|
92
|
+
query = User.jsonb_build_object(:personal, sub_query, as: :cool_dudes)
|
93
|
+
expect(query.take).to respond_to(:cool_dudes)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "allows for custom value statement" do
|
97
|
+
query = User.jsonb_build_object(
|
98
|
+
:personal,
|
99
|
+
sub_query.where.not(id: user_one),
|
100
|
+
value: "COALESCE(array_agg(\"personal\"), '{}')",
|
101
|
+
as: :cool_dudes
|
102
|
+
)
|
103
|
+
|
104
|
+
expect(query.take.cool_dudes["personal"]).to be_a(Array).and(be_empty)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "will raise a warning if the value doesn't include a double quoted input" do
|
108
|
+
expect do
|
109
|
+
User.jsonb_build_object(
|
110
|
+
:personal,
|
111
|
+
sub_query.where.not(id: user_one),
|
112
|
+
value: "COALESCE(array_agg(personal), '{}')",
|
113
|
+
as: :cool_dudes
|
114
|
+
)
|
115
|
+
end.to output.to_stderr
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "Json literal builds" do
|
120
|
+
let(:original_hash) { { p: 1, b: "three", x: 3.14 } }
|
121
|
+
let(:hash_as_array_objs) { original_hash.to_a.flatten }
|
122
|
+
|
123
|
+
shared_examples_for "literal builds" do
|
124
|
+
let(:method) { raise "You are expected to over ride this!" }
|
125
|
+
|
126
|
+
it "will accept a hash arguments that will return itself" do
|
127
|
+
query = User.send(method.to_sym, original_hash)
|
128
|
+
expect(query.take.results).to be_a(Hash).and(be_present)
|
129
|
+
expect(query.take.results).to match(original_hash.stringify_keys)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "will accept a standard array of key values" do
|
133
|
+
query = User.send(method.to_sym, hash_as_array_objs)
|
134
|
+
expect(query.take.results).to be_a(Hash).and(be_present)
|
135
|
+
expect(query.take.results).to match(original_hash.stringify_keys)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "will accept a splatted array of key-values" do
|
139
|
+
query = User.send(method.to_sym, *hash_as_array_objs)
|
140
|
+
expect(query.take.results).to be_a(Hash).and(be_present)
|
141
|
+
expect(query.take.results).to match(original_hash.stringify_keys)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe ".json_build_literal" do
|
146
|
+
it_behaves_like "literal builds" do
|
147
|
+
let!(:method) { :json_build_literal }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe ".jsonb_build_literal" do
|
152
|
+
it_behaves_like "literal builds" do
|
153
|
+
let!(:method) { :jsonb_build_literal }
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|