paginated 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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