active_record_extended 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +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)
|