grape-listing 1.4.9 → 2.0.0.pre.beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +177 -16
- data/lib/grape/dsl.rb +5 -5
- data/lib/grape_listing/version.rb +1 -1
- data/lib/listing_service/args_handling.rb +12 -4
- data/lib/listing_service/pagination.rb +2 -2
- data/lib/listing_service/search.rb +278 -53
- data/lib/listing_service/sorting.rb +5 -15
- data/lib/listing_service/spreadsheet.rb +1 -2
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d7ddb2edcb994044872c61a3c15c2ae58394f620b19d4486b833ae7f5f57e7d
|
4
|
+
data.tar.gz: 4bb801340d2f4f73af813e50008f002518ff3826bc67a30b81c9a46a4690b1c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1953a65f504b4911a82ae5f3520ea3100dd4b3c1aff9e28e17eecdc03bf6d26e34a0e505a5c70499ffd0e4d246348ea5cedb432176252e95e36016072ba2b639
|
7
|
+
data.tar.gz: dc1de49e752f69467e502c34ef2531f9f583115c2563f675cca0f995202bd2f42945ad1a4aa9443b646f4e5c94b14ceb05263397b7013fc8a40c3e7766ff6300
|
data/README.md
CHANGED
@@ -43,9 +43,9 @@ end
|
|
43
43
|
|
44
44
|
`fields: %i[...]` - список полей, которые должны присутствовать в списке записей (передается вместо `entity`)
|
45
45
|
|
46
|
-
`
|
46
|
+
`custom_search: %w[...]` - список параметров, по которым должна осуществляться специальная фильтрация с применением индивидуальных Rails-скоупов
|
47
47
|
|
48
|
-
`
|
48
|
+
`custom_sort: %w[...]` - список параметров, по которым может осуществляться специальная сортировка с применением индивидуальных Rails-скоупов
|
49
49
|
|
50
50
|
`paginate: false` - отдавать результат без пагинации, сразу всей коллекцией (по умолчанию - с пагинацией)
|
51
51
|
|
@@ -53,26 +53,187 @@ end
|
|
53
53
|
|
54
54
|
Некоторые функции, такие, как поиск (фильтрация), сортировка и формирование эл. таблиц осуществляется путем обработки параметров HTTP запроса.
|
55
55
|
|
56
|
-
###
|
57
|
-
|
58
|
-
Для
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
56
|
+
### Фильтрация
|
57
|
+
|
58
|
+
Для фильтрации требуется передать параметры в соответствующем формате.
|
59
|
+
|
60
|
+
<table>
|
61
|
+
<thead>
|
62
|
+
<tr>
|
63
|
+
<th colspan=3>Строки</th>
|
64
|
+
</tr>
|
65
|
+
</thead>
|
66
|
+
<tbody>
|
67
|
+
<tr>
|
68
|
+
<td>Заполнено</td>
|
69
|
+
<td>param.is.present</td>
|
70
|
+
</tr>
|
71
|
+
<tr>
|
72
|
+
<td>Не заполнено</td>
|
73
|
+
<td>param.not.present</td>
|
74
|
+
</tr>
|
75
|
+
<tr>
|
76
|
+
<td>Содержит</td>
|
77
|
+
<td>param.is.contain</td>
|
78
|
+
</tr>
|
79
|
+
<tr>
|
80
|
+
<td>Не содержит</td>
|
81
|
+
<td>param.not.contain</td>
|
82
|
+
</tr>
|
83
|
+
<tr>
|
84
|
+
<td>Равно</td>
|
85
|
+
<td>param.is.equal</td>
|
86
|
+
</tr>
|
87
|
+
<tr>
|
88
|
+
<td>Не равно</td>
|
89
|
+
<td>param.not.equal</td>
|
90
|
+
</tr>
|
91
|
+
<tr>
|
92
|
+
<td>Начинается с</td>
|
93
|
+
<td>param.is.start_with</td>
|
94
|
+
</tr>
|
95
|
+
<tr>
|
96
|
+
<td>Заканчивается на</td>
|
97
|
+
<td>param.is.end_with</td>
|
98
|
+
</tr>
|
99
|
+
</tbody>
|
100
|
+
<thead>
|
101
|
+
<tr>
|
102
|
+
<th colspan=3>Числа</th>
|
103
|
+
</tr>
|
104
|
+
</thead>
|
105
|
+
<tbody>
|
106
|
+
<tr>
|
107
|
+
<td>Заполнено</td>
|
108
|
+
<td>param.is.present</td>
|
109
|
+
</tr>
|
110
|
+
<tr>
|
111
|
+
<td>Не заполнено</td>
|
112
|
+
<td>param.not.present</td>
|
113
|
+
</tr>
|
114
|
+
<tr>
|
115
|
+
<td>Равно</td>
|
116
|
+
<td>param.is.equal</td>
|
117
|
+
</tr>
|
118
|
+
<tr>
|
119
|
+
<td>Не равно</td>
|
120
|
+
<td>param.not.equal</td>
|
121
|
+
</tr>
|
122
|
+
<tr>
|
123
|
+
<td>Больше</td>
|
124
|
+
<td>param.is.gt</td>
|
125
|
+
</tr>
|
126
|
+
<tr>
|
127
|
+
<td>Больше или равно</td>
|
128
|
+
<td>param.is.gte</td>
|
129
|
+
</tr>
|
130
|
+
<tr>
|
131
|
+
<td>Меньше</td>
|
132
|
+
<td>param.is.lt</td>
|
133
|
+
</tr>
|
134
|
+
<tr>
|
135
|
+
<td>Меньше или равно</td>
|
136
|
+
<td>param.is.lte</td>
|
137
|
+
</tr>
|
138
|
+
<tr>
|
139
|
+
<td>В диапазоне</td>
|
140
|
+
<td>param.is.range=[]</td>
|
141
|
+
</tr>
|
142
|
+
<tr>
|
143
|
+
<td>Вне диапазона</td>
|
144
|
+
<td>param.not.range=[]</td>
|
145
|
+
</tr>
|
146
|
+
<tr>
|
147
|
+
<td>В перечне</td>
|
148
|
+
<td>param.is.in=[]</td>
|
149
|
+
</tr>
|
150
|
+
<tr>
|
151
|
+
<td>Вне перечня</td>
|
152
|
+
<td>param.not.in=[]</td>
|
153
|
+
</tr>
|
154
|
+
</tbody>
|
155
|
+
<thead>
|
156
|
+
<tr>
|
157
|
+
<th colspan=3>Даты</th>
|
158
|
+
</tr>
|
159
|
+
</thead>
|
160
|
+
<tbody>
|
161
|
+
<tr>
|
162
|
+
<td>Заполнено</td>
|
163
|
+
<td>param.is.present</td>
|
164
|
+
</tr>
|
165
|
+
<tr>
|
166
|
+
<td>Не заполнено</td>
|
167
|
+
<td>param.not.present</td>
|
168
|
+
</tr>
|
169
|
+
<tr>
|
170
|
+
<td>Равно</td>
|
171
|
+
<td>param.is.equal</td>
|
172
|
+
</tr>
|
173
|
+
<tr>
|
174
|
+
<td>Не равно</td>
|
175
|
+
<td>param.not.equal</td>
|
176
|
+
</tr>
|
177
|
+
<tr>
|
178
|
+
<td>Больше</td>
|
179
|
+
<td>param.is.gt</td>
|
180
|
+
</tr>
|
181
|
+
<tr>
|
182
|
+
<td>Больше или равно</td>
|
183
|
+
<td>param.is.gte</td>
|
184
|
+
</tr>
|
185
|
+
<tr>
|
186
|
+
<td>Меньше</td>
|
187
|
+
<td>param.is.lt</td>
|
188
|
+
</tr>
|
189
|
+
<tr>
|
190
|
+
<td>Меньше или равно</td>
|
191
|
+
<td>param.is.lte</td>
|
192
|
+
</tr>
|
193
|
+
<tr>
|
194
|
+
<td>В диапазоне</td>
|
195
|
+
<td>param.is.range=[]</td>
|
196
|
+
</tr>
|
197
|
+
<tr>
|
198
|
+
<td>Вне диапазона</td>
|
199
|
+
<td>param.not.range=[]</td>
|
200
|
+
</tr>
|
201
|
+
<tr>
|
202
|
+
<td>В перечне</td>
|
203
|
+
<td>param.is.in=[]</td>
|
204
|
+
</tr>
|
205
|
+
<tr>
|
206
|
+
<td>Вне перечня</td>
|
207
|
+
<td>param.not.in=[]</td>
|
208
|
+
</tr>
|
209
|
+
</tbody>
|
210
|
+
<thead>
|
211
|
+
<tr>
|
212
|
+
<th colspan=3>Флаги</th>
|
213
|
+
</tr>
|
214
|
+
</thead>
|
215
|
+
<tbody>
|
216
|
+
<tr>
|
217
|
+
<td>Равно</td>
|
218
|
+
<td>param.is.equal</td>
|
219
|
+
</tr>
|
220
|
+
<tr>
|
221
|
+
<td>Не равно</td>
|
222
|
+
<td>param.not.equal</td>
|
223
|
+
</tr>
|
224
|
+
</tbody>
|
225
|
+
</table>
|
226
|
+
|
227
|
+
Для фильтрации по полям, отсутствующим в таблице, либо для применения специальной фильтрации, не предусмотренной условиями, описанными выше, требуется передать опцию `custom_search` со списком параметров и соответствующим им скоупам Rails:
|
66
228
|
```ruby
|
67
229
|
get 'users' do
|
68
230
|
listing model: User,
|
69
231
|
entity: UserEntity,
|
70
|
-
|
232
|
+
custom_search: %w[
|
233
|
+
name|for_name
|
234
|
+
]
|
71
235
|
end
|
72
236
|
```
|
73
|
-
```
|
74
|
-
GET http://localhost:3000/api/users?name=Ivan
|
75
|
-
```
|
76
237
|
|
77
238
|
### Сортировка
|
78
239
|
|
data/lib/grape/dsl.rb
CHANGED
@@ -4,13 +4,13 @@ module Grape
|
|
4
4
|
module DSL
|
5
5
|
module InsideRoute
|
6
6
|
|
7
|
-
def listing(model:, entity: nil, fields: nil, scopes: nil,
|
7
|
+
def listing(model:, entity: nil, fields: nil, scopes: nil, custom_search: nil, custom_sort: nil, paginate: true, cache: nil, preload: nil)
|
8
8
|
# параметры запроса API
|
9
9
|
request_method = request.env['REQUEST_METHOD']
|
10
10
|
request_uri = request.env['REQUEST_URI']
|
11
11
|
|
12
12
|
# опции для сервиса
|
13
|
-
opts = listing_opts(model, entity, fields, scopes,
|
13
|
+
opts = listing_opts(model, entity, fields, scopes, custom_search, custom_sort, cache, request_method, request_uri, preload)
|
14
14
|
|
15
15
|
if params[:spreadsheet]
|
16
16
|
listing_spreadsheet(**opts)
|
@@ -23,7 +23,7 @@ module Grape
|
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
|
-
def listing_opts(model, entity, fields, scopes,
|
26
|
+
def listing_opts(model, entity, fields, scopes, custom_search, custom_sort, cache, request_method, request_uri, preload)
|
27
27
|
# стандартные опции
|
28
28
|
opts = {
|
29
29
|
model:,
|
@@ -31,8 +31,8 @@ module Grape
|
|
31
31
|
entity:,
|
32
32
|
fields:,
|
33
33
|
scopes:,
|
34
|
-
|
35
|
-
|
34
|
+
custom_search:,
|
35
|
+
custom_sort:,
|
36
36
|
params:,
|
37
37
|
current_user:,
|
38
38
|
cache:,
|
@@ -6,6 +6,7 @@ module GrapeListing
|
|
6
6
|
def handle_args(**args)
|
7
7
|
# обрабатываемая модель
|
8
8
|
@model = args[:model]
|
9
|
+
@model_cols = @model.columns_hash
|
9
10
|
|
10
11
|
# список связей, необходимых для подгрузки eager load
|
11
12
|
@preload = args[:preload] || []
|
@@ -19,7 +20,17 @@ module GrapeListing
|
|
19
20
|
@current_user = args[:current_user]
|
20
21
|
|
21
22
|
# поля, по которым возможен поиск
|
22
|
-
@
|
23
|
+
@search_params = @params.except('offset', 'limit', 'sort_by', 'sort_order')
|
24
|
+
|
25
|
+
# настройки кастомного поиска
|
26
|
+
if args[:custom_search]
|
27
|
+
@custom_search = args[:custom_search].map { |i| i.split('|') }.to_h
|
28
|
+
end
|
29
|
+
|
30
|
+
# настройки кастомной сортировки
|
31
|
+
if args[:custom_sort]
|
32
|
+
@custom_sort = args[:custom_sort].map { |i| i.split('|') }.to_h
|
33
|
+
end
|
23
34
|
|
24
35
|
# сериализация - через Grape Entity или список полей
|
25
36
|
@grape_entity = args[:entity]
|
@@ -40,9 +51,6 @@ module GrapeListing
|
|
40
51
|
# требуемый список полей для коллекции
|
41
52
|
@only_columns = args[:only_columns]
|
42
53
|
|
43
|
-
# сортировка
|
44
|
-
@sortable = args[:sortable]
|
45
|
-
|
46
54
|
# временная директория (для генерации файлов)
|
47
55
|
@tempdir = args[:tempdir]
|
48
56
|
|
@@ -1,90 +1,315 @@
|
|
1
1
|
module GrapeListing
|
2
2
|
module Search
|
3
3
|
|
4
|
+
# условия
|
5
|
+
IS = 'is'.freeze
|
6
|
+
NOT = 'not'.freeze
|
7
|
+
|
8
|
+
# операторы
|
9
|
+
PRESENT = 'present'.freeze
|
10
|
+
CONTAIN = 'contain'.freeze
|
11
|
+
EQUAL = 'equal'.freeze
|
12
|
+
START_WITH = 'start_with'.freeze
|
13
|
+
END_WITH = 'end_with'.freeze
|
14
|
+
GT = 'gt'.freeze
|
15
|
+
GTE = 'gte'.freeze
|
16
|
+
LT = 'lt'.freeze
|
17
|
+
LTE = 'lte'.freeze
|
18
|
+
RANGE = 'range'.freeze
|
19
|
+
IN = 'in'.freeze
|
20
|
+
|
4
21
|
private
|
5
22
|
|
6
23
|
def search
|
7
|
-
@
|
24
|
+
@search_params&.each do |param, value|
|
8
25
|
# коллекция записей для дальнейшей фильтрации
|
9
26
|
list = @objects || @model.preload(@preload).merge(@scopes)
|
10
27
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
# пропускаем, если параметр для поиска не передан
|
15
|
-
next if value.nil? || value == '' || value == 'null'
|
28
|
+
# обработка параметра
|
29
|
+
column, condition, operator = param.split('.')
|
30
|
+
next unless [column, condition, operator].all?
|
16
31
|
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# применение скоупа
|
22
|
-
@objects = list.send(scope_name, value).distinct
|
32
|
+
# применение кастомного скоупа (при наличии)
|
33
|
+
custom_scope = @custom_search[column]
|
34
|
+
if custom_scope
|
35
|
+
@objects = list.send(custom_scope, value)
|
23
36
|
next
|
24
37
|
end
|
25
38
|
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
end
|
39
|
+
# формирование запроса для фильтрации
|
40
|
+
type = @model_cols[column].type
|
41
|
+
subject = "#{@model.table_name}.#{column}"
|
30
42
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
43
|
+
clause = case type
|
44
|
+
when :string, :text
|
45
|
+
string_clause(subject, condition, operator, value)
|
46
|
+
when :integer, :float
|
47
|
+
number_clause(subject, condition, operator, value)
|
48
|
+
when :date, :datetime
|
49
|
+
date_clause(subject, condition, operator, value)
|
50
|
+
when :boolean
|
51
|
+
boolean_clause(subject, condition, operator, value)
|
39
52
|
end
|
40
53
|
|
41
|
-
|
42
|
-
@objects = list.where(
|
54
|
+
# применение запроса для фильтрации
|
55
|
+
@objects = list.where(clause)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def string_clause(subject, condition, operator, value)
|
60
|
+
case operator
|
61
|
+
when PRESENT
|
62
|
+
string_present(subject, condition)
|
63
|
+
when CONTAIN
|
64
|
+
string_contain(subject, condition, value)
|
65
|
+
when EQUAL
|
66
|
+
string_equal(subject, condition, value)
|
67
|
+
when START_WITH
|
68
|
+
string_start(subject, condition, value)
|
69
|
+
when END_WITH
|
70
|
+
string_end(subject, condition, value)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def number_clause(subject, condition, operator, value)
|
75
|
+
case operator
|
76
|
+
when PRESENT
|
77
|
+
number_present(subject, condition)
|
78
|
+
when EQUAL
|
79
|
+
number_equal(subject, condition, value)
|
80
|
+
when GT
|
81
|
+
number_gt(subject, condition, value)
|
82
|
+
when GTE
|
83
|
+
number_gte(subject, condition, value)
|
84
|
+
when LT
|
85
|
+
number_lt(subject, condition, value)
|
86
|
+
when LTE
|
87
|
+
number_lte(subject, condition, value)
|
88
|
+
when RANGE
|
89
|
+
number_range(subject, condition, value)
|
90
|
+
when IN
|
91
|
+
number_in(subject, condition, value)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def date_clause(subject, condition, operator, value)
|
96
|
+
case operator
|
97
|
+
when PRESENT
|
98
|
+
date_present(subject, condition)
|
99
|
+
when EQUAL
|
100
|
+
date_equal(subject, condition, value)
|
101
|
+
when GT
|
102
|
+
date_gt(subject, condition, value)
|
103
|
+
when GTE
|
104
|
+
date_gte(subject, condition, value)
|
105
|
+
when LT
|
106
|
+
date_lt(subject, condition, value)
|
107
|
+
when LTE
|
108
|
+
date_lte(subject, condition, value)
|
109
|
+
when RANGE
|
110
|
+
date_range(subject, condition, value)
|
111
|
+
when IN
|
112
|
+
date_in(subject, condition, value)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def boolean_clause(subject, condition, operator, value)
|
117
|
+
case operator
|
118
|
+
when EQUAL
|
119
|
+
boolean_equal(subject, condition, value)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def string_present(subject, condition)
|
124
|
+
case condition
|
125
|
+
when IS
|
126
|
+
"#{subject} IS NOT NULL AND #{subject} != ''"
|
127
|
+
when NOT
|
128
|
+
"#{subject} IS NULL OR #{subject} = ''"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def string_contain(subject, condition, value)
|
133
|
+
case condition
|
134
|
+
when IS
|
135
|
+
"#{subject} ILIKE %#{value}%"
|
136
|
+
when NOT
|
137
|
+
"#{subject} NOT ILIKE %#{value}%"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def string_equal(subject, condition, value)
|
142
|
+
case condition
|
143
|
+
when IS
|
144
|
+
"#{subject} = '#{value}'"
|
145
|
+
when NOT
|
146
|
+
"#{subject} != '#{value}'"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def string_start(subject, condition, value)
|
151
|
+
case condition
|
152
|
+
when IS
|
153
|
+
"#{subject} ILIKE #{value}%"
|
154
|
+
when NOT
|
155
|
+
"#{subject} NOT ILIKE #{value}%"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def string_end(subject, condition, value)
|
160
|
+
case condition
|
161
|
+
when IS
|
162
|
+
"#{subject} ILIKE %#{value}"
|
163
|
+
when NOT
|
164
|
+
"#{subject} NOT ILIKE %#{value}"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def number_present(subject, condition)
|
169
|
+
case condition
|
170
|
+
when IS
|
171
|
+
"#{subject} IS NOT NULL"
|
172
|
+
when NOT
|
173
|
+
"#{subject} IS NULL"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def number_equal(subject, condition, value)
|
178
|
+
case condition
|
179
|
+
when IS
|
180
|
+
"#{subject} = #{value}"
|
181
|
+
when NOT
|
182
|
+
"#{subject} != #{value}"
|
43
183
|
end
|
44
184
|
end
|
45
185
|
|
46
|
-
def
|
47
|
-
|
186
|
+
def number_gt(subject, condition, value)
|
187
|
+
case condition
|
188
|
+
when IS
|
189
|
+
"#{subject} > #{value}"
|
190
|
+
end
|
191
|
+
end
|
48
192
|
|
49
|
-
|
50
|
-
|
193
|
+
def number_gte(subject, condition, value)
|
194
|
+
case condition
|
195
|
+
when IS
|
196
|
+
"#{subject} >= #{value}"
|
197
|
+
end
|
198
|
+
end
|
51
199
|
|
52
|
-
|
53
|
-
|
200
|
+
def number_lt(subject, condition, value)
|
201
|
+
case condition
|
202
|
+
when IS
|
203
|
+
"#{subject} < #{value}"
|
204
|
+
end
|
205
|
+
end
|
54
206
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
207
|
+
def number_lte(subject, condition, value)
|
208
|
+
case condition
|
209
|
+
when IS
|
210
|
+
"#{subject} <= #{value}"
|
59
211
|
end
|
212
|
+
end
|
60
213
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
214
|
+
def number_range(subject, condition, value)
|
215
|
+
min, max = value
|
216
|
+
|
217
|
+
case condition
|
218
|
+
when IS
|
219
|
+
"#{subject} >= #{min} AND #{subject} <= #{max}"
|
220
|
+
when NOT
|
221
|
+
"#{subject} < #{min} OR #{subject} > #{max}"
|
66
222
|
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def number_in(subject, condition, value)
|
226
|
+
opts = value.join(',')
|
67
227
|
|
68
|
-
|
228
|
+
case condition
|
229
|
+
when IS
|
230
|
+
"#{subject} IN (#{opts})"
|
231
|
+
when NOT
|
232
|
+
"#{subject} NOT IN (#{opts})"
|
233
|
+
end
|
69
234
|
end
|
70
235
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
236
|
+
def date_present(subject, condition)
|
237
|
+
case condition
|
238
|
+
when IS
|
239
|
+
"#{subject} IS NOT NULL"
|
240
|
+
when NOT
|
241
|
+
"#{subject} IS NULL"
|
242
|
+
end
|
243
|
+
end
|
74
244
|
|
75
|
-
|
245
|
+
def date_equal(subject, condition, value)
|
246
|
+
case condition
|
247
|
+
when IS
|
248
|
+
"#{subject} = '#{value}'"
|
249
|
+
when NOT
|
250
|
+
"#{subject} != '#{value}'"
|
251
|
+
end
|
76
252
|
end
|
77
253
|
|
78
|
-
def
|
79
|
-
|
254
|
+
def date_gt(subject, condition, value)
|
255
|
+
case condition
|
256
|
+
when IS
|
257
|
+
"#{subject} > '#{value}'"
|
258
|
+
end
|
80
259
|
end
|
81
260
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
261
|
+
def date_gte(subject, condition, value)
|
262
|
+
case condition
|
263
|
+
when IS
|
264
|
+
"#{subject} >= '#{value}'"
|
265
|
+
end
|
266
|
+
end
|
85
267
|
|
86
|
-
|
87
|
-
|
268
|
+
def date_lt(subject, condition, value)
|
269
|
+
case condition
|
270
|
+
when IS
|
271
|
+
"#{subject} < '#{value}'"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def date_lte(subject, condition, value)
|
276
|
+
case condition
|
277
|
+
when IS
|
278
|
+
"#{subject} <= '#{value}'"
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def date_range(subject, condition, value)
|
283
|
+
min, max = value
|
284
|
+
|
285
|
+
case condition
|
286
|
+
when IS
|
287
|
+
"#{subject} >= '#{min}' AND #{subject} <= '#{max}'"
|
288
|
+
when NOT
|
289
|
+
"#{subject} < '#{min}' OR #{subject} > '#{max}'"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def date_in(subject, condition, value)
|
294
|
+
opts = value.join(',')
|
295
|
+
|
296
|
+
case condition
|
297
|
+
when IS
|
298
|
+
"#{subject} IN (#{opts})"
|
299
|
+
when NOT
|
300
|
+
"#{subject} NOT IN (#{opts})"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def boolean_equal(subject, condition, value)
|
305
|
+
flag = value.to_s == 'true' ? 't' : 'f'
|
306
|
+
|
307
|
+
case condition
|
308
|
+
when IS
|
309
|
+
"#{subject} = #{flag}"
|
310
|
+
when NOT
|
311
|
+
"#{subject} != #{flag}"
|
312
|
+
end
|
88
313
|
end
|
89
314
|
|
90
315
|
end
|
@@ -4,28 +4,18 @@ module GrapeListing
|
|
4
4
|
private
|
5
5
|
|
6
6
|
def sort_proc
|
7
|
-
|
8
|
-
if !@sortable.is_a?(Array) || @sortable.empty? || custom_sorting_field.blank?
|
9
|
-
return proc { order('id desc') }
|
10
|
-
end
|
11
|
-
|
7
|
+
@sort_by = @params['sort_by'] || 'id'
|
12
8
|
@sort_order = @params['sort_order'] || 'desc'
|
13
|
-
|
9
|
+
custom_scope = @custom_sort[@sort_by]
|
14
10
|
|
15
|
-
if
|
16
|
-
|
17
|
-
proc { send(sorting_scope, direction) }
|
11
|
+
if custom_scope
|
12
|
+
proc { send(custom_scope, @sort_order) }
|
18
13
|
else
|
19
14
|
nulls_order = @sort_order.to_s.downcase == 'desc' ? 'LAST' : 'FIRST'
|
20
|
-
query = Arel.sql("#{
|
15
|
+
query = Arel.sql("#{@sort_by} #{@sort_order} NULLS #{nulls_order}")
|
21
16
|
proc { order(query) }
|
22
17
|
end
|
23
18
|
end
|
24
19
|
|
25
|
-
# поиск поля для индивидуальной сортировки среди допустимых
|
26
|
-
def custom_sorting_field
|
27
|
-
@custom_sorting_field ||= @sortable.select { |i| i.split('|')[0] == @params['sort_by'] }.first
|
28
|
-
end
|
29
|
-
|
30
20
|
end
|
31
21
|
end
|
@@ -97,7 +97,6 @@ module GrapeListing
|
|
97
97
|
def spreadsheet_titles
|
98
98
|
values = []
|
99
99
|
entity_doc = @grape_entity.documentation
|
100
|
-
model_cols = @model.columns_hash
|
101
100
|
|
102
101
|
spreadsheet_cols.each do |column|
|
103
102
|
# исключение: колонка с ИД
|
@@ -107,7 +106,7 @@ module GrapeListing
|
|
107
106
|
end
|
108
107
|
|
109
108
|
# поиск описания среди комментариев к таблице БД
|
110
|
-
db_col = model_cols[column.to_s]
|
109
|
+
db_col = @model_cols[column.to_s]
|
111
110
|
if db_col&.comment&.present?
|
112
111
|
values << db_col.comment
|
113
112
|
next
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape-listing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0.pre.beta.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Павел Бабин
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04
|
11
|
+
date: 2025-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -100,12 +100,12 @@ files:
|
|
100
100
|
- lib/listing_service/serialization.rb
|
101
101
|
- lib/listing_service/sorting.rb
|
102
102
|
- lib/listing_service/spreadsheet.rb
|
103
|
-
homepage:
|
103
|
+
homepage:
|
104
104
|
licenses:
|
105
105
|
- Copyright
|
106
106
|
metadata:
|
107
107
|
rubygems_mfa_required: 'true'
|
108
|
-
post_install_message:
|
108
|
+
post_install_message:
|
109
109
|
rdoc_options: []
|
110
110
|
require_paths:
|
111
111
|
- lib
|
@@ -116,12 +116,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
116
116
|
version: 3.1.0
|
117
117
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
118
|
requirements:
|
119
|
-
- - "
|
119
|
+
- - ">"
|
120
120
|
- !ruby/object:Gem::Version
|
121
|
-
version:
|
121
|
+
version: 1.3.1
|
122
122
|
requirements: []
|
123
|
-
rubygems_version: 3.
|
124
|
-
signing_key:
|
123
|
+
rubygems_version: 3.3.3
|
124
|
+
signing_key:
|
125
125
|
specification_version: 4
|
126
126
|
summary: Формирование списков записей для API на базе Grape
|
127
127
|
test_files: []
|