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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +87 -15
  3. data/lib/active_record_extended.rb +2 -1
  4. data/lib/active_record_extended/active_record.rb +2 -9
  5. data/lib/active_record_extended/active_record/relation_patch.rb +21 -4
  6. data/lib/active_record_extended/arel.rb +2 -0
  7. data/lib/active_record_extended/arel/aggregate_function_name.rb +40 -0
  8. data/lib/active_record_extended/arel/nodes.rb +32 -41
  9. data/lib/active_record_extended/arel/predications.rb +4 -1
  10. data/lib/active_record_extended/arel/sql_literal.rb +16 -0
  11. data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +40 -1
  12. data/lib/active_record_extended/query_methods/any_of.rb +10 -8
  13. data/lib/active_record_extended/query_methods/either.rb +1 -1
  14. data/lib/active_record_extended/query_methods/inet.rb +7 -3
  15. data/lib/active_record_extended/query_methods/json.rb +156 -50
  16. data/lib/active_record_extended/query_methods/select.rb +118 -0
  17. data/lib/active_record_extended/query_methods/unionize.rb +14 -43
  18. data/lib/active_record_extended/query_methods/where_chain.rb +14 -6
  19. data/lib/active_record_extended/query_methods/window.rb +93 -0
  20. data/lib/active_record_extended/query_methods/with_cte.rb +102 -35
  21. data/lib/active_record_extended/utilities/order_by.rb +77 -0
  22. data/lib/active_record_extended/utilities/support.rb +178 -0
  23. data/lib/active_record_extended/version.rb +1 -1
  24. data/spec/query_methods/any_of_spec.rb +40 -40
  25. data/spec/query_methods/array_query_spec.rb +14 -14
  26. data/spec/query_methods/either_spec.rb +14 -14
  27. data/spec/query_methods/hash_query_spec.rb +11 -11
  28. data/spec/query_methods/inet_query_spec.rb +33 -31
  29. data/spec/query_methods/json_spec.rb +42 -27
  30. data/spec/query_methods/select_spec.rb +115 -0
  31. data/spec/query_methods/unionize_spec.rb +56 -56
  32. data/spec/query_methods/window_spec.rb +51 -0
  33. data/spec/query_methods/with_cte_spec.rb +22 -12
  34. data/spec/spec_helper.rb +1 -1
  35. data/spec/sql_inspections/any_of_sql_spec.rb +12 -12
  36. data/spec/sql_inspections/arel/aggregate_function_name_spec.rb +41 -0
  37. data/spec/sql_inspections/arel/array_spec.rb +7 -7
  38. data/spec/sql_inspections/arel/inet_spec.rb +7 -7
  39. data/spec/sql_inspections/contains_sql_queries_spec.rb +14 -14
  40. data/spec/sql_inspections/either_sql_spec.rb +11 -11
  41. data/spec/sql_inspections/json_sql_spec.rb +44 -8
  42. data/spec/sql_inspections/unionize_sql_spec.rb +27 -27
  43. data/spec/sql_inspections/window_sql_spec.rb +98 -0
  44. data/spec/sql_inspections/with_cte_sql_spec.rb +52 -23
  45. data/spec/support/models.rb +24 -4
  46. metadata +31 -20
  47. data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
  48. 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) { Person.create! }
7
- let!(:two) { Person.create! }
8
- let!(:three) { Person.create! }
9
- let!(:profile_l) { ProfileL.create!(person_id: one.id, likes: 100) }
10
- let!(:profile_r) { ProfileR.create!(person_id: two.id, dislikes: 50) }
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 = Person.either_join(:profile_l, :profile_r)
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 = Person.either_joins(:profile_l, :profile_r)
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 = Person.either_order(:asc, profile_l: :likes, profile_r: :dislikes)
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 people based on their likes and dislikes in ascended order" do
38
- query = Person.either_order(:asc, profile_l: :likes, profile_r: :dislikes).where(id: [one.id, two.id])
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 people based on their likes and dislikes in descending order" do
45
- query = Person.either_order(:desc, profile_l: :likes, profile_r: :dislikes).where(id: [one.id, two.id])
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 people based on their likes and dislikes in ascended order" do
52
- query = Person.either_orders(:asc, profile_l: :likes, profile_r: :dislikes).where(id: [one.id, two.id])
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) { Person.create!(data: { nickname: "george" }, jsonb_data: { payment: "zip" }) }
7
- let!(:two) { Person.create!(data: { nickname: "dan" }, jsonb_data: { payment: "zipper" }) }
8
- let!(:three) { Person.create!(data: { nickname: "georgey" }) }
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!(person_id: one.id)
14
- tag_two = Tag.create!(person_id: two.id)
13
+ tag_one = Tag.create!(user_id: one.id)
14
+ tag_two = Tag.create!(user_id: two.id)
15
15
 
