active_record_extended 1.2.0 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +85 -7
- data/lib/active_record_extended/active_record.rb +2 -11
- data/lib/active_record_extended/active_record/relation_patch.rb +21 -4
- data/lib/active_record_extended/arel.rb +1 -0
- data/lib/active_record_extended/arel/nodes.rb +24 -21
- data/lib/active_record_extended/arel/predications.rb +3 -2
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +1 -1
- data/lib/active_record_extended/query_methods/any_of.rb +5 -4
- data/lib/active_record_extended/query_methods/either.rb +2 -1
- data/lib/active_record_extended/query_methods/inet.rb +6 -2
- data/lib/active_record_extended/query_methods/json.rb +14 -17
- data/lib/active_record_extended/query_methods/select.rb +13 -12
- data/lib/active_record_extended/query_methods/unionize.rb +13 -7
- data/lib/active_record_extended/query_methods/where_chain.rb +17 -8
- data/lib/active_record_extended/query_methods/window.rb +93 -0
- data/lib/active_record_extended/query_methods/with_cte.rb +104 -37
- data/lib/active_record_extended/utilities/order_by.rb +11 -30
- data/lib/active_record_extended/utilities/support.rb +21 -18
- data/lib/active_record_extended/version.rb +1 -1
- data/spec/query_methods/any_of_spec.rb +2 -2
- data/spec/query_methods/either_spec.rb +11 -0
- data/spec/query_methods/json_spec.rb +5 -5
- data/spec/query_methods/select_spec.rb +13 -13
- data/spec/query_methods/unionize_spec.rb +5 -5
- data/spec/query_methods/window_spec.rb +51 -0
- data/spec/query_methods/with_cte_spec.rb +12 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/sql_inspections/any_of_sql_spec.rb +2 -2
- data/spec/sql_inspections/contains_sql_queries_spec.rb +8 -8
- data/spec/sql_inspections/either_sql_spec.rb +19 -3
- data/spec/sql_inspections/json_sql_spec.rb +7 -1
- data/spec/sql_inspections/unionize_sql_spec.rb +2 -2
- data/spec/sql_inspections/window_sql_spec.rb +98 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +30 -1
- data/spec/support/models.rb +18 -0
- metadata +23 -20
- data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
- data/lib/active_record_extended/patch/5_0/regex_match.rb +0 -10
@@ -24,8 +24,8 @@ module ActiveRecordExtended
|
|
24
24
|
return false unless order_by && order_by.presence.present?
|
25
25
|
|
26
26
|
to_ordered_table_path(order_by)
|
27
|
-
.tap
|
28
|
-
.tap(
|
27
|
+
.tap { |order_args| process_ordering_arguments!(order_args) }
|
28
|
+
.tap { |order_args| scope_preprocess_order_args(order_args) }
|
29
29
|
end
|
30
30
|
|
31
31
|
#
|
@@ -60,34 +60,15 @@ module ActiveRecordExtended
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
ordering_args.flatten!
|
73
|
-
ordering_args.compact!
|
74
|
-
ordering_args.map! do |arg|
|
75
|
-
next to_arel_sql(arg) unless arg.is_a?(Hash) # ActiveRecord will reflect if an argument is a symbol
|
76
|
-
arg.each_with_object([]) do |(field, dir), ordering_object|
|
77
|
-
ordering_object << to_arel_sql(field).send(dir.to_s.downcase)
|
78
|
-
end
|
79
|
-
end.flatten!
|
80
|
-
end
|
81
|
-
else
|
82
|
-
def process_ordering_arguments!(ordering_args)
|
83
|
-
ordering_args.flatten!
|
84
|
-
ordering_args.compact!
|
85
|
-
ordering_args.map! do |arg|
|
86
|
-
next to_arel_sql(arg) unless arg.is_a?(Hash) # ActiveRecord will reflect if an argument is a symbol
|
87
|
-
arg.each_with_object({}) do |(field, dir), ordering_obj|
|
88
|
-
# ActiveRecord will not reflect if the Hash keys are a `Arel::Nodes::SqlLiteral` klass
|
89
|
-
ordering_obj[to_arel_sql(field)] = dir.to_s.downcase
|
90
|
-
end
|
63
|
+
def process_ordering_arguments!(ordering_args)
|
64
|
+
ordering_args.flatten!
|
65
|
+
ordering_args.compact!
|
66
|
+
ordering_args.map! do |arg|
|
67
|
+
next to_arel_sql(arg) unless arg.is_a?(Hash) # ActiveRecord will reflect if an argument is a symbol
|
68
|
+
|
69
|
+
arg.each_with_object({}) do |(field, dir), ordering_obj|
|
70
|
+
# ActiveRecord will not reflect if the Hash keys are a `Arel::Nodes::SqlLiteral` klass
|
71
|
+
ordering_obj[to_arel_sql(field)] = dir.to_s.downcase
|
91
72
|
end
|
92
73
|
end
|
93
74
|
end
|
@@ -20,7 +20,7 @@ module ActiveRecordExtended
|
|
20
20
|
|
21
21
|
def flatten_safely(values, &block)
|
22
22
|
unless values.is_a?(Array)
|
23
|
-
values = yield values if
|
23
|
+
values = yield values if block
|
24
24
|
return [values]
|
25
25
|
end
|
26
26
|
|
@@ -30,7 +30,7 @@ module ActiveRecordExtended
|
|
30
30
|
# Applies aliases to the given query
|
31
31
|
# Ex: `SELECT * FROM users` => `(SELECT * FROM users) AS "members"`
|
32
32
|
def nested_alias_escape(query, alias_name)
|
33
|
-
sql_query =
|
33
|
+
sql_query = generate_grouping(query)
|
34
34
|
Arel::Nodes::As.new(sql_query, to_arel_sql(double_quote(alias_name)))
|
35
35
|
end
|
36
36
|
|
@@ -81,7 +81,7 @@ module ActiveRecordExtended
|
|
81
81
|
else
|
82
82
|
replicate_klass = from.respond_to?(:unscoped) ? from.unscoped : @scope.unscoped
|
83
83
|
replicate_klass.from(from.dup, reference_key)
|
84
|
-
end
|
84
|
+
end.unscope(:where)
|
85
85
|
end
|
86
86
|
|
87
87
|
# Will carry defined CTE tables from the nested sub-query and gradually pushes it up to the parents query stack
|
@@ -91,20 +91,12 @@ module ActiveRecordExtended
|
|
91
91
|
def pipe_cte_with!(subquery)
|
92
92
|
return self unless subquery.try(:with_values?)
|
93
93
|
|
94
|
-
|
95
|
-
subquery.with_values = nil # Remove nested queries with values
|
96
|
-
|
97
|
-
# Add subquery's CTE's to the parents query stack. (READ THE SPECIAL NOTE ABOVE!)
|
94
|
+
# Add subquery CTE's to the parents query stack. (READ THE SPECIAL NOTE ABOVE!)
|
98
95
|
if @scope.with_values?
|
99
|
-
|
100
|
-
with_hash = cte_ary.each_with_object(@scope.with_values.first) do |from_cte, hash|
|
101
|
-
hash.reverse_merge!(from_cte)
|
102
|
-
end
|
103
|
-
|
104
|
-
@scope.with_values = [with_hash]
|
96
|
+
@scope.cte.pipe_cte_with!(subquery.cte)
|
105
97
|
else
|
106
98
|
# Top level has no with values
|
107
|
-
@scope.with!(
|
99
|
+
@scope.with!(subquery.cte)
|
108
100
|
end
|
109
101
|
|
110
102
|
self
|
@@ -143,13 +135,13 @@ module ActiveRecordExtended
|
|
143
135
|
# Converts a potential subquery into a compatible Arel SQL node.
|
144
136
|
#
|
145
137
|
# Note:
|
146
|
-
# We convert relations to SQL to maintain compatibility with Rails 5.
|
138
|
+
# We convert relations to SQL to maintain compatibility with Rails 5.1.
|
147
139
|
# Only Rails 5.2+ maintains bound attributes in Arel, so its better to be safe then sorry.
|
148
|
-
# When we drop support for Rails 5.
|
140
|
+
# When we drop support for Rails 5.1, we then can then drop the '.to_sql' conversation
|
149
141
|
|
150
142
|
def to_arel_sql(value)
|
151
143
|
case value
|
152
|
-
when Arel::Node, Arel::Nodes::SqlLiteral, nil
|
144
|
+
when Arel::Nodes::Node, Arel::Nodes::SqlLiteral, nil
|
153
145
|
value
|
154
146
|
when ActiveRecord::Relation
|
155
147
|
Arel.sql(value.spawn.to_sql)
|
@@ -160,13 +152,24 @@ module ActiveRecordExtended
|
|
160
152
|
|
161
153
|
def group_when_needed(arel_or_rel_query)
|
162
154
|
return arel_or_rel_query unless needs_to_be_grouped?(arel_or_rel_query)
|
163
|
-
|
155
|
+
|
156
|
+
generate_grouping(arel_or_rel_query)
|
164
157
|
end
|
165
158
|
|
166
159
|
def needs_to_be_grouped?(query)
|
167
160
|
query.respond_to?(:to_sql) || (query.is_a?(String) && /^SELECT.+/i.match?(query))
|
168
161
|
end
|
169
162
|
|
163
|
+
def generate_grouping(expr)
|
164
|
+
::Arel::Nodes::Grouping.new(to_arel_sql(expr))
|
165
|
+
end
|
166
|
+
|
167
|
+
def generate_named_function(function_name, *args)
|
168
|
+
args.map! { |arg| to_arel_sql(arg) }
|
169
|
+
function_name = function_name.to_s.upcase
|
170
|
+
::Arel::Nodes::NamedFunction.new(to_arel_sql(function_name), args)
|
171
|
+
end
|
172
|
+
|
170
173
|
def key_generator
|
171
174
|
A_TO_Z_KEYS.sample
|
172
175
|
end
|
@@ -61,7 +61,7 @@ RSpec.describe "Active Record Any / None of Methods" do
|
|
61
61
|
it "Return matched records of a joined table on the parent level" do
|
62
62
|
query = Tag.joins(:user).where.any_of(
|
63
63
|
{ users: { personal_id: 1 } },
|
64
|
-
{ users: { personal_id: 3 } }
|
64
|
+
{ users: { personal_id: 3 } }
|
65
65
|
)
|
66
66
|
|
67
67
|
expect(query).to include(tag_one, tag_three)
|
@@ -120,7 +120,7 @@ RSpec.describe "Active Record Any / None of Methods" do
|
|
120
120
|
it "Return matched records of a joined table on the parent level" do
|
121
121
|
query = Tag.joins(:user).where.none_of(
|
122
122
|
{ users: { personal_id: 1 } },
|
123
|
-
{ users: { personal_id: 3 } }
|
123
|
+
{ users: { personal_id: 3 } }
|
124
124
|
)
|
125
125
|
|
126
126
|
expect(query).to include(tag_two)
|
@@ -23,6 +23,17 @@ RSpec.describe "Active Record Either Methods" do
|
|
23
23
|
expect(query).to_not include(three)
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
context "Through association .either_joins/2" do
|
28
|
+
let!(:four) { User.create! }
|
29
|
+
let!(:group) { Group.create!(users: [four]) }
|
30
|
+
|
31
|
+
it "Should only only return records that belong to profile L or has group" do
|
32
|
+
query = User.either_joins(:profile_l, :groups)
|
33
|
+
expect(query).to include(one, four)
|
34
|
+
expect(query).to_not include(three, two)
|
35
|
+
end
|
36
|
+
end
|
26
37
|
end
|
27
38
|
|
28
39
|
describe ".either_order/2" do
|
@@ -47,7 +47,7 @@ RSpec.describe "Active Record JSON methods" do
|
|
47
47
|
end
|
48
48
|
|
49
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,
|
50
|
+
query = User.select(:id).select_row_to_json(sub_query, key: :tag_row, as: :results, cast_with: :array)
|
51
51
|
expect(query.take.results).to be_a(Array).and(be_present)
|
52
52
|
expect(query.take.results.first).to be_a(Hash)
|
53
53
|
end
|
@@ -61,14 +61,14 @@ RSpec.describe "Active Record JSON methods" do
|
|
61
61
|
|
62
62
|
describe ".json_build_object" do
|
63
63
|
let(:sub_query) do
|
64
|
-
User.select_row_to_json(from: User.select(:id),
|
64
|
+
User.select_row_to_json(from: User.select(:id), cast_with: :array, as: :ids).where(id: user_one.id)
|
65
65
|
end
|
66
66
|
|
67
67
|
it "defaults the column alias if one is not provided" do
|
68
68
|
query = User.json_build_object(:personal, sub_query)
|
69
69
|
expect(query.size).to eq(1)
|
70
70
|
expect(query.take.results).to match(
|
71
|
-
"personal" => match("ids" => match_array([{ "id" => user_one.id }, { "id" => user_two.id }]))
|
71
|
+
"personal" => match("ids" => match_array([{ "id" => user_one.id }, { "id" => user_two.id }]))
|
72
72
|
)
|
73
73
|
end
|
74
74
|
|
@@ -98,7 +98,7 @@ RSpec.describe "Active Record JSON methods" do
|
|
98
98
|
:personal,
|
99
99
|
sub_query.where.not(id: user_one),
|
100
100
|
value: "COALESCE(array_agg(\"personal\"), '{}')",
|
101
|
-
as: :cool_dudes
|
101
|
+
as: :cool_dudes
|
102
102
|
)
|
103
103
|
|
104
104
|
expect(query.take.cool_dudes["personal"]).to be_a(Array).and(be_empty)
|
@@ -110,7 +110,7 @@ RSpec.describe "Active Record JSON methods" do
|
|
110
110
|
:personal,
|
111
111
|
sub_query.where.not(id: user_one),
|
112
112
|
value: "COALESCE(array_agg(personal), '{}')",
|
113
|
-
as: :cool_dudes
|
113
|
+
as: :cool_dudes
|
114
114
|
)
|
115
115
|
end.to output.to_stderr
|
116
116
|
end
|
@@ -15,7 +15,7 @@ RSpec.describe "Active Record Select Methods" do
|
|
15
15
|
it "can accept a subquery" do
|
16
16
|
subquery = Tag.select("count(*)").joins("JOIN users u ON tags.user_id = u.id").where("u.ip = users.ip")
|
17
17
|
query =
|
18
|
-
User.foster_select(tag_count: [subquery, cast_with: :array_agg, distinct: true])
|
18
|
+
User.foster_select(tag_count: [subquery, { cast_with: :array_agg, distinct: true }])
|
19
19
|
.joins(:hm_tags)
|
20
20
|
.group(:ip)
|
21
21
|
.take
|
@@ -25,8 +25,8 @@ RSpec.describe "Active Record Select Methods" do
|
|
25
25
|
|
26
26
|
it "can be ordered" do
|
27
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 }]
|
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
30
|
).take
|
31
31
|
|
32
32
|
expect(query.asc_ordered_numbers).to eq(number_set.to_a.sort)
|
@@ -50,10 +50,10 @@ RSpec.describe "Active Record Select Methods" do
|
|
50
50
|
|
51
51
|
it "will return a boolean expression" do
|
52
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]
|
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
57
|
).take
|
58
58
|
|
59
59
|
expect(query.truthly_expr).to be_truthy
|
@@ -67,19 +67,19 @@ RSpec.describe "Active Record Select Methods" do
|
|
67
67
|
before { 2.times.flat_map { |i| Array.new(2) { |j| User.create!(number: (i + 1) * j + 3) } } }
|
68
68
|
|
69
69
|
it "max" do
|
70
|
-
query = User.foster_select(max_num: [:number, cast_with: :max]).take
|
70
|
+
query = User.foster_select(max_num: [:number, { cast_with: :max }]).take
|
71
71
|
expect(query.max_num).to eq(5)
|
72
72
|
end
|
73
73
|
|
74
74
|
it "min" do
|
75
|
-
query = User.foster_select(max_num: [:number, cast_with: :min]).take
|
75
|
+
query = User.foster_select(max_num: [:number, { cast_with: :min }]).take
|
76
76
|
expect(query.max_num).to eq(3)
|
77
77
|
end
|
78
78
|
|
79
79
|
it "sum" do
|
80
80
|
query = User.foster_select(
|
81
|
-
num_sum: [:number, cast_with: :sum],
|
82
|
-
distinct_sum: [:number, cast_with: :sum, distinct: true]
|
81
|
+
num_sum: [:number, { cast_with: :sum }],
|
82
|
+
distinct_sum: [:number, { cast_with: :sum, distinct: true }]
|
83
83
|
).take
|
84
84
|
|
85
85
|
expect(query.num_sum).to eq(15)
|
@@ -88,8 +88,8 @@ RSpec.describe "Active Record Select Methods" do
|
|
88
88
|
|
89
89
|
it "avg" do
|
90
90
|
query = User.foster_select(
|
91
|
-
num_avg: [:number, cast_with: :avg],
|
92
|
-
distinct_avg: [:number, cast_with: :avg, distinct: true]
|
91
|
+
num_avg: [:number, { cast_with: :avg }],
|
92
|
+
distinct_avg: [:number, { cast_with: :avg, distinct: true }]
|
93
93
|
).take
|
94
94
|
|
95
95
|
expect(query.num_avg).to eq(3.75)
|
@@ -17,13 +17,13 @@ RSpec.describe "Active Record Union Methods" do
|
|
17
17
|
|
18
18
|
it "should raise an error if the select statements do not align" do
|
19
19
|
expect { misaligned_cmd.to_a }.to(
|
20
|
-
raise_error(ActiveRecord::StatementInvalid, /each [[:alpha:]]+ query must have the same number of columns/)
|
20
|
+
raise_error(ActiveRecord::StatementInvalid, /each [[:alpha:]]+ query must have the same number of columns/)
|
21
21
|
)
|
22
22
|
end
|
23
23
|
|
24
24
|
it "should raise an argument error if there are less then two union statements" do
|
25
25
|
expect { lacking_union_cmd.to_a }.to(
|
26
|
-
raise_error(ArgumentError, "You are required to provide 2 or more unions to join!")
|
26
|
+
raise_error(ArgumentError, "You are required to provide 2 or more unions to join!")
|
27
27
|
)
|
28
28
|
end
|
29
29
|
end
|
@@ -88,7 +88,7 @@ RSpec.describe "Active Record Union Methods" do
|
|
88
88
|
query =
|
89
89
|
User.union.intersect(
|
90
90
|
User.select(:id, "profile_ls.likes").joins(:profile_l).where(profile_ls: { likes: 100 }),
|
91
|
-
User.select(:id, "profile_ls.likes").joins(:profile_l).where("profile_ls.likes < 150")
|
91
|
+
User.select(:id, "profile_ls.likes").joins(:profile_l).where("profile_ls.likes < 150")
|
92
92
|
)
|
93
93
|
|
94
94
|
expect(query.pluck(:id)).to have_attributes(size: 1).and(eq([user_one_pl.id]))
|
@@ -129,7 +129,7 @@ RSpec.describe "Active Record Union Methods" do
|
|
129
129
|
query =
|
130
130
|
User.union.all(
|
131
131
|
User.where(id: user_one.id),
|
132
|
-
User.where(id: user_three.id)
|
132
|
+
User.where(id: user_three.id)
|
133
133
|
).order_union(id: :desc)
|
134
134
|
|
135
135
|
expect(query).to eq([user_three, user_one])
|
@@ -144,7 +144,7 @@ RSpec.describe "Active Record Union Methods" do
|
|
144
144
|
query =
|
145
145
|
User.union.intersect(
|
146
146
|
User.where("id < ?", user_three.id),
|
147
|
-
User.where("id >= ?", user_one.id)
|
147
|
+
User.where("id >= ?", user_one.id)
|
148
148
|
).order_union(id: :desc)
|
149
149
|
|
150
150
|
expect(query).to eq([user_two, user_one])
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Active Record Window Function Query Methods" do
|
6
|
+
let!(:user_one) { User.create! }
|
7
|
+
let!(:user_two) { User.create! }
|
8
|
+
|
9
|
+
let!(:tag_one) { Tag.create!(user: user_one, tag_number: 1) }
|
10
|
+
let!(:tag_two) { Tag.create!(user: user_two, tag_number: 2) }
|
11
|
+
|
12
|
+
let!(:tag_three) { Tag.create!(user: user_one, tag_number: 3) }
|
13
|
+
let!(:tag_four) { Tag.create!(user: user_two, tag_number: 4) }
|
14
|
+
|
15
|
+
let(:tag_group1) { [tag_one, tag_three] }
|
16
|
+
let(:tag_group2) { [tag_two, tag_four] }
|
17
|
+
|
18
|
+
describe ".window_select" do
|
19
|
+
context "when using ROW_NUMBER() ordered in asc" do
|
20
|
+
let(:base_query) do
|
21
|
+
Tag.define_window(:w).partition_by(:user_id, order_by: :tag_number).select(:id)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return tag_one with r_id 1 and tag_three with r_id 2" do
|
25
|
+
results = base_query.select_window(:row_number, over: :w, as: :r_id).group_by(&:id)
|
26
|
+
tag_group1.each.with_index { |tag, idx| expect(results[tag.id].first.r_id).to eq(idx + 1) }
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return tag_two with r_id 1 and tag_four with r_id 2" do
|
30
|
+
results = base_query.select_window(:row_number, over: :w, as: :r_id).group_by(&:id)
|
31
|
+
tag_group2.each.with_index { |tag, idx| expect(results[tag.id].first.r_id).to eq(idx + 1) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when using ROW_NUMBER() ordered in desc" do
|
36
|
+
let(:base_query) do
|
37
|
+
Tag.define_window(:w).partition_by(:user_id, order_by: { tag_number: :desc }).select(:id)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should return tag_one with r_id 2 and tag_three with r_id 1" do
|
41
|
+
results = base_query.select_window(:row_number, over: :w, as: :r_id).group_by(&:id)
|
42
|
+
tag_group1.reverse_each.with_index { |tag, idx| expect(results[tag.id].first.r_id).to eq(idx + 1) }
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should return tag_two with r_id 2 and tag_four with r_id 1" do
|
46
|
+
results = base_query.select_window(:row_number, over: :w, as: :r_id).group_by(&:id)
|
47
|
+
tag_group2.reverse_each.with_index { |tag, idx| expect(results[tag.id].first.r_id).to eq(idx + 1) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -3,8 +3,8 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
RSpec.describe "Active Record With CTE Query Methods" do
|
6
|
-
let!(:user_one)
|
7
|
-
let!(:user_two)
|
6
|
+
let!(:user_one) { User.create! }
|
7
|
+
let!(:user_two) { User.create! }
|
8
8
|
let!(:profile_one) { ProfileL.create!(user_id: user_one.id, likes: 200) }
|
9
9
|
let!(:profile_two) { ProfileL.create!(user_id: user_two.id, likes: 500) }
|
10
10
|
|
@@ -35,6 +35,16 @@ RSpec.describe "Active Record With CTE Query Methods" do
|
|
35
35
|
|
36
36
|
expect(query).to match_array([user_one])
|
37
37
|
end
|
38
|
+
|
39
|
+
it "should contain a unique list of ordered CTE keys when merging in multiple children" do
|
40
|
+
x = User.with(profile: ProfileL.where("likes < 300"))
|
41
|
+
y = User.with(profile: ProfileL.where("likes > 400"))
|
42
|
+
z = y.merge(x).joins("JOIN profile ON profile.id = users.id") # Y should reject X's CTE (FIFO)
|
43
|
+
query = User.with(my_profile: z).joins("JOIN my_profile ON my_profile.id = users.id")
|
44
|
+
|
45
|
+
expect(query.cte.with_keys).to eq([:profile, :my_profile])
|
46
|
+
expect(query).to match_array([user_two])
|
47
|
+
end
|
38
48
|
end
|
39
49
|
end
|
40
50
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -13,7 +13,7 @@ end
|
|
13
13
|
ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"])
|
14
14
|
|
15
15
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require File.expand_path(f) }
|
16
|
-
Dir["#{File.dirname(__FILE__)}/**/*examples.rb"].each { |f| require f }
|
16
|
+
Dir["#{File.dirname(__FILE__)}/**/*examples.rb"].sort.each { |f| require f }
|
17
17
|
|
18
18
|
RSpec.configure do |config|
|
19
19
|
# Enable flags like --only-failures and --next-failure
|
@@ -5,8 +5,8 @@ require "spec_helper"
|
|
5
5
|
RSpec.describe "Any / None of SQL Queries" do
|
6
6
|
let(:equal_query) { '"users"."personal_id" = 1' }
|
7
7
|
let(:or_query) { 'OR "users"."personal_id" = 2' }
|
8
|
-
let(:equal_or) { equal_query
|
9
|
-
let(:join_query) { /INNER JOIN
|
8
|
+
let(:equal_or) { "#{equal_query} #{or_query}" }
|
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
|