bbk-utils 1.1.6.376358 → 1.1.6.409139

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: 102c2729e460c60af51ef53ba1e1d1dea7ff96cf4430092eda0c386921dfe8b1
4
- data.tar.gz: 561cf4cd85c5cc82d03b411f40b0ca4c47e3438953cbb8cf86ebfbdea550939e
3
+ metadata.gz: 40fafd60b7a8a9c337c28b002831b29f57da91633306857f5a9fff5c73c3d4a1
4
+ data.tar.gz: 5a67d746083afb0e0ed01b606ae8c1fe1f2ccb2f5d8184f5a584aaef82ea834f
5
5
  SHA512:
6
- metadata.gz: 62ded80cc3b747b281eb11d736243c06cd7306cb8d6f8a73ec2bb4d80255fc94f75c266ff351acfd800665df78caf3b6807a7ac1d560f94f76b61f4d5b8810c9
7
- data.tar.gz: 3b9968c1647b914fea6992d6d35aefd30d43a5733d388ead4fab0e94fb3e82b07c16a851cc70095cc4493a2a32486d98105a103d6233a32961df1b552c283884
6
+ metadata.gz: c4a4c724e029d7492dd2dcaeda86dc667deef657fb668ae518cd54d9fa1d8d1e1ae6f5bc896cb27cc20e063346fc12280a3ccb4aed6330e7ee67cd804b808b5c
7
+ data.tar.gz: 5f2b6c7980b27a9e1aa6e05e47adf0b1af6b6ddb9f62f06d25c4e2153ce05791527af20438e07c09647b2d9bad9ae6ee1fce8ff7b77cf7889e835a5fc856ec24
data/README.md CHANGED
@@ -13,7 +13,10 @@
13
13
 
14
14
  </div>
15
15
 
16
- Common classes and helpers for BBK library stack.
16
+ Набор общих классов и вспомогательных модулей для стека библиотек BBK. / Common classes and helpers for BBK library stack.
17
+
18
+
19
+ ## Установка / Installation
17
20
 
18
21
  ## Installation
19
22
 
@@ -37,9 +40,81 @@ Or adding to your project:
37
40
  gem "bbk-utils", "~> 1.0.0"