16
- query = Tag.joins(:person).where.contains(people: { data: { nickname: "george" } })
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 = Person.where.contains(data: { nickname: "george" })
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 = Person.where.contains(jsonb_data: { payment: "zip" })
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!(person_id: one.id)
37
- tag_two = Tag.create!(person_id: two.id)
36
+ tag_one = Tag.create!(user_id: one.id)
37
+ tag_two = Tag.create!(user_id: two.id)
38
38
 
39
- query = Tag.joins(:person).where.contains(people: { jsonb_data: { payment: "zip" } })
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) { Person.create!(ip: "127.0.0.1") }
8
- let!(:local_44) { Person.create!(ip: "127.0.0.44") }
9
- let!(:local_99_1) { Person.create!(ip: "127.0.99.1") }
10
- let!(:local_range) { Person.create!(ip: "127.0.0.1/10") }
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 people who have an IP within the 127.0.0.1/24 range" do
13
- query = Person.where.inet_contained_within(ip: "127.0.0.1/24")
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 = Person.where.inet_contained_within(ip: "127.0.0.1/16")
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) { Person.create!(ip: "127.0.0.1/10") }
27
- let!(:local_44) { Person.create!(ip: "127.0.0.44/32") }
28
- let!(:local_99_1) { Person.create!(ip: "127.0.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 = Person.where.inet_contained_within_or_equals(ip: "127.0.0.44/32")
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 = Person.where.inet_contained_within_or_equals(ip: "127.0.0.1/16")
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 = Person.where.inet_contained_within_or_equals(ip: "127.0.0.1/8")
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) { Person.create!(ip: "127.0.0.1/10") }
48
- let!(:local_44) { Person.create!(ip: "127.0.0.44/24") }
49
- let!(:local_99_1) { Person.create!(ip: "127.0.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 = Person.where.inet_contains_or_equals(ip: "127.0.255.255")
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 = Person.where.inet_contains_or_equals(ip: "127.0.0.255")
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 = Person.where.inet_contains_or_equals(ip: "127.0.0.1/10")
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 = Person.where.inet_contains_or_equals(ip: "127.0.0.1/32")
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) { Person.create!(ip: "127.0.0.1/10") }
74
- let!(:local_44) { Person.create!(ip: "127.0.0.44/24") }
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 = Person.where.inet_contains(ip: "127.0.0.1")
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 = Person.where.inet_contains(ip: "127.0.0.0/8")
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) { Person.create!(ip: "127.0.0.1/24") }
89
- let!(:local_44) { Person.create!(ip: "127.0.22.44/8") }
90
- let!(:local_99_1) { Person.create!(ip: "127.0.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 = Person.where.inet_contains_or_is_contained_within(ip: "127.0.255.80")
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 = Person.where.inet_contains_or_is_contained_within(ip: "127.0.0.80")
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 = Person.where.inet_contains_or_is_contained_within(ip: "127.0.0.80/8")
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 = Person.where.inet_contains_or_is_contained_within(ip: "127.0.0.80/16")
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!(:person_one) { Person.create! }
5
- let!(:person_two) { Person.create! }
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!(person: person_one, tag_number: 2) }
9
- let!(:tag_two) { Tag.create!(person: person_two, tag_number: 5) }
10
- let(:sub_query) { Tag.select(:tag_number).where("tags.person_id = people.id") }
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 = Person.select(:id).select_row_to_json(sub_query, as: :results).where(id: person_one.id)
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 = Person.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results) do |scope|
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 == person_one.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 = Person.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results, cast_as_array: true)
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
- Person.select(:id).select_row_to_json(key: :tag_row, as: :results)
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
- Person.select_row_to_json(from: Person.select(:id), cast_as_array: true, as: :ids).where(id: person_one.id)
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 = Person.json_build_object(:personal, sub_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" => person_one.id }, { "id" => person_two.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 = Person.json_build_object(:personal, sub_query, as: :cool_dudes)
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) { Person.select(:id, :number).where(id: person_one.id) }
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 = Person.jsonb_build_object(:personal, sub_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" => person_one.id, "number" => person_one.number))
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 = Person.jsonb_build_object(:personal, sub_query, as: :cool_dudes)
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 = Person.jsonb_build_object(
97
+ query = User.jsonb_build_object(
83
98
  :personal,
84
- sub_query.where.not(id: person_one),
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
- Person.jsonb_build_object(
109
+ User.jsonb_build_object(
95
110
  :personal,
96
- sub_query.where.not(id: person_one),
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 = Person.send(method.to_sym, original_hash)
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 = Person.send(method.to_sym, hash_as_array_objs)
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 = Person.send(method.to_sym, *hash_as_array_objs)
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