quo 1.0.0.alpha1 → 1.0.0.beta1
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/.standard.yml +1 -1
- data/Appraisals +4 -0
- data/Gemfile +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +222 -233
- data/gemfiles/rails_8.0.gemfile +15 -0
- data/lib/quo/composed_query.rb +154 -44
- data/lib/quo/query.rb +0 -1
- data/lib/quo/relation_backed_query.rb +51 -78
- data/lib/quo/relation_backed_query_specification.rb +154 -0
- data/lib/quo/version.rb +1 -1
- data/lib/quo.rb +1 -0
- data/sig/generated/quo/composed_query.rbs +45 -16
- data/sig/generated/quo/relation_backed_query.rbs +21 -44
- data/sig/generated/quo/relation_backed_query_specification.rbs +94 -0
- data/sig/generated/quo/testing/collection_backed_fake.rbs +13 -0
- data/sig/generated/quo/testing/relation_backed_fake.rbs +23 -0
- data/sig/literal.rbs +7 -0
- metadata +15 -12
data/lib/quo/composed_query.rb
CHANGED
@@ -14,44 +14,12 @@ module Quo
|
|
14
14
|
# @rbs joins: untyped
|
15
15
|
# @rbs return: singleton(Quo::ComposedQuery)
|
16
16
|
def composer(chosen_superclass, left_query_class, right_query_class, joins: nil)
|
17
|
-
|
18
|
-
raise ArgumentError, "Cannot compose #{left_query_class} and #{right_query_class}, are they both classes? If you want to use instances use `.merge_instances`"
|
19
|
-
end
|
20
|
-
props = {}
|
21
|
-
props.merge!(left_query_class.literal_properties.properties_index) if left_query_class < Quo::Query
|
22
|
-
props.merge!(right_query_class.literal_properties.properties_index) if right_query_class < Quo::Query
|
23
|
-
|
24
|
-
klass = Class.new(chosen_superclass) do
|
25
|
-
include Quo::ComposedQuery
|
26
|
-
|
27
|
-
class << self
|
28
|
-
attr_reader :_composing_joins, :_left_query, :_right_query
|
29
|
-
|
30
|
-
def inspect
|
31
|
-
left_desc = quo_operand_desc(_left_query)
|
32
|
-
right_desc = quo_operand_desc(_right_query)
|
33
|
-
klass_name = (self < Quo::RelationBackedQuery) ? Quo.relation_backed_query_base_class.name : Quo.collection_backed_query_base_class.name
|
34
|
-
"#{klass_name}<Quo::ComposedQuery>[#{left_desc}, #{right_desc}]"
|
35
|
-
end
|
17
|
+
validate_query_classes(left_query_class, right_query_class)
|
36
18
|
|
37
|
-
|
38
|
-
|
39
|
-
def quo_operand_desc(operand)
|
40
|
-
if operand < Quo::ComposedQuery
|
41
|
-
operand.inspect
|
42
|
-
else
|
43
|
-
operand.name || operand.superclass&.name || "(anonymous)"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
19
|
+
props = collect_properties(left_query_class, right_query_class)
|
20
|
+
klass = create_composed_class(chosen_superclass, props)
|
47
21
|
|
48
|
-
|
49
|
-
prop name, property.type, property.kind, reader: property.reader, writer: property.writer, default: property.default
|
50
|
-
end
|
51
|
-
end
|
52
|
-
klass.instance_variable_set(:@_composing_joins, joins)
|
53
|
-
klass.instance_variable_set(:@_left_query, left_query_class)
|
54
|
-
klass.instance_variable_set(:@_right_query, right_query_class)
|
22
|
+
assign_query_metadata(klass, left_query_class, right_query_class, joins)
|
55
23
|
klass
|
56
24
|
end
|
57
25
|
module_function :composer
|
@@ -62,17 +30,18 @@ module Quo
|
|
62
30
|
# @rbs joins: untyped
|
63
31
|
# @rbs return: Quo::ComposedQuery
|
64
32
|
def merge_instances(left_instance, right_instance, joins: nil)
|
65
|
-
|
66
|
-
|
33
|
+
validate_instances(left_instance, right_instance)
|
34
|
+
|
67
35
|
if left_instance.is_a?(Quo::Query) && right_instance.is_a?(::ActiveRecord::Relation)
|
68
|
-
return
|
36
|
+
return merge_query_and_relation(left_instance, right_instance, joins)
|
69
37
|
elsif right_instance.is_a?(Quo::Query) && left_instance.is_a?(::ActiveRecord::Relation)
|
70
|
-
return
|
38
|
+
return merge_relation_and_query(left_instance, right_instance, joins)
|
71
39
|
elsif left_instance.is_a?(Quo::Query) && right_instance.is_a?(Quo::Query)
|
72
|
-
|
73
|
-
return composer((left_instance.is_a?(Quo::RelationBackedQuery) && right_instance.is_a?(Quo::RelationBackedQuery)) ? Quo.relation_backed_query_base_class : Quo.collection_backed_query_base_class, left_instance.class, right_instance.class, joins: joins).new(**props)
|
40
|
+
return merge_query_instances(left_instance, right_instance, joins)
|
74
41
|
end
|
75
|
-
|
42
|
+
|
43
|
+
# Both are AR relations
|
44
|
+
composer(Quo.relation_backed_query_base_class, left_instance, right_instance, joins: joins).new
|
76
45
|
end
|
77
46
|
module_function :merge_instances
|
78
47
|
|
@@ -87,6 +56,139 @@ module Quo
|
|
87
56
|
"#{klass_name}<Quo::ComposedQuery>[#{self.class.quo_operand_desc(left.class)}, #{self.class.quo_operand_desc(right.class)}](#{super})"
|
88
57
|
end
|
89
58
|
|
59
|
+
class << self
|
60
|
+
private
|
61
|
+
|
62
|
+
# @rbs left_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
63
|
+
# @rbs right_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
64
|
+
def validate_query_classes(left_query_class, right_query_class)
|
65
|
+
unless left_query_class.respond_to?(:<) && right_query_class.respond_to?(:<)
|
66
|
+
raise ArgumentError, "Cannot compose #{left_query_class} and #{right_query_class}, are they both classes? If you want to use instances use `.merge_instances`"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# @rbs left_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
71
|
+
# @rbs right_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
72
|
+
def collect_properties(left_query_class, right_query_class)
|
73
|
+
props = {}
|
74
|
+
props.merge!(left_query_class.literal_properties.properties_index) if left_query_class < Quo::Query
|
75
|
+
props.merge!(right_query_class.literal_properties.properties_index) if right_query_class < Quo::Query
|
76
|
+
props
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_composed_class(chosen_superclass, props)
|
80
|
+
Class.new(chosen_superclass) do
|
81
|
+
include Quo::ComposedQuery
|
82
|
+
|
83
|
+
class << self
|
84
|
+
attr_reader :_composing_joins, :_left_query, :_right_query
|
85
|
+
|
86
|
+
def inspect
|
87
|
+
left_desc = quo_operand_desc(_left_query)
|
88
|
+
right_desc = quo_operand_desc(_right_query)
|
89
|
+
klass_name = determine_class_name
|
90
|
+
"#{klass_name}<Quo::ComposedQuery>[#{left_desc}, #{right_desc}]"
|
91
|
+
end
|
92
|
+
|
93
|
+
# @rbs operand: Quo::ComposedQuery | Quo::Query | ::ActiveRecord::Relation
|
94
|
+
# @rbs return: String
|
95
|
+
def quo_operand_desc(operand)
|
96
|
+
if operand < Quo::ComposedQuery
|
97
|
+
operand.inspect
|
98
|
+
else
|
99
|
+
operand.name || operand.superclass&.name || "(anonymous)"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# @rbs return: String
|
106
|
+
def determine_class_name
|
107
|
+
if self < Quo::RelationBackedQuery
|
108
|
+
Quo.relation_backed_query_base_class.name
|
109
|
+
else
|
110
|
+
Quo.collection_backed_query_base_class.name
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
props.each do |name, property|
|
116
|
+
prop(
|
117
|
+
name,
|
118
|
+
property.type,
|
119
|
+
property.kind,
|
120
|
+
reader: property.reader,
|
121
|
+
writer: property.writer,
|
122
|
+
default: property.default
|
123
|
+
)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# @rbs klass: Class
|
129
|
+
# @rbs left_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
130
|
+
# @rbs right_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
131
|
+
# @rbs joins: untyped
|
132
|
+
def assign_query_metadata(klass, left_query_class, right_query_class, joins)
|
133
|
+
klass.instance_variable_set(:@_composing_joins, joins)
|
134
|
+
klass.instance_variable_set(:@_left_query, left_query_class)
|
135
|
+
klass.instance_variable_set(:@_right_query, right_query_class)
|
136
|
+
end
|
137
|
+
|
138
|
+
# @rbs left_instance: Quo::Query | ::ActiveRecord::Relation
|
139
|
+
# @rbs right_instance: Quo::Query | ::ActiveRecord::Relation
|
140
|
+
def validate_instances(left_instance, right_instance)
|
141
|
+
unless left_instance.is_a?(Quo::Query) || left_instance.is_a?(::ActiveRecord::Relation)
|
142
|
+
raise ArgumentError, "Cannot merge, left has incompatible type #{left_instance.class}"
|
143
|
+
end
|
144
|
+
|
145
|
+
unless right_instance.is_a?(Quo::Query) || right_instance.is_a?(::ActiveRecord::Relation)
|
146
|
+
raise ArgumentError, "Cannot merge, right has incompatible type #{right_instance.class}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# @rbs relation: ::ActiveRecord::Relation
|
151
|
+
# @rbs query: Quo::Query
|
152
|
+
# @rbs joins: untyped
|
153
|
+
def merge_query_and_relation(query, relation, joins)
|
154
|
+
base_class = query.is_a?(Quo::RelationBackedQuery) ?
|
155
|
+
Quo.relation_backed_query_base_class :
|
156
|
+
Quo.collection_backed_query_base_class
|
157
|
+
|
158
|
+
composer(base_class, query.class, relation, joins: joins).new(**query.to_h)
|
159
|
+
end
|
160
|
+
|
161
|
+
# @rbs relation: ::ActiveRecord::Relation
|
162
|
+
# @rbs query: Quo::Query
|
163
|
+
# @rbs joins: untyped
|
164
|
+
def merge_relation_and_query(relation, query, joins)
|
165
|
+
base_class = query.is_a?(Quo::RelationBackedQuery) ?
|
166
|
+
Quo.relation_backed_query_base_class :
|
167
|
+
Quo.collection_backed_query_base_class
|
168
|
+
|
169
|
+
composer(base_class, relation, query.class, joins: joins).new(**query.to_h)
|
170
|
+
end
|
171
|
+
|
172
|
+
# @rbs left_query: Quo::Query | ::ActiveRecord::Relation
|
173
|
+
# @rbs right_query: Quo::Query | ::ActiveRecord::Relation
|
174
|
+
def merge_query_instances(left_query, right_query, joins)
|
175
|
+
props = left_query.to_h.merge(right_query.to_h.compact)
|
176
|
+
|
177
|
+
base_class = determine_base_class_for_queries(left_query, right_query)
|
178
|
+
composer(base_class, left_query.class, right_query.class, joins: joins).new(**props)
|
179
|
+
end
|
180
|
+
|
181
|
+
# @rbs left_query: Quo::Query | ::ActiveRecord::Relation
|
182
|
+
# @rbs right_query: Quo::Query | ::ActiveRecord::Relation
|
183
|
+
def determine_base_class_for_queries(left_query, right_query)
|
184
|
+
both_relation_backed = left_query.is_a?(Quo::RelationBackedQuery) &&
|
185
|
+
right_query.is_a?(Quo::RelationBackedQuery)
|
186
|
+
|
187
|
+
both_relation_backed ? Quo.relation_backed_query_base_class :
|
188
|
+
Quo.collection_backed_query_base_class
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
90
192
|
private
|
91
193
|
|
92
194
|
# @rbs return: Hash[Symbol, untyped]
|
@@ -118,8 +220,9 @@ module Quo
|
|
118
220
|
def merge_left_and_right
|
119
221
|
left_rel = quo_unwrap_unpaginated_query(left)
|
120
222
|
right_rel = quo_unwrap_unpaginated_query(right)
|
223
|
+
|
121
224
|
if both_relations?(left_rel, right_rel)
|
122
|
-
|
225
|
+
merge_active_record_relations(left_rel, right_rel)
|
123
226
|
elsif left_relation_right_enumerable?(left_rel, right_rel)
|
124
227
|
left_rel.to_a + right_rel
|
125
228
|
elsif left_enumerable_right_relation?(left_rel, right_rel) && left_rel.respond_to?(:+)
|
@@ -131,6 +234,13 @@ module Quo
|
|
131
234
|
end
|
132
235
|
end
|
133
236
|
|
237
|
+
# @rbs left_rel: ActiveRecord::Relation
|
238
|
+
# @rbs right_rel: ActiveRecord::Relation
|
239
|
+
# @rbs return: ActiveRecord::Relation
|
240
|
+
def merge_active_record_relations(left_rel, right_rel)
|
241
|
+
apply_joins(left_rel).merge(right_rel)
|
242
|
+
end
|
243
|
+
|
134
244
|
# @rbs left_rel: ActiveRecord::Relation
|
135
245
|
# @rbs return: ActiveRecord::Relation
|
136
246
|
def apply_joins(left_rel)
|
data/lib/quo/query.rb
CHANGED
@@ -48,82 +48,27 @@ module Quo
|
|
48
48
|
sanitize_sql_for_conditions(["?", value])
|
49
49
|
end
|
50
50
|
|
51
|
-
#
|
51
|
+
# The query specification stores all options related to building the query
|
52
52
|
# @rbs!
|
53
|
-
# @
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
#
|
60
|
-
|
61
|
-
prop :_rel_distinct, _Nilable(_Boolean), reader: false, writer: false
|
62
|
-
prop :_rel_order, _Nilable(_Any), reader: false, writer: false
|
63
|
-
prop :_rel_limit, _Nilable(_Any), reader: false, writer: false
|
64
|
-
prop :_rel_preload, _Nilable(_Any), reader: false, writer: false
|
65
|
-
prop :_rel_includes, _Nilable(_Any), reader: false, writer: false
|
66
|
-
prop :_rel_select, _Nilable(_Any), reader: false, writer: false
|
67
|
-
|
68
|
-
# Methods to prepare the query
|
69
|
-
|
70
|
-
# SQL 'SELECT' configuration, calls to underlying AR relation
|
71
|
-
# @rbs *options: untyped
|
53
|
+
# @_specification: Quo::RelationBackedQuerySpecification?
|
54
|
+
prop :_specification, _Nilable(Quo::RelationBackedQuerySpecification),
|
55
|
+
default: -> { RelationBackedQuerySpecification.blank },
|
56
|
+
reader: false,
|
57
|
+
writer: false
|
58
|
+
|
59
|
+
# Apply a query specification to this query
|
60
|
+
# @rbs specification: Quo::RelationBackedQuerySpecification
|
72
61
|
# @rbs return: Quo::Query
|
73
|
-
def
|
74
|
-
copy(
|
62
|
+
def with_specification(specification)
|
63
|
+
copy(_specification: specification)
|
75
64
|
end
|
76
65
|
|
77
|
-
#
|
78
|
-
# @rbs
|
66
|
+
# Apply query options using the specification
|
67
|
+
# @rbs options: Hash[Symbol, untyped]
|
79
68
|
# @rbs return: Quo::Query
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# SQL 'ORDER BY' configuration, calls to underlying AR relation
|
85
|
-
# @rbs options: untyped
|
86
|
-
# @rbs return: Quo::Query
|
87
|
-
def order(options)
|
88
|
-
copy(_rel_order: options)
|
89
|
-
end
|
90
|
-
|
91
|
-
# SQL 'GROUP BY' configuration, calls to underlying AR relation
|
92
|
-
# @rbs *options: untyped
|
93
|
-
# @rbs return: Quo::Query
|
94
|
-
def group(*options)
|
95
|
-
copy(_rel_group: options)
|
96
|
-
end
|
97
|
-
|
98
|
-
# Configures underlying AR relation to include associations
|
99
|
-
# @rbs *options: untyped
|
100
|
-
# @rbs return: Quo::Query
|
101
|
-
def includes(*options)
|
102
|
-
copy(_rel_includes: options)
|
103
|
-
end
|
104
|
-
|
105
|
-
# Configures underlying AR relation to preload associations
|
106
|
-
# @rbs *options: untyped
|
107
|
-
# @rbs return: Quo::Query
|
108
|
-
def preload(*options)
|
109
|
-
copy(_rel_preload: options)
|
110
|
-
end
|
111
|
-
|
112
|
-
# Calls to underlying AR distinct method
|
113
|
-
# @rbs enabled: bool
|
114
|
-
# @rbs return: Quo::Query
|
115
|
-
def distinct(enabled = true)
|
116
|
-
copy(_rel_distinct: enabled)
|
117
|
-
end
|
118
|
-
|
119
|
-
# Should these also be exposed? whats the point of exposing AR relation directly?
|
120
|
-
def eager_load
|
121
|
-
end
|
122
|
-
|
123
|
-
def joins
|
124
|
-
end
|
125
|
-
|
126
|
-
def left_outer_joins
|
69
|
+
def with(options = {})
|
70
|
+
spec = @_specification || RelationBackedQuerySpecification.blank
|
71
|
+
with_specification(spec.merge(options))
|
127
72
|
end
|
128
73
|
|
129
74
|
# Delegate methods that let us get the model class (available on AR relations)
|
@@ -145,6 +90,35 @@ module Quo
|
|
145
90
|
configured_query.to_sql if relation?
|
146
91
|
end
|
147
92
|
|
93
|
+
# Implements a fluent API for query methods
|
94
|
+
# This allows methods to be chained like query.where(...).order(...).limit(...)
|
95
|
+
# @rbs method_name: Symbol
|
96
|
+
# @rbs *args: untyped
|
97
|
+
# @rbs **kwargs: untyped
|
98
|
+
# @rbs &block: untyped
|
99
|
+
# @rbs return: Quo::Query
|
100
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
101
|
+
spec = @_specification || RelationBackedQuerySpecification.blank
|
102
|
+
|
103
|
+
# Check if the method exists in RelationBackedQuerySpecification
|
104
|
+
if spec.respond_to?(method_name)
|
105
|
+
# Call the method on the specification and return a new query with the updated specification
|
106
|
+
updated_spec = spec.method(method_name).call(*args, **kwargs, &block)
|
107
|
+
return with_specification(updated_spec)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Forward to underlying query if method not found in RelationBackedQuerySpecification
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
# @rbs method_name: Symbol
|
115
|
+
# @rbs include_private: bool
|
116
|
+
# @rbs return: bool
|
117
|
+
def respond_to_missing?(method_name, include_private = false)
|
118
|
+
spec_instance = RelationBackedQuerySpecification.new
|
119
|
+
spec_instance.respond_to?(method_name, include_private) || super
|
120
|
+
end
|
121
|
+
|
148
122
|
private
|
149
123
|
|
150
124
|
def validated_query
|
@@ -157,13 +131,12 @@ module Quo
|
|
157
131
|
def underlying_query #: ActiveRecord::Relation
|
158
132
|
rel = quo_unwrap_unpaginated_query(validated_query)
|
159
133
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
@_rel_select.present? ? rel.select(@_rel_select) : rel
|
134
|
+
# Apply specification if it exists
|
135
|
+
if @_specification
|
136
|
+
@_specification.apply_to(rel)
|
137
|
+
else
|
138
|
+
rel
|
139
|
+
end
|
167
140
|
end
|
168
141
|
|
169
142
|
# The configured query is the underlying query with paging
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rbs_inline: enabled
|
4
|
+
|
5
|
+
module Quo
|
6
|
+
# RelationBackedQuerySpecification encapsulates all the options for building a SQL query
|
7
|
+
# This separates the storage of query options from the actual query construction
|
8
|
+
# and provides a cleaner interface for RelationBackedQuery
|
9
|
+
class RelationBackedQuerySpecification
|
10
|
+
# @rbs!
|
11
|
+
# @options: Hash[Symbol, untyped]
|
12
|
+
attr_reader :options
|
13
|
+
|
14
|
+
# @rbs options: Hash[Symbol, untyped]
|
15
|
+
def initialize(options = {})
|
16
|
+
@options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates a new specification with merged options
|
20
|
+
# @rbs new_options: Hash[Symbol, untyped]
|
21
|
+
# @rbs return: Quo::QuerySpecification
|
22
|
+
def merge(new_options)
|
23
|
+
self.class.new(options.merge(new_options))
|
24
|
+
end
|
25
|
+
|
26
|
+
# Apply all the specification options to the given ActiveRecord relation
|
27
|
+
# @rbs relation: ActiveRecord::Relation
|
28
|
+
# @rbs return: ActiveRecord::Relation
|
29
|
+
def apply_to(relation)
|
30
|
+
rel = relation
|
31
|
+
rel = rel.select(*options[:select]) if options[:select]
|
32
|
+
rel = rel.where(options[:where]) if options[:where]
|
33
|
+
rel = rel.order(options[:order]) if options[:order]
|
34
|
+
rel = rel.group(*options[:group]) if options[:group]
|
35
|
+
rel = rel.limit(options[:limit]) if options[:limit]
|
36
|
+
rel = rel.offset(options[:offset]) if options[:offset]
|
37
|
+
rel = rel.joins(options[:joins]) if options[:joins]
|
38
|
+
rel = rel.left_outer_joins(options[:left_outer_joins]) if options[:left_outer_joins]
|
39
|
+
rel = rel.includes(*options[:includes]) if options[:includes]
|
40
|
+
rel = rel.preload(*options[:preload]) if options[:preload]
|
41
|
+
rel = rel.eager_load(*options[:eager_load]) if options[:eager_load]
|
42
|
+
rel = rel.distinct if options[:distinct]
|
43
|
+
rel = rel.reorder(options[:reorder]) if options[:reorder]
|
44
|
+
rel = rel.extending(*options[:extending]) if options[:extending]
|
45
|
+
rel = rel.unscope(options[:unscope]) if options[:unscope]
|
46
|
+
rel
|
47
|
+
end
|
48
|
+
|
49
|
+
# Create helpers for each query option
|
50
|
+
|
51
|
+
# @rbs *fields: untyped
|
52
|
+
# @rbs return: Quo::QuerySpecification
|
53
|
+
def select(*fields)
|
54
|
+
merge(select: fields)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @rbs conditions: untyped
|
58
|
+
# @rbs return: Quo::QuerySpecification
|
59
|
+
def where(conditions)
|
60
|
+
merge(where: conditions)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @rbs order_clause: untyped
|
64
|
+
# @rbs return: Quo::QuerySpecification
|
65
|
+
def order(order_clause)
|
66
|
+
merge(order: order_clause)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @rbs *columns: untyped
|
70
|
+
# @rbs return: Quo::QuerySpecification
|
71
|
+
def group(*columns)
|
72
|
+
merge(group: columns)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @rbs value: Integer
|
76
|
+
# @rbs return: Quo::QuerySpecification
|
77
|
+
def limit(value)
|
78
|
+
merge(limit: value)
|
79
|
+
end
|
80
|
+
|
81
|
+
# @rbs value: Integer
|
82
|
+
# @rbs return: Quo::QuerySpecification
|
83
|
+
def offset(value)
|
84
|
+
merge(offset: value)
|
85
|
+
end
|
86
|
+
|
87
|
+
# @rbs tables: untyped
|
88
|
+
# @rbs return: Quo::QuerySpecification
|
89
|
+
def joins(tables)
|
90
|
+
merge(joins: tables)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @rbs tables: untyped
|
94
|
+
# @rbs return: Quo::QuerySpecification
|
95
|
+
def left_outer_joins(tables)
|
96
|
+
merge(left_outer_joins: tables)
|
97
|
+
end
|
98
|
+
|
99
|
+
# @rbs *associations: untyped
|
100
|
+
# @rbs return: Quo::QuerySpecification
|
101
|
+
def includes(*associations)
|
102
|
+
merge(includes: associations)
|
103
|
+
end
|
104
|
+
|
105
|
+
# @rbs *associations: untyped
|
106
|
+
# @rbs return: Quo::QuerySpecification
|
107
|
+
def preload(*associations)
|
108
|
+
merge(preload: associations)
|
109
|
+
end
|
110
|
+
|
111
|
+
# @rbs *associations: untyped
|
112
|
+
# @rbs return: Quo::QuerySpecification
|
113
|
+
def eager_load(*associations)
|
114
|
+
merge(eager_load: associations)
|
115
|
+
end
|
116
|
+
|
117
|
+
# @rbs enabled: bool
|
118
|
+
# @rbs return: Quo::QuerySpecification
|
119
|
+
def distinct(enabled = true)
|
120
|
+
merge(distinct: enabled)
|
121
|
+
end
|
122
|
+
|
123
|
+
# @rbs order_clause: untyped
|
124
|
+
# @rbs return: Quo::QuerySpecification
|
125
|
+
def reorder(order_clause)
|
126
|
+
merge(reorder: order_clause)
|
127
|
+
end
|
128
|
+
|
129
|
+
# @rbs *modules: untyped
|
130
|
+
# @rbs return: Quo::QuerySpecification
|
131
|
+
def extending(*modules)
|
132
|
+
merge(extending: modules)
|
133
|
+
end
|
134
|
+
|
135
|
+
# @rbs *args: untyped
|
136
|
+
# @rbs return: Quo::QuerySpecification
|
137
|
+
def unscope(*args)
|
138
|
+
merge(unscope: args)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Builds a new specification from a hash of options
|
142
|
+
# @rbs options: Hash[Symbol, untyped]
|
143
|
+
# @rbs return: Quo::QuerySpecification
|
144
|
+
def self.build(options = {})
|
145
|
+
new(options)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns a blank specification
|
149
|
+
# @rbs return: Quo::QuerySpecification
|
150
|
+
def self.blank
|
151
|
+
@blank ||= new
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/quo/version.rb
CHANGED
data/lib/quo.rb
CHANGED
@@ -11,28 +11,14 @@ module Quo
|
|
11
11
|
# @rbs right_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
12
12
|
# @rbs joins: untyped
|
13
13
|
# @rbs return: singleton(Quo::ComposedQuery)
|
14
|
-
def composer: (untyped chosen_superclass, untyped left_query_class, untyped right_query_class, ?joins: untyped) -> singleton(Quo::ComposedQuery)
|
15
|
-
|
16
|
-
include Quo::ComposedQuery
|
17
|
-
|
18
|
-
attr_reader _composing_joins: untyped
|
19
|
-
|
20
|
-
attr_reader _left_query: untyped
|
21
|
-
|
22
|
-
attr_reader _right_query: untyped
|
23
|
-
|
24
|
-
def self.inspect: () -> untyped
|
25
|
-
|
26
|
-
# @rbs operand: Quo::ComposedQuery | Quo::Query | ::ActiveRecord::Relation
|
27
|
-
# @rbs return: String
|
28
|
-
def self.quo_operand_desc: (Quo::ComposedQuery | Quo::Query | ::ActiveRecord::Relation operand) -> String
|
14
|
+
def self?.composer: (untyped chosen_superclass, untyped left_query_class, untyped right_query_class, ?joins: untyped) -> singleton(Quo::ComposedQuery)
|
29
15
|
|
30
16
|
# We can also merge instance of prepared queries
|
31
17
|
# @rbs left_instance: Quo::Query | ::ActiveRecord::Relation
|
32
18
|
# @rbs right_instance: Quo::Query | ::ActiveRecord::Relation
|
33
19
|
# @rbs joins: untyped
|
34
20
|
# @rbs return: Quo::ComposedQuery
|
35
|
-
def merge_instances: (Quo::Query | ::ActiveRecord::Relation left_instance, Quo::Query | ::ActiveRecord::Relation right_instance, ?joins: untyped) -> Quo::ComposedQuery
|
21
|
+
def self?.merge_instances: (Quo::Query | ::ActiveRecord::Relation left_instance, Quo::Query | ::ActiveRecord::Relation right_instance, ?joins: untyped) -> Quo::ComposedQuery
|
36
22
|
|
37
23
|
# @rbs override
|
38
24
|
def query: ...
|
@@ -40,6 +26,44 @@ module Quo
|
|
40
26
|
# @rbs override
|
41
27
|
def inspect: ...
|
42
28
|
|
29
|
+
# @rbs left_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
30
|
+
# @rbs right_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
31
|
+
private def self.validate_query_classes: (untyped left_query_class, untyped right_query_class) -> untyped
|
32
|
+
|
33
|
+
# @rbs left_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
34
|
+
# @rbs right_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
35
|
+
private def self.collect_properties: (untyped left_query_class, untyped right_query_class) -> untyped
|
36
|
+
|
37
|
+
private def self.create_composed_class: (untyped chosen_superclass, untyped props) -> untyped
|
38
|
+
|
39
|
+
# @rbs klass: Class
|
40
|
+
# @rbs left_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
41
|
+
# @rbs right_query_class: singleton(Quo::Query | ::ActiveRecord::Relation)
|
42
|
+
# @rbs joins: untyped
|
43
|
+
private def self.assign_query_metadata: (Class klass, untyped left_query_class, untyped right_query_class, untyped joins) -> untyped
|
44
|
+
|
45
|
+
# @rbs left_instance: Quo::Query | ::ActiveRecord::Relation
|
46
|
+
# @rbs right_instance: Quo::Query | ::ActiveRecord::Relation
|
47
|
+
private def self.validate_instances: (Quo::Query | ::ActiveRecord::Relation left_instance, Quo::Query | ::ActiveRecord::Relation right_instance) -> untyped
|
48
|
+
|
49
|
+
# @rbs relation: ::ActiveRecord::Relation
|
50
|
+
# @rbs query: Quo::Query
|
51
|
+
# @rbs joins: untyped
|
52
|
+
private def self.merge_query_and_relation: (Quo::Query query, ::ActiveRecord::Relation relation, untyped joins) -> untyped
|
53
|
+
|
54
|
+
# @rbs relation: ::ActiveRecord::Relation
|
55
|
+
# @rbs query: Quo::Query
|
56
|
+
# @rbs joins: untyped
|
57
|
+
private def self.merge_relation_and_query: (::ActiveRecord::Relation relation, Quo::Query query, untyped joins) -> untyped
|
58
|
+
|
59
|
+
# @rbs left_query: Quo::Query | ::ActiveRecord::Relation
|
60
|
+
# @rbs right_query: Quo::Query | ::ActiveRecord::Relation
|
61
|
+
private def self.merge_query_instances: (Quo::Query | ::ActiveRecord::Relation left_query, Quo::Query | ::ActiveRecord::Relation right_query, untyped joins) -> untyped
|
62
|
+
|
63
|
+
# @rbs left_query: Quo::Query | ::ActiveRecord::Relation
|
64
|
+
# @rbs right_query: Quo::Query | ::ActiveRecord::Relation
|
65
|
+
private def self.determine_base_class_for_queries: (Quo::Query | ::ActiveRecord::Relation left_query, Quo::Query | ::ActiveRecord::Relation right_query) -> untyped
|
66
|
+
|
43
67
|
private
|
44
68
|
|
45
69
|
# @rbs return: Hash[Symbol, untyped]
|
@@ -57,6 +81,11 @@ module Quo
|
|
57
81
|
# @rbs return: ActiveRecord::Relation | CollectionBackedQuery
|
58
82
|
def merge_left_and_right: () -> (ActiveRecord::Relation | CollectionBackedQuery)
|
59
83
|
|
84
|
+
# @rbs left_rel: ActiveRecord::Relation
|
85
|
+
# @rbs right_rel: ActiveRecord::Relation
|
86
|
+
# @rbs return: ActiveRecord::Relation
|
87
|
+
def merge_active_record_relations: (ActiveRecord::Relation left_rel, ActiveRecord::Relation right_rel) -> ActiveRecord::Relation
|
88
|
+
|
60
89
|
# @rbs left_rel: ActiveRecord::Relation
|
61
90
|
# @rbs return: ActiveRecord::Relation
|
62
91
|
def apply_joins: (ActiveRecord::Relation left_rel) -> ActiveRecord::Relation
|