active_record_extended 1.4.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -7
- data/lib/active_record_extended/active_record.rb +1 -10
- data/lib/active_record_extended/active_record/relation_patch.rb +16 -1
- data/lib/active_record_extended/arel.rb +1 -0
- data/lib/active_record_extended/arel/nodes.rb +22 -21
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/query_methods/any_of.rb +5 -4
- data/lib/active_record_extended/query_methods/either.rb +1 -1
- data/lib/active_record_extended/query_methods/inet.rb +6 -2
- data/lib/active_record_extended/query_methods/json.rb +13 -16
- data/lib/active_record_extended/query_methods/select.rb +11 -10
- data/lib/active_record_extended/query_methods/unionize.rb +10 -4
- data/lib/active_record_extended/query_methods/where_chain.rb +14 -6
- data/lib/active_record_extended/query_methods/window.rb +4 -3
- data/lib/active_record_extended/query_methods/with_cte.rb +102 -35
- data/lib/active_record_extended/utilities/order_by.rb +9 -28
- data/lib/active_record_extended/utilities/support.rb +8 -15
- data/lib/active_record_extended/version.rb +1 -1
- data/spec/query_methods/any_of_spec.rb +2 -2
- 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/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 +3 -3
- data/spec/sql_inspections/json_sql_spec.rb +0 -1
- data/spec/sql_inspections/unionize_sql_spec.rb +2 -2
- data/spec/sql_inspections/window_sql_spec.rb +12 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +30 -1
- metadata +18 -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
@@ -54,10 +54,10 @@ module ActiveRecordExtended
|
|
54
54
|
elsif column.try(:array)
|
55
55
|
Arel::Nodes::ContainsArray.new(arel.left, arel.right)
|
56
56
|
else
|
57
|
-
raise ArgumentError
|
57
|
+
raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
|
58
58
|
end
|
59
59
|
else
|
60
|
-
raise ArgumentError
|
60
|
+
raise ArgumentError.new("Invalid argument for .where.contains(), got #{arel.class}")
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -88,7 +88,7 @@ module ActiveRecordExtended
|
|
88
88
|
when Arel::Nodes::Equality
|
89
89
|
Arel::Nodes::Equality.new(arel.right, Arel::Nodes::NamedFunction.new(function_name, [arel.left]))
|
90
90
|
else
|
91
|
-
raise ArgumentError
|
91
|
+
raise ArgumentError.new("Invalid argument for .where.#{function_name.downcase}(), got #{arel.class}")
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
@@ -99,10 +99,18 @@ module ActiveRecordExtended
|
|
99
99
|
when Arel::Nodes::In, Arel::Nodes::Equality
|
100
100
|
arel_node_class.new(arel.left, arel.right)
|
101
101
|
else
|
102
|
-
raise ArgumentError
|
102
|
+
raise ArgumentError.new("Invalid argument for .where.#{method}(), got #{arel.class}")
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
106
|
+
|
107
|
+
def build_where_clause_for(scope, opts, rest)
|
108
|
+
if ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR == 1
|
109
|
+
scope.send(:build_where_clause, opts, rest)
|
110
|
+
else
|
111
|
+
scope.send(:where_clause_factory).build(opts, rest)
|
112
|
+
end
|
113
|
+
end
|
106
114
|
end
|
107
115
|
end
|
108
116
|
|
@@ -112,9 +120,9 @@ module ActiveRecord
|
|
112
120
|
prepend ActiveRecordExtended::WhereChain
|
113
121
|
|
114
122
|
def build_where_chain(opts, rest, &block)
|
115
|
-
where_clause = @scope
|
123
|
+
where_clause = build_where_clause_for(@scope, opts, rest)
|
116
124
|
@scope.tap do |scope|
|
117
|
-
scope.references!(PredicateBuilder.references(opts)) if opts.is_a?(Hash)
|
125
|
+
scope.references!(PredicateBuilder.references(opts.stringify_keys)) if opts.is_a?(Hash)
|
118
126
|
scope.where_clause += where_clause.modified_predicates(&block)
|
119
127
|
end
|
120
128
|
end
|
@@ -16,7 +16,7 @@ module ActiveRecordExtended
|
|
16
16
|
@scope.window_values! << {
|
17
17
|
window_name: to_arel_sql(@window_name),
|
18
18
|
partition_by: flatten_to_sql(partitions),
|
19
|
-
order_by: order_by_expression(order_by)
|
19
|
+
order_by: order_by_expression(order_by)
|
20
20
|
}
|
21
21
|
|
22
22
|
@scope
|
@@ -81,8 +81,9 @@ module ActiveRecordExtended
|
|
81
81
|
|
82
82
|
def build_windows(arel)
|
83
83
|
window_values.each do |window_value|
|
84
|
-
window = arel.window(window_value[:window_name])
|
85
|
-
window.
|
84
|
+
window = arel.window(window_value[:window_name])
|
85
|
+
window = window.partition(window_value[:partition_by]) if window_value[:partition_by].present?
|
86
|
+
window.order(window_value[:order_by]) if window_value[:order_by]
|
86
87
|
end
|
87
88
|
end
|
88
89
|
end
|
@@ -3,78 +3,145 @@
|
|
3
3
|
module ActiveRecordExtended
|
4
4
|
module QueryMethods
|
5
5
|
module WithCTE
|
6
|
-
class
|
6
|
+
class WithCTE
|
7
|
+
include ::ActiveRecordExtended::Utilities::Support
|
8
|
+
include Enumerable
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def_delegators :@with_values, :empty?, :blank?, :present?
|
12
|
+
attr_reader :with_values, :with_keys
|
13
|
+
|
14
|
+
# @param [ActiveRecord::Relation] scope
|
7
15
|
def initialize(scope)
|
8
16
|
@scope = scope
|
17
|
+
reset!
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Enumerable] Returns the order for which CTE's were imported as.
|
21
|
+
def each
|
22
|
+
return to_enum(:each) unless block_given?
|
23
|
+
|
24
|
+
with_keys.each do |key|
|
25
|
+
yield(key, with_values[key])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
alias each_pair each
|
29
|
+
|
30
|
+
# @param [Hash, WithCTE] value
|
31
|
+
def with_values=(value)
|
32
|
+
reset!
|
33
|
+
pipe_cte_with!(value)
|
9
34
|
end
|
10
35
|
|
11
|
-
|
36
|
+
# @param [Hash, WithCTE] value
|
37
|
+
def pipe_cte_with!(value)
|
38
|
+
return if value.nil? || value.empty?
|
39
|
+
|
40
|
+
value.each_pair do |name, expression|
|
41
|
+
sym_name = name.to_sym
|
42
|
+
next if with_values.key?(sym_name)
|
43
|
+
|
44
|
+
# Ensure we follow FIFO pattern.
|
45
|
+
# If the parent has similar CTE alias keys, we want to favor the parent's expressions over its children's.
|
46
|
+
if expression.is_a?(ActiveRecord::Relation) && expression.with_values?
|
47
|
+
pipe_cte_with!(expression.cte)
|
48
|
+
expression.cte.reset!
|
49
|
+
end
|
50
|
+
|
51
|
+
@with_keys |= [sym_name]
|
52
|
+
@with_values[sym_name] = expression
|
53
|
+
end
|
54
|
+
|
55
|
+
value.reset! if value.is_a?(WithCTE)
|
56
|
+
end
|
57
|
+
|
58
|
+
def reset!
|
59
|
+
@with_keys = []
|
60
|
+
@with_values = {}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class WithChain
|
65
|
+
# @param [ActiveRecord::Relation] scope
|
66
|
+
def initialize(scope)
|
67
|
+
@scope = scope
|
68
|
+
@scope.cte ||= WithCTE.new(scope)
|
69
|
+
end
|
70
|
+
|
71
|
+
# @param [Hash, WithCTE] args
|
72
|
+
def recursive(args)
|
12
73
|
@scope.tap do |scope|
|
13
|
-
scope.with_values += args
|
14
74
|
scope.recursive_value = true
|
75
|
+
scope.cte.pipe_cte_with!(args)
|
15
76
|
end
|
16
77
|
end
|
17
78
|
end
|
18
79
|
|
19
|
-
|
20
|
-
|
80
|
+
# @return [WithCTE]
|
81
|
+
def cte
|
82
|
+
@values[:cte]
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param [WithCTE] cte
|
86
|
+
def cte=(cte)
|
87
|
+
raise TypeError.new("Must be a WithCTE class type") unless cte.is_a?(WithCTE)
|
88
|
+
|
89
|
+
@values[:cte] = cte
|
21
90
|
end
|
22
91
|
|
92
|
+
# @return [Boolean]
|
23
93
|
def with_values?
|
24
|
-
!(
|
94
|
+
!(cte.nil? || cte.empty?)
|
25
95
|
end
|
26
96
|
|
97
|
+
# @param [Hash, WithCTE] values
|
27
98
|
def with_values=(values)
|
28
|
-
|
99
|
+
cte.with_values = values
|
29
100
|
end
|
30
101
|
|
102
|
+
# @param [Boolean] value
|
31
103
|
def recursive_value=(value)
|
32
104
|
raise ImmutableRelation if @loaded
|
105
|
+
|
33
106
|
@values[:recursive] = value
|
34
107
|
end
|
35
108
|
|
36
|
-
|
37
|
-
|
109
|
+
# @return [Boolean]
|
110
|
+
def recursive_value?
|
111
|
+
!(!@values[:recursive])
|
38
112
|
end
|
39
|
-
alias recursive_value? recursive_value
|
40
113
|
|
114
|
+
# @param [Hash, WithCTE] opts
|
41
115
|
def with(opts = :chain, *rest)
|
42
116
|
return WithChain.new(spawn) if opts == :chain
|
117
|
+
|
43
118
|
opts.blank? ? self : spawn.with!(opts, *rest)
|
44
119
|
end
|
45
120
|
|
46
|
-
|
121
|
+
# @param [Hash, WithCTE] opts
|
122
|
+
def with!(opts = :chain, *_rest)
|
47
123
|
return WithChain.new(self) if opts == :chain
|
48
|
-
self.with_values += [opts] + rest
|
49
|
-
self
|
50
|
-
end
|
51
124
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
case expression
|
56
|
-
when String
|
57
|
-
Arel.sql("(#{expression})")
|
58
|
-
when ActiveRecord::Relation, Arel::SelectManager
|
59
|
-
Arel.sql("(#{expression.to_sql})")
|
60
|
-
end
|
61
|
-
next if select.nil?
|
62
|
-
Arel::Nodes::As.new(Arel.sql(PG::Connection.quote_ident(name.to_s)), select)
|
125
|
+
tap do |scope|
|
126
|
+
scope.cte ||= WithCTE.new(self)
|
127
|
+
scope.cte.pipe_cte_with!(opts)
|
63
128
|
end
|
64
129
|
end
|
65
130
|
|
66
131
|
def build_with(arel)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end.compact
|
132
|
+
return unless with_values?
|
133
|
+
|
134
|
+
cte_statements = cte.map do |name, expression|
|
135
|
+
grouped_expression = cte.generate_grouping(expression)
|
136
|
+
cte_name = cte.to_arel_sql(cte.double_quote(name.to_s))
|
137
|
+
Arel::Nodes::As.new(cte_name, grouped_expression)
|
138
|
+
end
|
75
139
|
|
76
|
-
|
77
|
-
|
140
|
+
if recursive_value?
|
141
|
+
arel.with(:recursive, cte_statements)
|
142
|
+
else
|
143
|
+
arel.with(cte_statements)
|
144
|
+
end
|
78
145
|
end
|
79
146
|
end
|
80
147
|
end
|
@@ -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
|
|
@@ -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,6 +152,7 @@ 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)
|
155
|
+
|
163
156
|
generate_grouping(arel_or_rel_query)
|
164
157
|
end
|
165
158
|
|
@@ -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)
|
@@ -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)
|