quo 0.3.1 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88c53b53f941762022f3b012abe6fe3e977e125c562f771aca630c28a087c13e
4
- data.tar.gz: dfdeb96c25d7a3e06fef0524b6c77be0cff1ae94368b39f17d6a3a3c8400e181
3
+ metadata.gz: 071f43a86ac28245731999ddf6f605c89802b71e3d51139d44263214ad25dbd5
4
+ data.tar.gz: 5eaf5d72a701679c599e5d07f60e8f1c7c9eb1f2e00a7f8ad1503270bc9cda64
5
5
  SHA512:
6
- metadata.gz: 281cad72c46e498166255f5f973fd09dfec37d309e406bccbfe27cd8ae80cfd95321f2dace615d0be36db8b9b9f38b3257e7e2acf3fd81c7f05788daa48d8e9d
7
- data.tar.gz: f09f1213677fb08bc45ac5c27262afc0692555fe24a0da0547ad37e15661019d92d9e49f7eda7651c9c518334cca2f1195a38e0c7d60b61384240f34046032d6
6
+ metadata.gz: ed91b9a82feb1c8f165672ffa4d2b1212ea7e8780cb85980f82353eca7fdfb247e67101fb6156ceaad65be94f27406daa4a5832e4950ab0204f7a9dc326329a7
7
+ data.tar.gz: 503525140bd10ed28817e61dc0a2de8ef4e60ca32805427b72763a039982fd069a147ce8eb18d91896ac83202645a12092a8b186b530c97322f465baeeba687a
data/README.md CHANGED
@@ -201,50 +201,44 @@ Specify extra options to enable pagination:
201
201
  * `page`: the current page number to fetch
202
202
  * `page_size`: the number of elements to fetch in the page
203
203
 
204
- ## 'Eager loaded' Quo::Query objects
204
+ ### `Quo::EagerQuery` & `Quo::LoadedQuery` objects
205
205
 
206
- When a query object returns an `Array` from `query` it is assumed as 'eager loaded', ie that the query has actually
207
- already been executed and the array contains the return values. This can also be used to encapsulate data that doesn't
208
- actually come from an ActiveRecord query.
209
-
210
- For example, the Query below executes the query inside on the first call and memoises the resulting data. Note
211
- however that this then means that this Query is not reusuable to `merge` with other ActiveRecord queries. If it is
212
- `compose`d with other Query objects then it will be seen as an array-like, and concatenated to whatever it is being
213
- joined to.
206
+ `Quo::EagerQuery` is a subclass of `Quo::Query` which can be used to create query objects which are 'eager loaded' by
207
+ default. This is useful for encapsulating data that doesn't come from an ActiveRecord query or queries that
208
+ execute immediately. Subclass EasyQuery and override `collection` to return the data you want to encapsulate.
214
209
 
215
210
  ```ruby
216
- class CachedTags < Quo::Query
217
- def query
218
- @tags ||= Tag.where(active: true).to_a
211
+ class MyEagerQuery < Quo::EagerQuery
212
+ def collection
213
+ [1, 2, 3]
219
214
  end
220
215
  end
221
-
222
- q = CachedTags.new(active: false)
216
+ q = MyEagerQuery.new
223
217
  q.eager? # is it 'eager'? Yes it is!
224
- q.count # array size
218
+ q.count # '3'
225
219
  ```
226
220
 
227
- ### `Quo::EagerQuery` objects
228
-
229
- `Quo::EagerQuery` is a subclass of `Quo::Query` which takes a data value on instantiation and returns it on calls to `query`
221
+ Sometimes it is useful to create similar Queries without needing to create a explicit subclass of your own. For this
222
+ use `Quo::LoadedQuery`:
230
223
 
231
224
  ```ruby
232
- q = Quo::EagerQuery.new(collection: [1, 2, 3])
225
+ q = Quo::LoadedQuery.new([1, 2, 3])
233
226
  q.eager? # is it 'eager'? Yes it is!
234
227
  q.count # '3'
235
228
  ```
236
229
 
237
- This is useful to create eager loaded Queries without needing to create a explicit subclass of your own.
238
-
239
230
  `Quo::EagerQuery` also uses `total_count` option value as the specified 'total count', useful when the data is
