active_record_extended 1.1.0 → 1.2.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -18
  3. data/lib/active_record_extended.rb +2 -1
  4. data/lib/active_record_extended/active_record.rb +2 -0
  5. data/lib/active_record_extended/active_record/relation_patch.rb +1 -1
  6. data/lib/active_record_extended/arel.rb +1 -0
  7. data/lib/active_record_extended/arel/aggregate_function_name.rb +40 -0
  8. data/lib/active_record_extended/arel/nodes.rb +31 -41
  9. data/lib/active_record_extended/arel/predications.rb +4 -1
  10. data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +40 -1
  11. data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +4 -4
  12. data/lib/active_record_extended/patch/5_0/regex_match.rb +10 -0
  13. data/lib/active_record_extended/query_methods/any_of.rb +5 -4
  14. data/lib/active_record_extended/query_methods/inet.rb +1 -1
  15. data/lib/active_record_extended/query_methods/json.rb +152 -43
  16. data/lib/active_record_extended/query_methods/select.rb +117 -0
  17. data/lib/active_record_extended/query_methods/unionize.rb +4 -39
  18. data/lib/active_record_extended/query_methods/with_cte.rb +3 -3
  19. data/lib/active_record_extended/utilities/order_by.rb +96 -0
  20. data/lib/active_record_extended/utilities/support.rb +175 -0
  21. data/lib/active_record_extended/version.rb +1 -1
  22. data/spec/query_methods/any_of_spec.rb +40 -40
  23. data/spec/query_methods/array_query_spec.rb +14 -14
  24. data/spec/query_methods/either_spec.rb +14 -14
  25. data/spec/query_methods/hash_query_spec.rb +11 -11
  26. data/spec/query_methods/inet_query_spec.rb +33 -31
  27. data/spec/query_methods/json_spec.rb +40 -25
  28. data/spec/query_methods/select_spec.rb +115 -0
  29. data/spec/query_methods/unionize_spec.rb +54 -54
  30. data/spec/query_methods/with_cte_spec.rb +12 -12
  31. data/spec/sql_inspections/any_of_sql_spec.rb +11 -11
  32. data/spec/sql_inspections/arel/aggregate_function_name_spec.rb +41 -0
  33. data/spec/sql_inspections/arel/array_spec.rb +7 -7
  34. data/spec/sql_inspections/arel/inet_spec.rb +7 -7
  35. data/spec/sql_inspections/contains_sql_queries_spec.rb +14 -14
  36. data/spec/sql_inspections/either_sql_spec.rb +11 -11
  37. data/spec/sql_inspections/json_sql_spec.rb +38 -8
  38. data/spec/sql_inspections/unionize_sql_spec.rb +27 -27
  39. data/spec/sql_inspections/with_cte_sql_spec.rb +22 -22
  40. data/spec/support/models.rb +18 -4
  41. metadata +11 -3
  42. data/lib/active_record_extended/utilities.rb +0 -141
@@ -3,25 +3,25 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe "Active Record With CTE Query Methods" do
6
- let!(:person_one) { Person.create! }
7
- let!(:person_two) { Person.create! }
8
- let!(:profile_one) { ProfileL.create!(person_id: person_one.id, likes: 200) }
9
- let!(:profile_two) { ProfileL.create!(person_id: person_two.id, likes: 500) }
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
10
 
11
11
  describe ".with/1" do
12
12
  context "when using as a standalone query" do
13
13
  it "should only return a person with less than 300 likes" do
14
- query = Person.with(profile: ProfileL.where("likes < 300"))
15
- .joins("JOIN profile ON profile.id = people.id")
14
+ query = User.with(profile: ProfileL.where("likes < 300"))
15
+ .joins("JOIN profile ON profile.id = users.id")
16
16
 
17
- expect(query).to match_array([person_one])
17
+ expect(query).to match_array([user_one])
18
18
  end
19
19
 
20
20
  it "should return anyone with likes greater than or equal to 200" do
21
- query = Person.with(profile: ProfileL.where("likes >= 200"))
22
- .joins("JOIN profile ON profile.id = people.id")
21
+ query = User.with(profile: ProfileL.where("likes >= 200"))
22
+ .joins("JOIN profile ON profile.id = users.id")
23
23
 
24
- expect(query).to match_array([person_one, person_two])
24
+ expect(query).to match_array([user_one, user_two])
25
25
  end
26
26
  end
27
27
 
@@ -31,9 +31,9 @@ RSpec.describe "Active Record With CTE Query Methods" do
31
31
 
32
32
  it "will maintain the CTE table when merging into existing AR queries" do
