active_record_extended 1.1.0 → 2.0.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/README.md +87 -15
- data/lib/active_record_extended.rb +2 -1
- data/lib/active_record_extended/active_record.rb +2 -9
- data/lib/active_record_extended/active_record/relation_patch.rb +21 -4
- data/lib/active_record_extended/arel.rb +2 -0
- data/lib/active_record_extended/arel/aggregate_function_name.rb +40 -0
- data/lib/active_record_extended/arel/nodes.rb +32 -41
- data/lib/active_record_extended/arel/predications.rb +4 -1
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +40 -1
- data/lib/active_record_extended/query_methods/any_of.rb +10 -8
- data/lib/active_record_extended/query_methods/either.rb +1 -1
- data/lib/active_record_extended/query_methods/inet.rb +7 -3
- data/lib/active_record_extended/query_methods/json.rb +156 -50
- data/lib/active_record_extended/query_methods/select.rb +118 -0
- data/lib/active_record_extended/query_methods/unionize.rb +14 -43
- data/lib/active_record_extended/query_methods/where_chain.rb +14 -6
- data/lib/active_record_extended/query_methods/window.rb +93 -0
- data/lib/active_record_extended/query_methods/with_cte.rb +102 -35
- 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 +1 -1
- data/spec/query_methods/any_of_spec.rb +40 -40
- data/spec/query_methods/array_query_spec.rb +14 -14
- data/spec/query_methods/either_spec.rb +14 -14
- data/spec/query_methods/hash_query_spec.rb +11 -11
- data/spec/query_methods/inet_query_spec.rb +33 -31
- data/spec/query_methods/json_spec.rb +42 -27
- data/spec/query_methods/select_spec.rb +115 -0
- data/spec/query_methods/unionize_spec.rb +56 -56
- data/spec/query_methods/window_spec.rb +51 -0
- data/spec/query_methods/with_cte_spec.rb +22 -12
- data/spec/spec_helper.rb +1 -1
- data/spec/sql_inspections/any_of_sql_spec.rb +12 -12
- data/spec/sql_inspections/arel/aggregate_function_name_spec.rb +41 -0
- data/spec/sql_inspections/arel/array_spec.rb +7 -7
- data/spec/sql_inspections/arel/inet_spec.rb +7 -7
- data/spec/sql_inspections/contains_sql_queries_spec.rb +14 -14
- data/spec/sql_inspections/either_sql_spec.rb +11 -11
- data/spec/sql_inspections/json_sql_spec.rb +44 -8
- data/spec/sql_inspections/unionize_sql_spec.rb +27 -27
- data/spec/sql_inspections/window_sql_spec.rb +98 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +52 -23
- data/spec/support/models.rb +24 -4
- metadata +31 -20
- data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
- data/lib/active_record_extended/utilities.rb +0 -141
@@ -3,22 +3,22 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
RSpec.describe "Active Record Either Methods" do
|
6
|
-
let!(:one) {
|
7
|
-
let!(:two) {
|
8
|
-
let!(:three) {
|
9
|
-
let!(:profile_l) { ProfileL.create!(
|
10
|
-
let!(:profile_r) { ProfileR.create!(
|
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
11
|
|
12
12
|
describe ".either_join/2" do
|
13
13
|
it "Should only only return records that belong to profile L or profile R" do
|
14
|
-
query =
|
14
|
+
query = User.either_join(:profile_l, :profile_r)
|
15
15
|
expect(query).to include(one, two)
|
16
16
|
expect(query).to_not include(three)
|
17
17
|
end
|
18
18
|
|
19
19
|
context "Alias .either_joins/2" do
|
20
20
|
it "Should only only return records that belong to profile L or profile R" do
|
21
|
-
query =
|
21
|
+
query = User.either_joins(:profile_l, :profile_r)
|
22
22
|
expect(query).to include(one, two)
|
23
23
|
expect(query).to_not include(three)
|
24
24
|
end
|
@@ -27,29 +27,29 @@ RSpec.describe "Active Record Either Methods" do
|
|
27
27
|
|
28
28
|
describe ".either_order/2" do
|
29
29
|
it "Should not exclude anyone who does not have a relationship" do
|
30
|
-
query =
|
30
|
+
query = User.either_order(:asc, profile_l: :likes, profile_r: :dislikes)
|
31
31
|
expect(query.count).to eq(3)
|
32
32
|
expect(query[0]).to eq(two)
|
33
33
|
expect(query[1]).to eq(one)
|
34
34
|
expect(query[2]).to eq(three)
|
35
35
|
end
|
36
36
|
|
37
|
-
it "Should order
|
38
|
-
query =
|
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
39
|
expect(query.count).to eq(2)
|
40
40
|
expect(query.first).to eq(two)
|
41
41
|
expect(query.last).to eq(one)
|
42
42
|
end
|
43
43
|
|
44
|
-
it "Should order
|
45
|
-
query =
|
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
46
|
expect(query.first).to eq(one)
|
47
47
|
expect(query.last).to eq(two)
|
48
48
|
end
|
49
49
|
|
50
50
|
context "Alias .either_order/2" do
|
51
|
-
it "Should order
|
52
|
-
query =
|
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
53
|
expect(query.count).to eq(2)
|
54
54
|
expect(query.first).to eq(two)
|
55
55
|
expect(query.last).to eq(one)
|
@@ -3,23 +3,23 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
RSpec.describe "Active Record Hash Related Query Methods" do
|
6
|
-
let!(:one) {
|
7
|
-
let!(:two) {
|
8
|
-
let!(:three) {
|
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
9
|
|
10
10
|
describe "#contains" do
|
11
11
|
context "HStore Column Type" do
|
12
12
|
it "returns records that contain hash elements in joined tables" do
|
13
|
-
tag_one = Tag.create!(
|
14
|
-
tag_two = Tag.create!(
|
13
|
+
tag_one = Tag.create!(user_id: one.id)
|
14
|
+
tag_two = Tag.create!(user_id: two.id)
|
15
15
|
|
16
|
-
query = Tag.joins(:
|
16
|
+
query = Tag.joins(:user).where.contains(users: { data: { nickname: "george" } })
|
17
17
|
expect(query).to include(tag_one)
|
18
18
|
expect(query).to_not include(tag_two)
|
19
19
|
end
|
20
20
|
|
21
21
|
it "returns records that contain hash value" do
|
22
|
-
query =
|
22
|
+
query = User.where.contains(data: { nickname: "george" })
|
23
23
|
expect(query).to include(one)
|
24
24
|
expect(query).to_not include(two, three)
|
25
25
|
end
|
@@ -27,16 +27,16 @@ RSpec.describe "Active Record Hash Related Query Methods" do
|
|
27
27
|
|
28
28
|
context "JSONB Column Type" do
|
29
29
|
it "returns records that contains a json hashed value" do
|
30
|
-
query =
|
30
|
+
query = User.where.contains(jsonb_data: { payment: "zip" })
|
31
31
|
expect(query).to include(one)
|
32
32
|
expect(query).to_not include(two, three)
|
33
33
|
end
|
34
34
|
|
35
35
|
it "returns records that contain jsonb elements in joined tables" do
|
36
|
-
tag_one = Tag.create!(
|
37
|
-
tag_two = Tag.create!(
|
36
|
+
tag_one = Tag.create!(user_id: one.id)
|
37
|
+
tag_two = Tag.create!(user_id: two.id)
|
38
38
|
|
39
|
-
query = Tag.joins(:
|
39
|
+
query = Tag.joins(:user).where.contains(users: { jsonb_data: { payment: "zip" } })
|
40
40
|
expect(query).to include(tag_one)
|
41
41
|
expect(query).to_not include(tag_two)
|
42
42
|
end
|
@@ -3,107 +3,109 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
RSpec.describe "Active Record Inet Query Methods" do
|
6
|
+
before { stub_const("User", Namespaced::Record) }
|
7
|
+
|
6
8
|
describe "#inet_contained_within" do
|
7
|
-
let!(:local_1) {
|
8
|
-
let!(:local_44) {
|
9
|
-
let!(:local_99_1) {
|
10
|
-
let!(:local_range) {
|
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") }
|
11
13
|
|
12
|
-
it "Should return
|
13
|
-
query =
|
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")
|
14
16
|
expect(query).to include(local_1, local_44)
|
15
17
|
expect(query).to_not include(local_99_1, local_range)
|
16
18
|
end
|
17
19
|
|
18
20
|
it "Should return all users who have an IP within the 127.0.0.1/16 range" do
|
19
|
-
query =
|
21
|
+
query = User.where.inet_contained_within(ip: "127.0.0.1/16")
|
20
22
|
expect(query).to include(local_1, local_44, local_99_1)
|
21
23
|
expect(query).to_not include(local_range)
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
27
|
describe "inet_contained_within_or_equals" do
|
26
|
-
let!(:local_1) {
|
27
|
-
let!(:local_44) {
|
28
|
-
let!(:local_99_1) {
|
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") }
|
29
31
|
|
30
32
|
it "Should find records that contain a matching submask" do
|
31
|
-
query =
|
33
|
+
query = User.where.inet_contained_within_or_equals(ip: "127.0.0.44/32")
|
32
34
|
expect(query).to include(local_44)
|
33
35
|
expect(query).to_not include(local_1, local_99_1)
|
34
36
|
end
|
35
37
|
|
36
38
|
it "Should find records that are within range of a given submask" do
|
37
|
-
query =
|
39
|
+
query = User.where.inet_contained_within_or_equals(ip: "127.0.0.1/16")
|
38
40
|
expect(query).to include(local_44, local_99_1)
|
39
41
|
expect(query).to_not include(local_1)
|
40
42
|
|
41
|
-
query =
|
43
|
+
query = User.where.inet_contained_within_or_equals(ip: "127.0.0.1/8")
|
42
44
|
expect(query).to include(local_1, local_44, local_99_1)
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
46
48
|
describe "#inet_contains_or_equals" do
|
47
|
-
let!(:local_1) {
|
48
|
-
let!(:local_44) {
|
49
|
-
let!(:local_99_1) {
|
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") }
|
50
52
|
|
51
53
|
it "Should find records with submask ranges that contain a given IP" do
|
52
|
-
query =
|
54
|
+
query = User.where.inet_contains_or_equals(ip: "127.0.255.255")
|
53
55
|
expect(query).to include(local_1)
|
54
56
|
expect(query).to_not include(local_44, local_99_1)
|
55
57
|
|
56
|
-
query =
|
58
|
+
query = User.where.inet_contains_or_equals(ip: "127.0.0.255")
|
57
59
|
expect(query).to include(local_1, local_44)
|
58
60
|
expect(query).to_not include(local_99_1)
|
59
61
|
end
|
60
62
|
|
61
63
|
it "Finds records when querying with a submasked value" do
|
62
|
-
query =
|
64
|
+
query = User.where.inet_contains_or_equals(ip: "127.0.0.1/10")
|
63
65
|
expect(query).to include(local_1)
|
64
66
|
expect(query).to_not include(local_44, local_99_1)
|
65
67
|
|
66
|
-
query =
|
68
|
+
query = User.where.inet_contains_or_equals(ip: "127.0.0.1/32")
|
67
69
|
expect(query).to include(local_1, local_44)
|
68
70
|
expect(query).to_not include(local_99_1)
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
72
74
|
describe "#inet_contains" do
|
73
|
-
let!(:local_1) {
|
74
|
-
let!(:local_44) {
|
75
|
+
let!(:local_1) { User.create!(ip: "127.0.0.1/10") }
|
76
|
+
let!(:local_44) { User.create!(ip: "127.0.0.44/24") }
|
75
77
|
|
76
78
|
it "Should find records that the given IP falls within" do
|
77
|
-
query =
|
79
|
+
query = User.where.inet_contains(ip: "127.0.0.1")
|
78
80
|
expect(query).to include(local_1, local_44)
|
79
81
|
end
|
80
82
|
|
81
83
|
it "Should not find records when querying with a submasked value" do
|
82
|
-
query =
|
84
|
+
query = User.where.inet_contains(ip: "127.0.0.0/8")
|
83
85
|
expect(query).to be_empty
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
87
89
|
describe "#inet_contains_or_is_contained_within" do
|
88
|
-
let!(:local_1) {
|
89
|
-
let!(:local_44) {
|
90
|
-
let!(:local_99_1) {
|
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") }
|
91
93
|
|
92
94
|
it "should find records where the records contain the given IP" do
|
93
|
-
query =
|
95
|
+
query = User.where.inet_contains_or_is_contained_within(ip: "127.0.255.80")
|
94
96
|
expect(query).to include(local_44)
|
95
97
|
expect(query).to_not include(local_1, local_99_1)
|
96
98
|
|
97
|
-
query =
|
99
|
+
query = User.where.inet_contains_or_is_contained_within(ip: "127.0.0.80")
|
98
100
|
expect(query).to include(local_1, local_44)
|
99
101
|
expect(query).to_not include(local_99_1)
|
100
102
|
end
|
101
103
|
|
102
104
|
it "Should find records that the where query contains a valid range" do
|
103
|
-
query =
|
105
|
+
query = User.where.inet_contains_or_is_contained_within(ip: "127.0.0.80/8")
|
104
106
|
expect(query).to include(local_1, local_44, local_99_1)
|
105
107
|
|
106
|
-
query =
|
108
|
+
query = User.where.inet_contains_or_is_contained_within(ip: "127.0.0.80/16")
|
107
109
|
expect(query).to include(local_1, local_44, local_99_1)
|
108
110
|
end
|
109
111
|
end
|
@@ -1,29 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
RSpec.describe "Active Record JSON methods" do
|
4
|
-
let!(:
|
5
|
-
let!(:
|
4
|
+
let!(:user_one) { User.create! }
|
5
|
+
let!(:user_two) { User.create! }
|
6
6
|
|
7
7
|
describe ".select_row_to_json" do
|
8
|
-
let!(:tag_one) { Tag.create!(
|
9
|
-
let!(:tag_two) { Tag.create!(
|
10
|
-
let(:sub_query) { Tag.select(:tag_number).where("tags.
|
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
11
|
|
12
12
|
it "should nest a json object in the query results" do
|
13
|
-
query =
|
13
|
+
query = User.select(:id).select_row_to_json(sub_query, as: :results).where(id: user_one.id)
|
14
14
|
expect(query.size).to eq(1)
|
15
15
|
expect(query.take.results).to be_a(Hash).and(match("tag_number" => 2))
|
16
16
|
end
|
17
17
|
|
18
18
|
# ugh wording here sucks, brain is fried.
|
19
19
|
it "accepts a block for appending additional scopes to the middle-top level" do
|
20
|
-
query =
|
20
|
+
query = User.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results) do |scope|
|
21
21
|
scope.where("tag_row.tag_number = 5")
|
22
22
|
end
|
23
23
|
|
24
24
|
expect(query.size).to eq(2)
|
25
25
|
query.each do |result|
|
26
|
-
if result.id ==
|
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
|
27
42
|
expect(result.results).to be_blank
|
28
43
|
else
|
29
44
|
expect(result.results).to be_present.and(match("tag_number" => 5))
|
@@ -32,58 +47,58 @@ RSpec.describe "Active Record JSON methods" do
|
|
32
47
|
end
|
33
48
|
|
34
49
|
it "allows for casting results in an aggregate-able Array function" do
|
35
|
-
query =
|
50
|
+
query = User.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results, cast_with: :array)
|
36
51
|
expect(query.take.results).to be_a(Array).and(be_present)
|
37
52
|
expect(query.take.results.first).to be_a(Hash)
|
38
53
|
end
|
39
54
|
|
40
55
|
it "raises an error if a from clause key is missing" do
|
41
56
|
expect do
|
42
|
-
|
57
|
+
User.select(:id).select_row_to_json(key: :tag_row, as: :results)
|
43
58
|
end.to raise_error(ArgumentError)
|
44
59
|
end
|
45
60
|
end
|
46
61
|
|
47
62
|
describe ".json_build_object" do
|
48
63
|
let(:sub_query) do
|
49
|
-
|
64
|
+
User.select_row_to_json(from: User.select(:id), cast_with: :array, as: :ids).where(id: user_one.id)
|
50
65
|
end
|
51
66
|
|
52
67
|
it "defaults the column alias if one is not provided" do
|
53
|
-
query =
|
68
|
+
query = User.json_build_object(:personal, sub_query)
|
54
69
|
expect(query.size).to eq(1)
|
55
70
|
expect(query.take.results).to match(
|
56
|
-
"personal" => match("ids" => match_array([{ "id" =>
|
71
|
+
"personal" => match("ids" => match_array([{ "id" => user_one.id }, { "id" => user_two.id }]))
|
57
72
|
)
|
58
73
|
end
|
59
74
|
|
60
75
|
it "allows for re-aliasing the default 'results' column" do
|
61
|
-
query =
|
76
|
+
query = User.json_build_object(:personal, sub_query, as: :cool_dudes)
|
62
77
|
expect(query.take).to respond_to(:cool_dudes)
|
63
78
|
end
|
64
79
|
end
|
65
80
|
|
66
81
|
describe ".jsonb_build_object" do
|
67
|
-
let(:sub_query) {
|
82
|
+
let(:sub_query) { User.select(:id, :number).where(id: user_one.id) }
|
68
83
|
|
69
84
|
it "defaults the column alias if one is not provided" do
|
70
|
-
query =
|
85
|
+
query = User.jsonb_build_object(:personal, sub_query)
|
71
86
|
expect(query.size).to eq(1)
|
72
87
|
expect(query.take.results).to be_a(Hash).and(be_present)
|
73
|
-
expect(query.take.results).to match("personal" => match("id" =>
|
88
|
+
expect(query.take.results).to match("personal" => match("id" => user_one.id, "number" => user_one.number))
|
74
89
|
end
|
75
90
|
|
76
91
|
it "allows for re-aliasing the default 'results' column" do
|
77
|
-
query =
|
92
|
+
query = User.jsonb_build_object(:personal, sub_query, as: :cool_dudes)
|
78
93
|
expect(query.take).to respond_to(:cool_dudes)
|
79
94
|
end
|
80
95
|
|
81
96
|
it "allows for custom value statement" do
|
82
|
-
query =
|
97
|
+
query = User.jsonb_build_object(
|
83
98
|
:personal,
|
84
|
-
sub_query.where.not(id:
|
99
|
+
sub_query.where.not(id: user_one),
|
85
100
|
value: "COALESCE(array_agg(\"personal\"), '{}')",
|
86
|
-
as: :cool_dudes
|
101
|
+
as: :cool_dudes
|
87
102
|
)
|
88
103
|
|
89
104
|
expect(query.take.cool_dudes["personal"]).to be_a(Array).and(be_empty)
|
@@ -91,11 +106,11 @@ RSpec.describe "Active Record JSON methods" do
|
|
91
106
|
|
92
107
|
it "will raise a warning if the value doesn't include a double quoted input" do
|
93
108
|
expect do
|
94
|
-
|
109
|
+
User.jsonb_build_object(
|
95
110
|
:personal,
|
96
|
-
sub_query.where.not(id:
|
111
|
+
sub_query.where.not(id: user_one),
|
97
112
|
value: "COALESCE(array_agg(personal), '{}')",
|
98
|
-
as: :cool_dudes
|
113
|
+
as: :cool_dudes
|
99
114
|
)
|
100
115
|
end.to output.to_stderr
|
101
116
|
end
|
@@ -109,19 +124,19 @@ RSpec.describe "Active Record JSON methods" do
|
|
109
124
|
let(:method) { raise "You are expected to over ride this!" }
|
110
125
|
|
111
126
|
it "will accept a hash arguments that will return itself" do
|
112
|
-
query =
|
127
|
+
query = User.send(method.to_sym, original_hash)
|
113
128
|
expect(query.take.results).to be_a(Hash).and(be_present)
|
114
129
|
expect(query.take.results).to match(original_hash.stringify_keys)
|
115
130
|
end
|
116
131
|
|
117
132
|
it "will accept a standard array of key values" do
|
118
|
-
query =
|
133
|
+
query = User.send(method.to_sym, hash_as_array_objs)
|
119
134
|
expect(query.take.results).to be_a(Hash).and(be_present)
|
120
135
|
expect(query.take.results).to match(original_hash.stringify_keys)
|
121
136
|
end
|
122
137
|
|
123
138
|
it "will accept a splatted array of key-values" do
|
124
|
-
query =
|
139
|
+
query = User.send(method.to_sym, *hash_as_array_objs)
|
125
140
|
expect(query.take.results).to be_a(Hash).and(be_present)
|
126
141
|
expect(query.take.results).to match(original_hash.stringify_keys)
|
127
142
|
end
|
@@ -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
|