quo 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f8a79b64bd0db1439df6ffcfb820786ed082405bb24d56ac2ac08cb47c052b7
4
- data.tar.gz: 0b46427745aa9433130c4ec495cf2c9d79bff517b00ea99f17029b3a2714b604
3
+ metadata.gz: 88c53b53f941762022f3b012abe6fe3e977e125c562f771aca630c28a087c13e
4
+ data.tar.gz: dfdeb96c25d7a3e06fef0524b6c77be0cff1ae94368b39f17d6a3a3c8400e181
5
5
  SHA512:
6
- metadata.gz: 514f649e606616d7a46de0c409b4c3f635c11fe900f179e0f94952a594adb49877e82ced68e9541e2dd398e2b0e35e9ea8ff1c4995701ed8f30361de48f8ae78
7
- data.tar.gz: 7562f9cfc712ac473e6a32bb1c9f5b9e88123fb0abaa696f29dbf24e90dcedaaf716d42e0cd8bf6b8a5594a6f888fbf20f3e2b665ff9dda108ddc7ededfbe9a9
6
+ metadata.gz: 281cad72c46e498166255f5f973fd09dfec37d309e406bccbfe27cd8ae80cfd95321f2dace615d0be36db8b9b9f38b3257e7e2acf3fd81c7f05788daa48d8e9d
7
+ data.tar.gz: f09f1213677fb08bc45ac5c27262afc0692555fe24a0da0547ad37e15661019d92d9e49f7eda7651c9c518334cca2f1195a38e0c7d60b61384240f34046032d6
data/Steepfile CHANGED
@@ -27,8 +27,10 @@
27
27
  # end
28
28
 
29
29
  target :lib do
30
- check "lib/quo"
30
+ check "lib"
31
31
  signature "sig"
32
+ ignore "lib/quo/rspec/*.rb"
33
+ ignore "lib/tasks/*"
32
34
 
33
35
  library "forwardable"
34
36
  end
@@ -21,30 +21,36 @@ module Quo
21
21
  :any?,
22
22
  :none?,
23
23
  :one?,
24
- :tally,
25
- :count,
26
- :group_by,
27
- :partition,
28
- :slice_before,
29
- :slice_after,
30
- :slice_when,
31
- :chunk,
32
- :chunk_while,
33
- :sum,
34
- :zip
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.present? ? transformer.call(x) : x
44
- block.call(transformed, *block_args[1..])
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.present?
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) }
@@ -2,39 +2,52 @@
2
2
 
3
3
  module Quo
4
4
  class MergedQuery < Quo::Query
5
- def initialize(options, source_queries = [])
6
- @source_queries = source_queries
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
- @scope
31
+ @merged_query
12
32
  end
13
33
 
14
- def to_s
15
- left = operand_desc(source_queries_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
- private
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
- def source_queries_right
27
- source_queries&.last
28
- end
42
+ private
29
43
 
30
- attr_reader :source_queries
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 = nil)
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, :map, :flat_map, :reduce, :reject, :filter, to: :enumerator
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&.formatted_query_log
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.positive? ? current_page : 1
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
@@ -9,9 +9,11 @@ module Quo
9
9
  end
10
10
 
11
11
  def compose
12
- combined = merge
13
12
  Quo::MergedQuery.new(
14
- merged_options.merge({scope: combined, source_queries: [left, right]})
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 merge
24
+ def merge_left_and_right
23
25
  left_rel = unwrap_relation(left)
24
26
  right_rel = unwrap_relation(right)
25
- left_type = relation_type?(left)
26
- right_type = relation_type?(right)
27
+ left_is_relation = relation_type?(left)
28
+ right_is_relation = relation_type?(right)
27
29
 
28
- if both_relations?(left_type, right_type)
30
+ if left_is_relation && right_is_relation
29
31
  apply_joins(left_rel, joins).merge(right_rel)
30
- elsif left_relation_right_eager?(left_type, right_type)
32
+ elsif left_is_relation && !right_is_relation
31
33
  left_rel.to_a + right_rel
32
- elsif left_eager_right_relation?(left_type, right_type) && left_rel.respond_to?(:+)
34
+ elsif !left_is_relation && right_is_relation
33
35
  left_rel + right_rel.to_a
34
- elsif both_eager_loaded?(left_type, right_type) && left_rel.respond_to?(:+)
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.to_sym) : left_rel
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 Quo.configuration&.query_show_callstack_size&.positive? && Rails.env.development?
8
- max_stack = Quo.configuration.query_show_callstack_size
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
- trace_message = stack[0..max_stack].join("\n &> ")
13
- message = "\n[Query stack]: -> #{trace_message}\n"
14
- message += " (truncated to #{max_stack} most recent)" if stack.size > max_stack
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 = nil)
9
+ def compose(query1, query2, joins: nil)
10
10
  Quo::QueryComposer.new(query1, query2, joins).compose
11
11
  end
12
12
 
@@ -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.merge(scope: query_rel_or_data))
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quo
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.1"
5
5
  end
@@ -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,7 @@
1
+ module Quo
2
+ module Utilities
3
+ module Callstack
4
+ def debug_callstack: () -> void
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Quo
2
+ module Utilities
3
+ module Compose
4
+ def compose: (composable query1, composable query2, ?joins: untyped?) -> Quo::MergedQuery
5
+ def composable_with?: (queryOrRel query) -> bool
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Quo
2
+ module Utilities
3
+ module Sanitize
4
+ def sanitize_sql_for_conditions: (untyped conditions) -> untyped?
5
+ def sanitize_sql_string: (untyped string) -> untyped?
6
+ def sanitize_sql_parameter: (untyped value) -> untyped?
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Quo
2
+ module Utilities
3
+ interface _Wrapable
4
+ def new: (**untyped options) -> query
5
+ end
6
+
7
+ module Wrap : _Wrapable
8
+ def wrap: (composable query_rel_or_data, **untyped options) -> query
9
+ end
10
+ end
11
+ 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 Quo
2
- VERSION: String
3
-
4
- interface _Enumerable
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
- class Query
75
- include Quo::Utilities::Callstack
76
- extend Quo::Utilities::Compose
77
- extend Quo::Utilities::Sanitize
78
- extend Quo::Utilities::Wrap
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
- def source_queries_left: () -> composable
166
- def source_queries_right: () -> composable
167
- attr_reader source_queries: Array[composable]
168
- def operand_desc: (untyped operand) -> (nil | String)
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 EagerQuery < Quo::Query
172
- def initialize: (**untyped options) -> void
173
- def query: () -> enumerable
174
- def relation?: () -> false
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 preload_includes: (untyped records, ?untyped? preload) -> untyped
30
+ def initialize: () -> void
181
31
  end
32
+ attr_reader self.configuration: Configuration
182
33
 
183
- class QueryComposer
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.2.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-20 00:00:00.000000000 Z
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