params_ready_rails5 0.0.7
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 +7 -0
- data/lib/arel/cte_name.rb +20 -0
- data/lib/params_ready/builder.rb +161 -0
- data/lib/params_ready/error.rb +31 -0
- data/lib/params_ready/extensions/class_reader_writer.rb +33 -0
- data/lib/params_ready/extensions/collection.rb +43 -0
- data/lib/params_ready/extensions/delegation.rb +25 -0
- data/lib/params_ready/extensions/finalizer.rb +26 -0
- data/lib/params_ready/extensions/freezer.rb +49 -0
- data/lib/params_ready/extensions/hash.rb +46 -0
- data/lib/params_ready/extensions/late_init.rb +38 -0
- data/lib/params_ready/extensions/registry.rb +44 -0
- data/lib/params_ready/extensions/undefined.rb +23 -0
- data/lib/params_ready/format.rb +132 -0
- data/lib/params_ready/helpers/arel_builder.rb +68 -0
- data/lib/params_ready/helpers/callable.rb +14 -0
- data/lib/params_ready/helpers/conditional_block.rb +31 -0
- data/lib/params_ready/helpers/find_in_hash.rb +22 -0
- data/lib/params_ready/helpers/interface_definer.rb +48 -0
- data/lib/params_ready/helpers/key_map.rb +176 -0
- data/lib/params_ready/helpers/memo.rb +41 -0
- data/lib/params_ready/helpers/options.rb +107 -0
- data/lib/params_ready/helpers/parameter_definer_class_methods.rb +39 -0
- data/lib/params_ready/helpers/parameter_storage_class_methods.rb +63 -0
- data/lib/params_ready/helpers/parameter_user_class_methods.rb +35 -0
- data/lib/params_ready/helpers/relation_builder_wrapper.rb +35 -0
- data/lib/params_ready/helpers/rule.rb +76 -0
- data/lib/params_ready/helpers/storage.rb +30 -0
- data/lib/params_ready/helpers/usage_rule.rb +36 -0
- data/lib/params_ready/input_context.rb +31 -0
- data/lib/params_ready/intent.rb +70 -0
- data/lib/params_ready/marshaller/array_marshallers.rb +132 -0
- data/lib/params_ready/marshaller/builder_module.rb +9 -0
- data/lib/params_ready/marshaller/collection.rb +165 -0
- data/lib/params_ready/marshaller/definition_module.rb +63 -0
- data/lib/params_ready/marshaller/enum_set_marshallers.rb +96 -0
- data/lib/params_ready/marshaller/parameter_module.rb +11 -0
- data/lib/params_ready/marshaller/polymorph_marshallers.rb +67 -0
- data/lib/params_ready/marshaller/struct_marshallers.rb +100 -0
- data/lib/params_ready/marshaller/tuple_marshallers.rb +103 -0
- data/lib/params_ready/ordering/column.rb +60 -0
- data/lib/params_ready/ordering/ordering.rb +276 -0
- data/lib/params_ready/output_parameters.rb +138 -0
- data/lib/params_ready/pagination/abstract_pagination.rb +18 -0
- data/lib/params_ready/pagination/cursor.rb +171 -0
- data/lib/params_ready/pagination/direction.rb +148 -0
- data/lib/params_ready/pagination/keyset_pagination.rb +254 -0
- data/lib/params_ready/pagination/keysets.rb +70 -0
- data/lib/params_ready/pagination/nulls.rb +31 -0
- data/lib/params_ready/pagination/offset_pagination.rb +130 -0
- data/lib/params_ready/pagination/tendency.rb +28 -0
- data/lib/params_ready/parameter/abstract_struct_parameter.rb +204 -0
- data/lib/params_ready/parameter/array_parameter.rb +197 -0
- data/lib/params_ready/parameter/definition.rb +272 -0
- data/lib/params_ready/parameter/enum_set_parameter.rb +102 -0
- data/lib/params_ready/parameter/parameter.rb +475 -0
- data/lib/params_ready/parameter/polymorph_parameter.rb +172 -0
- data/lib/params_ready/parameter/state.rb +132 -0
- data/lib/params_ready/parameter/struct_parameter.rb +64 -0
- data/lib/params_ready/parameter/tuple_parameter.rb +152 -0
- data/lib/params_ready/parameter/value_parameter.rb +186 -0
- data/lib/params_ready/parameter_definer.rb +14 -0
- data/lib/params_ready/parameter_user.rb +35 -0
- data/lib/params_ready/query/array_grouping.rb +68 -0
- data/lib/params_ready/query/custom_predicate.rb +102 -0
- data/lib/params_ready/query/exists_predicate.rb +103 -0
- data/lib/params_ready/query/fixed_operator_predicate.rb +77 -0
- data/lib/params_ready/query/grouping.rb +177 -0
- data/lib/params_ready/query/join_clause.rb +87 -0
- data/lib/params_ready/query/nullness_predicate.rb +71 -0
- data/lib/params_ready/query/polymorph_predicate.rb +77 -0
- data/lib/params_ready/query/predicate.rb +203 -0
- data/lib/params_ready/query/predicate_operator.rb +132 -0
- data/lib/params_ready/query/relation.rb +337 -0
- data/lib/params_ready/query/structured_grouping.rb +58 -0
- data/lib/params_ready/query/variable_operator_predicate.rb +125 -0
- data/lib/params_ready/query_context.rb +21 -0
- data/lib/params_ready/restriction.rb +252 -0
- data/lib/params_ready/result.rb +109 -0
- data/lib/params_ready/value/coder.rb +210 -0
- data/lib/params_ready/value/constraint.rb +198 -0
- data/lib/params_ready/value/custom.rb +56 -0
- data/lib/params_ready/value/validator.rb +81 -0
- data/lib/params_ready/version.rb +7 -0
- data/lib/params_ready.rb +28 -0
- metadata +227 -0
@@ -0,0 +1,254 @@
|
|
1
|
+
require_relative '../parameter/struct_parameter'
|
2
|
+
require_relative '../value/constraint'
|
3
|
+
require_relative '../helpers/arel_builder'
|
4
|
+
require_relative 'abstract_pagination'
|
5
|
+
require_relative 'direction'
|
6
|
+
|
7
|
+
module ParamsReady
|
8
|
+
module Pagination
|
9
|
+
class KeysetPagination < Parameter::StructParameter
|
10
|
+
include AbstractPagination
|
11
|
+
|
12
|
+
def select_keysets(query, limit, direction, keyset, ordering, arel_table, context)
|
13
|
+
query = keyset_query(query, limit, direction, keyset, ordering, arel_table, context)
|
14
|
+
query.project(*cursor_columns_arel(ordering, arel_table, context))
|
15
|
+
end
|
16
|
+
|
17
|
+
def keyset_query(query, limit, direction, keyset, ordering, arel_table, context)
|
18
|
+
cursor, grouping = cursor_predicates(direction, keyset, ordering, arel_table, context)
|
19
|
+
cte = cursor.cte_for_query(query, arel_table) unless cursor.nil?
|
20
|
+
query = query.where(grouping) unless grouping.nil?
|
21
|
+
|
22
|
+
query = query.with(cte) unless cte.nil?
|
23
|
+
ordered = query.order(ordering_arel(direction, ordering, arel_table, context))
|
24
|
+
ordered.take(limit)
|
25
|
+
end
|
26
|
+
|
27
|
+
def keysets_for_relation(relation, limit, direction, keyset, ordering, context)
|
28
|
+
arel_table = relation.arel_table
|
29
|
+
|
30
|
+
cursor, predicates = cursor_predicates(direction, keyset, ordering, arel_table, context)
|
31
|
+
full_query = relation.where(predicates)
|
32
|
+
.reorder(ordering_arel(direction, ordering, arel_table, context))
|
33
|
+
.limit(limit)
|
34
|
+
.select(*cursor_columns_arel(ordering, arel_table, context))
|
35
|
+
full_query = Arel::Nodes::SqlLiteral.new(full_query.to_sql)
|
36
|
+
with_cte(relation, full_query, cursor)
|
37
|
+
end
|
38
|
+
|
39
|
+
def paginate_relation(relation, ordering, context)
|
40
|
+
arel_table = relation.arel_table
|
41
|
+
cursor, predicates = cursor_predicates(direction, keyset, ordering, arel_table, context)
|
42
|
+
|
43
|
+
subselect = relation.where(predicates)
|
44
|
+
.reorder(ordering_arel(direction, ordering, arel_table, context))
|
45
|
+
.limit(limit)
|
46
|
+
.select(primary_keys_arel(ordering, arel_table, context))
|
47
|
+
|
48
|
+
subselect_sql = Arel::Nodes::SqlLiteral.new(subselect.to_sql)
|
49
|
+
subselect_sql = with_cte_grouped(relation, subselect_sql, cursor)
|
50
|
+
|
51
|
+
exists = exists_predicate(subselect_sql, ordering, arel_table)
|
52
|
+
relation.where(exists)
|
53
|
+
end
|
54
|
+
|
55
|
+
def paginate_query(query, ordering, arel_table, context)
|
56
|
+
cursor, predicates = cursor_predicates(direction, keyset, ordering, arel_table, context)
|
57
|
+
cte = cursor.cte_for_query(query, arel_table) unless cursor.nil?
|
58
|
+
subquery = query.deep_dup
|
59
|
+
subquery = subquery.where(predicates) unless predicates.nil?
|
60
|
+
subquery = subquery.with(cte) unless cte.nil?
|
61
|
+
|
62
|
+
subselect = subquery.order(ordering_arel(direction, ordering, arel_table, context))
|
63
|
+
.take(limit)
|
64
|
+
.project(primary_keys_arel(ordering, arel_table, context))
|
65
|
+
|
66
|
+
exists = exists_predicate(subselect, ordering, arel_table)
|
67
|
+
query.where(exists)
|
68
|
+
end
|
69
|
+
|
70
|
+
def exists_predicate(subselect, ordering, arel_table)
|
71
|
+
table_alias = self.table_alias(arel_table)
|
72
|
+
aliased = arel_table.alias(table_alias)
|
73
|
+
select_manager = Arel::SelectManager.new.from(subselect.as(table_alias))
|
74
|
+
related = related_clause(arel_table, aliased, ordering.definition.primary_keys)
|
75
|
+
select_manager.where(related).project('1').exists
|
76
|
+
end
|
77
|
+
|
78
|
+
def related_clause(arel_table, aliased, primary_keys)
|
79
|
+
cursor_columns.reduce(nil) do |clause, name|
|
80
|
+
next clause unless primary_keys.member?(name)
|
81
|
+
|
82
|
+
predicate = arel_table[name].eq(aliased[name])
|
83
|
+
next predicate if clause.nil?
|
84
|
+
|
85
|
+
next clause.and(predicate)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def with_cte_grouped(relation, select, cursor)
|
90
|
+
with_cte = with_cte(relation, select, cursor)
|
91
|
+
Arel::Nodes::Grouping.new(with_cte)
|
92
|
+
end
|
93
|
+
|
94
|
+
def with_cte(relation, select, cursor)
|
95
|
+
return select if cursor.nil?
|
96
|
+
|
97
|
+
cte = cursor.cte_for_relation(relation)
|
98
|
+
return select if cte.nil?
|
99
|
+
|
100
|
+
Arel::Nodes::SqlLiteral.new([cte.to_sql, select].join(' '))
|
101
|
+
end
|
102
|
+
|
103
|
+
def table_alias(arel_table)
|
104
|
+
Helpers::ArelBuilder.safe_name "#{arel_table.name}_#{cursor_columns.join('_')}"
|
105
|
+
end
|
106
|
+
|
107
|
+
def ordering_arel(direction, ordering, arel_table, context)
|
108
|
+
inverted = Direction.instance(direction).invert_ordering?
|
109
|
+
ordering.to_arel(arel_table, context: context, inverted: inverted)
|
110
|
+
end
|
111
|
+
|
112
|
+
def cursor_predicates(direction, keyset, ordering, arel_table, context)
|
113
|
+
direction = Direction.instance(direction)
|
114
|
+
direction.cursor_predicates(keyset, ordering, arel_table, context)
|
115
|
+
end
|
116
|
+
|
117
|
+
def first_page_value
|
118
|
+
{ limit: limit, direction: :aft, keyset: {} }
|
119
|
+
end
|
120
|
+
|
121
|
+
def last_page_value
|
122
|
+
{ limit: limit, direction: :bfr, keyset: {} }
|
123
|
+
end
|
124
|
+
|
125
|
+
def before_page_value(keyset)
|
126
|
+
keyset ||= {}
|
127
|
+
{ limit: limit, direction: :bfr, keyset: keyset }
|
128
|
+
end
|
129
|
+
|
130
|
+
def after_page_value(keyset)
|
131
|
+
keyset ||= {}
|
132
|
+
{ limit: limit, direction: :aft, keyset: keyset }
|
133
|
+
end
|
134
|
+
|
135
|
+
def limit
|
136
|
+
self[:limit].unwrap
|
137
|
+
end
|
138
|
+
|
139
|
+
def limit_key
|
140
|
+
:limit
|
141
|
+
end
|
142
|
+
|
143
|
+
def direction
|
144
|
+
self[:direction].unwrap
|
145
|
+
end
|
146
|
+
|
147
|
+
def keyset
|
148
|
+
self[:keyset].unwrap
|
149
|
+
end
|
150
|
+
|
151
|
+
def cursor_columns
|
152
|
+
self[:keyset].names.keys
|
153
|
+
end
|
154
|
+
|
155
|
+
def cursor_columns_arel(ordering, arel_table, context, columns: cursor_columns)
|
156
|
+
columns.map do |name|
|
157
|
+
column = ordering.definition.columns[name]
|
158
|
+
column.attribute(name, arel_table, context)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def primary_keys_arel(ordering, arel_table, context)
|
163
|
+
columns = cursor_columns.lazy.select do |name|
|
164
|
+
ordering.definition.primary_keys.member? name
|
165
|
+
end
|
166
|
+
|
167
|
+
cursor_columns_arel(ordering, arel_table, context, columns: columns).force
|
168
|
+
end
|
169
|
+
|
170
|
+
def cursor
|
171
|
+
return nil unless is_definite?
|
172
|
+
|
173
|
+
keyset = self[:keyset]
|
174
|
+
keyset.names.keys.map do |column_name|
|
175
|
+
keyset[column_name].unwrap
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
class KeysetPaginationDefinition < Parameter::StructParameterDefinition
|
181
|
+
MIN_LIMIT = 1
|
182
|
+
|
183
|
+
parameter_class KeysetPagination
|
184
|
+
|
185
|
+
attr_reader :default_limit
|
186
|
+
|
187
|
+
def initialize(default_limit, max_limit = nil)
|
188
|
+
super :pagination,
|
189
|
+
altn: :pgn
|
190
|
+
|
191
|
+
@default_limit = default_limit
|
192
|
+
|
193
|
+
direction = Builder.define_symbol(:direction, altn: :dir) do
|
194
|
+
constrain :enum, [:bfr, :aft]
|
195
|
+
end
|
196
|
+
|
197
|
+
limit = Builder.define_integer(:limit, altn: :lmt) do
|
198
|
+
constrain Value::OperatorConstraint.new(:>=, MIN_LIMIT), strategy: :clamp
|
199
|
+
constrain Value::OperatorConstraint.new(:<=, max_limit), strategy: :clamp unless max_limit.nil?
|
200
|
+
end
|
201
|
+
add_child(direction)
|
202
|
+
add_child(limit)
|
203
|
+
end
|
204
|
+
|
205
|
+
def finish
|
206
|
+
keyset = names[:keyset]
|
207
|
+
raise ParamsReadyError, "No cursor defined" if keyset.nil? || keyset.names.length < 1
|
208
|
+
super
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class KeysetPaginationBuilder
|
213
|
+
def initialize(ordering_builder, default_limit, max_limit = nil)
|
214
|
+
definition = KeysetPaginationDefinition.new(default_limit, max_limit)
|
215
|
+
@cursor_builder = Parameter::StructParameterBuilder.send :new, definition
|
216
|
+
@default = {
|
217
|
+
limit: default_limit,
|
218
|
+
direction: :aft,
|
219
|
+
keyset: {}
|
220
|
+
}
|
221
|
+
@ordering_builder = ordering_builder
|
222
|
+
@keyset = Parameter::StructParameterBuilder.instance(:keyset, altn: :ks)
|
223
|
+
end
|
224
|
+
|
225
|
+
def key(type, name, direction, &block)
|
226
|
+
add_to_cursor(type, name, &block)
|
227
|
+
@ordering_builder.column name, direction, required: true, pk: true
|
228
|
+
end
|
229
|
+
|
230
|
+
def column(type, name, direction, **opts, &block)
|
231
|
+
add_to_cursor(type, name, &block)
|
232
|
+
@ordering_builder.column name, direction, **opts
|
233
|
+
end
|
234
|
+
|
235
|
+
def base64
|
236
|
+
@cursor_builder.marshal using: :base64
|
237
|
+
end
|
238
|
+
|
239
|
+
def add_to_cursor(type, name, &block)
|
240
|
+
@keyset.add type, name do
|
241
|
+
optional
|
242
|
+
include(&block) unless block.nil?
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def build(&block)
|
247
|
+
instance_eval(&block)
|
248
|
+
@cursor_builder.add @keyset.build
|
249
|
+
@cursor_builder.default @default
|
250
|
+
@cursor_builder.build
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ParamsReady
|
2
|
+
module Pagination
|
3
|
+
class AbstractKeysets
|
4
|
+
attr_reader :keysets
|
5
|
+
|
6
|
+
def initialize(keysets, &block)
|
7
|
+
@keysets = keysets
|
8
|
+
@transform = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def length
|
12
|
+
@keysets.length
|
13
|
+
end
|
14
|
+
|
15
|
+
def transform(raw)
|
16
|
+
return raw if @transform.nil?
|
17
|
+
@transform.call(raw)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class BeforeKeysets < AbstractKeysets
|
22
|
+
def page(delta, limit)
|
23
|
+
raise "Expected positive integer for limit, got: #{limit}" if limit < 1
|
24
|
+
raise "Expected non-negative integer for delta, got: #{delta}" if delta < 0
|
25
|
+
|
26
|
+
if delta == 0
|
27
|
+
transform(@keysets.first)
|
28
|
+
else
|
29
|
+
shift = delta * limit
|
30
|
+
diff = @keysets.length - shift
|
31
|
+
if diff > 0
|
32
|
+
transform(@keysets[shift])
|
33
|
+
elsif diff.abs < limit
|
34
|
+
{}
|
35
|
+
else
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class AfterKeysets < AbstractKeysets
|
43
|
+
attr_reader :last
|
44
|
+
|
45
|
+
def initialize(last, keysets, &block)
|
46
|
+
@last = last
|
47
|
+
super keysets, &block
|
48
|
+
end
|
49
|
+
|
50
|
+
def page(delta, limit)
|
51
|
+
raise "Expected positive integer for limit, got: #{limit}" if limit < 1
|
52
|
+
raise "Expected positive integer for delta, got: #{delta}" if delta < 1
|
53
|
+
return if @keysets.length.zero?
|
54
|
+
|
55
|
+
shift = (delta - 1) * limit
|
56
|
+
|
57
|
+
if shift == 0
|
58
|
+
@last
|
59
|
+
else
|
60
|
+
diff = @keysets.length - shift
|
61
|
+
if diff < 1
|
62
|
+
nil
|
63
|
+
else
|
64
|
+
transform(@keysets[shift - 1])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative '../error'
|
2
|
+
require_relative 'tendency'
|
3
|
+
|
4
|
+
module ParamsReady
|
5
|
+
module Pagination
|
6
|
+
module Nulls
|
7
|
+
module First
|
8
|
+
def self.if_null_predicate(column, nested)
|
9
|
+
is_null_and_all = column.eq(nil).and(nested)
|
10
|
+
grouping = Arel::Nodes::Grouping.new(is_null_and_all)
|
11
|
+
is_not_null = column.not_eq(nil)
|
12
|
+
grouping.or(is_not_null)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.if_not_null_predicate(tendency, column, value, nested)
|
16
|
+
tendency.non_nullable_predicate(column, value, nested)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Last
|
21
|
+
def self.if_null_predicate(column, nested)
|
22
|
+
Arel::Nodes::Grouping.new(column.eq(nil).and(nested))
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.if_not_null_predicate(tendency, column, value, nested)
|
26
|
+
tendency.non_nullable_predicate(column, value, nested).or(column.eq(nil))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require_relative '../parameter/tuple_parameter'
|
2
|
+
require_relative '../value/validator'
|
3
|
+
require_relative 'abstract_pagination'
|
4
|
+
|
5
|
+
module ParamsReady
|
6
|
+
module Pagination
|
7
|
+
class OffsetPagination < Parameter::TupleParameter
|
8
|
+
include AbstractPagination
|
9
|
+
|
10
|
+
def paginate_relation(relation, _, _)
|
11
|
+
relation.offset(offset).limit(limit)
|
12
|
+
end
|
13
|
+
|
14
|
+
def paginate_query(query, _, _, _)
|
15
|
+
query.skip(offset).take(limit)
|
16
|
+
end
|
17
|
+
|
18
|
+
def offset=(off)
|
19
|
+
self.first.set_value off
|
20
|
+
end
|
21
|
+
|
22
|
+
def offset
|
23
|
+
first.unwrap
|
24
|
+
end
|
25
|
+
|
26
|
+
def limit=(lmt)
|
27
|
+
self.second.set_value lmt
|
28
|
+
end
|
29
|
+
|
30
|
+
def limit
|
31
|
+
second.unwrap
|
32
|
+
end
|
33
|
+
|
34
|
+
def limit_key
|
35
|
+
1
|
36
|
+
end
|
37
|
+
|
38
|
+
def page_no
|
39
|
+
((offset + limit - 1) / limit) + 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def page_value(delta, count: nil)
|
43
|
+
return nil unless can_yield_page?(delta, count: count)
|
44
|
+
|
45
|
+
[new_offset(delta), limit]
|
46
|
+
end
|
47
|
+
|
48
|
+
def current_page_value
|
49
|
+
page_value(0)
|
50
|
+
end
|
51
|
+
|
52
|
+
def previous_page_value(delta = 1)
|
53
|
+
page_value(-delta)
|
54
|
+
end
|
55
|
+
|
56
|
+
def next_page_value(delta = 1, count: nil)
|
57
|
+
page_value(delta, count: count)
|
58
|
+
end
|
59
|
+
|
60
|
+
def first_page_value
|
61
|
+
[0, limit]
|
62
|
+
end
|
63
|
+
|
64
|
+
def last_page_value(count:)
|
65
|
+
num_pages = num_pages(count: count)
|
66
|
+
return nil if num_pages == 0
|
67
|
+
|
68
|
+
new_offset = (num_pages - 1) * limit
|
69
|
+
[new_offset, limit]
|
70
|
+
end
|
71
|
+
|
72
|
+
def new_offset(delta)
|
73
|
+
shift = delta * limit
|
74
|
+
no = offset + shift
|
75
|
+
return no if no >= 0
|
76
|
+
return 0 if shift.abs < offset + limit
|
77
|
+
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_previous?(delta = 1)
|
82
|
+
raise ParamsReadyError, 'Negative delta unexpected' if delta < 0
|
83
|
+
return false if offset == 0
|
84
|
+
|
85
|
+
delta * limit < offset + limit
|
86
|
+
end
|
87
|
+
|
88
|
+
def has_next?(delta = 1, count:)
|
89
|
+
raise ParamsReadyError, 'Nil count unexpected' if count.nil?
|
90
|
+
raise ParamsReadyError, 'Negative delta unexpected' if delta < 0
|
91
|
+
|
92
|
+
offset + (delta * limit) < count
|
93
|
+
end
|
94
|
+
|
95
|
+
def has_page?(delta, count: nil)
|
96
|
+
if delta > 0
|
97
|
+
has_next? delta, count: count
|
98
|
+
else
|
99
|
+
has_previous? -delta
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def can_yield_page?(delta, count: nil)
|
104
|
+
return true if delta >= 0 && count.nil?
|
105
|
+
|
106
|
+
has_page?(delta, count: count)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class OffsetPaginationDefinition < Parameter::TupleParameterDefinition
|
111
|
+
MIN_LIMIT = 1
|
112
|
+
parameter_class OffsetPagination
|
113
|
+
|
114
|
+
def initialize(default_offset, default_limit, max_limit = nil)
|
115
|
+
offset = Builder.define_integer(:offset, altn: :off) do
|
116
|
+
constrain Value::OperatorConstraint.new(:>=, 0), strategy: :clamp
|
117
|
+
end
|
118
|
+
limit = Builder.define_integer(:limit, altn: :lmt) do
|
119
|
+
constrain Value::OperatorConstraint.new(:>=, MIN_LIMIT), strategy: :clamp
|
120
|
+
constrain Value::OperatorConstraint.new(:<=, max_limit), strategy: :clamp unless max_limit.nil?
|
121
|
+
end
|
122
|
+
super :pagination,
|
123
|
+
altn: :pgn,
|
124
|
+
marshaller: { using: :string, separator: '-' },
|
125
|
+
fields: [offset, limit],
|
126
|
+
default: [default_offset, default_limit]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ParamsReady
|
2
|
+
module Pagination
|
3
|
+
module Tendency
|
4
|
+
def non_nullable_predicate(column, value, nested)
|
5
|
+
if_equal = column.eq(value).and(nested)
|
6
|
+
grouping = Arel::Nodes::Grouping.new(if_equal)
|
7
|
+
comparison = comparison_predicate(column, value)
|
8
|
+
grouping.or(comparison)
|
9
|
+
end
|
10
|
+
|
11
|
+
module Growing
|
12
|
+
extend Tendency
|
13
|
+
|
14
|
+
def self.comparison_predicate(column, value)
|
15
|
+
column.gt(value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Falling
|
20
|
+
extend Tendency
|
21
|
+
|
22
|
+
def self.comparison_predicate(column, value)
|
23
|
+
column.lt(value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|