240
231
  actually just a page of the data and not the total count.
241
232
 
242
233
  Example of an EagerQuery used to wrap a page of enumerable data:
243
234
 
244
235
  ```ruby
245
- Quo::EagerQuery.new(collection: my_data, total_count: 100, page: current_page)
236
+ Quo::LoadedQuery.new(my_data, total_count: 100, page: current_page)
246
237
  ```
247
238
 
239
+ If a loaded query is `compose`d with other Query objects then it will be seen as an array-like, and concatenated to whatever
240
+ results are returned from the other queries. An loaded or eager query will force all other queries to be eager loaded.
241
+
248
242
  ### Composition
249
243
 
250
244
  Examples of composition of eager loaded queries
@@ -262,7 +256,7 @@ composed.last
262
256
  composed.first
263
257
  # => #<Tag id: ...>
264
258
 
265
- Quo::EagerQuery.new([3, 4]).compose(Quo::EagerQuery.new(collection: [1, 2])).last
259
+ Quo::LoadedQuery.new([3, 4]).compose(Quo::LoadedQuery.new([1, 2])).last
266
260
  # => 2
267
261
  Quo::Query.compose([1, 2], [3, 4]).last
268
262
  # => 4
@@ -292,7 +286,7 @@ maybe desirable.
292
286
 
293
287
  The spec helper method `stub_query(query_class, {results: ..., with: ...})` can do this for you.
294
288
 
295
- It stubs `.new` on the Query object and returns instances of `EagerQuery` instead with the given `results`.
289
+ It stubs `.new` on the Query object and returns instances of `LoadedQuery` instead with the given `results`.
296
290
  The `with` option is passed to the Query object on initialisation and used when setting up the method stub on the
297
291
  query class.
298
292
 
data/Steepfile CHANGED
@@ -31,6 +31,7 @@ target :lib do
31
31
  signature "sig"
32
32
  ignore "lib/quo/rspec/*.rb"
33
33
  ignore "lib/tasks/*"
34
+ ignore "lib/quo/railtie.rb"
34
35
 
35
36
  library "forwardable"
36
37
  end
@@ -2,25 +2,22 @@
2
2
 
3
3
  module Quo
4
4
  class EagerQuery < Quo::Query
5
- def initialize(**options)
6
- @collection = Array.wrap(options[:collection])
7
- super(**options.except(:collection))
8
- end
9
-
10
5
  # Optionally return the `total_count` option if it has been set.
11
6
  # This is useful when the total count is known and not equal to size
12
7
  # of wrapped collection.
13
8
  def count
14
9
  options[:total_count] || super
15
10
  end
16
- alias_method :total_count, :count
17
- alias_method :size, :count
18
11
 
19
12
  # Is this query object paged? (when no total count)
20
13
  def paged?
21
14
  options[:total_count].nil? && current_page.present?
22
15
  end
23
16
 
17
+ def collection
18
+ raise NotImplementedError, "EagerQuery objects must define a 'collection' method"
19
+ end
20
+
24
21
  def query
25
22
  preload_includes(collection) if options[:includes]
26
23
  collection
@@ -36,7 +33,13 @@ module Quo
36
33
 
37
34
  private
38
35
 
39
- attr_reader :collection
36
+ def underlying_query
37
+ unwrap_relation(query)
38
+ end
39
+
40
+ def unwrap_relation(query)
41
+ query.is_a?(Quo::Query) ? query.unwrap : query
42
+ end
40
43
 
41
44
  def preload_includes(records, preload = nil)
