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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57d03b7ce638f9f4479a442502913d783709256374857e5240d29bdaead8e065
4
- data.tar.gz: 03ad2e62425fbba99d5b4fe0af1485770d1a6a1fdabd952b7a190bfb6178d2cf
3
+ metadata.gz: 7fe5c8597f68375fe0f5bc85ec04e99894353fc7f6b558bf7bea52abff51c0eb
4
+ data.tar.gz: ba14ee452fe704de69c9e4f1e378f4f6962f09245b2326fa3173ff8320e834b4
5
5
  SHA512:
6
- metadata.gz: a371a7e342f26ba6456d2417ca0ad7d6d1d427f3641eb885624f4b3003afd40dc30690b1fc9ffca8ede575d55c28aceb23a4db9569d59ef70e4311c98264e2b0
7
- data.tar.gz: 3469b76b5e933e19fdc2c703174eec2de0e697245438b3a23d04c175467ffde518926b3571b6748ad59eaa8c624131563fa8477055811176f757f774807bbe29
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.5)
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 (2.5.0)
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.1)
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.18.1)
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.16.0)
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
- 2.2.33
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
- ### Supported Ruby versions
40
+ ## Features
41
41
 
42
- * Ruby (MRI) >= 2.5.0
42
+ ### bbkdocs
43
43
 
44
- ### Tested Ruby versions
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
+ # },
@@ -0,0 +1,8 @@
1
+ require 'bbk/utils/cli/docs'
2
+
3
+ module BBK
4
+ module Utils
5
+ module Cli
6
+ end
7
+ end
8
+ end
@@ -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
+
@@ -3,7 +3,7 @@
3
3
  module BBK
4
4
  module Utils
5
5
 
6
- VERSION = '1.1.5'
6
+ VERSION = '1.1.6'
7
7
 
8
8
  end
9
9
  end
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.5.353947
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-02-04 00:00:00.000000000 Z
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/env_docs.rb
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,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -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