quo 0.3.1 → 0.5.0

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: 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