42
45
  ::ActiveRecord::Associations::Preloader.new(
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quo
4
+ class LoadedQuery < Quo::EagerQuery
5
+ def initialize(collection, **options)
6
+ @collection = collection
7
+ super(**options)
8
+ end
9
+
10
+ def copy(**options)
11
+ self.class.new(@collection, **@options.merge(options))
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :collection
17
+ end
18
+ end
@@ -2,24 +2,6 @@
2
2
 
3
3
  module Quo
4
4
  class MergedQuery < 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
- 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
5
  def initialize(merged_query, left, right, **options)
24
6
  @merged_query = merged_query
25
7
  @left = left
data/lib/quo/query.rb CHANGED
@@ -28,7 +28,7 @@ module Quo
28
28
  def initialize(**options)
29
29
  @options = options
30
30
  @current_page = options[:page]&.to_i || options[:current_page]&.to_i
31
- @page_size = options[:page_size]&.to_i || 20
31
+ @page_size = options[:page_size]&.to_i || Quo.configuration.default_page_size || 20
32
32
  end
33
33
 
34
34
  # Returns a active record query, or a Quo::Query instance
@@ -93,57 +93,60 @@ module Quo
93
93
  delegate :model, :klass, to: :underlying_query
94
94
 
95
95
  # Get first elements
96
- def first(*args)
96
+ def first(limit = nil)
97
97
  if transform?
98
- res = query_with_logging.first(*args)
98
+ res = query_with_logging.first(limit)
99
99
  if res.is_a? Array
100
- res.map.with_index { |r, i| transformer.call(r, i) }
100
+ res.map.with_index { |r, i| transformer&.call(r, i) }
101
101
  elsif !res.nil?
102
- transformer.call(query_with_logging.first(*args))
102
+ transformer&.call(query_with_logging.first(limit))
103
103
  end
104
+ elsif limit
105
+ query_with_logging.first(limit)
104
106
  else
105
- query_with_logging.first(*args)
107
+ # Array#first will not take nil as a limit
108
+ query_with_logging.first
106
109
  end
107
110
  end
108
111
 
109
- def first!(*args)
110
- item = first(*args)
112
+ def first!(limit = nil)
113
+ item = first(limit)
111
114
  raise ActiveRecord::RecordNotFound, "No item could be found!" unless item
112
115
  item
113
116
  end
114
117
 
115
118
  # Get last elements
116
- def last(*args)
119
+ def last(limit = nil)
117
120
  if transform?
118
- res = query_with_logging.last(*args)
121
+ res = query_with_logging.last(limit)
119
122
  if res.is_a? Array
120
- res.map.with_index { |r, i| transformer.call(r, i) }
123
+ res.map.with_index { |r, i| transformer&.call(r, i) }
121
124
  elsif !res.nil?
122
- transformer.call(query_with_logging.last(*args))
125
+ transformer&.call(res)
123
126
  end
127
+ elsif limit
128
+ query_with_logging.last(limit)
124
129
  else
125
- query_with_logging.last(*args)
130
+ query_with_logging.last
126
131
  end
127
132
  end
128
133
 
129
134
  # Convert to array
130
135
  def to_a
131
136
  arr = query_with_logging.to_a
132
- transform? ? arr.map.with_index { |r, i| transformer.call(r, i) } : arr
137
+ transform? ? arr.map.with_index { |r, i| transformer&.call(r, i) } : arr
133
138
  end
134
139
 
135
- # Convert to EagerQuery, and load all data
136
140
  def to_eager(more_opts = {})
137
- Quo::EagerQuery.new(collection: to_a, **options.merge(more_opts))
141
+ Quo::LoadedQuery.new(to_a, **options.merge(more_opts))
138
142
  end
139
143
  alias_method :load, :to_eager
140
144
 
141
- # Return an enumerable
142
- def enumerator
143
- Quo::Enumerator.new(self, transformer: transformer)
145
+ def results
146
+ Quo::Results.new(self, transformer: transformer)
144
147
  end
145
148
 
146
- # Some convenience methods for iterating over the results
149
+ # Some convenience methods for working with results
147
150
  delegate :each,
148
151
  :map,
149
152
  :flat_map,
@@ -151,8 +154,9 @@ module Quo
151
154
  :reject,
152
155
  :filter,
153
156
  :find,
157
+ :include?,
154
158
  :each_with_object,
155
- to: :enumerator
159
+ to: :results
156
160
 
157
161
  # Set a block used to transform data after query fetching
158
162
  def transform(&block)
@@ -226,7 +230,11 @@ module Quo
226
230
 
227
231
  def offset
228
232
  per_page = sanitised_page_size
229
- page = current_page&.positive? ? current_page : 1
233
+ page = if current_page && current_page&.positive?
234
+ current_page
235
+ else
236
+ 1
237
+ end
230
238
  per_page * (page - 1)
231
239
  end
232
240
 
@@ -238,7 +246,17 @@ module Quo
238
246
  end
239
247
 
240
248
  def sanitised_page_size
241
- (page_size.present? && page_size.positive?) ? [page_size.to_i, 200].min : 20
249
+ if page_size && page_size.positive?
250
+ given_size = page_size.to_i
251
+ max_page_size = Quo.configuration.max_page_size || 200
252
+ if given_size > max_page_size
253
+ max_page_size
254
+ else
255
+ given_size
256
+ end
257
+ else
258
+ Quo.configuration.default_page_size || 20
259
+ end
242
260
  end
243
261
 
244
262
  def query_with_logging
@@ -268,7 +286,7 @@ module Quo
268
286
  end
269
287
 
270
288
  def test_eager(rel)
271
- rel.is_a?(Enumerable) && !test_relation(rel)
289
+ rel.is_a?(Quo::LoadedQuery) || (rel.is_a?(Enumerable) && !test_relation(rel))
272
290
  end
273
291
 
274
292
  def test_relation(rel)
@@ -5,6 +5,10 @@ module Quo
5
5
  def initialize(left, right, joins = nil)
6
6
  @left = left
7
7
  @right = right
8
+ @unwrapped_left = unwrap_relation(left)
9
+ @unwrapped_right = unwrap_relation(right)
10
+ @left_relation = @unwrapped_left.is_a?(::ActiveRecord::Relation)
11
+ @right_relation = @unwrapped_right.is_a?(::ActiveRecord::Relation)
8
12
  @joins = joins
9
13
  end
10
14
 
@@ -19,22 +23,28 @@ module Quo
19
23
 
20
24
  private
21
25
 
22
- attr_reader :left, :right, :joins
26
+ attr_reader :left, :right, :joins, :unwrapped_left, :unwrapped_right
23
27
 
24
- def merge_left_and_right
25
- left_rel = unwrap_relation(left)
26
- right_rel = unwrap_relation(right)
27
- left_is_relation = relation_type?(left)
28
- right_is_relation = relation_type?(right)
28
+ def left_relation?
29
+ @left_relation
30
+ end
31
+
32
+ def right_relation?
33
+ @right_relation
34
+ end
29
35
 
30
- if left_is_relation && right_is_relation
31
- apply_joins(left_rel, joins).merge(right_rel)
32
- elsif left_is_relation && !right_is_relation
33
- left_rel.to_a + right_rel
34
- elsif !left_is_relation && right_is_relation
35
- left_rel + right_rel.to_a
36
- elsif !left_is_relation && !right_is_relation
37
- left_rel + right_rel
36
+ def merge_left_and_right
37
+ # FIXME: Skipping type checks here, as not sure how to make this type check with RBS
38
+ __skip__ = if both_relations?
39
+ apply_joins(unwrapped_left, joins).merge(unwrapped_right)
40
+ elsif left_relation_right_enumerable?
41
+ unwrapped_left.to_a + unwrapped_right
42
+ elsif left_enumerable_right_relation? && unwrapped_left.respond_to?(:+)
43
+ unwrapped_left + unwrapped_right.to_a
44
+ elsif unwrapped_left.respond_to?(:+)
45
+ unwrapped_left + unwrapped_right
46
+ else
47
+ raise ArgumentError, "Cannot merge #{left.class} with #{right.class}"
38
48
  end
39
49
  end
40
50
 
@@ -49,16 +59,20 @@ module Quo
49
59
  query.is_a?(Quo::Query) ? query.unwrap : query
50
60
  end
51
61
 
52
- def relation_type?(query)
53
- if query.is_a?(::Quo::Query)
54
- query.relation?
55
- else
56
- query.is_a?(::ActiveRecord::Relation)
57
- end
58
- end
59
-
60
62
  def apply_joins(left_rel, joins)
61
63
  joins.present? ? left_rel.joins(joins) : left_rel
62
64
  end
65
+
66
+ def both_relations?
67
+ left_relation? && right_relation?
68
+ end
69
+
70
+ def left_relation_right_enumerable?
71
+ left_relation? && !right_relation?
72
+ end
73
+
74
+ def left_enumerable_right_relation?
75
+ !left_relation? && right_relation?
76
+ end
63
77
  end
64
78
  end
@@ -4,7 +4,7 @@ require "forwardable"
4
4
  require_relative "./utilities/callstack"
5
5
 
6
6
  module Quo
7
- class Enumerator
7
+ class Results
8
8
  extend Forwardable
9
9
  include Quo::Utilities::Callstack
10
10
 
@@ -28,7 +28,7 @@ module Quo
28
28
  grouped = unwrapped.group_by do |*block_args|
29
29
  x = block_args.first
30
30
  transformed = transformer ? transformer.call(x) : x
31
- block.call(transformed, *block_args[1..])
31
+ block ? block.call(transformed, *(block_args[1..] || [])) : transformed
32
32
  end
33
33
 
34
34
  grouped.tap do |groups|
@@ -51,7 +51,8 @@ module Quo
51
51
  end
52
52
  else
53
53
  raw = unwrapped.send(method, *args, **kwargs)
54
- return raw if raw.is_a?(Quo::Enumerator) || raw.is_a?(::Enumerator)
54
+ # FIXME: consider how to handle applying a transformer to a Enumerator...
55
+ return raw if raw.is_a?(Quo::Results) || raw.is_a?(::Enumerator)
55
56
  transform_results(raw)
56
57
  end
57
58
  else
@@ -9,10 +9,10 @@ module Quo
9
9
  unless with.nil?
10
10
  return(
11
11
  allow(query_class).to receive(:new)
12
- .with(with) { ::Quo::EagerQuery.new(collection: results) }
12
+ .with(with) { ::Quo::LoadedQuery.new(results) }
13
13
  )
14
14
  end
15
- allow(query_class).to receive(:new) { ::Quo::EagerQuery.new(collection: results) }
15
+ allow(query_class).to receive(:new) { ::Quo::LoadedQuery.new(results) }
16
16
  end
17
17
  end
18
18
  end
@@ -12,7 +12,7 @@ module Quo
12
12
  stack = Kernel.caller.grep_v(exclude).map { |l| l.gsub(working_dir + "/", "") }
13
13
  stack_to_display = stack[0..callstack_size]
14
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
+ message += " (truncated to #{callstack_size} most recent)" if callstack_size && stack.size > callstack_size
16
16
  Quo.configuration.logger&.info(message)
17
17
  end
18
18
  end
@@ -15,7 +15,7 @@ module Quo
15
15
  if query_rel_or_data.is_a? ActiveRecord::Relation
16
16
  Quo::WrappedQuery.new(query_rel_or_data, **options)
17
17
  else
18
- Quo::EagerQuery.new(**options.merge(collection: query_rel_or_data))
18
+ Quo::LoadedQuery.new(query_rel_or_data, **options)
19
19
  end
20
20
  end
21
21
  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.3.1"
4
+ VERSION = "0.5.0"
5
5
  end
@@ -2,27 +2,15 @@
2
2
 
3
3
  module Quo
4
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
5
  def initialize(wrapped_query, **options)
22
6
  @wrapped_query = wrapped_query
23
7
  super(**options)
24
8
  end
25
9
 
10
+ def copy(**options)
11
+ self.class.new(query, **@options.merge(options))
12
+ end
13
+
26
14
  def query
27
15
  @wrapped_query
28
16
  end
data/lib/quo.rb CHANGED
@@ -4,10 +4,11 @@ require_relative "quo/version"
4
4
  require_relative "quo/railtie" if defined?(Rails)
5
5
  require_relative "quo/query"
6
6
  require_relative "quo/eager_query"
7
+ require_relative "quo/loaded_query"
7
8
  require_relative "quo/merged_query"
8
9
  require_relative "quo/wrapped_query"
9
10
  require_relative "quo/query_composer"
10
- require_relative "quo/enumerator"
11
+ require_relative "quo/results"
11
12
 
12
13
  module Quo
13
14
  class << self
@@ -22,12 +23,18 @@ module Quo
22
23
  end
23
24
 
24
25
  class Configuration
25
- attr_accessor :formatted_query_log, :query_show_callstack_size, :logger
26
+ attr_accessor :formatted_query_log,
27
+ :query_show_callstack_size,
28
+ :logger,
29
+ :max_page_size,
30
+ :default_page_size
26
31
 
27
32
  def initialize
28
33
  @formatted_query_log = true
29
34
  @query_show_callstack_size = 10
30
35
  @logger = nil
36
+ @max_page_size = 200
37
+ @default_page_size = 20
31
38
  end
32
39
  end
33
40
  end
@@ -1,13 +1,15 @@
1
1
  module Quo
2
2
  class EagerQuery < Quo::Query
3
- def initialize: (**untyped options) -> void
4
- def query: () -> enumerable
3
+ def collection: () -> loadedQueryOrEnumerable
4
+ def query: () -> loadedQueryOrEnumerable
5
+
5
6
  def relation?: () -> false
6
7
  def eager?: () -> true
7
- attr_reader collection: enumerable
8
8
 
9
9
  private
10
10
 
11
11
  def preload_includes: (untyped records, ?untyped? preload) -> untyped
12
+ def underlying_query: () -> enumerable
13
+ def unwrap_relation: (loadedQueryOrEnumerable collection) -> enumerable
12
14
  end
13
15
  end
@@ -0,0 +1,7 @@
1
+ module Quo
2
+ class LoadedQuery < Quo::EagerQuery
3
+ @collection: enumerable
4
+
5
+ def initialize: (enumerable, **untyped options) -> void
6
+ end
7
+ end
@@ -1,10 +1,12 @@
1
1
  module Quo
2
2
  class MergedQuery < Quo::Query
3
+ def self.build_from_options: (queryOptions) -> MergedQuery
4
+
3
5
  def initialize: (relOrEnumerable merged, composable left, composable right, **untyped options) -> void
4
6
 
5
7
  @merged_query: relOrEnumerable
6
8
 
7
- def query: () -> composable
9
+ def query: () -> relOrEnumerable
8
10
 
9
11
  def to_s: () -> ::String
10
12
 
data/sig/quo/query.rbs CHANGED
@@ -40,13 +40,13 @@ module Quo
40
40
 
41
41
  alias size count
42
42
  def page_count: () -> Integer
43
- def first: (*untyped args) -> untyped
44
- def first!: (*untyped args) -> untyped
45
- def last: (*untyped args) -> untyped
43
+ def first: (?Integer? limit) -> untyped
44
+ def first!: (?Integer? limit) -> untyped
45
+ def last: (?Integer? limit) -> untyped
46
46
  def to_a: () -> Array[untyped]
47
47
  def to_eager: (?::Hash[untyped, untyped] more_opts) -> Quo::EagerQuery
48
48
  alias load to_eager
49
- def enumerator: () -> Quo::Enumerator
49
+ def results: () -> Quo::Results
50
50
 
51
51
  # Set a block used to transform data after query fetching
52
52
  def transform: () ?{ () -> untyped } -> self
@@ -70,7 +70,7 @@ module Quo
70
70
  def formatted_queries?: () -> bool
71
71
  def trim_query: (String sql) -> String
72
72
  def format_query: (String sql_str) -> String
73
- def transformer: () -> (nil | ^(untyped) -> untyped)
73
+ def transformer: () -> (nil | ^(untyped, ?Integer) -> untyped)
74
74
  def offset: () -> Integer
75
75
  def configured_query: () -> ActiveRecord::Relation
76
76
  def sanitised_page_size: () -> Integer
@@ -1,5 +1,8 @@
1
1
  module Quo
2
2
  class QueryComposer
3
+ @left_relation: bool
4
+ @right_relation: bool
5
+
3
6
  def initialize: (composable left, composable right, ?untyped? joins) -> void
4
7
  def compose: () -> Quo::MergedQuery
5
8
 
@@ -9,10 +12,21 @@ module Quo
9
12
  attr_reader right: composable
10
13
  attr_reader joins: untyped
11
14
 
12
- def merge_left_and_right: () -> (ActiveRecord::Relation | Array[untyped])
15
+ attr_reader unwrapped_left: relOrEnumerable
16
+ attr_reader unwrapped_right: relOrEnumerable
17
+
18
+ def left_relation?: -> bool
19
+
20
+ def merge_left_and_right: () -> relOrEnumerable
13
21
  def merged_options: () -> ::Hash[untyped, untyped]
22
+
23
+ def right_relation?: -> bool
24
+
14
25
  def unwrap_relation: (composable) -> relOrEnumerable
15
- def relation_type?: (composable) -> bool
26
+ def relation_type?: (relOrEnumerable) -> bool
16
27
  def apply_joins: (ActiveRecord::Relation left_rel, untyped joins) -> ActiveRecord::Relation
28
+ def both_relations?: () -> bool
29
+ def left_relation_right_enumerable?: () -> bool
30
+ def left_enumerable_right_relation?: () -> bool
17
31
  end
18
32
  end
@@ -1,5 +1,5 @@
1
1
  module Quo
2
- class Enumerator
2
+ class Results
3
3
  extend Forwardable
4
4
 
5
5
  include Quo::Utilities::Callstack
@@ -8,7 +8,7 @@ module Quo
8
8
 
9
9
  @query: Quo::Query
10
10
 
11
- def group_by: -> Hash[untyped, Array[untyped]]
11
+ def group_by: () { (untyped, *untyped) -> untyped } -> Hash[untyped, Array[untyped]]
12
12
 
13
13
  def respond_to_missing?: (Symbol name, ?bool include_private) -> bool
14
14
 
@@ -1,9 +1,9 @@
1
1
  module Quo
2
2
  class WrappedQuery < Quo::Query
3
- @wrapped_query: ActiveRecord::Relation
4
-
5
3
  def self.build_from_options: (**untyped options) -> WrappedQuery
6
4
 
5
+ @wrapped_query: ActiveRecord::Relation
6
+
7
7
  def initialize: (ActiveRecord::Relation query, **untyped options) -> void
8
8
 
9
9
  def query: () -> ActiveRecord::Relation
data/sig/quo.rbs CHANGED
@@ -11,11 +11,15 @@ module Quo
11
11
 
12
12
  type query = Quo::Query
13
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]
14
+ type enumerable = Object & Enumerable[untyped]
16
15
  type relOrEnumerable = ActiveRecord::Relation | enumerable
