quo 0.2.0 → 0.3.1
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/Steepfile +3 -1
- data/lib/quo/enumerator.rb +23 -17
- data/lib/quo/merged_query.rb +30 -17
- data/lib/quo/query.rb +12 -8
- data/lib/quo/query_composer.rb +12 -34
- data/lib/quo/utilities/callstack.rb +6 -5
- data/lib/quo/utilities/compose.rb +1 -1
- data/lib/quo/utilities/wrap.rb +1 -1
- data/lib/quo/version.rb +1 -1
- data/lib/quo/wrapped_query.rb +30 -0
- data/lib/quo.rb +1 -0
- data/sig/quo/eager_query.rbs +13 -0
- data/sig/quo/enumerator.rbs +22 -0
- data/sig/quo/merged_query.rbs +17 -0
- data/sig/quo/query.rbs +83 -0
- data/sig/quo/query_composer.rbs +18 -0
- data/sig/quo/utilities/callstack.rbs +7 -0
- data/sig/quo/utilities/compose.rbs +8 -0
- data/sig/quo/utilities/sanitize.rbs +9 -0
- data/sig/quo/utilities/wrap.rbs +11 -0
- data/sig/quo/wrapped_query.rbs +11 -0
- data/sig/quo.rbs +24 -194
- metadata +13 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88c53b53f941762022f3b012abe6fe3e977e125c562f771aca630c28a087c13e
|
4
|
+
data.tar.gz: dfdeb96c25d7a3e06fef0524b6c77be0cff1ae94368b39f17d6a3a3c8400e181
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 281cad72c46e498166255f5f973fd09dfec37d309e406bccbfe27cd8ae80cfd95321f2dace615d0be36db8b9b9f38b3257e7e2acf3fd81c7f05788daa48d8e9d
|
7
|
+
data.tar.gz: f09f1213677fb08bc45ac5c27262afc0692555fe24a0da0547ad37e15661019d92d9e49f7eda7651c9c518334cca2f1195a38e0c7d60b61384240f34046032d6
|
data/Steepfile
CHANGED
data/lib/quo/enumerator.rb
CHANGED
@@ -21,30 +21,36 @@ module Quo
|
|
21
21
|
:any?,
|
22
22
|
:none?,
|
23
23
|
:one?,
|
24
|
-
:
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
24
|
+
:count
|
25
|
+
|
26
|
+
def group_by(&block)
|
27
|
+
debug_callstack
|
28
|
+
grouped = unwrapped.group_by do |*block_args|
|
29
|
+
x = block_args.first
|
30
|
+
transformed = transformer ? transformer.call(x) : x
|
31
|
+
block.call(transformed, *block_args[1..])
|
32
|
+
end
|
33
|
+
|
34
|
+
grouped.tap do |groups|
|
35
|
+
groups.transform_values! do |values|
|
36
|
+
transformer ? values.map { |x| transformer.call(x) } : values
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
35
40
|
|
36
41
|
# Delegate other enumerable methods to underlying collection but also transform
|
37
|
-
def method_missing(method, *args, &block)
|
42
|
+
def method_missing(method, *args, **kwargs, &block)
|
38
43
|
if unwrapped.respond_to?(method)
|
39
44
|
debug_callstack
|
40
45
|
if block
|
41
|
-
unwrapped.send(method, *args) do |*block_args|
|
46
|
+
unwrapped.send(method, *args, **kwargs) do |*block_args|
|
42
47
|
x = block_args.first
|
43
|
-
transformed = transformer
|
44
|
-
|
48
|
+
transformed = transformer ? transformer.call(x) : x
|
49
|
+
other_args = block_args[1..] || []
|
50
|
+
block.call(transformed, *other_args)
|
45
51
|
end
|
46
52
|
else
|
47
|
-
raw = unwrapped.send(method, *args)
|
53
|
+
raw = unwrapped.send(method, *args, **kwargs)
|
48
54
|
return raw if raw.is_a?(Quo::Enumerator) || raw.is_a?(::Enumerator)
|
49
55
|
transform_results(raw)
|
50
56
|
end
|
@@ -62,7 +68,7 @@ module Quo
|
|
62
68
|
attr_reader :transformer, :unwrapped
|
63
69
|
|
64
70
|
def transform_results(results)
|
65
|
-
return results unless transformer
|
71
|
+
return results unless transformer
|
66
72
|
|
67
73
|
if results.is_a?(Enumerable)
|
68
74
|
results.map.with_index { |item, i| transformer.call(item, i) }
|
data/lib/quo/merged_query.rb
CHANGED
@@ -2,39 +2,52 @@
|
|
2
2
|
|
3
3
|
module Quo
|
4
4
|
class MergedQuery < Quo::Query
|
5
|
-
|
6
|
-
|
5
|
+
class << self
|
6
|
+
def call(**options)
|
7
|
+
build_from_options(**options).first
|
8
|
+
end
|
9
|
+
|
10
|
+
def call!(**options)
|
11
|
+
build_from_options(**options).first!
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_from_options(**options)
|
15
|
+
merged_query = options[:merged_query]
|
16
|
+
left = options[:left]
|
17
|
+
right = options[:right]
|
18
|
+
raise ArgumentError "MergedQuery needs the merged result and operands" unless merged_query && left && right
|
19
|
+
new(merged_query, left, right, **options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(merged_query, left, right, **options)
|
24
|
+
@merged_query = merged_query
|
25
|
+
@left = left
|
26
|
+
@right = right
|
7
27
|
super(**options)
|
8
28
|
end
|
9
29
|
|
10
30
|
def query
|
11
|
-
@
|
31
|
+
@merged_query
|
12
32
|
end
|
13
33
|
|
14
|
-
def
|
15
|
-
left
|
16
|
-
right = operand_desc(source_queries_right)
|
17
|
-
"Quo::MergedQuery[#{left}, #{right}]"
|
34
|
+
def copy(**options)
|
35
|
+
self.class.new(query, left, right, **@options.merge(options))
|
18
36
|
end
|
19
37
|
|
20
|
-
|
21
|
-
|
22
|
-
def source_queries_left
|
23
|
-
source_queries&.first
|
38
|
+
def to_s
|
39
|
+
"Quo::MergedQuery[#{operand_desc(left)}, #{operand_desc(right)}]"
|
24
40
|
end
|
25
41
|
|
26
|
-
|
27
|
-
source_queries&.last
|
28
|
-
end
|
42
|
+
private
|
29
43
|
|
30
|
-
attr_reader :
|
44
|
+
attr_reader :left, :right
|
31
45
|
|
32
46
|
def operand_desc(operand)
|
33
|
-
return unless operand
|
34
47
|
if operand.is_a? Quo::MergedQuery
|
35
48
|
operand.to_s
|
36
49
|
else
|
37
|
-
operand.class.name
|
50
|
+
operand.class.name || "(anonymous)"
|
38
51
|
end
|
39
52
|
end
|
40
53
|
end
|
data/lib/quo/query.rb
CHANGED
@@ -29,19 +29,16 @@ module Quo
|
|
29
29
|
@options = options
|
30
30
|
@current_page = options[:page]&.to_i || options[:current_page]&.to_i
|
31
31
|
@page_size = options[:page_size]&.to_i || 20
|
32
|
-
@scope = unwrap_relation(options[:scope])
|
33
32
|
end
|
34
33
|
|
35
34
|
# Returns a active record query, or a Quo::Query instance
|
36
|
-
# You must provide an implementation of this of pass the 'scope' option on instantiation
|
37
35
|
def query
|
38
|
-
return @scope unless @scope.nil?
|
39
36
|
raise NotImplementedError, "Query objects must define a 'query' method"
|
40
37
|
end
|
41
38
|
|
42
39
|
# Combine (compose) this query object with another composeable entity, see notes for `.compose` above.
|
43
40
|
# Compose is aliased as `+`. Can optionally take `joins()` parameters to perform a joins before the merge
|
44
|
-
def compose(right, joins
|
41
|
+
def compose(right, joins: nil)
|
45
42
|
Quo::QueryComposer.new(self, right, joins).compose
|
46
43
|
end
|
47
44
|
|
@@ -147,7 +144,15 @@ module Quo
|
|
147
144
|
end
|
148
145
|
|
149
146
|
# Some convenience methods for iterating over the results
|
150
|
-
delegate :each,
|
147
|
+
delegate :each,
|
148
|
+
:map,
|
149
|
+
:flat_map,
|
150
|
+
:reduce,
|
151
|
+
:reject,
|
152
|
+
:filter,
|
153
|
+
:find,
|
154
|
+
:each_with_object,
|
155
|
+
to: :enumerator
|
151
156
|
|
152
157
|
# Set a block used to transform data after query fetching
|
153
158
|
def transform(&block)
|
@@ -202,7 +207,7 @@ module Quo
|
|
202
207
|
private
|
203
208
|
|
204
209
|
def formatted_queries?
|
205
|
-
Quo.configuration
|
210
|
+
!!Quo.configuration.formatted_query_log
|
206
211
|
end
|
207
212
|
|
208
213
|
# 'trim' a query, ie remove comments and remove newlines
|
@@ -221,7 +226,7 @@ module Quo
|
|
221
226
|
|
222
227
|
def offset
|
223
228
|
per_page = sanitised_page_size
|
224
|
-
page = current_page
|
229
|
+
page = current_page&.positive? ? current_page : 1
|
225
230
|
per_page * (page - 1)
|
226
231
|
end
|
227
232
|
|
@@ -252,7 +257,6 @@ module Quo
|
|
252
257
|
rel = rel.limit(@options[:limit]) if @options[:limit].present?
|
253
258
|
rel = rel.preload(@options[:preload]) if @options[:preload].present?
|
254
259
|
rel = rel.includes(@options[:includes]) if @options[:includes].present?
|
255
|
-
rel = rel.joins(@options[:joins]) if @options[:joins].present?
|
256
260
|
rel = rel.select(@options[:select]) if @options[:select].present?
|
257
261
|
end
|
258
262
|
rel
|
data/lib/quo/query_composer.rb
CHANGED
@@ -9,9 +9,11 @@ module Quo
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def compose
|
12
|
-
combined = merge
|
13
12
|
Quo::MergedQuery.new(
|
14
|
-
|
13
|
+
merge_left_and_right,
|
14
|
+
left,
|
15
|
+
right,
|
16
|
+
**merged_options
|
15
17
|
)
|
16
18
|
end
|
17
19
|
|
@@ -19,22 +21,20 @@ module Quo
|
|
19
21
|
|
20
22
|
attr_reader :left, :right, :joins
|
21
23
|
|
22
|
-
def
|
24
|
+
def merge_left_and_right
|
23
25
|
left_rel = unwrap_relation(left)
|
24
26
|
right_rel = unwrap_relation(right)
|
25
|
-
|
26
|
-
|
27
|
+
left_is_relation = relation_type?(left)
|
28
|
+
right_is_relation = relation_type?(right)
|
27
29
|
|
28
|
-
if
|
30
|
+
if left_is_relation && right_is_relation
|
29
31
|
apply_joins(left_rel, joins).merge(right_rel)
|
30
|
-
elsif
|
32
|
+
elsif left_is_relation && !right_is_relation
|
31
33
|
left_rel.to_a + right_rel
|
32
|
-
elsif
|
34
|
+
elsif !left_is_relation && right_is_relation
|
33
35
|
left_rel + right_rel.to_a
|
34
|
-
elsif
|
36
|
+
elsif !left_is_relation && !right_is_relation
|
35
37
|
left_rel + right_rel
|
36
|
-
else
|
37
|
-
raise_error
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -58,29 +58,7 @@ module Quo
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def apply_joins(left_rel, joins)
|
61
|
-
joins.present? ? left_rel.joins(joins
|
62
|
-
end
|
63
|
-
|
64
|
-
def both_relations?(left_rel_type, right_rel_type)
|
65
|
-
left_rel_type && right_rel_type
|
66
|
-
end
|
67
|
-
|
68
|
-
def left_relation_right_eager?(left_rel_type, right_rel_type)
|
69
|
-
left_rel_type && !right_rel_type
|
70
|
-
end
|
71
|
-
|
72
|
-
def left_eager_right_relation?(left_rel_type, right_rel_type)
|
73
|
-
!left_rel_type && right_rel_type
|
74
|
-
end
|
75
|
-
|
76
|
-
def both_eager_loaded?(left_rel_type, right_rel_type)
|
77
|
-
!left_rel_type && !right_rel_type
|
78
|
-
end
|
79
|
-
|
80
|
-
def raise_error
|
81
|
-
raise ArgumentError, "Unable to composite queries #{left.class.name} and " \
|
82
|
-
"#{right.class.name}. You cannot compose queries where #query " \
|
83
|
-
"returns an ActiveRecord::Relation in one and an Enumerable in the other."
|
61
|
+
joins.present? ? left_rel.joins(joins) : left_rel
|
84
62
|
end
|
85
63
|
end
|
86
64
|
end
|
@@ -4,14 +4,15 @@ module Quo
|
|
4
4
|
module Utilities
|
5
5
|
module Callstack
|
6
6
|
def debug_callstack
|
7
|
-
return unless
|
8
|
-
|
7
|
+
return unless Rails.env.development?
|
8
|
+
callstack_size = Quo.configuration.query_show_callstack_size
|
9
|
+
return unless callstack_size&.positive?
|
9
10
|
working_dir = Dir.pwd
|
10
11
|
exclude = %r{/(gems/|rubies/|query\.rb)}
|
11
12
|
stack = Kernel.caller.grep_v(exclude).map { |l| l.gsub(working_dir + "/", "") }
|
12
|
-
|
13
|
-
message = "\n[Query stack]: -> #{
|
14
|
-
message += " (truncated to #{
|
13
|
+
stack_to_display = stack[0..callstack_size]
|
14
|
+
message = "\n[Query stack]: -> #{stack_to_display&.join("\n &> ")}\n"
|
15
|
+
message += " (truncated to #{callstack_size} most recent)" if stack.size > callstack_size
|
15
16
|
Quo.configuration.logger&.info(message)
|
16
17
|
end
|
17
18
|
end
|
@@ -6,7 +6,7 @@ module Quo
|
|
6
6
|
# These can be Quo::Query, Quo::MergedQuery, Quo::EagerQuery and ActiveRecord::Relations.
|
7
7
|
# See the `README.md` docs for more details.
|
8
8
|
module Compose
|
9
|
-
def compose(query1, query2, joins
|
9
|
+
def compose(query1, query2, joins: nil)
|
10
10
|
Quo::QueryComposer.new(query1, query2, joins).compose
|
11
11
|
end
|
12
12
|
|
data/lib/quo/utilities/wrap.rb
CHANGED
@@ -13,7 +13,7 @@ module Quo
|
|
13
13
|
end
|
14
14
|
|
15
15
|
if query_rel_or_data.is_a? ActiveRecord::Relation
|
16
|
-
new(**options
|
16
|
+
Quo::WrappedQuery.new(query_rel_or_data, **options)
|
17
17
|
else
|
18
18
|
Quo::EagerQuery.new(**options.merge(collection: query_rel_or_data))
|
19
19
|
end
|
data/lib/quo/version.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quo
|
4
|
+
class WrappedQuery < Quo::Query
|
5
|
+
class << self
|
6
|
+
def call(**options)
|
7
|
+
build_from_options(**options).first
|
8
|
+
end
|
9
|
+
|
10
|
+
def call!(**options)
|
11
|
+
build_from_options(**options).first!
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_from_options(**options)
|
15
|
+
query = options[:wrapped_query]
|
16
|
+
raise ArgumentError "WrappedQuery needs a scope" unless query
|
17
|
+
new(query, **options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(wrapped_query, **options)
|
22
|
+
@wrapped_query = wrapped_query
|
23
|
+
super(**options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def query
|
27
|
+
@wrapped_query
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/quo.rb
CHANGED
@@ -5,6 +5,7 @@ require_relative "quo/railtie" if defined?(Rails)
|
|
5
5
|
require_relative "quo/query"
|
6
6
|
require_relative "quo/eager_query"
|
7
7
|
require_relative "quo/merged_query"
|
8
|
+
require_relative "quo/wrapped_query"
|
8
9
|
require_relative "quo/query_composer"
|
9
10
|
require_relative "quo/enumerator"
|
10
11
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Quo
|
2
|
+
class EagerQuery < Quo::Query
|
3
|
+
def initialize: (**untyped options) -> void
|
4
|
+
def query: () -> enumerable
|
5
|
+
def relation?: () -> false
|
6
|
+
def eager?: () -> true
|
7
|
+
attr_reader collection: enumerable
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def preload_includes: (untyped records, ?untyped? preload) -> untyped
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Quo
|
2
|
+
class Enumerator
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
include Quo::Utilities::Callstack
|
6
|
+
|
7
|
+
def initialize: (Quo::Query query, ?transformer: (^(untyped, ?Integer) -> untyped)?) -> void
|
8
|
+
|
9
|
+
@query: Quo::Query
|
10
|
+
|
11
|
+
def group_by: -> Hash[untyped, Array[untyped]]
|
12
|
+
|
13
|
+
def respond_to_missing?: (Symbol name, ?bool include_private) -> bool
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader transformer: (^(untyped, ?Integer) -> untyped)?
|
18
|
+
attr_reader unwrapped: relOrEnumerable
|
19
|
+
|
20
|
+
def transform_results: (untyped) -> untyped
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Quo
|
2
|
+
class MergedQuery < Quo::Query
|
3
|
+
def initialize: (relOrEnumerable merged, composable left, composable right, **untyped options) -> void
|
4
|
+
|
5
|
+
@merged_query: relOrEnumerable
|
6
|
+
|
7
|
+
def query: () -> composable
|
8
|
+
|
9
|
+
def to_s: () -> ::String
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_reader left: composable
|
14
|
+
attr_reader right: composable
|
15
|
+
def operand_desc: (composable operand) -> String
|
16
|
+
end
|
17
|
+
end
|
data/sig/quo/query.rbs
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Quo
|
2
|
+
class Query
|
3
|
+
include Quo::Utilities::Callstack
|
4
|
+
extend Quo::Utilities::Compose
|
5
|
+
extend Quo::Utilities::Sanitize
|
6
|
+
extend Quo::Utilities::Wrap
|
7
|
+
|
8
|
+
@underlying_query: ActiveRecord::Relation
|
9
|
+
|
10
|
+
def self.call: (**untyped options) -> untyped
|
11
|
+
def self.call!: (**untyped options) -> untyped
|
12
|
+
|
13
|
+
@scope: ActiveRecord::Relation | nil
|
14
|
+
|
15
|
+
attr_reader current_page: Integer?
|
16
|
+
attr_reader page_size: Integer?
|
17
|
+
attr_reader options: Hash[untyped, untyped]
|
18
|
+
|
19
|
+
def initialize: (**untyped options) -> void
|
20
|
+
def query: () -> queryOrRel
|
21
|
+
def compose: (composable right, ?joins: untyped?) -> Quo::MergedQuery
|
22
|
+
alias + compose
|
23
|
+
|
24
|
+
def copy: (**untyped options) -> Quo::Query
|
25
|
+
|
26
|
+
def limit: (untyped limit) -> Quo::Query
|
27
|
+
def order: (untyped options) -> Quo::Query
|
28
|
+
def group: (*untyped options) -> Quo::Query
|
29
|
+
def includes: (*untyped options) -> Quo::Query
|
30
|
+
def preload: (*untyped options) -> Quo::Query
|
31
|
+
def select: (*untyped options) -> Quo::Query
|
32
|
+
|
33
|
+
def sum: (?untyped column_name) -> Numeric
|
34
|
+
def average: (untyped column_name) -> Numeric
|
35
|
+
def minimum: (untyped column_name) -> Numeric
|
36
|
+
def maximum: (untyped column_name) -> Numeric
|
37
|
+
def count: () -> Integer
|
38
|
+
|
39
|
+
alias total_count count
|
40
|
+
|
41
|
+
alias size count
|
42
|
+
def page_count: () -> Integer
|
43
|
+
def first: (*untyped args) -> untyped
|
44
|
+
def first!: (*untyped args) -> untyped
|
45
|
+
def last: (*untyped args) -> untyped
|
46
|
+
def to_a: () -> Array[untyped]
|
47
|
+
def to_eager: (?::Hash[untyped, untyped] more_opts) -> Quo::EagerQuery
|
48
|
+
alias load to_eager
|
49
|
+
def enumerator: () -> Quo::Enumerator
|
50
|
+
|
51
|
+
# Set a block used to transform data after query fetching
|
52
|
+
def transform: () ?{ () -> untyped } -> self
|
53
|
+
|
54
|
+
def exists?: () -> bool
|
55
|
+
def none?: () -> bool
|
56
|
+
alias empty? none?
|
57
|
+
def relation?: () -> bool
|
58
|
+
def eager?: () -> bool
|
59
|
+
def paged?: () -> bool
|
60
|
+
|
61
|
+
def model: () -> (untyped | nil)
|
62
|
+
def klass: () -> (untyped | nil)
|
63
|
+
|
64
|
+
def transform?: () -> bool
|
65
|
+
def to_sql: () -> (String | nil)
|
66
|
+
def unwrap: () -> ActiveRecord::Relation
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def formatted_queries?: () -> bool
|
71
|
+
def trim_query: (String sql) -> String
|
72
|
+
def format_query: (String sql_str) -> String
|
73
|
+
def transformer: () -> (nil | ^(untyped) -> untyped)
|
74
|
+
def offset: () -> Integer
|
75
|
+
def configured_query: () -> ActiveRecord::Relation
|
76
|
+
def sanitised_page_size: () -> Integer
|
77
|
+
def query_with_logging: () -> ActiveRecord::Relation
|
78
|
+
def underlying_query: () -> ActiveRecord::Relation
|
79
|
+
def unwrap_relation: (queryOrRel query) -> ActiveRecord::Relation
|
80
|
+
def test_eager: (composable rel) -> bool
|
81
|
+
def test_relation: (composable rel) -> bool
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Quo
|
2
|
+
class QueryComposer
|
3
|
+
def initialize: (composable left, composable right, ?untyped? joins) -> void
|
4
|
+
def compose: () -> Quo::MergedQuery
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
attr_reader left: composable
|
9
|
+
attr_reader right: composable
|
10
|
+
attr_reader joins: untyped
|
11
|
+
|
12
|
+
def merge_left_and_right: () -> (ActiveRecord::Relation | Array[untyped])
|
13
|
+
def merged_options: () -> ::Hash[untyped, untyped]
|
14
|
+
def unwrap_relation: (composable) -> relOrEnumerable
|
15
|
+
def relation_type?: (composable) -> bool
|
16
|
+
def apply_joins: (ActiveRecord::Relation left_rel, untyped joins) -> ActiveRecord::Relation
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Quo
|
2
|
+
class WrappedQuery < Quo::Query
|
3
|
+
@wrapped_query: ActiveRecord::Relation
|
4
|
+
|
5
|
+
def self.build_from_options: (**untyped options) -> WrappedQuery
|
6
|
+
|
7
|
+
def initialize: (ActiveRecord::Relation query, **untyped options) -> void
|
8
|
+
|
9
|
+
def query: () -> ActiveRecord::Relation
|
10
|
+
end
|
11
|
+
end
|
data/sig/quo.rbs
CHANGED
@@ -1,205 +1,35 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def each: { (untyped) -> void } -> untyped
|
6
|
-
end
|
7
|
-
|
8
|
-
interface _Collection
|
9
|
-
include _Enumerable
|
10
|
-
def is_a?: (Class) -> bool
|
11
|
-
def class: -> Class
|
12
|
-
def +: (untyped) -> _Collection
|
13
|
-
end
|
14
|
-
|
15
|
-
type query = Quo::MergedQuery | Quo::EagerQuery | Quo::Query
|
16
|
-
type merge_composable = query | ActiveRecord::Relation
|
17
|
-
type enumerable = _Collection
|
18
|
-
type query_like = ActiveRecord::Relation | enumerable
|
19
|
-
type composable = query | query_like
|
20
|
-
|
21
|
-
module Utilities
|
22
|
-
module Callstack
|
23
|
-
def debug_callstack: () -> void
|
24
|
-
end
|
25
|
-
|
26
|
-
module Compose
|
27
|
-
# Combine two query-like or composeable entities:
|
28
|
-
# These can be Quo::Query, Quo::MergedQuery, Quo::EagerQuery and ActiveRecord::Relations.
|
29
|
-
# See the `README.md` docs for more details.
|
30
|
-
def compose: (composable query1, composable query2, ?untyped? joins) -> Quo::MergedQuery
|
31
|
-
|
32
|
-
# Determines if the object `query` is something which can be composed with query objects
|
33
|
-
def composable_with?: (merge_composable query) -> bool
|
34
|
-
end
|
35
|
-
|
36
|
-
module Sanitize
|
37
|
-
def sanitize_sql_for_conditions: (untyped conditions) -> (untyped | nil)
|
38
|
-
|
39
|
-
def sanitize_sql_string: (untyped string) -> (untyped | nil)
|
40
|
-
|
41
|
-
def sanitize_sql_parameter: (untyped value) -> (untyped | nil)
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
def initialize: (records: untyped, associations: untyped, ?scope: untyped, ?available_records: Array[untyped], ?associate_by_default: bool) -> void
|
42
5
|
end
|
43
|
-
|
44
|
-
interface _Wrapable
|
45
|
-
def new: (**untyped options) -> query
|
46
|
-
end
|
47
|
-
|
48
|
-
module Wrap : _Wrapable
|
49
|
-
def wrap: (composable query_rel_or_data, **untyped options) -> query
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
class Enumerator
|
55
|
-
extend Forwardable
|
56
|
-
|
57
|
-
include Quo::Utilities::Callstack
|
58
|
-
|
59
|
-
def initialize: (Quo::Query query, ?transformer: ^(untyped, ?Integer) -> untyped) -> void
|
60
|
-
|
61
|
-
@query: Quo::Query
|
62
|
-
|
63
|
-
def respond_to_missing?: (Symbol name, ?bool include_private) -> bool
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
attr_reader transformer: ^(untyped, ?Integer) -> untyped
|
68
|
-
attr_reader unwrapped: ActiveRecord::Relation
|
69
|
-
|
70
|
-
def transform_results: (untyped) -> untyped
|
71
6
|
end
|
7
|
+
end
|
72
8
|
|
9
|
+
module Quo
|
10
|
+
VERSION: String
|
73
11
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
def self.call: (**untyped options) -> untyped
|
81
|
-
def self.call!: (**untyped options) -> untyped
|
82
|
-
def self.description: (?String) -> (String | nil)
|
83
|
-
self.@description: String | nil
|
84
|
-
|
85
|
-
@scope: ActiveRecord::Relation | nil
|
86
|
-
|
87
|
-
attr_reader current_page: (Integer | nil)
|
88
|
-
attr_reader page_size: (Integer | nil)
|
89
|
-
attr_reader options: Hash[untyped, untyped]
|
90
|
-
|
91
|
-
def initialize: (**untyped options) -> void
|
92
|
-
def query: () -> composable
|
93
|
-
def compose: (composable right, ?untyped? joins) -> Quo::MergedQuery
|
94
|
-
alias + compose
|
95
|
-
|
96
|
-
def copy: (**untyped options) -> query
|
97
|
-
|
98
|
-
def limit: (untyped limit) -> query
|
99
|
-
def order: (untyped options) -> query
|
100
|
-
def group: (*untyped options) -> query
|
101
|
-
def includes: (*untyped options) -> query
|
102
|
-
def preload: (*untyped options) -> query
|
103
|
-
def select: (*untyped options) -> query
|
104
|
-
|
105
|
-
def sum: (?untyped column_name) -> Numeric
|
106
|
-
def average: (untyped column_name) -> Numeric
|
107
|
-
def minimum: (untyped column_name) -> Numeric
|
108
|
-
def maximum: (untyped column_name) -> Numeric
|
109
|
-
def count: () -> Integer
|
110
|
-
|
111
|
-
alias total_count count
|
112
|
-
|
113
|
-
alias size count
|
114
|
-
def page_count: () -> Integer
|
115
|
-
def first: (*untyped args) -> untyped
|
116
|
-
def first!: (*untyped args) -> untyped
|
117
|
-
def last: (*untyped args) -> untyped
|
118
|
-
def to_a: () -> Array[untyped]
|
119
|
-
def to_eager: (?::Hash[untyped, untyped] more_opts) -> Quo::EagerQuery
|
120
|
-
alias load to_eager
|
121
|
-
def enumerator: () -> Quo::Enumerator
|
122
|
-
|
123
|
-
# Set a block used to transform data after query fetching
|
124
|
-
def transform: () ?{ () -> untyped } -> self
|
125
|
-
|
126
|
-
def exists?: () -> bool
|
127
|
-
def none?: () -> bool
|
128
|
-
alias empty? none?
|
129
|
-
def relation?: () -> bool
|
130
|
-
def eager?: () -> bool
|
131
|
-
def paged?: () -> bool
|
132
|
-
|
133
|
-
def model: () -> (untyped | nil)
|
134
|
-
def klass: () -> (untyped | nil)
|
135
|
-
|
136
|
-
def transform?: () -> bool
|
137
|
-
def to_sql: () -> (String | nil)
|
138
|
-
def unwrap: () -> query_like
|
139
|
-
|
140
|
-
private
|
141
|
-
|
142
|
-
def formatted_queries?: () -> bool
|
143
|
-
def trim_query: (String sql) -> String
|
144
|
-
def format_query: (String sql_str) -> String
|
145
|
-
def transformer: () -> (nil | ^(untyped) -> untyped)
|
146
|
-
def offset: () -> Integer
|
147
|
-
def configured_query: () -> ActiveRecord::Relation
|
148
|
-
def sanitised_page_size: () -> Integer
|
149
|
-
def query_with_logging: () -> ActiveRecord::Relation
|
150
|
-
def underlying_query: () -> ActiveRecord::Relation
|
151
|
-
def unwrap_relation: (composable query) -> ActiveRecord::Relation
|
152
|
-
def test_eager: (composable rel) -> bool
|
153
|
-
def test_relation: (composable rel) -> bool
|
154
|
-
end
|
155
|
-
|
156
|
-
class MergedQuery < Quo::Query
|
157
|
-
def initialize: (untyped options, ?untyped source_queries) -> void
|
158
|
-
|
159
|
-
def query: () -> composable
|
160
|
-
|
161
|
-
def to_s: () -> ::String
|
162
|
-
|
163
|
-
private
|
12
|
+
type query = Quo::Query
|
13
|
+
type queryOrRel = query | ActiveRecord::Relation
|
14
|
+
# FIXME: anything that is Enumerable should be composable, but I don't know how to express that
|
15
|
+
type enumerable = Array[untyped] | Set[untyped]
|
16
|
+
type relOrEnumerable = ActiveRecord::Relation | enumerable
|
17
|
+
type composable = query | relOrEnumerable
|
164
18
|
|
165
|
-
|
166
|
-
def
|
167
|
-
|
168
|
-
def
|
19
|
+
interface _Logger
|
20
|
+
def info: (String) -> void
|
21
|
+
def error: (String) -> void
|
22
|
+
def debug: (String) -> void
|
169
23
|
end
|
170
24
|
|
171
|
-
class
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
def eager?: () -> true
|
176
|
-
attr_reader collection: enumerable
|
177
|
-
|
178
|
-
private
|
25
|
+
class Configuration
|
26
|
+
attr_accessor formatted_query_log: bool?
|
27
|
+
attr_accessor query_show_callstack_size: Integer?
|
28
|
+
attr_accessor logger: _Logger?
|
179
29
|
|
180
|
-
def
|
30
|
+
def initialize: () -> void
|
181
31
|
end
|
32
|
+
attr_reader self.configuration: Configuration
|
182
33
|
|
183
|
-
|
184
|
-
def initialize: (composable left, composable right, ?untyped? joins) -> void
|
185
|
-
def compose: () -> Quo::MergedQuery
|
186
|
-
|
187
|
-
private
|
188
|
-
|
189
|
-
attr_reader left: composable
|
190
|
-
attr_reader right: composable
|
191
|
-
attr_reader joins: untyped
|
192
|
-
|
193
|
-
def merge: () -> (ActiveRecord::Relation | Array[untyped] | void)
|
194
|
-
def merged_options: () -> ::Hash[untyped, untyped]
|
195
|
-
def unwrap_relation: (composable) -> query_like
|
196
|
-
def relation_type?: (composable) -> bool
|
197
|
-
def apply_joins: (ActiveRecord::Relation left_rel, untyped joins) -> ActiveRecord::Relation
|
198
|
-
def both_relations?: (bool left_rel_type, bool right_rel_type) -> bool
|
199
|
-
def left_relation_right_eager?: (bool left_rel_type, bool right_rel_type) -> bool
|
200
|
-
def left_eager_right_relation?: (bool left_rel_type, bool right_rel_type) -> bool
|
201
|
-
def both_eager_loaded?: (bool left_rel_type, bool right_rel_type) -> bool
|
202
|
-
|
203
|
-
def raise_error: () -> void
|
204
|
-
end
|
34
|
+
def self.configure: () { (Configuration config) -> void } -> void
|
205
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Ierodiaconou
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-12-
|
11
|
+
date: 2022-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -77,9 +77,20 @@ files:
|
|
77
77
|
- lib/quo/utilities/sanitize.rb
|
78
78
|
- lib/quo/utilities/wrap.rb
|
79
79
|
- lib/quo/version.rb
|
80
|
+
- lib/quo/wrapped_query.rb
|
80
81
|
- lib/tasks/quo.rake
|
81
82
|
- rbs_collection.yaml
|
82
83
|
- sig/quo.rbs
|
84
|
+
- sig/quo/eager_query.rbs
|
85
|
+
- sig/quo/enumerator.rbs
|
86
|
+
- sig/quo/merged_query.rbs
|
87
|
+
- sig/quo/query.rbs
|
88
|
+
- sig/quo/query_composer.rbs
|
89
|
+
- sig/quo/utilities/callstack.rbs
|
90
|
+
- sig/quo/utilities/compose.rbs
|
91
|
+
- sig/quo/utilities/sanitize.rbs
|
92
|
+
- sig/quo/utilities/wrap.rbs
|
93
|
+
- sig/quo/wrapped_query.rbs
|
83
94
|
homepage: https://github.com/stevegeek/quo
|
84
95
|
licenses:
|
85
96
|
- MIT
|