paginated 1.0.8 → 1.0.9

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: 198ac3104064417c27a8177a6000b4344999b26715857a79b05c67f112f8a6b3
4
- data.tar.gz: 4e8b69b3de828f3a3295a7bfb638dd0c851ecb4e20f7d36d0cea406f1a7def46
3
+ metadata.gz: b9bcce808539f59666e9f47ac429c4fb081f8d1ead5152ceb9cf33ba7bcb1e00
4
+ data.tar.gz: 665b4534980a20527920130fb5b9593517f82ce0a7d30de05c0bbd756fa6f76a
5
5
  SHA512:
6
- metadata.gz: 51c867ec559e36bbadda8cf1f1d5658e840878a5a8d6d1670926e373457502e247698489a99841ce2f74637f6f25cd15b520bb00216cf98077d8f4a0b2b99c9f
7
- data.tar.gz: 0a18ebb83509c2c3be91f6ce1d793548f954b9b692db560239d40a118340cf6908d8704ba8dc89c8244313f9e67ce37d6ef4458c22bd0ff596ab7d63ac3a40d0
6
+ metadata.gz: a680390d0db51559a570dc52d373b6388dd9487bd179a572f75224b9d2b6d036e16cae112ea8d07478890926f6b01b38db3d8ccbcf7e3952939f21abb4546b13
7
+ data.tar.gz: 0a6e40dd565c4d6b7887bf7174bd6984b087f09785ea049ff67a95c3a36732bfd3b2516309e4b58a47d2647d1f2d8644746ec7939a08911e869940b5a0f3921c
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
- # Paginated
1
+ # Grape Paginated
2
+ [![Gem Version](https://badge.fury.io/rb/paginated.svg)](https://badge.fury.io/rb/paginated)
2
3
 
3
- Гем для пагинации и фильтрации записей.
4
+ Гем для пагинации и фильтрации записей с возможностью формирования электронных таблиц (XLSX) на базе Grape.
4
5
 
5
6
  ## Установка
6
7
 
@@ -24,9 +25,12 @@ bundle
24
25
  Например:
25
26
 
26
27
  ```
27
- paginated model: User,
28
- entity: UserEntity,
29
- search: %w[email name|ilike role|custom.for_role]
28
+ get 'users' do
29
+ paginated model: User,
30
+ entity: UserEntity,
31
+ search: %w[email name|ilike role|custom.for_role]
32
+ end
33
+
30
34
  ```
31
35
 
32
36
  Опции (с примерами):
@@ -70,3 +74,16 @@ paginated model: User,
70
74
  - `sort_order` направление, по которому должна осуществляться сортировка (`asc/desc`).
71
75
 
72
76
  По умолчанию сортировка осуществляется по `id` записей в направлении `DESC`.
77
+
78
+ ## Конфигурация
79
+
80
+ Добавьте файл конфигурации `config/initializers/grape_paginated.rb` с содержимым:
81
+
82
+ ```
83
+ GrapePaginated::Configuration.configure do |config|
84
+ end
85
+ ```
86
+
87
+ Доступные опции для конфигурирования:
88
+
89
+ `spreadsheet_limit` - максимальный размер формируемой электронной таблицы. По умолчанию отстутствует.
@@ -39,15 +39,16 @@ module Grape
39
39
  end
40
40
 
41
41
  def paginated_spreadsheet(opts)
42
- Dir.mktmpdir do |tempdir|
43
- file_path = Paginated.spreadsheet **opts.merge(tempdir:)
42
+ Dir.mktmpdir do |dir|
43
+ opts[:tempdir] = dir
44
+ file_path = Paginated.spreadsheet(**opts)
44
45
 
45
46
  spreadsheet_file_headers File.basename(file_path)
46
- File.open(file_path).read
47
+ File.read(file_path)
47
48
  end
48
49
  end
49
50
 
50
- def spreadsheet_file_headers(filename)
51
+ def spreadsheet_file_headers(filename)
51
52
  header['Content-Type'] =
52
53
  case File.extname(filename)
53
54
  when '.csv'
@@ -0,0 +1,63 @@
1
+ module GrapePaginated
2
+ module Args
3
+
4
+ private
5
+
6
+ def handle_args(**args)
7
+ # обрабатываемая Rails модель
8
+ @model = args[:model]
9
+
10
+ # параметры запроса и текущий пользователь
11
+ @params = args[:params].stringify_keys
12
+ @current_user = args[:current_user]
13
+
14
+ # поля, по которым возможен поиск
15
+ @search_fields = args[:search]
16
+
17
+ # сериализация - через Grape Entity или список полей
18
+ @grape_entity = args[:entity]
19
+ @fields = args[:fields]
20
+
21
+ # Rails scopes для применения
22
+ @scopes = args[:scopes] || {}
23
+
24
+ # offset / limit
25
+ @offset = @params['offset'].to_i
26
+ @limit = @params['limit'] || 20
27
+
28
+ # требуемый список полей для коллекции
29
+ @only_columns = args[:only_columns]
30
+
31
+ # список полей для эл. таблицы
32
+ @spreadsheet_columns = args[:spreadsheet_columns]
33
+
34
+ # сортировка
35
+ @sort_by = @params['sort_by'] || :id
36
+ @sort_order = @params['sort_order'] || :desc
37
+
38
+ # подсчет кол-ва записей
39
+ @objects_count = records_count
40
+
41
+ # временная директория (для генерации файлов)
42
+ @tempdir = args[:tempdir]
43
+ end
44
+
45
+ def records_count
46
+ # получение кол-ва записей из кеша
47
+ cache_key = "#{@model}_records_count"
48
+ cached = Rails.cache.read(cache_key)
49
+ return cached if cached
50
+
51
+ # получение кол-ва записей из запроса к БД
52
+ count = @model.merge(@scopes).count(:id)
53
+
54
+ # кеширование кол-ва записей при превышении порогового значения
55
+ if count >= 1_000
56
+ Rails.cache.write(cache_key, count, expires_in: 30.minutes)
57
+ end
58
+
59
+ count
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,58 @@
1
+ module GrapePaginated
2
+ module Collection
3
+
4
+ def collection
5
+ if @objects_count > 0
6
+ search
7
+ serialize
8
+ else
9
+ @objects = []
10
+ end
11
+
12
+ # результат с пагинацией
13
+ { count: @objects_count, objects: @objects }
14
+ end
15
+
16
+ private
17
+
18
+ def serialize
19
+ # коллекция записей ActiveRecord для применения аггрегирования
20
+ list = @objects || @model.unscoped.merge(@scopes)
21
+
22
+ # применение сортировки и пагинации к коллекции записей
23
+ @objects = list.offset(@offset).merge(sort_proc).limit(@limit)
24
+
25
+ # сериализация с помощью переданных полей или сериалайзера
26
+ if @fields
27
+ serialize_with_fields
28
+ elsif @grape_entity
29
+ serialize_with_entity
30
+ end
31
+ end
32
+
33
+ def serialize_with_fields
34
+ # добавление ID в список полей по умолчанию
35
+ @fields.push(:id)
36
+
37
+ # список массивов значений
38
+ @objects = @objects.map { |i| obtain_fields_values(i) }
39
+
40
+ # трансформация массивов значений в объекты (ключ-значение)
41
+ @objects = @objects.map { |i| @fields.zip(i).to_h }
42
+ end
43
+
44
+ def serialize_with_entity
45
+ opts = { current_user: @current_user }
46
+
47
+ # требуемый список полей (если был передан)
48
+ opts[:only] = @only_columns if @only_columns
49
+
50
+ @objects = @objects.map { |i| @grape_entity.represent(i, opts).as_json }
51
+ end
52
+
53
+ def obtain_fields_values(record)
54
+ @fields.map { |i| record.send(i) }
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,20 @@
1
+ module GrapePaginated
2
+ class Configuration
3
+
4
+ attr_accessor :spreadsheet_limit
5
+
6
+ def spreadsheet_limit
7
+ @spreadsheet_limit || Float::INFINITY
8
+ end
9
+
10
+ def self.configure
11
+ yield(config)
12
+ config
13
+ end
14
+
15
+ def self.config
16
+ @config ||= Configuration.new
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,99 @@
1
+ module GrapePaginated
2
+ module Search
3
+
4
+ private
5
+
6
+ def search
7
+ @search_fields&.each do |field|
8
+ # коллекция записей для дальнейшей фильтрации
9
+ list = @objects || @model.unscoped.merge(@scopes)
10
+
11
+ # оператор и значение для поиска
12
+ operator, value = search_operands(field)
13
+
14
+ # пропускаем, если параметр для поиска не передан
15
+ next if value.blank? || value == 'null'
16
+
17
+ # кастомный поиск
18
+ if operator.include?('custom') && value.present?
19
+ # название скоупа из модели, который нужно применить
20
+ scope_name = operator.split('.')[1]
21
+ # применение скоупа
22
+ @objects = list.send(scope_name, value)
23
+ @objects_count = @objects.count
24
+ next
25
+ end
26
+
27
+ # форматирование кавычек
28
+ if operator != 'IN' && !@model.defined_enums.key?(field)
29
+ value = quotations_formatting(value)
30
+ end
31
+
32
+ if field.include?('.')
33
+ # нужно приджойнить таблицу
34
+ join_assoc, field = field.split('.')
35
+ table = join_assoc.tableize
36
+ list = list.joins(join_assoc.to_sym)
37
+ else
38
+ table = @model.table_name
39
+ field = field.split('|')[0]
40
+ end
41
+
42
+ query = where_query(table, field, operator, value)
43
+ @objects = list.where(query)
44
+ @objects_count = @objects.count
45
+ end
46
+ end
47
+
48
+ def search_operands(field)
49
+ value = default_param_value(field)
50
+
51
+ # WHERE query operator
52
+ operator = field.split('|').size > 1 ? field.split('|').last : '='
53
+
54
+ # условие для поиска ILIKE
55
+ value = "%#{value}%" if operator == 'ilike' && value.present?
56
+
57
+ # проверка мульти-поиска по параметру с массивом
58
+ if operator == 'multi' && !value.blank?
59
+ operator = 'IN'
60
+ value = "(#{value.join(', ')})"
61
+ end
62
+
63
+ # проверка диапазона (MIN/MAX)
64
+ if %w[min max].include?(operator)
65
+ range_field = field.split('.').last.split(':')[0].try { |i| i.split('|')[0] }
66
+ value = @params["#{range_field}_#{operator}"]
67
+ operator = operator == 'min' ? '>=' : '<='
68
+ end
69
+
70
+ # check array operator
71
+ if operator == 'array' && value.present?
72
+ operator = '='
73
+ value = "{#{value}}"
74
+ end
75
+
76
+ [operator, value]
77
+ end
78
+
79
+ def default_param_value(field)
80
+ # название ключа-поля без оператора (напр. ilike)
81
+ key = field.split('|')[0]
82
+
83
+ @params[key]
84
+ end
85
+
86
+ def where_query(table, field, operator, value)
87
+ "#{table}.#{field} #{operator} #{value}"
88
+ end
89
+
90
+ def quotations_formatting(string)
91
+ # удаление одинарных кавычек (напр. внутри строки)
92
+ value = string.to_s.gsub("'", '')
93
+
94
+ # добавление одинарных кавычек вокруг строки
95
+ "'#{value}'"
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,12 @@
1
+ module GrapePaginated
2
+ module Sorting
3
+
4
+ private
5
+
6
+ def sort_proc
7
+ query = Arel.sql("#{@sort_by} #{@sort_order} NULLS LAST")
8
+ proc { order(query) }
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,69 @@
1
+ require 'fast_excel'
2
+
3
+ module GrapePaginated
4
+ module Spreadsheet
5
+
6
+ def spreadsheet
7
+ # поиск и фильтрация
8
+ search
9
+
10
+ # коллекция записей ActiveRecord для формирования эл. таблицы
11
+ records = @objects || @model.unscoped.merge(@scopes)
12
+
13
+ # exception в случае превышения лимита записей
14
+ raise StandardError.new('Кол-во записей превышает заданный лимит') if records.count > spreadsheet_limit
15
+
16
+ # формирования файла эл. таблицы
17
+ generate_spreadsheet(records)
18
+ end
19
+
20
+ private
21
+
22
+ def spreadsheet_limit
23
+ GrapePaginated::Configuration.config.spreadsheet_limit
24
+ end
25
+
26
+ def generate_spreadsheet(records)
27
+ filepath = "#{@tempdir}/#{Time.now.strftime('%Y%m%d_%H%M%S')}.xlsx"
28
+
29
+ # создание таблицы и ее конфигурация
30
+ workbook = FastExcel.open(filepath, constant_memory: true)
31
+ workbook.default_format.set(
32
+ font_size: 0,
33
+ font_family: 'Arial'
34
+ )
35
+
36
+ # стили
37
+ bold = workbook.bold_format
38
+
39
+ # лист
40
+ worksheet = workbook.add_worksheet('Основной')
41
+ worksheet.auto_width = true
42
+
43
+ # заголовки
44
+ worksheet.append_row(spreadsheet_titles, bold)
45
+
46
+ # содержимое
47
+ cols = spreadsheet_columns
48
+ records.order(:id).each do |record|
49
+ values = cols.map { |col| record.send(col) }
50
+ worksheet.append_row(values)
51
+ end
52
+
53
+ # закрытие/сохранение файла
54
+ workbook.close
55
+
56
+ # путь до файла для дальнейшей обработки
57
+ filepath
58
+ end
59
+
60
+ def spreadsheet_titles
61
+ @spreadsheet_columns.map { |i| i['title'] }
62
+ end
63
+
64
+ def spreadsheet_columns
65
+ @spreadsheet_columns.map { |i| i['column'] }
66
+ end
67
+
68
+ end
69
+ end
data/lib/paginated.rb CHANGED
@@ -1,18 +1,18 @@
1
- require 'paginated/args_andling'
2
- require 'paginated/collection'
3
- require 'paginated/search'
4
- require 'paginated/sorting'
5
- require 'paginated/spreadsheet'
6
-
7
- require 'grape_extensions/dsl'
1
+ require 'grape/dsl'
2
+ require 'grape-paginated/args'
3
+ require 'grape-paginated/collection'
4
+ require 'grape-paginated/configuration'
5
+ require 'grape-paginated/search'
6
+ require 'grape-paginated/sorting'
7
+ require 'grape-paginated/spreadsheet'
8
8
 
9
9
  class Paginated
10
10
 
11
- include ArgsHandling
12
- include Collection
13
- include Search
14
- include Sorting
15
- include Spreadsheet
11
+ include GrapePaginated::Args
12
+ include GrapePaginated::Collection
13
+ include GrapePaginated::Search
14
+ include GrapePaginated::Sorting
15
+ include GrapePaginated::Spreadsheet
16
16
 
17
17
  def initialize(**args)
18
18
  handle_args(**args)
metadata CHANGED
@@ -1,71 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paginated
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Павел Бабин
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-26 00:00:00.000000000 Z
11
+ date: 2024-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: grape
14
+ name: fast_excel
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.0
19
+ version: 0.5.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.0
26
+ version: 0.5.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: grape-entity
28
+ name: grape
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.0.0
33
+ version: 2.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.0.0
40
+ version: 2.0.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: addressable
42
+ name: grape-entity
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2.6'
47
+ version: 1.0.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '2.6'
54
+ version: 1.0.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: fast_excel
56
+ name: rails
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.5.0
61
+ version: '7.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.5.0
68
+ version: '7.0'
69
69
  description: Простая пагинация записей, поиск и формирование эл. таблиц для API на
70
70
  базе Grape
71
71
  email: babin359@gmail.com
@@ -74,17 +74,19 @@ extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
76
  - README.md
77
- - lib/grape_extensions/dsl.rb
77
+ - lib/grape-paginated/args.rb
78
+ - lib/grape-paginated/collection.rb
79
+ - lib/grape-paginated/configuration.rb
80
+ - lib/grape-paginated/search.rb
81
+ - lib/grape-paginated/sorting.rb
82
+ - lib/grape-paginated/spreadsheet.rb
83
+ - lib/grape/dsl.rb
78
84
  - lib/paginated.rb
79
- - lib/paginated/args_andling.rb
80
- - lib/paginated/collection.rb
81
- - lib/paginated/search.rb
82
- - lib/paginated/sorting.rb
83
- - lib/paginated/spreadsheet.rb
84
85
  homepage:
85
86
  licenses:
86
87
  - MIT
87
- metadata: {}
88
+ metadata:
89
+ rubygems_mfa_required: 'true'
88
90
  post_install_message:
89
91
  rdoc_options: []
90
92
  require_paths:
@@ -93,14 +95,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
93
95
  requirements:
94
96
  - - ">="
95
97
  - !ruby/object:Gem::Version
96
- version: 3.0.0
98
+ version: 3.1.0
97
99
  required_rubygems_version: !ruby/object:Gem::Requirement
98
100
  requirements:
99
101
  - - ">="
100
102
  - !ruby/object:Gem::Version
101
103
  version: '0'
102
104
  requirements: []
103
- rubygems_version: 3.0.6
105
+ rubygems_version: 3.4.10
104
106
  signing_key:
105
107
  specification_version: 4
106
108
  summary: Пагинация для API на базе Grape
@@ -1,63 +0,0 @@
1
- module ArgsHandling
2
-
3
- extend ActiveSupport::Concern
4
-
5
- private
6
-
7
- def handle_args(**args)
8
- # обрабатываемая Rails модель
9
- @model = args[:model]
10
-
11
- # параметры запроса и текущий пользователь
12
- @params = args[:params].stringify_keys
13
- @current_user = args[:current_user]
14
-
15
- # поля, по которым возможен поиск
16
- @search_fields = args[:search]
17
-
18
- # сериализация - через Grape Entity или список полей
19
- @grape_entity = args[:entity]
20
- @fields = args[:fields]
21
-
22
- # Rails scopes для применения
23
- @scopes = args[:scopes] || {}
24
-
25
- # offset / limit
26
- @offset = @params['offset'].to_i
27
- @limit = @params['limit'] || 20
28
-
29
- # требуемый список полей для коллекции
30
- @only_columns = args[:only_columns]
31
-
32
- # список полей для эл. таблицы
33
- @spreadsheet_columns = args[:spreadsheet_columns]
34
-
35
- # сортировка
36
- @sort_by = @params['sort_by'] || :id
37
- @sort_order = @params['sort_order'] || :desc
38
-
39
- # подсчет кол-ва записей
40
- @objects_count = records_count
41
-
42
- # временная директория (для генерации файлов)
43
- @tempdir = args[:tempdir]
44
- end
45
-
46
- def records_count
47
- # получение кол-ва записей из кеша
48
- cache_key = "#{@model}_records_count"
49
- cached = Rails.cache.read(cache_key)
50
- return cached if cached
51
-
52
- # получение кол-ва записей из запроса к БД
53
- count = @model.merge(@scopes).count(:id)
54
-
55
- # кеширование кол-ва записей при превышении порогового значения
56
- if count >= 1_000
57
- Rails.cache.write(cache_key, count, expires_in: 30.minutes)
58
- end
59
-
60
- count
61
- end
62
-
63
- end
@@ -1,58 +0,0 @@
1
- module Collection
2
-
3
- extend ActiveSupport::Concern
4
-
5
- def collection
6
- if @objects_count > 0
7
- search
8
- serialize
9
- else
10
- @objects = []
11
- end
12
-
13
- # результат с пагинацией
14
- { count: @objects_count, objects: @objects }
15
- end
16
-
17
- private
18
-
19
- def serialize
20
- # коллекция записей ActiveRecord для применения аггрегирования
21
- list = @objects || @model.unscoped.merge(@scopes)
22
-
23
- # применение сортировки и пагинации к коллекции записей
24
- @objects = list.offset(@offset).merge(sort_proc).limit(@limit)
25
-
26
- # сериализация с помощью переданных полей или сериалайзера
27
- if @fields
28
- serialize_with_fields
29
- elsif @grape_entity
30
- serialize_with_entity
31
- end
32
- end
33
-
34
- def serialize_with_fields
35
- # добавление ID в список полей по умолчанию
36
- @fields.push(:id)
37
-
38
- # список массивов значений
39
- @objects = @objects.map { |i| obtain_fields_values(i) }
40
-
41
- # трансформация массивов значений в объекты (ключ-значение)
42
- @objects = @objects.map { |i| @fields.zip(i).to_h }
43
- end
44
-
45
- def serialize_with_entity
46
- opts = { current_user: @current_user }
47
-
48
- # требуемый список полей (если был передан)
49
- opts[:only] = @only_columns if @only_columns
50
-
51
- @objects = @objects.map { |i| @grape_entity.represent(i, opts).as_json }
52
- end
53
-
54
- def obtain_fields_values(record)
55
- @fields.map { |i| record.send(i) }
56
- end
57
-
58
- end
@@ -1,99 +0,0 @@
1
- module Search
2
-
3
- extend ActiveSupport::Concern
4
-
5
- private
6
-
7
- def search
8
- @search_fields&.each do |field|
9
- # коллекция записей для дальнейшей фильтрации
10
- list = @objects || @model.unscoped.merge(@scopes)
11
-
12
- # оператор и значение для поиска
13
- operator, value = search_operands(field)
14
-
15
- # пропускаем, если параметр для поиска не передан
16
- next if value.blank? || value == 'null'
17
-
18
- # кастомный поиск
19
- if operator.include?('custom') && value.present?
20
- # название скоупа из модели, который нужно применить
21
- scope_name = operator.split('.')[1]
22
- # применение скоупа
23
- @objects = list.send(scope_name, value)
24
- @objects_count = @objects.count
25
- next
26
- end
27
-
28
- # форматирование кавычек
29
- if operator != 'IN' && !@model.defined_enums.key?(field)
30
- value = quotations_formatting(value)
31
- end
32
-
33
- if field.include?('.')
34
- # нужно приджойнить таблицу
35
- join_assoc, field = field.split('.')
36
- table = join_assoc.tableize
37
- list = list.joins(join_assoc.to_sym)
38
- else
39
- table = @model.table_name
40
- field = field.split('|')[0]
41
- end
42
-
43
- query = where_query(table, field, operator, value)
44
- @objects = list.where(query)
45
- @objects_count = @objects.count
46
- end
47
- end
48
-
49
- def search_operands(field)
50
- value = default_param_value(field)
51
-
52
- # WHERE query operator
53
- operator = field.split('|').size > 1 ? field.split('|').last : '='
54
-
55
- # условие для поиска ILIKE
56
- value = "%#{value}%" if operator == 'ilike' && value.present?
57
-
58
- # проверка мульти-поиска по параметру с массивом
59
- if operator == 'multi' && !value.blank?
60
- operator = 'IN'
61
- value = "(#{value.join(', ')})"
62
- end
63
-
64
- # проверка диапазона (MIN/MAX)
65
- if %w[min max].include?(operator)
66
- range_field = field.split('.').last.split(':')[0].try { |i| i.split('|')[0] }
67
- value = @params["#{range_field}_#{operator}"]
68
- operator = operator == 'min' ? '>=' : '<='
69
- end
70
-
71
- # check array operator
72
- if operator == 'array' && value.present?
73
- operator = '='
74
- value = "{#{value}}"
75
- end
76
-
77
- [operator, value]
78
- end
79
-
80
- def default_param_value(field)
81
- # название ключа-поля без оператора (напр. ilike)
82
- key = field.split('|')[0]
83
-
84
- @params[key]
85
- end
86
-
87
- def where_query(table, field, operator, value)
88
- "#{table}.#{field} #{operator} #{value}"
89
- end
90
-
91
- def quotations_formatting(string)
92
- # удаление одинарных кавычек (напр. внутри строки)
93
- value = string.to_s.gsub(/'/, '')
94
-
95
- # добавление одинарных кавычек вокруг строки
96
- "'#{value}'"
97
- end
98
-
99
- end
@@ -1,12 +0,0 @@
1
- module Sorting
2
-
3
- extend ActiveSupport::Concern
4
-
5
- private
6
-
7
- def sort_proc
8
- query = Arel.sql("#{@sort_by} #{@sort_order} NULLS LAST")
9
- proc { order(query) }
10
- end
11
-
12
- end
@@ -1,61 +0,0 @@
1
- require 'fast_excel'
2
-
3
- module Spreadsheet
4
-
5
- extend ActiveSupport::Concern
6
-
7
- def spreadsheet
8
- search if @objects_count > 0
9
-
10
- # коллекция записей ActiveRecord для формирования эл. таблицы
11
- records = @objects || @model.unscoped.merge(@scopes)
12
-
13
- # формирования файла эл. таблицы
14
- generate_spreadsheet(records)
15
- end
16
-
17
- private
18
-
19
- def generate_spreadsheet(records)
20
- filepath = "#{@tempdir}/#{Time.now.strftime('%Y%m%d_%H%M%S')}.xlsx"
21
-
22
- # создание таблицы и ее конфигурация
23
- workbook = FastExcel.open(filepath, constant_memory: true)
24
- workbook.default_format.set(
25
- font_size: 0,
26
- font_family: 'Arial'
27
- )
28
-
29
- # стили
30
- bold = workbook.bold_format
31
-
32
- # лист
33
- worksheet = workbook.add_worksheet('Основной')
34
- worksheet.auto_width = true
35
-
36
- # заголовки
37
- worksheet.append_row(spreadsheet_titles, bold)
38
-
39
- # содержимое
40
- cols = spreadsheet_columns
41
- records.each do |record|
42
- values = cols.map { |col| record.send(col) }
43
- worksheet.append_row(values)
44
- end
45
-
46
- # закрытие/сохранение файла
47
- workbook.close
48
-
49
- # путь до файла для дальнейшей обработки
50
- filepath
51
- end
52
-
53
- def spreadsheet_titles
54
- @spreadsheet_columns.map { |i| i['title'] }
55
- end
56
-
57
- def spreadsheet_columns
58
- @spreadsheet_columns.map { |i| i['column'] }
59
- end
60
-
61
- end