16
+ type loadedQueryOrEnumerable = LoadedQuery | EagerQuery | enumerable
17
17
  type composable = query | relOrEnumerable
18
18
 
19
+ # TODO: how can we do the known options, eg `page` and then allow anything else?
20
+ # Maybe we should separate out the known options from the unknown options
21
+ type queryOptions = Hash[Symbol, untyped]
22
+
19
23
  interface _Logger
20
24
  def info: (String) -> void
21
25
  def error: (String) -> void
@@ -26,6 +30,8 @@ module Quo
26
30
  attr_accessor formatted_query_log: bool?
27
31
  attr_accessor query_show_callstack_size: Integer?
28
32
  attr_accessor logger: _Logger?
33
+ attr_accessor max_page_size: Integer?
34
+ attr_accessor default_page_size: Integer?
29
35
 
30
36
  def initialize: () -> void
31
37
  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.3.1
4
+ version: 0.5.0
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-22 00:00:00.000000000 Z
11
+ date: 2022-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -66,11 +66,12 @@ files:
66
66
  - Steepfile
67
67
  - lib/quo.rb
68
68
  - lib/quo/eager_query.rb
69
- - lib/quo/enumerator.rb
69
+ - lib/quo/loaded_query.rb
70
70
  - lib/quo/merged_query.rb
71
71
  - lib/quo/query.rb
72
72
  - lib/quo/query_composer.rb
73
73
  - lib/quo/railtie.rb
74
+ - lib/quo/results.rb
74
75
  - lib/quo/rspec/helpers.rb
75
76
  - lib/quo/utilities/callstack.rb
76
77
  - lib/quo/utilities/compose.rb
@@ -82,10 +83,11 @@ files:
82
83
  - rbs_collection.yaml
83
84
  - sig/quo.rbs
84
85
  - sig/quo/eager_query.rbs
85
- - sig/quo/enumerator.rbs
86
+ - sig/quo/loaded_query.rbs
86
87
  - sig/quo/merged_query.rbs
87
88
  - sig/quo/query.rbs
88
89
  - sig/quo/query_composer.rbs
90
+ - sig/quo/results.rbs
89
91
  - sig/quo/utilities/callstack.rbs
90
92
  - sig/quo/utilities/compose.rbs
91
93
  - sig/quo/utilities/sanitize.rbs