38
41
  ```
39
42
 
40
- ## Features
43
+ ## Возможности / Features
44
+
45
+ ### EnvHelper — сборка URL подключений / URL Building Helper
46
+
47
+ Нормализует переменные окружения: собирает URL из отдельных компонентов и раскладывает обратно. Три уровня приоритетов: ENV-переменная > компонент из базового URL > значение по умолчанию.
48
+
49
+ Normalizes environment variables: builds URLs from individual components and decomposes back. Three-level priorities: env var > component from base URL > default.
50
+
51
+ ```ruby
52
+ # Базы данных / Databases — prepare_database_envs
53
+ env = { 'DATABASE_URL' => 'postgres://user:pass@host:5432/db', 'DATABASE_HOST' => 'newhost' }
54
+ BBK::Utils::EnvHelper.prepare_database_envs(env)
55
+ # => env['DATABASE_URL'] = 'postgres://user:pass@newhost:5432/db'
56
+
57
+ # Очереди сообщений / Message Queues — prepare_mq_envs
58
+ env = { 'MQ_HOST' => 'mq1;mq2;mq3', 'MQ_USER' => 'guest' }
59
+ BBK::Utils::EnvHelper.prepare_mq_envs(env)
60
+ # => env['MQ_URL'] = 'amqps://guest@mq1:5671/;amqps://guest@mq2:5671/;amqps://guest@mq3:5671/'
61
+
62
+ # Jaeger Tracing — prepare_jaeger_envs
63
+ BBK::Utils::EnvHelper.prepare_jaeger_envs(ENV)
64
+ ```
65
+
66
+ ### Config — декларативная работа с переменными окружения / Declarative ENV Management
67
+
68
+ Декларативное описание, валидация и чтение переменных окружения с поддержкой типов, значений по умолчанию, подконфигураций и маскирования секретов.
69
+
70
+ Declarative description, validation, and reading of environment variables with type casting, defaults, subconfigurations, and secret masking.
71
+
72
+ ```ruby
73
+ require 'bbk/utils'
74
+
75
+ BBK::Utils::Config.instance.tap do |cfg|
76
+ cfg.require('API_KEY', desc: 'API key', secure: true)
77
+ cfg.optional('LOG_LEVEL', default: 'info', desc: 'Logging level')
78
+ cfg.optional('TIMEOUT', default: 30, type: method(:Integer), desc: 'Timeout in seconds')
79
+ cfg.optional('DEBUG_MODE', default: false, bool: true, desc: 'Enable debug mode')
80
+ cfg.optional('CLEAN_INTERVAL', default: '3month', type: method(:duration_parser))
81
+
82
+ cfg.map('SSL_CERT', '/etc/ssl/cert.pem', desc: 'SSL certificate')
83
+
84
+ cfg.subconfig(prefix: 'REDIS') do |redis|
85
+ redis.optional('URL', default: 'redis://redis:6379/0', desc: 'Redis URL')
86
+ redis.optional('POOL_SIZE', default: 5, type: method(:Integer), desc: 'Pool size')
87
+ end
88
+ end
89
+
90
+ # Важно: сначала EnvHelper, потом Config
91
+ # Important: EnvHelper first, then Config
92
+ BBK::Utils::EnvHelper.prepare_database_envs(ENV)
93
+ BBK::Utils::EnvHelper.prepare_mq_envs(ENV)
94
+ BBK::Utils::Config.run!(ENV)
95
+
96
+ # Доступ к значениям / Accessing values
97
+ BBK::Utils::Config['LOG_LEVEL'] # => "info"
98
+ BBK::Utils::Config['REDIS_URL'] # => "redis://redis:6379/0"
99
+ puts BBK::Utils::Config.to_s
100
+ ```
101
+
102
+ #### Префиксы и альтернативные имена / Prefixes & Alternative Names
103
+
104
+ ```ruby
105
+ config = BBK::Utils::Config.instance(prefix: 'MYAPP')
106
+ config.optional('PORT', default: 3000) # читает MYAPP_PORT
107
+
108
+ cfg.require('DB_URL', key: 'DATABASE_URL', desc: 'Database URL') # ищет DATABASE_URL, доступен как DB_URL
109
+ ```
110
+
111
+ #### Приведение булевых значений / Boolean Casting
112
+
113
+ `false, 0, '0', 'f', 'false', 'off'` (и их вариации) → `false`. Всё остальное → `true`. Пустая строка и `nil` → `nil`.
114
+
115
+ `false, 0, '0', 'f', 'false', 'off'` (and variations) → `false`. Everything else → `true`. Empty string and `nil` → `nil`.
41
116
 
42
- ### bbkdocs
117
+ ### bbkdocs — генерация документации / Documentation Generator
43
118
 
44
119
  Создать `bin/bbkdocs`:
45
120
 
@@ -2,18 +2,82 @@
2
2
 
3
3
  module BBK
4
4
  module Utils
5
+ # Класс управления конфигурацией приложения через переменные окружения.
6
+ #
7
+ # Предоставляет декларативный способ описания конфигурационных параметров
8
+ # с поддержкой префиксов, подконфигураций, приведения типов, безопасных значений
9
+ # и файловых конфигураций. Реализует паттерн Singleton.
10
+ #
11
+ # @example Базовое использование
12
+ # BBK::Utils::Config.instance.tap do |cfg|
13
+ # cfg.optional('LOG_LEVEL', default: 'info', desc: 'Уровень логирования')
14
+ # cfg.require('DATABASE_URL', desc: 'Строка подключения к БД')
15
+ # cfg.optional('REDIS_URL', default: 'redis://localhost:6379/0')
16
+ # end
17
+ # BBK::Utils::Config.run!(ENV)
18
+ #
19
+ # # Доступ к значениям
20
+ # BBK::Utils::Config['LOG_LEVEL'] # => "info"
21
+ # BBK::Utils::Config['DATABASE_URL'] # => значение из ENV
22
+ #
23
+ # @example Использование с префиксами
24
+ # config = BBK::Utils::Config.instance(prefix: 'MYAPP')
25
+ # config.optional('PORT', default: 3000)
26
+ # config.run!(ENV)
27
+ # # Читает переменную MYAPP_PORT из ENV
28
+ #
29
+ # @example Подконфигурации
30
+ # config = BBK::Utils::Config.instance
31
+ # config.subconfig(prefix: 'DB') do |db|
32
+ # db.require('HOST', desc: 'Хост базы данных')
33
+ # db.optional('PORT', default: '5432')
34
+ # end
35
+ # config.run!(ENV)
36
+ # # Читает переменные DB_HOST и DB_PORT из ENV
37
+ #
38
+ # @note Все методы класса делегируются единственному экземпляру (Singleton)
39
+ # @see Config::BooleanCaster Класс для приведения булевых значений
5
40
  class Config
6
41
 
42
+ # @return [String] Разделитель между частями префикса
7
43
  PREFIX_SEP = '_'
44
+
45
+ # @return [String] Заглушка для отображения безопасных значений в выводе
8
46
  FILTERED_VALUE = '[FILTERED]'
9
47
 
10
- attr_accessor :store, :name
11
- attr_reader :prefix, :env_prefix, :parent
48
+ # @return [Hash] Хранилище конфигурационных элементов
49
+ attr_accessor :store
50
+
51
+ # @return [String, nil] Имя конфигурации (для отображения)
52
+ attr_accessor :name
53
+
54
+ # @return [String, nil] Префикс данной конфигурации
55
+ attr_reader :prefix
56
+
57
+ # @return [String] Полный префикс с учётом родительских (для ENV)
58
+ attr_reader :env_prefix
12
59
 
60
+ # @return [Config, nil] Родительская конфигурация
61
+ attr_reader :parent
62
+
63
+ # Ошибка при обращении к несуществующему ключу конфигурации
13
64
  class KeyError < StandardError; end
14
65
 
66
+ # Класс для приведения значений к булевому типу.
67
+ #
68
+ # Распознаёт широкий набор "ложных" значений: 0, 'f', 'false', 'off' и их варианты.
69
+ # Всё остальное считается истиной. Пустые значения возвращают +nil+.
70
+ #
71
+ # @example
72
+ # BooleanCaster.cast('true') # => true
73
+ # BooleanCaster.cast('0') # => false
74
+ # BooleanCaster.cast('off') # => false
75
+ # BooleanCaster.cast(nil) # => nil
76
+ # BooleanCaster.cast('') # => nil
77
+ # BooleanCaster.cast('yes') # => true
15
78
  class BooleanCaster
16
79
 
80
+ # Множество значений, интерпретируемых как +false+
17
81
  FALSE_VALUES = [
18
82
  false, 0,
19
83
  '0', :"0",
@@ -25,6 +89,10 @@ module BBK
25
89
  'OFF', :OFF
26
90
  ].to_set.freeze
27
91
 
92
+ # Приводит значение к булевому типу.
93
+ #
94
+ # @param value [Object] Значение для приведения
95
+ # @return [Boolean, nil] +true+, +false+ или +nil+ (для пустых значений)
28
96
  def self.cast(value)
29
97
  if value.nil? || value == ''
30
98
  nil
@@ -35,14 +103,55 @@ module BBK
35
103
 
36
104
  end
37
105
 
106
+ # Возвращает единственный экземпляр конфигурации (Singleton).
107
+ #
108
+ # @param prefix [String, nil] Префикс для переменных окружения (только при первом вызове)
109
+ # @return [Config] Единственный экземпляр
110
+ #
111
+ # @example
112
+ # config = BBK::Utils::Config.instance
113
+ # config = BBK::Utils::Config.instance(prefix: 'MYAPP')
38
114
  def self.instance(prefix: nil)
39
115
  @instance ||= new(prefix: prefix)
40
116
  end
41
117
 
118
+ # Приводит значение к булевому типу.
119
+ #
120
+ # @param value [Object] Значение для приведения
121
+ # @return [Boolean, nil] Результат приведения
122
+ # @see BooleanCaster.cast
42
123
  def self.parse_bool_value(value)
43
124
  BooleanCaster.cast(value)
44
125
  end
45
126
 
127
+ # Делегирование методов экземпляра на уровень класса
128
+ #
129
+ # @!method self.map(env, file, required: true, desc: nil, bool: false, key: nil, rewrite: true, category: nil, warning: nil)
130
+ # @see #map
131
+ # @!method self.require(env, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil)
132
+ # @see #require
133
+ # @!method self.optional(env, default: nil, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil)
134
+ # @see #optional
135
+ # @!method self.run!(source = ENV)
136
+ # @see #run!
137
+ # @!method self.[](key)
138
+ # @see #[]
139
+ # @!method self.[]=(key, value)
140
+ # @see #[]=
141
+ # @!method self.content(key)
142
+ # @see #content
143
+ # @!method self.to_s
144
+ # @see #to_s
145
+ # @!method self.as_json(*args)
146
+ # @see #as_json
147
+ # @!method self.to_json(*args)
148
+ # @see #to_json
149
+ # @!method self.to_yaml(*args)
150
+ # @see #to_yaml
151
+ # @!method self.fetch(key, default = nil)
152
+ # @see #fetch
153
+ # @!method self.root?
154
+ # @see #root?
46
155
  class << self
47
156
 
48
157
  delegate :map, :require, :optional, :run!, :[], :[]=, :content, :to_s,
@@ -51,6 +160,16 @@ module BBK
51
160
 
52
161
  end
53
162
 
163
+ # Инициализирует новый экземпляр конфигурации.
164
+ #
165
+ # Обычно не вызывается напрямую — используйте {.instance}.
166
+ #
167
+ # @param name [String, nil] Имя конфигурации (для отображения в логах)
168
+ # @param prefix [String, nil] Префикс для переменных окружения
169
+ # @param parent [Config, nil] Родительская конфигурация (для подконфигураций)
170
+ #
171
+ # @example
172
+ # config = BBK::Utils::Config.new(name: 'myapp', prefix: 'MYAPP')
54
173
  def initialize(name: nil, prefix: nil, parent: nil)
55
174
  @name = name
56
175
  @store = {}
@@ -65,6 +184,27 @@ module BBK
65
184
  @env_prefix = normalize_key(@prefixes.join(PREFIX_SEP))
66
185
  end
67
186
 
187
+ # Регистрирует файловый конфигурационный параметр.
188
+ #
189
+ # Значение переменной окружения записывается в указанный файл.
190
+ # Используется для сертификатов, ключей и других файловых данных.
191
+ #
192
+ # @param env [String] Имя переменной окружения (без префикса)
193
+ # @param file [String] Путь к файлу, куда будет записано значение
194
+ # @param required [Boolean] Обязательность параметра (по умолчанию: true)
195
+ # @param desc [String, nil] Описание параметра
196
+ # @param bool [Boolean] (устарел) Не используется для файловых параметров, оставлен для совместимости
197
+ # @param key [String, nil] Альтернативное имя переменной окружения
198
+ # @param rewrite [Boolean] Перезаписывать при повторной регистрации (по умолчанию: true)
199
+ # @param category [String, nil] Имя категории, к которой относится параметр.
200
+ # Используется генератором документации `BBK::Utils::Cli::Docs::Builder`
201
+ # @param warning [String, nil] Предупреждение для отображения
202
+ # @return [void]
203
+ #
204
+ # @example
205
+ # config.map('SSL_CERT', '/etc/ssl/cert.pem', desc: 'SSL-сертификат')
206
+ # config.run!(ENV)
207
+ # # Значение ENV['SSL_CERT'] будет записано в /etc/ssl/cert.pem
68
208
  def map(env, file, required: true, desc: nil, bool: false, key: nil, rewrite: true, category: nil, warning: nil)
69
209
  conf_key = full_prefixed_key(env)
70
210
  return if @store.key?(conf_key) && !rewrite
@@ -81,10 +221,34 @@ module BBK
81
221
  }
82
222
  end
83
223
 
224
+ # Регистрирует обязательный конфигурационный параметр.
225
+ #
226
+ # При отсутствии переменной в источнике во время {#run!} будет выброшено исключение.
227
+ #
228
+ # @param env [String] Имя переменной окружения (без префикса)
229
+ # @param desc [String, nil] Описание параметра
230
+ # @param bool [Boolean] Интерпретировать как булево значение (по умолчанию: false)
231
+ # @param type [#call, Class, nil] Кастер типа: объект с методом +call+ или класс с методом +new+
232
+ # *Примечание:* для встроенных классов (например, `Integer`, `Float`) передавайте `method(:Integer)`, так как они не имеют публичного метода `new`.
233
+ # @param key [String, nil] Альтернативное имя переменной окружения (используется для чтения из ENV).
234
+ # Например: `config.require('MY_DB', key: 'DATABASE_URL')` прочитает `DATABASE_URL`,
235
+ # но в коде вы будете обращаться к нему как `config['MY_DB']`.
236
+ # @param rewrite [Boolean] Перезаписывать при повторной регистрации (по умолчанию: true)
237
+ # @param secure [Boolean] Скрывать значение в выводе (по умолчанию: false)
238
+ # @param category [String, nil] Имя категории, к которой относится параметр.
239
+ # Используется генератором документации `BBK::Utils::Cli::Docs::Builder`
240
+ # @param warning [String, nil] Предупреждение для отображения
241
+ # @return [void]
242
+ # @raise [ArgumentError] Если одновременно указаны +bool+ и +type+
243
+ #
244
+ # @example
245
+ # config.require('DATABASE_URL', desc: 'Строка подключения к БД')
246
+ # config.require('API_KEY', secure: true, desc: 'Секретный ключ')
247
+ # config.require('WORKERS_COUNT', type: method(:Integer), desc: 'Число воркеров')
84
248
  def require(env, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil)
85
249
  raise ArgumentError.new('Specified type and bool') if bool && type.present?
86
250
 
87
- type = BBK::Config::BooleanCaster.singleton_method(:cast) if bool
251
+ type = BBK::Utils::Config::BooleanCaster.singleton_method(:cast) if bool
88
252
  conf_key = full_prefixed_key(env)
89
253
  return if @store.key?(conf_key) && !rewrite
90
254
 
@@ -101,6 +265,30 @@ module BBK
101
265
  }
102
266
  end
103
267
 
268
+ # Регистрирует опциональный конфигурационный параметр со значением по умолчанию.
269
+ #
270
+ # @param env [String] Имя переменной окружения (без префикса)
271
+ # @param default [Object] Значение по умолчанию (по умолчанию: nil)
272
+ # @param desc [String, nil] Описание параметра
273
+ # @param bool [Boolean] Интерпретировать как булево значение (по умолчанию: false)
274
+ # @param type [#call, Class, nil] Кастер типа: объект с методом +call+ или класс с методом +new+
275
+ # *Примечание:* для встроенных классов (например, `Integer`, `Float`) передавайте `method(:Integer)`, так как они не имеют публичного метода `new`.
276
+ # @param key [String, nil] Альтернативное имя переменной окружения (используется для чтения из ENV).
277
+ # Например: `config.optional('REDIS_URL', key: 'REDIS_CACHE_URL', default: 'redis://localhost:6379/0')` прочитает `REDIS_CACHE_URL`,
278
+ # но в коде вы будете обращаться к нему как `config['REDIS_URL']`.
279
+ # @param rewrite [Boolean] Перезаписывать при повторной регистрации (по умолчанию: true)
280
+ # @param secure [Boolean] Скрывать значение в выводе (по умолчанию: false)
281
+ # @param category [String, nil] Имя категории, к которой относится параметр.
282
+ # Используется генератором документации `BBK::Utils::Cli::Docs::Builder`
283
+ # @param warning [String, nil] Предупреждение для отображения
284
+ # @return [void]
285
+ # @raise [ArgumentError] Если одновременно указаны +bool+ и +type+
286
+ #
287
+ # @example
288
+ # config.optional('LOG_LEVEL', default: 'info', desc: 'Уровень логирования')
289
+ # config.optional('PORT', default: '3000', type: method(:Integer))
290
+ # config.optional('DEBUG', default: false, bool: true)
291
+ # config.optional('PASSWORD', default: '', secure: true)
104
292
  def optional(env, default: nil, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil)
105
293
  raise ArgumentError.new('Specified type and bool') if bool && type.present?
106
294
 
@@ -122,6 +310,20 @@ module BBK
122
310
  }
123
311
  end
124
312
 
313
+ # Запускает обработку всех зарегистрированных параметров.
314
+ #
315
+ # Проходит по всем элементам хранилища и читает значения из источника.
316
+ # Рекурсивно обрабатывает все подконфигурации.
317
+ #
318
+ # @param source [Hash{String=>String}, #fetch] Источник значений (объект с методом #fetch, например ENV)
319
+ # @return [nil]
320
+ # @raise [RuntimeError] Если обязательный параметр отсутствует в источнике
321
+ #
322
+ # @example Чтение из ENV
323
+ # config.run!
324
+ #
325
+ # @example Чтение из хэша
326
+ # config.run!({ 'DATABASE_URL' => 'postgres://...' })
125
327
  def run!(source = ENV)
126
328
  @store.each_value do |item|
127
329
  process(source, item)
@@ -130,6 +332,26 @@ module BBK
130
332
  nil
131
333
  end
132
334
 
335
+ # Создаёт подконфигурацию с собственным префиксом.
336
+ #
337
+ # Переменные окружения подконфигурации получают дополнительный префикс.
338
+ # Например, при префиксе родителя 'APP' и префиксе подконфигурации 'DB',
339
+ # переменная 'HOST' будет читаться как 'APP_DB_HOST'.
340
+ #
341
+ # @param prefix [String, Symbol] Префикс подконфигурации
342
+ # @param name [String, nil] Имя подконфигурации
343
+ # @yield [sub] Блок для регистрации параметров подконфигурации
344
+ # @yieldparam sub [Config] Экземпляр подконфигурации
345
+ # @return [Config] Созданная подконфигурация
346
+ # @raise [ArgumentError] Если подконфигурация с таким префиксом уже существует
347
+ #
348
+ # @example
349
+ # config = BBK::Utils::Config.instance(prefix: 'APP')
350
+ # config.subconfig(prefix: 'DB', name: 'database') do |db|
351
+ # db.require('HOST')
352
+ # db.optional('PORT', default: '5432')
353
+ # end
354
+ # # Будут читаться переменные APP_DB_HOST и APP_DB_PORT
133
355
  def subconfig(prefix:, name: nil)
134
356
  raise ArgumentError.new("Subconfig with prefix #{prefix} already exists") if @subconfigs.any? {|sub| sub.prefix == prefix.to_s }
135
357
 
@@ -139,14 +361,43 @@ module BBK
139
361
  sub
140
362
  end
141
363
 
364
+ # Возвращает значение конфигурационного параметра.
365
+ #
366
+ # Поиск происходит с учётом префиксов, вверх по иерархии (к родителю)
367
+ # и вниз (в подконфигурации).
368
+ #
369
+ # @param key [String] Имя параметра
370
+ # @return [Object] Значение параметра
371
+ # @raise [Config::KeyError] Если параметр не найден
372
+ #
373
+ # @example
374
+ # BBK::Utils::Config['LOG_LEVEL'] # => "info"
142
375
  def [](key)
143
376
  self.get(key, search_up: true, search_down: true)[:value]
144
377
  end
145
378
 
379
+ # Устанавливает значение конфигурационного параметра.
380
+ #
381
+ # @param key [String] Имя параметра
382
+ # @param value [Object] Новое значение
383
+ # @return [Object] Установленное значение
384
+ #
385
+ # @example
386
+ # BBK::Utils::Config['LOG_LEVEL'] = 'debug'
146
387
  def []=(key, value)
147
388
  @store[normalize_key(key)][:value] = value
148
389
  end
149
390
 
391
+ # Возвращает содержимое параметра.
392
+ #
393
+ # Для файловых параметров читает содержимое файла.
394
+ # Для остальных возвращает значение.
395
+ #
396
+ # @param key [String] Имя параметра
397
+ # @return [String, Object] Содержимое файла или значение параметра
398
+ #
399
+ # @example
400
+ # config.content('SSL_CERT') # => содержимое файла сертификата
150
401
  def content(key)
151
402
  item = @store[normalize_key(key)]
152
403
  if (file = item[:file])
@@ -156,6 +407,17 @@ module BBK
156
407
  end
157
408
  end
158
409
 
410
+ # Возвращает значение параметра или значение по умолчанию.
411
+ #
412
+ # В отличие от {#[]}, не выбрасывает исключение при отсутствии параметра.
413
+ #
414
+ # @param key [String] Имя параметра
415
+ # @param default [Object] Значение по умолчанию (по умолчанию: nil)
416
+ # @return [Object] Значение параметра или +default+
417
+ #
418
+ # @example
419
+ # BBK::Utils::Config.fetch('LOG_LEVEL', 'warn') # => "info" (если значение установлено) или "warn" (если не установлено)
420
+ # BBK::Utils::Config.fetch('NONEXISTENT', 'fallback') # => "fallback"
159
421
  def fetch(key, default = nil)
160
422
  if (rec = self.get(key, search_up: true, search_down: true)) && rec.key?(:value)
161
423
  rec[:value]
@@ -166,6 +428,19 @@ module BBK
166
428
  default
167
429
  end
168
430
 
431
+ # Возвращает человекочитаемое представление конфигурации.
432
+ #
433
+ # Выводит все параметры с их значениями, описаниями и статусом обязательности.
434
+ # Безопасные параметры отображаются как +[FILTERED]+.
435
+ #
436
+ # @return [String] Форматированный вывод конфигурации
437
+ #
438
+ # @example Вывод
439
+ # # Environment variables:
440
+ # # <DATABASE_URL> Строка подключения к БД
441
+ # # -> "postgres://localhost/mydb"
442
+ # # [LOG_LEVEL] (=info) Уровень логирования
443
+ # # -> "info"
169
444
  def to_s
170
445
  result = StringIO.new
171
446
  result.puts "Environment variables#{@name ? " for #{@name}" : ''}:"
@@ -184,6 +459,12 @@ module BBK
184
459
  result.string
185
460
  end
186
461
 
462
+ # Возвращает конфигурацию как хэш.
463
+ #
464
+ # @param _args [Array] Игнорируется (для совместимости с ActiveSupport)
465
+ # @return [Hash] Хэш вида { "ИМЯ_ПЕРЕМЕННОЙ" => { параметры } }
466
+ # @note Если в конфигурации используются кастомные type-кастеры (Method, Proc, Class),
467
+ # поле `type` будет сериализовано как строковое представление объекта (например, "#<Method: Object(Kernel)#Integer(*)>").
187
468
  def as_json(*_args)
188
469
  values = store_with_subconfigs.values.sort_by do |item|
189
470
  [item[:file].present? ? 0 : 1, item[:required] ? 0 : 1]
@@ -194,22 +475,46 @@ module BBK
194
475
  @name ? { @name => values } : values
195
476
  end
196
477
 
478
+ # Возвращает конфигурацию в формате JSON.
479
+ #
480
+ # @param _args [Array] Игнорируется (для совместимости с ActiveSupport)
481
+ # @return [String] JSON-представление конфигурации
482
+ # @note Если в конфигурации используются кастомные type-кастеры (Method, Proc, Class),
483
+ # поле `type` будет сериализовано как строковое представление объекта (например, "#<Method: Object(Kernel)#Integer(*)>").
197
484
  def to_json(*_args)
198
485
  JSON.pretty_generate(as_json)
199
486
  end
200
487
 
488
+ # Возвращает конфигурацию в формате YAML.
489
+ #
490
+ # @param _args [Array] Игнорируется (для совместимости с ActiveSupport)
491
+ # @return [String] YAML-представление конфигурации
492
+ # @note Если в конфигурации используются кастомные type-кастеры (Method, Proc, Class),
493
+ # поле `type` будет сериализовано как строковое представление объекта (например, "#<Method: Object(Kernel)#Integer(*)>").
201
494
  def to_yaml(*_args)
202
495
  JSON.parse(to_json).to_yaml
203
496
  end
204
497
 
498
+ # Проверяет, является ли конфигурация корневой (не имеет родителя).
499
+ #
500
+ # @return [Boolean] +true+ если конфигурация корневая
205
501
  def root?
206
502
  @parent.nil?
207
503
  end
208
504
 
209
505
  protected
210
506
 
507
+ # @return [Array<String>] Массив префиксов от корня до текущего уровня
211
508
  attr_reader :prefixes
212
509
 
510
+ # Ищет конфигурационный элемент по ключу.
511
+ #
512
+ # @param key [String] Имя параметра
513
+ # @param search_up [Boolean] Искать в родительских конфигурациях
514
+ # @param search_down [Boolean] Искать в подконфигурациях
515
+ # @return [Hash] Конфигурационный элемент
516
+ # @raise [KeyError] Если параметр не найден
517
+ # @api private
213
518
  def get(key, search_up: false, search_down: false)
214
519
  normalized_key = normalize_key(key)
215
520
  return @store[normalized_key] if @store.key?(normalized_key)
@@ -232,6 +537,10 @@ module BBK
232
537
  raise KeyError.new("There is no such key: #{key} in config!")
233
538
  end
234
539
 
540
+ # Возвращает все элементы хранилища включая подконфигурации.
541
+ #
542
+ # @return [Hash] Объединённое хранилище всех уровней
543
+ # @api private
235
544
  def store_with_subconfigs
236
545
  res = @store.dup
237
546
  @subconfigs.each do |sub|
@@ -242,12 +551,22 @@ module BBK
242
551
 
243
552
  private
244
553
 
554
+ # Нормализует ключ: переводит в верхний регистр, заменяет дефисы на подчёркивания.
555
+ #
556
+ # @param key [String, nil] Ключ для нормализации
557
+ # @return [String, nil] Нормализованный ключ
558
+ # @api private
245
559
  def normalize_key(key)
246
560
  return nil if key.nil?
247
561
 
248
562
  key.to_s.upcase.gsub('-', '_')
249
563
  end
250
564
 
565
+ # Формирует полный ключ с учётом префикса.
566
+ #
567
+ # @param key [String] Базовый ключ
568
+ # @return [String] Ключ с префиксом (например, "APP_DB_HOST")
569
+ # @api private
251
570
  def full_prefixed_key(key)
252
571
  p_key = if env_prefix.empty?
253
572
  [key.to_s]
@@ -257,6 +576,11 @@ module BBK
257
576
  normalize_key(p_key)
258
577
  end
259
578
 
579
+ # Генерирует варианты ключей с разными комбинациями префиксов.
580
+ #
581
+ # @param key [String] Базовый ключ
582
+ # @return [Enumerator] Перечислитель вариантов ключей
583
+ # @api private
260
584
  def sub_prefixed_keys(key)
261
585
  Enumerator.new do |yielder|
262
586
  @prefixes.size.downto(0).each do |last_index|
@@ -265,6 +589,20 @@ module BBK
265
589
  end
266
590
  end
267
591
 
592
+ # Обрабатывает один конфигурационный элемент: читает значение из источника.
593
+ #
594
+ # Логика обработки:
595
+ # - Если значение присутствует или указан тип — обрабатывает значение
596
+ # - Для файловых параметров — записывает значение в файл
597
+ # - Для типизированных — применяет кастер типа
598
+ # - Если значение отсутствует и параметр обязательный — выбрасывает ошибку
599
+ # - Если значение отсутствует и параметр опциональный — использует дефолт
600
+ #
601
+ # @param source [Hash{String=>String}, #fetch] Источник значений (объект с методом #fetch, например ENV)
602
+ # @param item [Hash] Конфигурационный элемент из хранилища
603
+ # @return [void]
604
+ # @raise [RuntimeError] Если обязательный параметр отсутствует
605
+ # @api private
268
606
  def process(source, item)
269
607
  content = source.fetch(item[:env], item[:default])
270
608
 
@@ -305,10 +643,21 @@ module BBK
305
643
  raise
306
644
  end
307
645
 
646
+ # Выбрасывает ошибку об отсутствии обязательного параметра.
647
+ #
648
+ # @param item [Hash] Конфигурационный элемент
649
+ # @raise [RuntimeError] Всегда выбрасывает ошибку
650
+ # @api private
308
651
  def required!(item)
309
652
  raise "ENV [#{item[:env]}] is required!"
310
653
  end
311
654
 
655
+ # Формирует строку вывода для файлового параметра.
656
+ #
657
+ # @param item [Hash] Конфигурационный элемент
658
+ # @param padding [String] Отступ для форматирования
659
+ # @return [String] Форматированная строка
660
+ # @api private
312
661
  def print_file_item(item, padding)
313
662
  line = "#{padding}File #{wrap_required(item)}"
314
663
  line = if item[:desc].present?
@@ -320,11 +669,19 @@ module BBK
320
669
  "#{line}\n#{padding * 2}-> #{item[:file].inspect}"
321
670
  end
322
671
 
672
+ # Формирует строку вывода для обычного параметра.
673
+ #
674
+ # @param item [Hash] Конфигурационный элемент
675
+ # @param padding [String] Отступ для форматирования
676
+ # @return [String] Форматированная строка
677
+ # @api private
323
678
  def print_item(item, padding)
324
679
  line = padding + wrap_required(item)
325
680
  if item[:default].present?
326
681
  def_value = if item[:secure]
327
682
  FILTERED_VALUE
683
+ elsif item[:default].respond_to?(:secure_inspect)
684
+ item[:default].secure_inspect
328
685
  else
329
686
  item[:default]
330
687
  end
@@ -338,12 +695,22 @@ module BBK
338
695
  end
339
696
  value = if item[:secure]
340
697
  FILTERED_VALUE
698
+ elsif item[:value].respond_to?(:secure_inspect)
699
+ item[:value].secure_inspect
341
700
  else
342
701
  item[:value].inspect
343
702
  end
344
703
  "#{line}\n#{padding * 2}-> #{value}"
345
704
  end
346
705
 
706
+ # Оборачивает имя переменной в скобки в зависимости от обязательности.
707
+ #
708
+ # Обязательные параметры выводятся в угловых скобках: <ИМЯ>
709
+ # Опциональные параметры выводятся в квадратных скобках: [ИМЯ]
710
+ #
711
+ # @param item [Hash] Конфигурационный элемент
712
+ # @return [String] Имя переменной в скобках
713
+ # @api private
347
714
  def wrap_required(item)
348
715
  if item[:required]
349
716
  "<#{item[:env]}>"
@@ -354,5 +721,4 @@ module BBK
354
721
 
355
722
  end
356
723
  end
357
- end
358
-
724
+ end
@@ -4,21 +4,119 @@ require 'uri'
4
4
 
5
5
  module BBK
6
6
  module Utils
7
+ # Вспомогательный модуль для сборки и нормализации переменных окружения
8
+ # при подключении к внешним сервисам.
9
+ #
10
+ # Предоставляет методы для конструирования URL-подключений из переменных окружения
11
+ # с интеллектуальными значениями по умолчанию и механизмом переопределения.
12
+ # Поддерживает базы данных, очереди сообщений и сервисы трейсинга.
13
+ #
14
+ # @example Базовое использование для конфигурации базы данных
15
+ # env = {
16
+ # 'DATABASE_URL' => 'postgres://old:pass@oldhost:5432/olddb',
17
+ # 'DATABASE_HOST' => 'newhost',
18
+ # 'DATABASE_NAME' => 'newdb'
19
+ # }
20
+ # BBK::Utils::EnvHelper.prepare_database_envs(env)
21
+ # # => env['DATABASE_URL'] = 'postgres://old:pass@newhost:5432/newdb'
22
+ # # => env['DATABASE_HOST'] = 'newhost'
23
+ # # => env['DATABASE_NAME'] = 'newdb'
24
+ #
25
+ # @note Все методы изменяют переданный хэш env in-place и возвращают его
7
26
  module EnvHelper
8
27
 
28
+ # @return [String] Префикс по умолчанию для переменных окружения, связанных с базой данных
9
29
  DEFAULT_DATABASE_PREFIX = 'DATABASE'
10
30
 
31
+ # Подготавливает переменные окружения для подключения к базе данных.
32
+ #
33
+ # Собирает URL подключения к БД из отдельных переменных окружения или
34
+ # переопределяет компоненты существующего URL. После сборки URL раскладывает
35
+ # его обратно в индивидуальные переменные окружения.
36
+ #
37
+ # Поддерживаемые переменные окружения (с префиксом):
38
+ # - {prefix}_URL - базовый URL (опционально, используется как шаблон)
39
+ # - {prefix}_ADAPTER - адаптер/схема БД (по умолчанию: 'postgresql')
40
+ # - {prefix}_HOST - хост БД (по умолчанию: 'db')
41
+ # - {prefix}_PORT - порт БД (по умолчанию: 5432)
42
+ # - {prefix}_USER - пользователь БД (по умолчанию: 'postgres')
43
+ # - {prefix}_PASS - пароль БД (по умолчанию: nil)
44
+ # - {prefix}_NAME - имя БД/путь
45
+ # - {prefix}_POOL - размер пула соединений. Используется **только если в {prefix}_URL присутствует query-строка** (не обязательно содержащая параметр `pool`). Если query-строки нет, значение игнорируется.
46
+ #
47
+ # @param env [Hash{String=>String}, ENV] Хэш переменных окружения или объект ENV (изменяется in-place)
48
+ # @param prefix [String] Префикс для имен переменных окружения
49
+ # @return [Hash{String=>String}, ENV] Тот же объект env (изменён in-place)
50
+ #
51
+ # @example Только с URL
52
+ # env = { 'DATABASE_URL' => 'postgres://user:pass@host:5432/mydb' }
53
+ # prepare_database_envs(env)
54
+ # # => env['DATABASE_HOST'] = 'host', env['DATABASE_PORT'] = '5432', и т.д.
55
+ #
56
+ # @example С URL и переопределениями
57
+ # env = { 'DATABASE_URL' => 'postgres://user:pass@host:5432/mydb', 'DATABASE_HOST' => 'newhost' }
58
+ # prepare_database_envs(env)
59
+ # # => env['DATABASE_URL'] = 'postgres://user:pass@newhost:5432/mydb'
60
+ #
61
+ # @example Только с индивидуальными переменными
62
+ # env = { 'DATABASE_HOST' => 'myhost', 'DATABASE_NAME' => 'mydb' }
63
+ # prepare_database_envs(env)
64
+ # # => env['DATABASE_URL'] = 'postgresql://postgres@myhost:5432/mydb'
11
65
  def self.prepare_database_envs(env, prefix: DEFAULT_DATABASE_PREFIX)
12
66
  uri = build_uri_with_defaults(env, prefix: prefix)
13
67
  apply_env_from_uri(env, uri, prefix: prefix)
14
68
  env
15
69
  end
16
70
 
71
+ # Подготавливает переменные окружения для подключения к очереди сообщений.
72
+ #
73
+ # Собирает URL для MQ из переменных окружения с поддержкой нескольких хостов (кластер).
74
+ # Хосты могут быть указаны в виде списка, разделенного точкой с запятой или вертикальной чертой,
75
+ # в переменной MQ_HOST.
76
+ #
77
+ # Поддерживаемые переменные окружения:
78
+ # - MQ_URL - базовый URL-шаблон (опционально, используется первый, если несколько)
79
+ # - MQ_HOST - хост(ы), могут быть разделены ';' или '|' (по умолчанию: 'mq')
80
+ # - MQ_PORT - порт (по умолчанию: 5671)
81
+ # - MQ_USER - имя пользователя
82
+ # - MQ_PASS - пароль
83
+ # - MQ_VHOST - виртуальный хост (по умолчанию: '/')
84
+ #
85
+ # @param env [Hash{String=>String}, ENV] Хэш переменных окружения или объект ENV (изменяется in-place)
86
+ # @return [Hash{String=>String}, ENV] Тот же объект env (изменён in-place)
87
+ #
88
+ # @example Один хост
89
+ # env = { 'MQ_HOST' => 'rabbitmq', 'MQ_USER' => 'guest' }
90
+ # prepare_mq_envs(env)
91
+ # # => env['MQ_URL'] = 'amqps://guest@rabbitmq:5671/'
92
+ #
93
+ # @example Несколько хостов (кластер)
94
+ # env = { 'MQ_HOST' => 'mq1;mq2;mq3', 'MQ_USER' => 'guest' }
95
+ # prepare_mq_envs(env)
96
+ # # => env['MQ_URL'] = 'amqps://guest@mq1:5671/;amqps://guest@mq2:5671/;amqps://guest@mq3:5671/'
97
+ # # => env['MQ_HOST'] = 'mq1;mq2;mq3'
17
98
  def self.prepare_mq_envs(env)
18
99
  apply_mq_env_from_uri(env, build_mq_uri_with_defaults(env))
19
100
  env
20
101
  end
21
102
 
103
+ # Подготавливает переменные окружения для Jaeger трейсинга.
104
+ #
105
+ # Собирает URL подключения к Jaeger из переменных окружения.
106
+ #
107
+ # Поддерживаемые переменные окружения:
108
+ # - JAEGER_URL - базовый URL (опционально)
109
+ # - JAEGER_SENDER - протокол/схема отправки (по умолчанию: 'udp')
110
+ # - JAEGER_HOST - хост Jaeger агента (по умолчанию: 'jaeger')
111
+ # - JAEGER_PORT - порт Jaeger агента (по умолчанию: 6831)
112
+ #
113
+ # @param env [Hash{String=>String}, ENV] Хэш переменных окружения или объект ENV (изменяется in-place)
114
+ # @return [Hash{String=>String}, ENV] Тот же объект env (изменён in-place)
115
+ #
116
+ # @example
117
+ # env = { 'JAEGER_HOST' => 'jaeger-agent' }
118
+ # prepare_jaeger_envs(env)
119
+ # # => env['JAEGER_URL'] = 'udp://jaeger-agent:6831'
22
120
  def self.prepare_jaeger_envs(env)
23
121
  jaeger_uri = ::URI.parse(env['JAEGER_URL'] || '').tap do |uri|
24
122
  uri.scheme = env.fetch('JAEGER_SENDER', uri.scheme) || 'udp'
@@ -32,6 +130,18 @@ module BBK
32
130
  env
33
131
  end
34
132
 
133
+ # Собирает объект URI из переменных окружения со значениями по умолчанию.
134
+ # При отсутствии {prefix}_URL используется пустой URI (URI.parse('')).
135
+ #
136
+ # @note Приоритет для каждого компонента:
137
+ # 1. Индивидуальная переменная окружения (например, DATABASE_HOST)
138
+ # 2. Компонент из базового URL (например, host из DATABASE_URL)
139
+ # 3. Значение по умолчанию (например, 'db')
140
+ #
141
+ # @param env [Hash{String=>String}, ENV] Хэш переменных окружения или объект ENV
142
+ # @param prefix [String] Префикс для имен переменных окружения
143
+ # @return [URI::Generic] Сконструированный объект URI
144
+ # @api private
35
145
  def self.build_uri_with_defaults(env, prefix: DEFAULT_DATABASE_PREFIX)
36
146
  ::URI.parse(env[prefixed_key(prefix, 'URL')] || '').then do |uri|
37
147
  result = uri.clone
@@ -54,6 +164,13 @@ module BBK
54
164
  end
55
165
  end
56
166
 
167
+ # Раскладывает URI в индивидуальные переменные окружения.
168
+ #
169
+ # @param env [Hash{String=>String}, ENV] Хэш переменных окружения или объект ENV (изменяется in-place)
170
+ # @param uri [URI::Generic] Объект URI для декомпозиции
171
+ # @param prefix [String] Префикс для имен переменных окружения
172
+ # @return [void]
173
+ # @api private
57
174
  def self.apply_env_from_uri(env, uri, prefix: DEFAULT_DATABASE_PREFIX)
58
175
  env[prefixed_key(prefix, 'URL')] = uri.to_s
59
176
  env[prefixed_key(prefix, 'ADAPTER')] = uri.scheme
@@ -69,11 +186,16 @@ module BBK
69
186
  end
70
187
  end
71
188
 
189
+ # Собирает объекты URI для MQ из переменных окружения с поддержкой нескольких хостов.
190
+ #
191
+ # @param env [Hash{String=>String}, ENV] Хэш переменных окружения или объект ENV
192
+ # @return [Array<URI>] Массив объектов URI (по одному на каждый хост)
193
+ # @api private
72
194
  def self.build_mq_uri_with_defaults(env)
73
- # Only first MQ_URL selected as template if any
195
+ # Только первый MQ_URL выбирается как шаблон, если их несколько
74
196
  url = [env.fetch('MQ_URL', '').split(/[;|]/)].flatten.select(&:present?).first || ''
75
197
 
76
- # all hosts if form of list fills url template
198
+ # Все хосты в виде списка заполняют шаблон URL
77
199
  hosts = [env.fetch('MQ_HOST',
78
200
  URI.parse(url).hostname || 'mq').split(/[;|]/)].flatten.select(&:present?).uniq
79
201
 
@@ -95,6 +217,12 @@ module BBK
95
217
  end
96
218
  end
97
219
 
220
+ # Раскладывает URI для MQ в переменные окружения.
221
+ #
222
+ # @param env [Hash{String=>String}, ENV] Хэш переменных окружения или объект ENV (изменяется in-place)
223
+ # @param uris [Array<URI>] Массив объектов URI
224
+ # @return [void]
225
+ # @api private
98
226
  def self.apply_mq_env_from_uri(env, uris)
99
227
  uri = uris.first
100
228
 
@@ -111,11 +239,16 @@ module BBK
111
239
  env['MQ_VHOST'] = vhost
112
240
  end
113
241
 
242
+ # Конструирует имя переменной окружения с префиксом.
243
+ #
244
+ # @param prefix [String] Префикс (например, 'DATABASE')
245
+ # @param name [String] Имя переменной (например, 'HOST')
246
+ # @return [String] Объединенное имя (например, 'DATABASE_HOST')
247
+ # @api private
114
248
  def self.prefixed_key(prefix, name)
115
249
  [prefix, name].select(&:present?).join('_')
116
250
  end
117
251
 
118
252
  end
119
253
  end
120
- end
121
-
254
+ end
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.6.376358
4
+ version: 1.1.6.409139
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-05-19 00:00:00.000000000 Z
11
+ date: 2026-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport