bbk-utils 1.1.5.353947 → 1.1.6.359865
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/Gemfile.lock +8 -6
- data/README.md +19 -5
- data/lib/bbk/utils/cli/bbkdocs.yml +56 -0
- data/lib/bbk/utils/cli/docs/builder.rb +149 -0
- data/lib/bbk/utils/cli/docs/markdown.rb +214 -0
- data/lib/bbk/utils/cli/docs.rb +151 -0
- data/lib/bbk/utils/cli.rb +8 -0
- data/lib/bbk/utils/config.rb +12 -6
- data/lib/bbk/utils/tasks/docs.rake +16 -0
- data/lib/bbk/utils/version.rb +1 -1
- data/lib/bbk/utils.rb +8 -0
- metadata +8 -5
- data/bin/console +0 -16
- data/bin/setup +0 -8
- data/lib/bbk/utils/cli/env_docs.rb +0 -163
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7fe5c8597f68375fe0f5bc85ec04e99894353fc7f6b558bf7bea52abff51c0eb
|
|
4
|
+
data.tar.gz: ba14ee452fe704de69c9e4f1e378f4f6962f09245b2326fa3173ff8320e834b4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eed9ae4a2d2f7562a8bea56deafdbbb27c4ccffcbef7f04729d432ee2eaf7c7b288e6185cc3cf59f656f7c06f9b6d32d85ec5cfad8a2de44490924f0de77c191
|
|
7
|
+
data.tar.gz: 84ecb583308143da125d3cd4106e5794f186c2f1ca02cd064cfdc07cb4b8f0d884372faa30fbf8e3a94f36be6c1a1025bd2427b37a03d15aa624a72af3496e63
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
bbk-utils (1.1.
|
|
4
|
+
bbk-utils (1.1.6)
|
|
5
5
|
activesupport (>= 7.0)
|
|
6
6
|
russian
|
|
7
7
|
|
|
@@ -36,7 +36,7 @@ GEM
|
|
|
36
36
|
coercible (1.0.0)
|
|
37
37
|
descendants_tracker (~> 0.0.1)
|
|
38
38
|
concurrent-ruby (1.2.2)
|
|
39
|
-
connection_pool (
|
|
39
|
+
connection_pool (3.0.2)
|
|
40
40
|
descendants_tracker (0.0.4)
|
|
41
41
|
thread_safe (~> 0.3, >= 0.3.1)
|
|
42
42
|
diff-lcs (1.6.0)
|
|
@@ -62,7 +62,8 @@ GEM
|
|
|
62
62
|
launchy (2.5.0)
|
|
63
63
|
addressable (~> 2.7)
|
|
64
64
|
logger (1.7.0)
|
|
65
|
-
minitest (6.0.
|
|
65
|
+
minitest (6.0.2)
|
|
66
|
+
drb (~> 2.0)
|
|
66
67
|
prism (~> 1.5)
|
|
67
68
|
mutex_m (0.3.0)
|
|
68
69
|
ostruct (0.6.3)
|
|
@@ -110,7 +111,8 @@ GEM
|
|
|
110
111
|
rubocop-rspec (2.6.0)
|
|
111
112
|
rubocop (~> 1.19)
|
|
112
113
|
ruby-progressbar (1.11.0)
|
|
113
|
-
ruby_parser (3.
|
|
114
|
+
ruby_parser (3.22.0)
|
|
115
|
+
racc (~> 1.5)
|
|
114
116
|
sexp_processor (~> 4.16)
|
|
115
117
|
rubycritic (4.6.1)
|
|
116
118
|
flay (~> 2.8)
|
|
@@ -126,7 +128,7 @@ GEM
|
|
|
126
128
|
russian (0.6.0)
|
|
127
129
|
i18n (>= 0.5.0)
|
|
128
130
|
securerandom (0.4.1)
|
|
129
|
-
sexp_processor (4.
|
|
131
|
+
sexp_processor (4.17.5)
|
|
130
132
|
simplecov (0.21.2)
|
|
131
133
|
docile (~> 1.1)
|
|
132
134
|
simplecov-html (~> 0.11)
|
|
@@ -176,4 +178,4 @@ DEPENDENCIES
|
|
|
176
178
|
terminal-table
|
|
177
179
|
|
|
178
180
|
BUNDLED WITH
|
|
179
|
-
|
|
181
|
+
2.7.2
|
data/README.md
CHANGED
|
@@ -37,14 +37,28 @@ Or adding to your project:
|
|
|
37
37
|
gem "bbk-utils", "~> 1.0.0"
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
## Features
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
### bbkdocs
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Создать `bin/bbkdocs`:
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
#!/usr/bin/env ruby
|
|
48
|
+
|
|
49
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
|
|
50
|
+
require 'bbk/utils/cli'
|
|
51
|
+
|
|
52
|
+
BBK::Utils::Cli::Docs.new(ARGV).run
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Добавить в `Rakefile`:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
# Загружаем таски из BBK::Utils. В частности генерацию документации
|
|
59
|
+
BBK::Utils.load_tasks
|
|
60
|
+
```
|
|
45
61
|
|
|
46
|
-
* Ruby (MRI) 2.5.x
|
|
47
|
-
* Ruby (MRI) 3.0.x
|
|
48
62
|
|
|
49
63
|
## Contributing
|
|
50
64
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
categories:
|
|
2
|
+
transport_amqp:
|
|
3
|
+
name: Transport Amqp
|
|
4
|
+
desc: Настройки подключения к RabbitMQ и работы с очередями
|
|
5
|
+
order: 10
|
|
6
|
+
patterns:
|
|
7
|
+
- AMQP_
|
|
8
|
+
- MQ_
|
|
9
|
+
envs:
|
|
10
|
+
- CA_CERT
|
|
11
|
+
- CLIENT_CERT
|
|
12
|
+
- CLIENT_KEY
|
|
13
|
+
|
|
14
|
+
database:
|
|
15
|
+
name: База данных
|
|
16
|
+
desc: Настройки подключения к PostgreSQL
|
|
17
|
+
order: 20
|
|
18
|
+
patterns:
|
|
19
|
+
- DATABASE_
|
|
20
|
+
- REQUEST_CREATE_PARTITION_
|
|
21
|
+
|
|
22
|
+
redis:
|
|
23
|
+
name: Redis
|
|
24
|
+
desc: Настройки подключения к Redis для кэширования и очередей
|
|
25
|
+
order: 30
|
|
26
|
+
patterns:
|
|
27
|
+
- REDIS_
|
|
28
|
+
|
|
29
|
+
discovery:
|
|
30
|
+
name: Обнаружение сервисов
|
|
31
|
+
order: 40
|
|
32
|
+
patterns:
|
|
33
|
+
- CONSUL_
|
|
34
|
+
|
|
35
|
+
app:
|
|
36
|
+
name: Настройки приложения
|
|
37
|
+
order: 50
|
|
38
|
+
envs:
|
|
39
|
+
- RAILS_ENV
|
|
40
|
+
- LOG_LEVEL
|
|
41
|
+
|
|
42
|
+
filator:
|
|
43
|
+
name: Файловое хранилище
|
|
44
|
+
desc: Настройки интеграции с файловым хранилищем
|
|
45
|
+
order: 60
|
|
46
|
+
patterns:
|
|
47
|
+
- FILATOR_
|
|
48
|
+
|
|
49
|
+
monitoring:
|
|
50
|
+
name: Мониторинг
|
|
51
|
+
desc: Настройки систем мониторинга и трейсинга
|
|
52
|
+
order: 70
|
|
53
|
+
patterns:
|
|
54
|
+
- ELASTIC_
|
|
55
|
+
- JAEGER_
|
|
56
|
+
- PROMETHEUS_
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
module BBK
|
|
2
|
+
module Utils
|
|
3
|
+
module Cli
|
|
4
|
+
# Класс-строитель документации по категориям конфигурации
|
|
5
|
+
#
|
|
6
|
+
# Группирует конфигурационные параметры по категориям,
|
|
7
|
+
# обеспечивает сортировку и генерацию документации в различных форматах
|
|
8
|
+
class Docs::Builder
|
|
9
|
+
# Инициализирует строитель документации
|
|
10
|
+
#
|
|
11
|
+
# @param bbk [Hash] конфигурация BBK
|
|
12
|
+
# @param config [Hash] конфигурация документирования
|
|
13
|
+
# @option config [Hash] :categories настройки категорий
|
|
14
|
+
def initialize(bbk, config)
|
|
15
|
+
@bbk = bbk
|
|
16
|
+
@config = config
|
|
17
|
+
|
|
18
|
+
@categories = config[:categories].each_with_object({}) do |(name, c), cats|
|
|
19
|
+
cats[name.to_s] = Category.new(id: name.to_s, **c)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@default = Category.new(id: 'other', name: 'Other')
|
|
23
|
+
@categories[@default.id] = @default
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Выполняет разбор конфигурации по категориям
|
|
27
|
+
#
|
|
28
|
+
# @return [self] возвращает сам объект для цепочного вызова
|
|
29
|
+
def run
|
|
30
|
+
@bbk.each do |(env_name, cfg)|
|
|
31
|
+
category = @categories[cfg[:category]]
|
|
32
|
+
category ||= @categories.values.find { |c| c.match?(env_name) } || @default
|
|
33
|
+
|
|
34
|
+
category.add(cfg)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@sorted = @categories.values.sort_by { |category| [category.order, category.id.to_s] }
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Преобразует документацию в JSON-хеш
|
|
42
|
+
#
|
|
43
|
+
# @param _args [Array] игнорируемые аргументы
|
|
44
|
+
# @param _kwargs [Hash] игнорируемые именованные аргументы
|
|
45
|
+
# @return [Hash] хеш со структурой документации по категориям
|
|
46
|
+
def as_json(*_args, **_kwargs)
|
|
47
|
+
@sorted.each_with_object({}) do |category, result|
|
|
48
|
+
result[category.id] = category.as_json
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Преобразует документацию в JSON-строку
|
|
53
|
+
#
|
|
54
|
+
# @param args [Array] аргументы для JSON-генерации
|
|
55
|
+
# @param kwargs [Hash] именованные аргументы для JSON-генерации
|
|
56
|
+
# @return [String] JSON-строка документации
|
|
57
|
+
def to_json(*args, **kwargs)
|
|
58
|
+
as_json.to_json(*args, **kwargs)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Генерирует документацию в формате Markdown
|
|
62
|
+
#
|
|
63
|
+
# @return [String] markdown-разметка полной документации
|
|
64
|
+
def to_markdown
|
|
65
|
+
markdown_opts = {
|
|
66
|
+
columns: { env: 'Название', _class: 'Тип', desc: 'Описание', default: 'Умолчание' },
|
|
67
|
+
alignments: { 1 => :center, 3 => :center }, # :left, :right, :center для каждой колонки
|
|
68
|
+
wrappers: { 1 => '`', 3 => '`' }, # символ или строка для обрамления значений колонки, например: "`", "```", "**"
|
|
69
|
+
title_level: 4, # уровень заголовка от 1 до 6
|
|
70
|
+
warning: {
|
|
71
|
+
column_index: 2,
|
|
72
|
+
# mode: :footnote # :footnote или :inline
|
|
73
|
+
mode: :inline # :footnote или :inline
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
generator = Docs::Markdown.new(markdown_opts)
|
|
78
|
+
|
|
79
|
+
@sorted.map do |category|
|
|
80
|
+
category.cfgs.any? ? generator.generate(category) : ''
|
|
81
|
+
end.join("\n")
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Структура категории для группировки конфигураций
|
|
86
|
+
#
|
|
87
|
+
# @!attribute [r] id
|
|
88
|
+
# @return [String] идентификатор категории
|
|
89
|
+
# @!attribute [r] name
|
|
90
|
+
# @return [String] название категории
|
|
91
|
+
# @!attribute [r] desc
|
|
92
|
+
# @return [String] описание категории
|
|
93
|
+
# @!attribute [r] envs
|
|
94
|
+
# @return [Array<String>] список переменных окружения категории
|
|
95
|
+
# @!attribute [r] order
|
|
96
|
+
# @return [Numeric] порядок сортировки
|
|
97
|
+
# @!attribute [r] patterns
|
|
98
|
+
# @return [Array<String>] шаблоны для поиска элементов категории
|
|
99
|
+
# @!attribute [r] cfgs
|
|
100
|
+
# @return [Array<Hash>] конфигурации категории
|
|
101
|
+
Category = Struct.new(:id, :name, :desc, :envs, :order, :patterns, :cfgs, keyword_init: true) do
|
|
102
|
+
# Инициализирует категорию
|
|
103
|
+
#
|
|
104
|
+
# @param kwargs [Hash] параметры категории
|
|
105
|
+
# @option kwargs [String] :id идентификатор категории
|
|
106
|
+
# @option kwargs [String] :name название категории
|
|
107
|
+
# @option kwargs [String] :desc описание категории
|
|
108
|
+
# @option kwargs [Array<String>] :envs переменные окружения
|
|
109
|
+
# @option kwargs [Numeric] :order порядок сортировки
|
|
110
|
+
# @option kwargs [Array<String>] :patterns шаблоны поиска
|
|
111
|
+
# @option kwargs [Array<Hash>] :cfgs конфигурации
|
|
112
|
+
def initialize(**kwargs)
|
|
113
|
+
kwargs[:id] = kwargs[:id].to_s
|
|
114
|
+
kwargs[:name] = kwargs[:name].to_s || kwargs[:id].capitalize
|
|
115
|
+
kwargs[:desc] = kwargs[:desc].to_s
|
|
116
|
+
|
|
117
|
+
kwargs[:patterns] ||= []
|
|
118
|
+
kwargs[:envs] ||= []
|
|
119
|
+
kwargs[:order] ||= Float::INFINITY
|
|
120
|
+
kwargs[:cfgs] ||= []
|
|
121
|
+
|
|
122
|
+
super
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Проверяет соответствие имени переменной окружающей категории
|
|
126
|
+
#
|
|
127
|
+
# @param env_name [String, Symbol] имя переменной окружения
|
|
128
|
+
# @return [Boolean] true если соответствует категории
|
|
129
|
+
def match?(env_name)
|
|
130
|
+
return true if envs.any? { |e| e == env_name.to_s.strip }
|
|
131
|
+
|
|
132
|
+
return true if patterns.any? { |p| env_name.to_s.strip.start_with?(p) }
|
|
133
|
+
|
|
134
|
+
false
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Добавляет конфигурацию в категорию
|
|
138
|
+
#
|
|
139
|
+
# @param cfg [Hash] конфигурация для добавления
|
|
140
|
+
# @return [self] возвращает сам объект для цепочного вызова
|
|
141
|
+
def add(cfg)
|
|
142
|
+
cfgs << cfg
|
|
143
|
+
self.cfgs = cfgs.sort_by { |c| c[:env] }
|
|
144
|
+
self
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
module BBK
|
|
2
|
+
module Utils
|
|
3
|
+
module Cli
|
|
4
|
+
# Класс для генерации документации в формате Markdown
|
|
5
|
+
#
|
|
6
|
+
# Создает таблицы с переменными окружения, параметрами конфигурации и т.д.
|
|
7
|
+
# Поддерживает настраиваемые колонки, выравнивание, обрамление значений
|
|
8
|
+
# и предупреждения в двух режимах: сноски или встроенные
|
|
9
|
+
class Docs::Markdown
|
|
10
|
+
# Генерация markdown-документации для категории
|
|
11
|
+
# category_data = {
|
|
12
|
+
# category_data: {
|
|
13
|
+
# },
|
|
14
|
+
# env_vars: category.env_vars
|
|
15
|
+
# }
|
|
16
|
+
#
|
|
17
|
+
# options = {
|
|
18
|
+
# columns: { env: "Название"},
|
|
19
|
+
# alignments: { 1 => :center, 3 => :center }, # :left, :right, :center для каждой колонки
|
|
20
|
+
# wrappers: { 1 => "`", 3 => "`" }, # wrappers - символ или строка для обрамления значений колонки, например: "`", "```", "**"
|
|
21
|
+
# title_level: 4, # уровень заголовка от 1 до 6
|
|
22
|
+
# warning: {
|
|
23
|
+
# column_index: 3, # индекс колонки для вывода предупреждений
|
|
24
|
+
# mode: :footnote # или :inline
|
|
25
|
+
# }
|
|
26
|
+
# }
|
|
27
|
+
|
|
28
|
+
attr_reader :opts
|
|
29
|
+
|
|
30
|
+
# Инициализирует генератор Markdown
|
|
31
|
+
#
|
|
32
|
+
# @param opts [Hash] опции генерации
|
|
33
|
+
# @option opts [Hash] :columns отображаемые колонки с заголовками
|
|
34
|
+
# @option opts [Hash] :alignments выравнивание для каждой колонки
|
|
35
|
+
# @option opts [Hash] :wrappers обрамление значений колонок
|
|
36
|
+
# @option opts [Integer] :title_level уровень заголовка (1-6)
|
|
37
|
+
# @option opts [Hash] :warning настройки вывода предупреждений
|
|
38
|
+
def initialize(opts = {})
|
|
39
|
+
@opts = opts
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Генерирует markdown-документацию для заданной категории
|
|
43
|
+
#
|
|
44
|
+
# @param category [Docs::Builder::Category] категория конфигурации
|
|
45
|
+
# @return [String] markdown-разметка документации
|
|
46
|
+
def generate(category)
|
|
47
|
+
# Соберем заголовок
|
|
48
|
+
level = opts[:title_level].clamp(1, 6)
|
|
49
|
+
title = "#{'#' * level} (#{category.id}) #{category.name}"
|
|
50
|
+
desc = category.desc.to_s
|
|
51
|
+
|
|
52
|
+
# Конвертируем данные в формат для таблицы
|
|
53
|
+
rows, footnotes = generate_rows(category, opts)
|
|
54
|
+
# Создаем таблицу с заголовком и описанием
|
|
55
|
+
|
|
56
|
+
table = generate_table(opts[:columns].values, opts[:alignments], rows)
|
|
57
|
+
[
|
|
58
|
+
title,
|
|
59
|
+
'',
|
|
60
|
+
desc,
|
|
61
|
+
'',
|
|
62
|
+
*table,
|
|
63
|
+
'',
|
|
64
|
+
*footnotes
|
|
65
|
+
].join("\n")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Генерирует строки для таблицы документации
|
|
69
|
+
#
|
|
70
|
+
# @param category [Docs::Builder::Category] категория конфигурации
|
|
71
|
+
# @param opts [Hash] опции генерации
|
|
72
|
+
# @return [Array] массив строк и сносок
|
|
73
|
+
def generate_rows(category, opts)
|
|
74
|
+
# извлекаем даныне
|
|
75
|
+
rows = category.cfgs.map do |cfg|
|
|
76
|
+
opts[:columns].keys.each_with_object([]) do |key, row|
|
|
77
|
+
row << cfg[key.to_sym]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Применяем обрамление к значениям
|
|
82
|
+
rows = wrap_row_values(rows, opts[:wrappers])
|
|
83
|
+
|
|
84
|
+
# Применяем форматирование предупреждений
|
|
85
|
+
rows, footnotes = process_warnings(rows, category, opts[:warning])
|
|
86
|
+
|
|
87
|
+
[rows, footnotes]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Применяет обрамление к значениям в строках таблицы
|
|
91
|
+
#
|
|
92
|
+
# @param rows [Array<Array>] строки таблицы
|
|
93
|
+
# @param wrappers [Hash] хеш обрамления по индексам колонок
|
|
94
|
+
# @return [Array<Array>] строки с обрамленными значениями
|
|
95
|
+
def wrap_row_values(rows, wrappers)
|
|
96
|
+
return rows if wrappers.empty?
|
|
97
|
+
|
|
98
|
+
rows.map do |row|
|
|
99
|
+
row.each_with_index.map do |value, index|
|
|
100
|
+
wrapper = wrappers[index]
|
|
101
|
+
if wrapper && !value.nil? # может быть пустым
|
|
102
|
+
"#{wrapper}#{value}#{wrapper}"
|
|
103
|
+
else
|
|
104
|
+
value
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Обрабатывает предупреждения для строк таблицы
|
|
111
|
+
#
|
|
112
|
+
# @param rows [Array<Array>] строки таблицы
|
|
113
|
+
# @param category [Docs::Builder::Category] категория конфигурации
|
|
114
|
+
# @param warning_opts [Hash] опции обработки предупреждений
|
|
115
|
+
# @option warning_opts [Integer] :column_index индекс колонки для предупреждений
|
|
116
|
+
# @option warning_opts [Symbol] :mode режим вывода (:footnote или :inline)
|
|
117
|
+
# @return [Array] кортеж из обработанных строк и массива сносок
|
|
118
|
+
def process_warnings(rows, category, warning_opts)
|
|
119
|
+
return [rows, []] if !warning_opts || !warning_opts[:column_index]
|
|
120
|
+
|
|
121
|
+
column_index = warning_opts[:column_index]
|
|
122
|
+
mode = warning_opts[:mode] || :footnote
|
|
123
|
+
footnotes = []
|
|
124
|
+
footnote_counter = 1
|
|
125
|
+
|
|
126
|
+
processed_rows = rows.each_with_index.map do |row, row_index|
|
|
127
|
+
cfg = category.cfgs[row_index]
|
|
128
|
+
warning = cfg[:warning]
|
|
129
|
+
|
|
130
|
+
if warning && !warning.empty?
|
|
131
|
+
case mode
|
|
132
|
+
when :footnote
|
|
133
|
+
# Добавляем номер сноски
|
|
134
|
+
id = "#{category.id}_#{footnote_counter}"
|
|
135
|
+
row[column_index] = "#{row[column_index]} [^#{id}]"
|
|
136
|
+
footnotes << "[^#{id}]: #{warning}"
|
|
137
|
+
footnote_counter += 1
|
|
138
|
+
when :inline
|
|
139
|
+
# Добавляем предупреждение жирным текстом с переносом строки
|
|
140
|
+
row[column_index] = "#{row[column_index]}<br>⚠️ **#{warning}**"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
row
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
[processed_rows, footnotes]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Генерирует таблицу в формате Markdown
|
|
150
|
+
#
|
|
151
|
+
# @param headers [Array<String>] заголовки колонок
|
|
152
|
+
# @param alignments [Hash] выравнивание для каждой колонки
|
|
153
|
+
# @param rows [Array<Array>] строки данных таблицы
|
|
154
|
+
# @return [Array<String>] массив строк markdown-таблицы
|
|
155
|
+
def generate_table(headers = [], alignments = {}, rows)
|
|
156
|
+
# Вычисляем ширину колонок
|
|
157
|
+
column_widths = if headers.any?
|
|
158
|
+
headers.map(&:size)
|
|
159
|
+
else
|
|
160
|
+
Array.new(rows[0].count, 0)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
rows.each do |row|
|
|
164
|
+
row.each_with_index do |data, i|
|
|
165
|
+
column_widths[i] = data.to_s.size if data.to_s.size > column_widths[i]
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Создаем заголовок таблицы
|
|
170
|
+
if headers.any?
|
|
171
|
+
header_string = '|'
|
|
172
|
+
separator_string = '|'
|
|
173
|
+
|
|
174
|
+
headers.each_with_index do |header, col|
|
|
175
|
+
header_string += " #{header.ljust(column_widths[col])} |"
|
|
176
|
+
|
|
177
|
+
# Создаем разделитель с учетом выравнивания
|
|
178
|
+
separator_string += case alignments[col]
|
|
179
|
+
when :right
|
|
180
|
+
"#{'-' * (column_widths[col] + 1)}:|"
|
|
181
|
+
when :center
|
|
182
|
+
if column_widths[col] >= 1
|
|
183
|
+
":#{'-' * column_widths[col]}:|"
|
|
184
|
+
else
|
|
185
|
+
'::|'
|
|
186
|
+
end
|
|
187
|
+
when :left
|
|
188
|
+
":#{'-' * (column_widths[col] + 1)}|"
|
|
189
|
+
else
|
|
190
|
+
"#{'-' * (column_widths[col] + 2)}|"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Создаем строки данных
|
|
197
|
+
lines = rows.map do |row|
|
|
198
|
+
line = row.each_with_index.map do |value, col|
|
|
199
|
+
value.to_s.ljust(column_widths[col])
|
|
200
|
+
end.join(' | ')
|
|
201
|
+
|
|
202
|
+
"| #{line} |"
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
[
|
|
206
|
+
header_string,
|
|
207
|
+
separator_string,
|
|
208
|
+
*lines
|
|
209
|
+
]
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
module BBK
|
|
6
|
+
module Utils
|
|
7
|
+
module Cli
|
|
8
|
+
# Класс для генерации документации по конфигурации BBK
|
|
9
|
+
#
|
|
10
|
+
# Обеспечивает парсинг аргументов командной строки, загрузку конфигурации
|
|
11
|
+
# и генерацию документации в форматах JSON и Markdown
|
|
12
|
+
class Docs
|
|
13
|
+
# Инициализирует объект генератора документации
|
|
14
|
+
#
|
|
15
|
+
# @param argv [Array<String>] аргументы командной строки
|
|
16
|
+
def initialize(argv)
|
|
17
|
+
@argv = argv
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Основной метод запуска генерации документации
|
|
21
|
+
#
|
|
22
|
+
# @param argv [Array<String>] аргументы командной строки
|
|
23
|
+
# @return [void]
|
|
24
|
+
def run(argv = @argv)
|
|
25
|
+
options = parse!(argv)
|
|
26
|
+
|
|
27
|
+
bbk = extract_bbk_config
|
|
28
|
+
|
|
29
|
+
config = load_configuration("#{__dir__}/bbkdocs.yml", options[:config])
|
|
30
|
+
|
|
31
|
+
@builder = Builder.new(bbk, config).tap do |b|
|
|
32
|
+
b.run
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
FileUtils.mkdir_p File.dirname(options[:categories])
|
|
36
|
+
|
|
37
|
+
File.open(options[:categories], 'w') do |file|
|
|
38
|
+
file << JSON.pretty_generate(@builder.as_json)
|
|
39
|
+
puts "Documentation JSON saved to: #{options[:categories]}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
FileUtils.mkdir_p File.dirname(options[:output])
|
|
43
|
+
|
|
44
|
+
File.open(options[:output], 'w') do |file|
|
|
45
|
+
file << @builder.to_markdown
|
|
46
|
+
puts "Documentation MArkdown saved to: #{options[:output]}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Парсит аргументы командной строки
|
|
51
|
+
#
|
|
52
|
+
# @param argv [Array<String>] аргументы командной строки
|
|
53
|
+
# @return [Hash] хеш с опциями конфигурации
|
|
54
|
+
def parse!(argv)
|
|
55
|
+
options = {
|
|
56
|
+
config: './.bbkdocs.yml',
|
|
57
|
+
output: './bbkdocs.md',
|
|
58
|
+
categories: './bbkdocs.json'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
OptionParser.new do |opts|
|
|
62
|
+
opts.banner = 'Usage: bundle exec bin/bbkdocs [options]'
|
|
63
|
+
|
|
64
|
+
opts.on('-c', '--config CONFIG', "Path to config file (default: #{options[:config]})") do |path|
|
|
65
|
+
options[:config] = File.absolute_path(path.to_s.strip)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
opts.on('-o', '--output OUTPUT', "Path to output file (default: #{options[:output]})") do |path|
|
|
69
|
+
options[:output] = File.absolute_path(path.to_s.strip)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
opts.on('-g', '--categories CATEGORIES',
|
|
73
|
+
"Path to categories output file (default: #{options[:categories]})") do |path|
|
|
74
|
+
options[:categories] = File.absolute_path(path.to_s.strip)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
opts.on('-h', '--help', 'Prints this help') do
|
|
78
|
+
puts opts
|
|
79
|
+
exit
|
|
80
|
+
end
|
|
81
|
+
end.parse!(argv)
|
|
82
|
+
|
|
83
|
+
options
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Загружает и объединяет конфигурации из нескольких файлов
|
|
87
|
+
#
|
|
88
|
+
# @param files [Array<String>] пути к файлам конфигурации YAML
|
|
89
|
+
# @return [Hash] объединенная конфигурация с символизированными ключами
|
|
90
|
+
def load_configuration(*files)
|
|
91
|
+
files.each_with_object({}) do |file, config|
|
|
92
|
+
loaded = YAML.load_file(file).deep_symbolize_keys!
|
|
93
|
+
|
|
94
|
+
config.deep_merge!(loaded) do |key, oldv, newv|
|
|
95
|
+
result = newv
|
|
96
|
+
|
|
97
|
+
if oldv.is_a?(Hash) || newv.is_a?(Hash)
|
|
98
|
+
result = (oldv || {}).deep_merge(newv || {})
|
|
99
|
+
elsif oldv.is_a?(Array) || newv.is_a?(Array)
|
|
100
|
+
result = if key.to_s == 'patterns' || key.to_s == 'envs'
|
|
101
|
+
([oldv].flatten + [newv].flatten).compact.map(&:to_s).map(&:strip)
|
|
102
|
+
else
|
|
103
|
+
(newv || [])
|
|
104
|
+
end
|
|
105
|
+
else
|
|
106
|
+
newv
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
result
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Извлекает конфигурацию BBK и преобразует её в удобный формат
|
|
115
|
+
#
|
|
116
|
+
# @return [Hash] конфигурация BBK с дополнительной информацией о типах
|
|
117
|
+
def extract_bbk_config
|
|
118
|
+
bbk_cfg = BBK::Utils::Config.instance.as_json.deep_dup
|
|
119
|
+
bbk_cfg = bbk_cfg[BBK::Utils::Config.instance.name] unless BBK::Utils::Config.instance.name.nil?
|
|
120
|
+
|
|
121
|
+
BBK::Utils::Config.instance.send(:store_with_subconfigs).each do |k, v|
|
|
122
|
+
bbk_cfg[k][:default] = v[:default].original if v[:default]&.class&.to_s == 'Fugit::Duration'
|
|
123
|
+
|
|
124
|
+
bbk_cfg[k][:_class] = v[:default].class.to_s unless v[:default].nil?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
bbk_cfg.deep_symbolize_keys
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
require_relative 'docs/builder'
|
|
135
|
+
require_relative 'docs/markdown'
|
|
136
|
+
|
|
137
|
+
# пример одного элемента bbk_cfg после преобразований и до засовывания в Env::CategoryBuilder
|
|
138
|
+
# "BILLING_ENABLED": {
|
|
139
|
+
# "env": "BILLING_ENABLED",
|
|
140
|
+
# "file": null,
|
|
141
|
+
# "required": false,
|
|
142
|
+
# "default": false,
|
|
143
|
+
# "desc": "Send data to billing by api",
|
|
144
|
+
# "bool": true,
|
|
145
|
+
# "type": "#<Method: BBK::Utils::Config::BooleanCaster.cast(value) /home/user/dev/rndsoft/aggredator/consumers/bbk/utils/lib/bbk/utils/config.rb:28>",
|
|
146
|
+
# "secure": false,
|
|
147
|
+
# "category": null,
|
|
148
|
+
# "warning": null,
|
|
149
|
+
# "value": false,
|
|
150
|
+
# "_class": "FalseClass"
|
|
151
|
+
# },
|
data/lib/bbk/utils/config.rb
CHANGED
|
@@ -65,7 +65,7 @@ module BBK
|
|
|
65
65
|
@env_prefix = normalize_key(@prefixes.join(PREFIX_SEP))
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
def map(env, file, required: true, desc: nil, bool: false, key: nil, rewrite: true)
|
|
68
|
+
def map(env, file, required: true, desc: nil, bool: false, key: nil, rewrite: true, category: nil, warning: nil)
|
|
69
69
|
conf_key = full_prefixed_key(env)
|
|
70
70
|
return if @store.key?(conf_key) && !rewrite
|
|
71
71
|
|
|
@@ -75,11 +75,13 @@ module BBK
|
|
|
75
75
|
required: required,
|
|
76
76
|
desc: desc,
|
|
77
77
|
bool: bool,
|
|
78
|
-
type: nil
|
|
78
|
+
type: nil,
|
|
79
|
+
category: category,
|
|
80
|
+
warning: warning
|
|
79
81
|
}
|
|
80
82
|
end
|
|
81
83
|
|
|
82
|
-
def require(env, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false)
|
|
84
|
+
def require(env, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil)
|
|
83
85
|
raise ArgumentError.new('Specified type and bool') if bool && type.present?
|
|
84
86
|
|
|
85
87
|
type = BBK::Config::BooleanCaster.singleton_method(:cast) if bool
|
|
@@ -93,11 +95,13 @@ module BBK
|
|
|
93
95
|
desc: desc,
|
|
94
96
|
bool: bool,
|
|
95
97
|
type: type,
|
|
96
|
-
secure: secure
|
|
98
|
+
secure: secure,
|
|
99
|
+
category: category,
|
|
100
|
+
warning: warning
|
|
97
101
|
}
|
|
98
102
|
end
|
|
99
103
|
|
|
100
|
-
def optional(env, default: nil, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false)
|
|
104
|
+
def optional(env, default: nil, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil)
|
|
101
105
|
raise ArgumentError.new('Specified type and bool') if bool && type.present?
|
|
102
106
|
|
|
103
107
|
type = BBK::Utils::Config::BooleanCaster.singleton_method(:cast) if bool
|
|
@@ -112,7 +116,9 @@ module BBK
|
|
|
112
116
|
desc: desc,
|
|
113
117
|
bool: true,
|
|
114
118
|
type: type,
|
|
115
|
-
secure: secure
|
|
119
|
+
secure: secure,
|
|
120
|
+
category: category,
|
|
121
|
+
warning: warning
|
|
116
122
|
}
|
|
117
123
|
end
|
|
118
124
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
|
|
3
|
+
namespace :bbk do
|
|
4
|
+
namespace :utils do
|
|
5
|
+
desc "Generate documentation for environment variables (options: BBKDOCS_CONFIG=./.bbkdocs.yml, BBKDOCS_OUTPUT=./bbkdocs.md, BBKDOCS_CATEGORIES=./bbkdocs.json)"
|
|
6
|
+
task :docs do
|
|
7
|
+
args = []
|
|
8
|
+
args.concat(['-c', ENV['BBKDOCS_CONFIG']]) if ENV['BBKDOCS_CONFIG']
|
|
9
|
+
args.concat(['-o', ENV['BBKDOCS_OUTPUT']]) if ENV['BBKDOCS_OUTPUT']
|
|
10
|
+
args.concat(['-g', ENV['BBKDOCS_CATEGORIES']]) if ENV['BBKDOCS_CATEGORIES']
|
|
11
|
+
|
|
12
|
+
sh "ruby", "bin/bbkdocs", *args
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
data/lib/bbk/utils/version.rb
CHANGED
data/lib/bbk/utils.rb
CHANGED
|
@@ -36,10 +36,18 @@ module BBK
|
|
|
36
36
|
e.status
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def load_tasks
|
|
40
|
+
path = File.expand_path(__dir__)
|
|
41
|
+
Dir.glob("#{path}/utils/tasks/**/*.rake").each { |f| load f }
|
|
42
|
+
end
|
|
43
|
+
|
|
39
44
|
end
|
|
40
45
|
|
|
41
46
|
self.logger = ::BBK::Utils::Logger.default
|
|
42
47
|
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
43
51
|
end
|
|
44
52
|
end
|
|
45
53
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bbk-utils
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.6.359865
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samoilenko Yuri
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -258,10 +258,12 @@ files:
|
|
|
258
258
|
- Gemfile
|
|
259
259
|
- Gemfile.lock
|
|
260
260
|
- README.md
|
|
261
|
-
- bin/console
|
|
262
|
-
- bin/setup
|
|
263
261
|
- lib/bbk/utils.rb
|
|
264
|
-
- lib/bbk/utils/cli
|
|
262
|
+
- lib/bbk/utils/cli.rb
|
|
263
|
+
- lib/bbk/utils/cli/bbkdocs.yml
|
|
264
|
+
- lib/bbk/utils/cli/docs.rb
|
|
265
|
+
- lib/bbk/utils/cli/docs/builder.rb
|
|
266
|
+
- lib/bbk/utils/cli/docs/markdown.rb
|
|
265
267
|
- lib/bbk/utils/combined_logger.rb
|
|
266
268
|
- lib/bbk/utils/config.rb
|
|
267
269
|
- lib/bbk/utils/crypt.rb
|
|
@@ -269,6 +271,7 @@ files:
|
|
|
269
271
|
- lib/bbk/utils/log_formatter.rb
|
|
270
272
|
- lib/bbk/utils/logger.rb
|
|
271
273
|
- lib/bbk/utils/proxy_logger.rb
|
|
274
|
+
- lib/bbk/utils/tasks/docs.rake
|
|
272
275
|
- lib/bbk/utils/version.rb
|
|
273
276
|
- lib/bbk/utils/xml.rb
|
|
274
277
|
- sig/bbk/config.rbs
|
data/bin/console
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require 'bundler/setup'
|
|
5
|
-
require 'bbk/utils'
|
|
6
|
-
|
|
7
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
-
|
|
10
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
-
# require "pry"
|
|
12
|
-
# Pry.start
|
|
13
|
-
|
|
14
|
-
require 'irb'
|
|
15
|
-
IRB.start(__FILE__)
|
|
16
|
-
|
data/bin/setup
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
require 'yaml'
|
|
2
|
-
require 'optparse'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
module BBK
|
|
6
|
-
module Utils
|
|
7
|
-
module Cli
|
|
8
|
-
class EnvDocs
|
|
9
|
-
|
|
10
|
-
def self.run(*args) new.run(*args) end
|
|
11
|
-
|
|
12
|
-
def run(*args)
|
|
13
|
-
options = {
|
|
14
|
-
config: './.env_docs.yml',
|
|
15
|
-
output: './env_docs.md'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
OptionParser.new do |opts|
|
|
19
|
-
opts.banner = "Usage: env_docs [options]"
|
|
20
|
-
|
|
21
|
-
opts.on("-c", "--config CONFIG", "Path to config file (default: #{options[:config]})") do |c|
|
|
22
|
-
options[:config] = c
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
opts.on("-o", "--output OUTPUT", "Path to output file (default: #{options[:output]})") do |o|
|
|
26
|
-
options[:output] = o
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
opts.on("-h", "--help", "Prints this help") do
|
|
30
|
-
puts opts
|
|
31
|
-
exit
|
|
32
|
-
end
|
|
33
|
-
end.parse!(args)
|
|
34
|
-
|
|
35
|
-
output_file = Rails.root.join(options[:output])
|
|
36
|
-
config_file = Rails.root.join(options[:config])
|
|
37
|
-
|
|
38
|
-
if File.exist?(config_file)
|
|
39
|
-
config = YAML.load_file(config_file) || {}
|
|
40
|
-
else
|
|
41
|
-
puts "Config file not found: #{config_file}. Continuing without config."
|
|
42
|
-
config = {}
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
cfg = BBK::Utils::Config.instance.as_json.deep_dup
|
|
46
|
-
cfg = cfg[BBK::Utils::Config.instance.name] unless BBK::Utils::Config.instance.name.nil?
|
|
47
|
-
|
|
48
|
-
BBK::Utils::Config.instance.send(:store_with_subconfigs).each do |k, v|
|
|
49
|
-
if v[:default]&.class&.to_s == "Fugit::Duration"
|
|
50
|
-
cfg[k][:default] = v[:default].original
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
if v[:default]
|
|
54
|
-
cfg[k][:class] = "#{v[:default].class}"
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
env_prefix_order = config.fetch('env_prefix_order', [])
|
|
59
|
-
|
|
60
|
-
matching, remaining = separate(cfg, env_prefix_order)
|
|
61
|
-
|
|
62
|
-
File.open(output_file, 'w') do |file|
|
|
63
|
-
file.puts "# Переменные окружения\n\n"
|
|
64
|
-
file.puts to_markdown(matching)
|
|
65
|
-
file.puts "\n\n"
|
|
66
|
-
file.puts to_markdown(remaining.sort)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
puts "Documentation saved to: #{output_file}"
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def separate(cfg, env_prefix_order)
|
|
73
|
-
matching = {}
|
|
74
|
-
remaining = {}
|
|
75
|
-
|
|
76
|
-
cfg.each do |key, value|
|
|
77
|
-
matched = env_prefix_order.any? do |prefix|
|
|
78
|
-
key == prefix || key.start_with?(prefix + '_')
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
if matched
|
|
82
|
-
matching[key] = value
|
|
83
|
-
else
|
|
84
|
-
remaining[key] = value
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
[matching, remaining]
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def to_markdown(cfg, columns = { :env => "Название", :desc => "Описание", :default => "По умолчанию", :class => "Тип"} )
|
|
92
|
-
rows = cfg_to_array(cfg, columns.keys)
|
|
93
|
-
|
|
94
|
-
create_markdown_table(rows, columns.values)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def cfg_to_array(cfg, keys)
|
|
99
|
-
cfg.each_with_object([]) do |entry, rows|
|
|
100
|
-
rows << keys.each_with_object([]) do |key, row|
|
|
101
|
-
row << entry[1][key]
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def create_markdown_table(rows, headers = [])
|
|
107
|
-
return "| No data |" if rows.empty?
|
|
108
|
-
|
|
109
|
-
column_widths = if headers.size != 0
|
|
110
|
-
headers.map(&:size)
|
|
111
|
-
else
|
|
112
|
-
Array.new(rows[0].count, 0)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
rows.each do |row|
|
|
116
|
-
row.each_with_index do |string, i|
|
|
117
|
-
column_widths[i] = string.to_s.size if string.present? && string.to_s.size > column_widths[i]
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
markdown_table = []
|
|
122
|
-
|
|
123
|
-
# Создаем заголовок таблицы, если он есть
|
|
124
|
-
if headers.size != 0
|
|
125
|
-
header_string = "|"
|
|
126
|
-
separator_string = "|"
|
|
127
|
-
|
|
128
|
-
headers.each_with_index do |header, col|
|
|
129
|
-
header_string += " #{header.ljust(column_widths[col])} |"
|
|
130
|
-
separator_string += "-#{'-' * column_widths[col]}-|"
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
markdown_table << header_string
|
|
134
|
-
markdown_table << separator_string
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Создаем строки данных
|
|
138
|
-
rows_string = []
|
|
139
|
-
rows.each do |row|
|
|
140
|
-
row_string = "|"
|
|
141
|
-
row.each_with_index do |data, col|
|
|
142
|
-
value = (data || '').to_s
|
|
143
|
-
row_string += " #{value.ljust(column_widths[col])} |"
|
|
144
|
-
end
|
|
145
|
-
rows_string << row_string
|
|
146
|
-
end
|
|
147
|
-
markdown_table += rows_string
|
|
148
|
-
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# "BILLING_ENABLED"=>
|
|
156
|
-
# {:env=>"BILLING_ENABLED",
|
|
157
|
-
# :file=>nil,
|
|
158
|
-
# :required=>false,
|
|
159
|
-
# :default=>false,
|
|
160
|
-
# :desc=>"Send data to billing by api",
|
|
161
|
-
# :bool=>true,
|
|
162
|
-
# :type=>
|
|
163
|
-
# #<Method: BBK::Utils::Config::BooleanCaster.cast(value) /home/user/.asdf/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/bbk-utils-1.1.2.304819/lib/bbk/utils/config.rb:27
|