33
33
  sub_query = ProfileL.with(version_controls: VersionControl.where.contains(source: { help: "me" }))
34
- query = Person.joins(profile_l: :version).merge(sub_query)
34
+ query = User.joins(profile_l: :version).merge(sub_query)
35
35
 
36
- expect(query).to match_array([person_one])
36
+ expect(query).to match_array([user_one])
37
37
  end
38
38
  end
39
39
  end
@@ -3,30 +3,30 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe "Any / None of SQL Queries" do
6
- let(:equal_query) { '"people"."personal_id" = 1' }
7
- let(:or_query) { 'OR "people"."personal_id" = 2' }
6
+ let(:equal_query) { '"users"."personal_id" = 1' }
7
+ let(:or_query) { 'OR "users"."personal_id" = 2' }
8
8
  let(:equal_or) { equal_query + " " + or_query }
9
- let(:join_query) { /INNER JOIN \"tags\" ON \"tags\".\"person_id\" = \"people\".\"id/ }
9
+ let(:join_query) { /INNER JOIN \"tags\" ON \"tags\".\"user_id\" = \"users\".\"id/ }
10
10
 
11
11
  describe "where.any_of/1" do
12
12
  it "should group different column arguments into nested or conditions" do
13
- query = Person.where.any_of({ personal_id: 1 }, { id: 2 }, { personal_id: 2 }).to_sql
13
+ query = User.where.any_of({ personal_id: 1 }, { id: 2 }, { personal_id: 2 }).to_sql
14
14
  expect(query).to match_regex(/WHERE \(\(.+ = 1 OR .+ = 2\) OR .+ = 2\)/)
15
15
  end
16
16
 
17
17
  it "Should assign where clause predicates for standard queries" do
18
- query = Person.where.any_of({ personal_id: 1 }, { personal_id: 2 }).to_sql
18
+ query = User.where.any_of({ personal_id: 1 }, { personal_id: 2 }).to_sql
19
19
  expect(query).to include(equal_or)
20
20
 
21
- personal_one = Person.where(personal_id: 1)
22
- personal_two = Person.where(personal_id: 2)
23
- query = Person.where.any_of(personal_one, personal_two).to_sql
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
24
  expect(query).to include(equal_or)
25
25
  end
26
26
 
27
27
  it "Joining queries should be added to the select statement" do
28
- person_two_tag = Person.where(personal_id: 1).joins(:hm_tags)
29
- query = Person.where.any_of(person_two_tag).to_sql
28
+ user_two_tag = User.where(personal_id: 1).joins(:hm_tags)
29
+ query = User.where.any_of(user_two_tag).to_sql
30
30
  expect(query).to match_regex(join_query)
31
31
  expect(query).to include(equal_query)
32
32
  end
@@ -34,7 +34,7 @@ RSpec.describe "Any / None of SQL Queries" do
34
34
 
35
35
  describe "where.none_of/1" do
36
36
  it "Should surround the query in a WHERE NOT clause" do
37
- query = Person.where.none_of({ personal_id: 1 }, { id: 2 }, { personal_id: 2 }).to_sql
37
+ query = User.where.none_of({ personal_id: 1 }, { id: 2 }, { personal_id: 2 }).to_sql
38
38
  expect(query).to match_regex(/WHERE.+NOT \(\(.+ = 1 OR .+ = 2\) OR .+ = 2\)/)
39
39
  end
40
40
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Arel::Nodes::AggregateFunctionName do
6
+ describe "Custom Aggregate function" do
7
+ it "constructs an aggregate function based on a given name" do
8
+ query = described_class.new("MY_CUSTOM_AGG", [Arel.sql("id == me")])
9
+ expect(query.to_sql).to eq("MY_CUSTOM_AGG(id == me)")
10
+ end
11
+
12
+ it "can append multiple expressions" do
13
+ query = described_class.new("MY_CUSTOM_AGG", [Arel.sql("id == me"), Arel.sql("id == you")])
14
+ expect(query.to_sql).to eq("MY_CUSTOM_AGG(id == me, id == you)")
15
+ end
16
+
17
+ it "can append a distinct clause inside the aggregate" do
18
+ query = described_class.new("MY_CUSTOM_AGG", [Arel.sql("id == me")], true)
19
+ expect(query.to_sql).to eq("MY_CUSTOM_AGG(DISTINCT id == me)")
20
+ end
21
+
22
+ it "can append an order by clause when providing a ordering expression" do
23
+ order_expr = Arel.sql("id").desc
24
+ query = described_class.new("MY_CUSTOM_AGG", [Arel.sql("id == me")], true).order_by([order_expr])
25
+ expect(query.to_sql).to eq("MY_CUSTOM_AGG(DISTINCT id == me ORDER BY id DESC)")
26
+ end
27
+
28
+ it "can append multiple ordering clauses" do
29
+ expr = Arel.sql("id").desc
30
+ other_expr = Arel.sql("name").asc
31
+ query = described_class.new("MY_CUSTOM_AGG", [Arel.sql("id == me")], true).order_by([expr, other_expr])
32
+ expect(query.to_sql).to eq("MY_CUSTOM_AGG(DISTINCT id == me ORDER BY id DESC, name ASC)")
33
+ end
34
+
35
+ it "can be aliased" do
36
+ alias_as = Arel.sql("new_name")
37
+ query = described_class.new("MY_CUSTOM_AGG", [Arel.sql("id == me")], true).as(alias_as)
38
+ expect(query.to_sql).to eq("MY_CUSTOM_AGG(DISTINCT id == me) AS new_name")
39
+ end
40
+ end
41
+ end
@@ -3,7 +3,7 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe "Array Column Predicates" do
6
- let(:arel_table) { Person.arel_table }
6
+ let(:arel_table) { User.arel_table }
7
7
 
8
8
  describe "Array Overlap" do
9
9
  it "converts Arel overlap statement" do
@@ -17,7 +17,7 @@ RSpec.describe "Array Column Predicates" do
17
17
  end
18
18
 
19
19
  it "works with count (and other predicates)" do
20
- expect(Person.where(arel_table[:tag_ids].overlap([1, 2])).count).to eq 0
20
+ expect(User.where(arel_table[:tag_ids].overlap([1, 2])).count).to eq 0
21
21
  end
22
22
  end
23
23
 
@@ -33,31 +33,31 @@ RSpec.describe "Array Column Predicates" do
33
33
  end
34
34
 
35
35
  it "works with count (and other predicates)" do
36
- expect(Person.where(arel_table[:tag_ids].contains([1, 2])).count).to eq 0
36
+ expect(User.where(arel_table[:tag_ids].contains([1, 2])).count).to eq 0
37
37
  end
38
38
  end
39
39
 
40
40
  describe "Any Array Element" do
41
41
  it "creates any predicates that contain a string value" do
42
42
  query = arel_table.where(arel_table[:tags].any("tag")).to_sql
43
- expect(query).to match_regex(/'tag' = ANY\("people"\."tags"\)/)
43
+ expect(query).to match_regex(/'tag' = ANY\("users"\."tags"\)/)
44
44
  end
45
45
 
46
46
  it "creates any predicates that contain a integer value" do
47
47
  query = arel_table.where(arel_table[:tags].any(2)).to_sql
48
- expect(query).to match_regex(/2 = ANY\("people"\."tags"\)/)
48
+ expect(query).to match_regex(/2 = ANY\("users"\."tags"\)/)
49
49
  end
50
50
  end
51
51
 
52
52
  describe "All Array Elements" do
53
53
  it "create all predicates that contain a string value" do
54
54
  query = arel_table.where(arel_table[:tags].all("tag")).to_sql
55
- expect(query).to match_regex(/'tag' = ALL\("people"\."tags"\)/)
55
+ expect(query).to match_regex(/'tag' = ALL\("users"\."tags"\)/)
56
56
  end
57
57
 
58
58
  it "create all predicates that contain a interger value" do
59
59
  query = arel_table.where(arel_table[:tags].all(2)).to_sql
60
- expect(query).to match_regex(/2 = ALL\("people"\."tags"\)/)
60
+ expect(query).to match_regex(/2 = ALL\("users"\."tags"\)/)
61
61
  end
62
62
  end
63
63
  end
@@ -3,7 +3,7 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe "Inet Column Predicates" do
6
- let(:arel_table) { Person.arel_table }
6
+ let(:arel_table) { User.arel_table }
7
7
 
8
8
  describe "#inet_contained_within" do
9
9
  it "converts Arel inet contained within statement" do
@@ -12,7 +12,7 @@ RSpec.describe "Inet Column Predicates" do
12
12
  end
13
13
 
14
14
  it "works with count" do
15
- expect(Person.where(arel_table[:ip].inet_contained_within(IPAddr.new("127.0.0.1"))).count).to eq(0)
15
+ expect(User.where(arel_table[:ip].inet_contained_within(IPAddr.new("127.0.0.1"))).count).to eq(0)
16
16
  end
17
17
  end
18
18
 
@@ -23,7 +23,7 @@ RSpec.describe "Inet Column Predicates" do
23
23
  end
24
24
 
25
25
  it "works with count" do
26
- expect(Person.where(arel_table[:ip].inet_contained_within_or_equals(IPAddr.new("127.0.0.1"))).count).to eq(0)
26
+ expect(User.where(arel_table[:ip].inet_contained_within_or_equals(IPAddr.new("127.0.0.1"))).count).to eq(0)
27
27
  end
28
28
  end
29
29
 
@@ -34,7 +34,7 @@ RSpec.describe "Inet Column Predicates" do
34
34
  end
35
35
 
36
36
  it "works with count" do
37
- expect(Person.where(arel_table[:ip].inet_contains_or_equals(IPAddr.new("127.0.0.1"))).count).to eq(0)
37
+ expect(User.where(arel_table[:ip].inet_contains_or_equals(IPAddr.new("127.0.0.1"))).count).to eq(0)
38
38
  end
39
39
  end
40
40
 
@@ -45,7 +45,7 @@ RSpec.describe "Inet Column Predicates" do
45
45
  end
46
46
 
47
47
  it "works with count" do
48
- expect(Person.where(arel_table[:ip].inet_contains("127.0.0.1")).count).to eq(0)
48
+ expect(User.where(arel_table[:ip].inet_contains("127.0.0.1")).count).to eq(0)
49
49
  end
50
50
  end
51
51
 
@@ -59,8 +59,8 @@ RSpec.describe "Inet Column Predicates" do
59
59
  end
60
60
 
61
61
  it "works with count" do
62
- expect(Person.where(arel_table[:ip].inet_contains_or_is_contained_within("127.0.0.1")).count).to eq(0)
63
- expect(Person.where(arel_table[:ip].inet_contains_or_is_contained_within(IPAddr.new("127.0.0.1"))).count).to eq(0)
62
+ expect(User.where(arel_table[:ip].inet_contains_or_is_contained_within("127.0.0.1")).count).to eq(0)
63
+ expect(User.where(arel_table[:ip].inet_contains_or_is_contained_within(IPAddr.new("127.0.0.1"))).count).to eq(0)
64
64
  end
65
65
  end
66
66
  end
@@ -3,44 +3,44 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe "Contains SQL Queries" do
6
- let(:contains_array_regex) { /\"people\"\.\"tag_ids\" @> '\{1,2\}'/ }
7
- let(:contains_hstore_regex) { /\"people\"\.\"data\" @> '\"nickname\"=>"Dan"'/ }
8
- let(:contains_jsonb_regex) { /\"people\"\.\"jsonb_data\" @> '\{"nickname\":\"Dan"}'/ }
9
- let(:contained_in_array_regex) { /\"people\"\.\"tag_ids\" <@ '\{1,2\}'/ }
10
- let(:contained_in_hstore_regex) { /\"people\"\.\"data\" <@ '\"nickname\"=>"Dan"'/ }
11
- let(:contained_in_jsonb_regex) { /\"people\"\.\"jsonb_data\" <@ '\{"nickname\":\"Dan"}'/ }
12
- let(:contains_equals_regex) { /\"people\"\.\"ip\" >>= '127.0.0.1'/ }
13
- let(:equality_regex) { /\"people\"\.\"tags\" = '\{"?working"?\}'/ }
6
+ let(:contains_array_regex) { /\"users\"\.\"tag_ids\" @> '\{1,2\}'/ }
7
+ let(:contains_hstore_regex) { /\"users\"\.\"data\" @> '\"nickname\"=>"Dan"'/ }
8
+ let(:contains_jsonb_regex) { /\"users\"\.\"jsonb_data\" @> '\{"nickname\":\"Dan"}'/ }
9
+ let(:contained_in_array_regex) { /\"users\"\.\"tag_ids\" <@ '\{1,2\}'/ }
10
+ let(:contained_in_hstore_regex) { /\"users\"\.\"data\" <@ '\"nickname\"=>"Dan"'/ }
11
+ let(:contained_in_jsonb_regex) { /\"users\"\.\"jsonb_data\" <@ '\{"nickname\":\"Dan"}'/ }
12
+ let(:contains_equals_regex) { /\"users\"\.\"ip\" >>= '127.0.0.1'/ }
13
+ let(:equality_regex) { /\"users\"\.\"tags\" = '\{"?working"?\}'/ }
14
14
 
15
15
  describe ".where.contains(:column => value)" do
16
16
  it "generates the appropriate where clause for array columns" do
17
- query = Person.where.contains(tag_ids: [1, 2]).to_sql
17
+ query = User.where.contains(tag_ids: [1, 2]).to_sql
18
18
  expect(query).to match_regex(contains_array_regex)
19
19
  end
20
20
 
21
21
  it "generates the appropriate where clause for hstore columns" do
22
- query = Person.where.contains(data: { nickname: "Dan" }).to_sql
22
+ query = User.where.contains(data: { nickname: "Dan" }).to_sql
23
23
  expect(query).to match_regex(contains_hstore_regex)
24
24
  end
25
25
 
26
26
  it "generates the appropriate where clause for jsonb columns" do
27
- query = Person.where.contains(jsonb_data: { nickname: "Dan" }).to_sql
27
+ query = User.where.contains(jsonb_data: { nickname: "Dan" }).to_sql
28
28
  expect(query).to match_regex(contains_jsonb_regex)
29
29
  end
30
30
 
31
31
  it "generates the appropriate where clause for hstore columns on joins" do
32
- query = Tag.joins(:person).where.contains(people: { data: { nickname: "Dan" } }).to_sql
32
+ query = Tag.joins(:user).where.contains(users: { data: { nickname: "Dan" } }).to_sql
33
33
  expect(query).to match_regex(contains_hstore_regex)
34
34
  end
35
35
 
36
36
  it "allows chaining" do
37
- query = Person.where.contains(tag_ids: [1, 2]).where(tags: ["working"]).to_sql
37
+ query = User.where.contains(tag_ids: [1, 2]).where(tags: ["working"]).to_sql
38
38
  expect(query).to match_regex(contains_array_regex)
39
39
  expect(query).to match_regex(equality_regex)
40
40
  end
41
41
 
42
42
  it "generates the appropriate where clause for array columns on joins" do
43
- query = Tag.joins(:person).where.contains(people: { tag_ids: [1, 2] }).to_sql
43
+ query = Tag.joins(:user).where.contains(users: { tag_ids: [1, 2] }).to_sql
44
44
  expect(query).to match_regex(contains_array_regex)
45
45
  end
46
46
  end
@@ -3,14 +3,14 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe "Either Methods SQL Queries" do
6
- let(:contains_array_regex) { /\"people\"\.\"tag_ids\" @> '\{1,2\}'/ }
7
- let(:profile_l_outer_join) { /LEFT OUTER JOIN \"profile_ls\" ON \"profile_ls\".\"person_id\" = \"people\".\"id\"/ }
8
- let(:profile_r_outer_join) { /LEFT OUTER JOIN \"profile_rs\" ON \"profile_rs\".\"person_id\" = \"people\".\"id\"/ }
6
+ let(:contains_array_regex) { /\"users\"\.\"tag_ids\" @> '\{1,2\}'/ }
7
+ let(:profile_l_outer_join) { /LEFT OUTER JOIN \"profile_ls\" ON \"profile_ls\".\"user_id\" = \"users\".\"id\"/ }
8
+ let(:profile_r_outer_join) { /LEFT OUTER JOIN \"profile_rs\" ON \"profile_rs\".\"user_id\" = \"users\".\"id\"/ }
9
9
  let(:where_join_case) do
10
- "WHERE ((CASE WHEN profile_ls.person_id IS NULL"\
11
- " THEN profile_rs.person_id"\
12
- " ELSE profile_ls.person_id END) "\
13
- "= people.id)"
10
+ "WHERE ((CASE WHEN profile_ls.user_id IS NULL"\
11
+ " THEN profile_rs.user_id"\
12
+ " ELSE profile_ls.user_id END) "\
13
+ "= users.id)"
14
14
  end
15
15
 
16
16
  let(:order_case) do
@@ -22,20 +22,20 @@ RSpec.describe "Either Methods SQL Queries" do
22
22
 
23
23
  describe ".either_join/2" do
24
24
  it "Should contain outer joins on the provided relationships" do
25
- query = Person.either_join(:profile_l, :profile_r).to_sql
25
+ query = User.either_join(:profile_l, :profile_r).to_sql
26
26
  expect(query).to match_regex(profile_l_outer_join)
27
27
  expect(query).to match_regex(profile_r_outer_join)
28
28
  end
29
29
 
30
30
  it "Should contain a case statement that will conditionally alternative between tables" do
31
- query = Person.either_join(:profile_l, :profile_r).to_sql
31
+ query = User.either_join(:profile_l, :profile_r).to_sql
32
32
  expect(query).to include(where_join_case)
33
33
  end
34
34
  end
35
35
 
36
36
  describe ".either_order/2" do
37
- let(:ascended_order) { Person.either_order(:asc, profile_l: :likes, profile_r: :dislikes).to_sql }
38
- let(:descended_order) { Person.either_order(:desc, profile_l: :likes, profile_r: :dislikes).to_sql }
37
+ let(:ascended_order) { User.either_order(:asc, profile_l: :likes, profile_r: :dislikes).to_sql }
38
+ let(:descended_order) { User.either_order(:desc, profile_l: :likes, profile_r: :dislikes).to_sql }
39
39
 
40
40
  it "Should contain outer joins on the provided relationships" do
41
41
  expect(ascended_order).to match_regex(profile_l_outer_join)
@@ -4,39 +4,69 @@ require "spec_helper"
4
4
 
5
5
  RSpec.describe "JSON Methods SQL Queries" do
6
6
  let(:single_with) { /^WITH .all_others. AS(?!.*WITH \w?)/mi }
7
- let(:override_with) { /^WITH .all_others. AS \(.+WHERE .people.\..id. = 10\)/mi }
7
+ let(:override_with) { /^WITH .all_others. AS \(.+WHERE .users.\..id. = 10\)/mi }
8
8
 
9
9
  describe ".select_row_to_json" do
10
10
  context "when a subquery contains a CTE table" do
11
- let(:cte_person) { Person.with(all_others: Person.where.not(id: 1)).where(id: 2) }
11
+ let(:cte_person) { User.with(all_others: User.where.not(id: 1)).where(id: 2) }
12
12
 
13
13
  it "should push the CTE to the callee's level" do
14
- query = Person.select_row_to_json(cte_person, as: :results).to_sql
14
+ query = User.select_row_to_json(cte_person, as: :results).to_sql
15
15
  expect(query).to match_regex(single_with)
16
16
  end
17
17
 
18
18
  it "should favor the parents CTE table if names collide" do
19
- query = Person.with(all_others: Person.where(id: 10))
19
+ query = User.with(all_others: User.where(id: 10))
20
20
  query = query.select_row_to_json(cte_person, as: :results).to_sql
21
21
 
22
22
  expect(query).to match_regex(single_with)
23
23
  expect(query).to match_regex(override_with)
24
24
  end
25
25
  end
26
+
27
+ context "When adding cast_with: option" do
28
+ it "should wrap the row_to_json expression with to_jsonb" do
29
+ query = User.select_row_to_json(User.where(id: 10), cast_with: :to_jsonb, key: :convert_this, as: :results).to_sql
30
+ puts query
31
+ expect(query).to match_regex(/SELECT \(SELECT TO_JSONB\(ROW_TO_JSON\("convert_this"\)\) FROM \(.+\).+\) AS "results"/)
32
+ end
33
+
34
+ it "should cast object to an array" do
35
+ query = User.select_row_to_json(User.where(id: 10), cast_with: :array, key: :convert_this, as: :results).to_sql
36
+ expect(query).to match_regex(/SELECT \(ARRAY\(SELECT ROW_TO_JSON\("convert_this"\) FROM \(.+\).+\)\) AS "results"/)
37
+ end
38
+
39
+ it "should cast object to an aggregated array" do
40
+ query = User.select_row_to_json(User.where(id: 10), cast_with: :array_agg, key: :convert_this, as: :results).to_sql
41
+ expect(query).to match_regex(/SELECT \(ARRAY_AGG\(\(SELECT TO_JSONB\(ROW_TO_JSON\("convert_this"\)\) FROM \(.+\).+\)\)\) AS "results"/)
42
+ end
43
+
44
+ context "When multiple cast_with options are used" do
45
+ it "should cast query with to_jsonb and as an Array" do
46
+ query = User.select_row_to_json(User.where(id: 10), cast_with: [:to_jsonb, :array], key: :convert_this, as: :results).to_sql
47
+ expect(query).to match_regex(/SELECT \(ARRAY\(SELECT TO_JSONB\(ROW_TO_JSON\("convert_this"\)\) FROM \(.+\).+\)\) AS "results"/)
48
+ end
49
+
50
+ it "should cast query as a distinct Aggregated Array" do
51
+ query = User.select_row_to_json(User.where(id: 10), cast_with: [:array_agg, :distinct], key: :convert_this, as: :results).to_sql
52
+ expect(query).to match_regex(/SELECT \(ARRAY_AGG\(DISTINCT \(SELECT TO_JSONB\(ROW_TO_JSON\("convert_this"\)\) FROM \(.+\).+\)\)\) AS "results"/)
53
+ end
54
+ end
55
+ end
26
56
  end
27
57
 
28
58
  describe ".json_build_object" do
29
59
  context "when a subquery contains a CTE table" do
30
- let(:cte_person) { Person.with(all_others: Person.where.not(id: 1)).where(id: 2) }
60
+ let(:cte_person) { User.with(all_others: User.where.not(id: 1)).where(id: 2) }
31
61
 
32
62
  it "should push the CTE to the callee's level" do
33
- query = Person.json_build_object(:peoples, cte_person).to_sql
63
+ query = User.json_build_object(:userss, cte_person).to_sql
34
64
  expect(query).to match_regex(single_with)
35
65
  end
36
66
 
37
67
  it "should favor the parents CTE table if names collide" do
38
- query = Person.with(all_others: Person.where(id: 10))
39
- query = query.json_build_object(:peoples, cte_person).to_sql
68
+ query = User.with(all_others: User.where(id: 10))
69
+ query = query.json_build_object(:users, cte_person).to_sql
40
70
 
41
71
  expect(query).to match_regex(single_with)
42
72
  expect(query).to match_regex(override_with)
@@ -3,27 +3,27 @@
3
3
  require "spec_helper"
4
4
 
5
5
  RSpec.describe "Union SQL Queries" do
6
- let(:person) { Person.where(id: 1) }
7
- let(:other_person) { Person.where("id = 2") }
6
+ let(:user) { User.where(id: 1) }
7
+ let(:other_user) { User.where("id = 2") }
8
8
 
9
9
  shared_examples_for "unions" do
10
- it { is_expected.to eq("( (#{person.to_sql}) #{described_union} (#{other_person.to_sql}) )") }
10
+ it { is_expected.to eq("( (#{user.to_sql}) #{described_union} (#{other_user.to_sql}) )") }
11
11
  end
12
12
 
13
13
  shared_examples_for "piping nest CTE tables" do
14
- let(:cte_person) { Person.with(all_others: Person.where.not(id: 1)).where(id: 2) }
14
+ let(:cte_user) { User.with(all_others: User.where.not(id: 1)).where(id: 2) }
15
15
  let(:method) { raise "Required to override this method!" }
16
16
  let(:single_with) { /^WITH .all_others. AS(?!.*WITH \w?)/mi }
17
- let(:override_with) { /^WITH .all_others. AS \(.+WHERE .people.\..id. = 10\)/mi }
17
+ let(:override_with) { /^WITH .all_others. AS \(.+WHERE .users.\..id. = 10\)/mi }
18
18
 
19
19
  it "should push the CTE to the callee's level" do
20
- query = Person.send(method.to_sym, cte_person, other_person).to_sql
20
+ query = User.send(method.to_sym, cte_user, other_user).to_sql
21
21
  expect(query).to match_regex(single_with)
22
22
  end
23
23
 
24
24
  it "should favor the parents CTE table if names collide" do
25
- query = Person.with(all_others: Person.where(id: 10))
26
- query = query.send(method.to_sym, cte_person, other_person).to_sql
25
+ query = User.with(all_others: User.where(id: 10))
26
+ query = query.send(method.to_sym, cte_user, other_user).to_sql
27
27
 
28
28
  expect(query).to match_regex(single_with)
29
29
  expect(query).to match_regex(override_with)
@@ -32,7 +32,7 @@ RSpec.describe "Union SQL Queries" do
32
32
 
33
33
  describe ".union" do
34
34
  let!(:described_union) { "UNION" }
35
- subject(:described_method) { Person.union(person, other_person).to_union_sql }
35
+ subject(:described_method) { User.union(user, other_user).to_union_sql }
36
36
  it_behaves_like "unions"
37
37
  it_behaves_like "piping nest CTE tables" do
38
38
  let!(:method) { :union }
@@ -41,7 +41,7 @@ RSpec.describe "Union SQL Queries" do
41
41
 
42
42
  describe ".union.all" do
43
43
  let!(:described_union) { "UNION ALL" }
44
- subject(:described_method) { Person.union.all(person, other_person).to_union_sql }
44
+ subject(:described_method) { User.union.all(user, other_user).to_union_sql }
45
45
  it_behaves_like "unions"
46
46
  it_behaves_like "piping nest CTE tables" do
47
47
  let!(:method) { :union_all }
@@ -50,7 +50,7 @@ RSpec.describe "Union SQL Queries" do
50
50
 
51
51
  describe ".union.except" do
52
52
  let!(:described_union) { "EXCEPT" }
53
- subject(:described_method) { Person.union.except(person, other_person).to_union_sql }
53
+ subject(:described_method) { User.union.except(user, other_user).to_union_sql }
54
54
  it_behaves_like "unions"
55
55
  it_behaves_like "piping nest CTE tables" do
56
56
  let!(:method) { :union_except }
@@ -59,7 +59,7 @@ RSpec.describe "Union SQL Queries" do
59
59
 
60
60
  describe "union.intersect" do
61
61
  let!(:described_union) { "INTERSECT" }
62
- subject(:described_method) { Person.union.intersect(person, other_person).to_union_sql }
62
+ subject(:described_method) { User.union.intersect(user, other_user).to_union_sql }
63
63
  it_behaves_like "unions"
64
64
  it_behaves_like "piping nest CTE tables" do
65
65
  let!(:method) { :union_intersect }
@@ -69,28 +69,28 @@ RSpec.describe "Union SQL Queries" do
69
69
  describe "union.as" do
70
70
  context "when a union.as has been called" do
71
71
  subject(:described_method) do
72
- Person.select("happy_people.id").union(person, other_person).union.as(:happy_people).to_sql
72
+ User.select("happy_users.id").union(user, other_user).union.as(:happy_users).to_sql
73
73
  end
74
74
 
75
- it "should alias the union from clause to 'happy_people'" do
76
- expect(described_method).to match_regex(/FROM \(+.+\) UNION \(.+\)+ happy_people$/)
77
- expect(described_method).to match_regex(/^SELECT happy_people\.id FROM.+happy_people$/)
75
+ it "should alias the union from clause to 'happy_users'" do
76
+ expect(described_method).to match_regex(/FROM \(+.+\) UNION \(.+\)+ happy_users$/)
77
+ expect(described_method).to match_regex(/^SELECT happy_users\.id FROM.+happy_users$/)
78
78
  end
79
79
  end
80
80
 
81
81
  context "when user.as hasn't been called" do
82
- subject(:described_method) { Person.select(:id).union(person, other_person).to_sql }
82
+ subject(:described_method) { User.select(:id).union(user, other_user).to_sql }
83
83
 
84
84
  it "should retain the actual class calling table name as the union alias" do
85
- expect(described_method).to match_regex(/FROM \(+.+\) UNION \(.+\)+ people$/)
86
- expect(described_method).to match_regex(/^SELECT \"people\"\.\"id\" FROM.+people$/)
85
+ expect(described_method).to match_regex(/FROM \(+.+\) UNION \(.+\)+ users$/)
86
+ expect(described_method).to match_regex(/^SELECT \"users\"\.\"id\" FROM.+users$/)
87
87
  end
88
88
  end
89
89
  end
90
90
 
91
91
  describe "union.order" do
92
92
  context "when rendering with .to_union_sql" do
93
- subject(:described_method) { Person.union(person, other_person).union.order(:id, name: :desc).to_union_sql }
93
+ subject(:described_method) { User.union(user, other_user).union.order(:id, name: :desc).to_union_sql }
94
94
 
95
95
  it "Should append an 'ORDER BY' to the end of the union statements" do
96
96
  expect(described_method).to match_regex(/^\(+.+\) UNION \(.+\) \) ORDER BY id, name DESC$/)
@@ -98,10 +98,10 @@ RSpec.describe "Union SQL Queries" do
98
98
  end
99
99
 
100
100
  context "when rendering with .to_sql" do
101
- subject(:described_method) { Person.union(person, other_person).union.order(:id, name: :desc).to_sql }
101
+ subject(:described_method) { User.union(user, other_user).union.order(:id, name: :desc).to_sql }
102
102
 
103
103
  it "Should append an 'ORDER BY' to the end of the union statements" do
104
- expect(described_method).to match_regex(/FROM \(+.+\) UNION \(.+\) \) ORDER BY id, name DESC\) people$/)
104
+ expect(described_method).to match_regex(/FROM \(+.+\) UNION \(.+\) \) ORDER BY id, name DESC\) users$/)
105
105
  end
106
106
  end
107
107
 
@@ -109,11 +109,11 @@ RSpec.describe "Union SQL Queries" do
109
109
  let(:query_regex) { /(?<=\)\s(ORDER BY)) id/ }
110
110
 
111
111
  it "should only append an order by to the very end of a union statements" do
112
- query = Person.union.order(id: :asc, tags: :desc)
113
- .union(person.order(id: :asc, tags: :desc))
114
- .union(person.order(:id, :tags))
115
- .union(other_person.order(id: :desc, tags: :desc))
116
- .to_union_sql
112
+ query = User.union.order(id: :asc, tags: :desc)
113
+ .union(user.order(id: :asc, tags: :desc))
114
+ .union(user.order(:id, :tags))
115
+ .union(other_user.order(id: :desc, tags: :desc))
116
+ .to_union_sql
117
117
 
118
118
  index = query.index(query_regex)
119
119
  expect(index).to be_truthy