Class: BBK::Utils::Config
- Inherits:
-
Object
- Object
- BBK::Utils::Config
- Defined in:
- lib/bbk/utils/config.rb,
sig/bbk/config.rbs
Overview
Все методы класса делегируются единственному экземпляру (Singleton)
Класс управления конфигурацией приложения через переменные окружения.
Предоставляет декларативный способ описания конфигурационных параметров с поддержкой префиксов, подконфигураций, приведения типов, безопасных значений и файловых конфигураций. Реализует паттерн Singleton.
Defined Under Namespace
Modules: _CallableCaster, _ClassCaster Classes: BooleanCaster, KeyError
Constant Summary collapse
- PREFIX_SEP =
Returns Разделитель между частями префикса.
'_'- FILTERED_VALUE =
Returns Заглушка для отображения безопасных значений в выводе.
'[FILTERED]'
Instance Attribute Summary collapse
-
#env_prefix ⇒ String
readonly
Полный префикс с учётом родительских (для ENV).
-
#name ⇒ String?
Имя конфигурации (для отображения).
-
#parent ⇒ Config?
readonly
Родительская конфигурация.
-
#prefix ⇒ String?
readonly
Префикс данной конфигурации.
-
#store ⇒ Hash
Хранилище конфигурационных элементов.
Class Method Summary collapse
-
.instance(prefix: nil) ⇒ Config
Возвращает единственный экземпляр конфигурации (Singleton).
-
.parse_bool_value(value) ⇒ Boolean?
Приводит значение к булевому типу.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Возвращает значение конфигурационного параметра.
-
#[]=(key, value) ⇒ Object
Устанавливает значение конфигурационного параметра.
-
#as_json(*_args) ⇒ Hash[String, untyped]
def to_s: () -> String.
-
#content(key) ⇒ String, Object
Возвращает содержимое параметра.
-
#fetch(key, default = nil) ⇒ Object
Возвращает значение параметра или значение по умолчанию.
-
#initialize(name: nil, prefix: nil, parent: nil) ⇒ Config
constructor
Инициализирует новый экземпляр конфигурации.
-
#map(env, file, required: true, desc: nil, bool: false, key: nil, rewrite: true, category: nil, warning: nil) ⇒ void
Регистрирует файловый конфигурационный параметр.
-
#normalize_key(key) ⇒ String?
private
Нормализует ключ: переводит в верхний регистр, заменяет дефисы на подчёркивания.
-
#optional(env, default: nil, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil) ⇒ void
Регистрирует опциональный конфигурационный параметр со значением по умолчанию.
-
#print_file_item(item, padding) ⇒ String
private
Формирует строку вывода для файлового параметра.
-
#print_item(item, padding) ⇒ String
private
Формирует строку вывода для обычного параметра.
-
#process(source, item) ⇒ void
private
Обрабатывает один конфигурационный элемент: читает значение из источника.
-
#require(env, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil) ⇒ void
Регистрирует обязательный конфигурационный параметр.
-
#required!(item) ⇒ void
private
Выбрасывает ошибку об отсутствии обязательного параметра.
-
#root? ⇒ Boolean
Проверяет, является ли конфигурация корневой (не имеет родителя).
-
#run!(source = ENV) ⇒ nil
Запускает обработку всех зарегистрированных параметров.
-
#subconfig(prefix:, name: nil) {|sub| ... } ⇒ Config
Создаёт подконфигурацию с собственным префиксом.
-
#to_json(*_args) ⇒ String
Возвращает конфигурацию в формате JSON.
-
#to_s ⇒ String
Возвращает человекочитаемое представление конфигурации.
-
#to_yaml(*_args) ⇒ String
Возвращает конфигурацию в формате YAML.
-
#wrap_required(item) ⇒ String
private
Оборачивает имя переменной в скобки в зависимости от обязательности.
Constructor Details
#initialize(name: nil, prefix: nil, parent: nil) ⇒ Config
Инициализирует новый экземпляр конфигурации.
Обычно не вызывается напрямую — используйте instance.
173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/bbk/utils/config.rb', line 173 def initialize(name: nil, prefix: nil, parent: nil) @name = name @store = {} @parent = parent @subconfigs = [] @prefix = normalize_key(prefix) @prefixes = if parent.nil? [@prefix] else parent.prefixes.dup + [@prefix] end.compact @env_prefix = normalize_key(@prefixes.join(PREFIX_SEP)) end |
Instance Attribute Details
#env_prefix ⇒ String (readonly)
Returns Полный префикс с учётом родительских (для ENV).
58 59 60 |
# File 'lib/bbk/utils/config.rb', line 58 def env_prefix @env_prefix end |
#name ⇒ String?
Returns Имя конфигурации (для отображения).
52 53 54 |
# File 'lib/bbk/utils/config.rb', line 52 def name @name end |
#parent ⇒ Config? (readonly)
Returns Родительская конфигурация.
61 62 63 |
# File 'lib/bbk/utils/config.rb', line 61 def parent @parent end |
#prefix ⇒ String? (readonly)
Returns Префикс данной конфигурации.
55 56 57 |
# File 'lib/bbk/utils/config.rb', line 55 def prefix @prefix end |
#store ⇒ Hash
Returns Хранилище конфигурационных элементов.
49 50 51 |
# File 'lib/bbk/utils/config.rb', line 49 def store @store end |
Class Method Details
.instance(prefix: nil) ⇒ Config
Возвращает единственный экземпляр конфигурации (Singleton).
114 115 116 |
# File 'lib/bbk/utils/config.rb', line 114 def self.instance(prefix: nil) @instance ||= new(prefix: prefix) end |
.parse_bool_value(value) ⇒ Boolean?
Приводит значение к булевому типу.
123 124 125 |
# File 'lib/bbk/utils/config.rb', line 123 def self.parse_bool_value(value) BooleanCaster.cast(value) end |
Instance Method Details
#[](key) ⇒ Object
Возвращает значение конфигурационного параметра.
Поиск происходит с учётом префиксов, вверх по иерархии (к родителю) и вниз (в подконфигурации).
375 376 377 |
# File 'lib/bbk/utils/config.rb', line 375 def [](key) self.get(key, search_up: true, search_down: true)[:value] end |
#[]=(key, value) ⇒ Object
Устанавливает значение конфигурационного параметра.
387 388 389 |
# File 'lib/bbk/utils/config.rb', line 387 def []=(key, value) @store[normalize_key(key)][:value] = value end |
#as_json(*_args) ⇒ Hash[String, untyped]
def to_s: () -> String
468 469 470 471 472 473 474 475 476 |
# File 'lib/bbk/utils/config.rb', line 468 def as_json(*_args) values = store_with_subconfigs.values.sort_by do |item| [item[:file].present? ? 0 : 1, item[:required] ? 0 : 1] end.reduce({}) do |ret, item| ret.merge(item[:env] => item) end @name ? { @name => values } : values end |
#content(key) ⇒ String, Object
Возвращает содержимое параметра.
Для файловых параметров читает содержимое файла. Для остальных возвращает значение.
401 402 403 404 405 406 407 408 |
# File 'lib/bbk/utils/config.rb', line 401 def content(key) item = @store[normalize_key(key)] if (file = item[:file]) File.read(file) else item[:value] end end |
#fetch(key, default = nil) ⇒ Object
Возвращает значение параметра или значение по умолчанию.
В отличие от #[], не выбрасывает исключение при отсутствии параметра.
421 422 423 424 425 426 427 428 429 |
# File 'lib/bbk/utils/config.rb', line 421 def fetch(key, default = nil) if (rec = self.get(key, search_up: true, search_down: true)) && rec.key?(:value) rec[:value] else default end rescue KeyError default end |
#map(env, file, required: true, desc: nil, bool: false, key: nil, rewrite: true, category: nil, warning: nil) ⇒ void
This method returns an undefined value.
Регистрирует файловый конфигурационный параметр.
Значение переменной окружения записывается в указанный файл. Используется для сертификатов, ключей и других файловых данных.
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/bbk/utils/config.rb', line 208 def map(env, file, required: true, desc: nil, bool: false, key: nil, rewrite: true, category: nil, warning: nil) conf_key = full_prefixed_key(env) return if @store.key?(conf_key) && !rewrite @store[conf_key] = { env: full_prefixed_key(key || env), file: file, required: required, desc: desc, bool: bool, type: nil, category: category, warning: warning } end |
#normalize_key(key) ⇒ String?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Нормализует ключ: переводит в верхний регистр, заменяет дефисы на подчёркивания.
559 560 561 562 563 |
# File 'lib/bbk/utils/config.rb', line 559 def normalize_key(key) return nil if key.nil? key.to_s.upcase.gsub('-', '_') end |
#optional(env, default: nil, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil) ⇒ void
This method returns an undefined value.
Регистрирует опциональный конфигурационный параметр со значением по умолчанию.
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/bbk/utils/config.rb', line 292 def optional(env, default: nil, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil) raise ArgumentError.new('Specified type and bool') if bool && type.present? type = BBK::Utils::Config::BooleanCaster.singleton_method(:cast) if bool conf_key = full_prefixed_key(env) return if @store.key?(conf_key) && !rewrite @store[conf_key] = { env: full_prefixed_key(key || env), file: nil, required: false, default: default, desc: desc, bool: true, type: type, secure: secure, category: category, warning: warning } end |
#print_file_item(item, padding) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Формирует строку вывода для файлового параметра.
661 662 663 664 665 666 667 668 669 670 |
# File 'lib/bbk/utils/config.rb', line 661 def print_file_item(item, padding) line = "#{padding}File #{wrap_required(item)}" line = if item[:desc].present? "#{line.ljust(50)} #{item[:desc]}" else line end "#{line}\n#{padding * 2}-> #{item[:file].inspect}" end |
#print_item(item, padding) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Формирует строку вывода для обычного параметра.
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 |
# File 'lib/bbk/utils/config.rb', line 678 def print_item(item, padding) line = padding + wrap_required(item) if item[:default].present? def_value = if item[:secure] FILTERED_VALUE elsif item[:default].respond_to?(:secure_inspect) item[:default].secure_inspect else item[:default] end line += " (=#{def_value})" end line = if item[:desc].present? "#{line.ljust(50)} #{item[:desc]}" else line end value = if item[:secure] FILTERED_VALUE elsif item[:value].respond_to?(:secure_inspect) item[:value].secure_inspect else item[:value].inspect end "#{line}\n#{padding * 2}-> #{value}" end |
#process(source, item) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Обрабатывает один конфигурационный элемент: читает значение из источника.
Логика обработки:
- Если значение присутствует или указан тип — обрабатывает значение
- Для файловых параметров — записывает значение в файл
- Для типизированных — применяет кастер типа
- Если значение отсутствует и параметр обязательный — выбрасывает ошибку
- Если значение отсутствует и параметр опциональный — использует дефолт
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 |
# File 'lib/bbk/utils/config.rb', line 606 def process(source, item) content = source.fetch(item[:env], item[:default]) # Если данные есть, либо указан тип (нужно для того чтобы переменная была нужного типа) if content.present? || item[:type].present? if (file = item[:file]) dirname = File.dirname(file) FileUtils.mkdir_p(dirname) unless File.directory?(dirname) File.write(file, content) item[:value] = file else item[:value] = if (type = item[:type]) if type.respond_to? :call type.call(content) else type.new(content) end else content end end elsif item[:required] required!(item) else item[:value] = if (file = item[:file]).present? && File.exist?(file) file else content end end rescue StandardError => e msg = "Failed processing #{item[:env]} parameter. #{e.inspect}" if $logger $logger.error msg else puts msg end raise end |
#require(env, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil) ⇒ void
This method returns an undefined value.
Регистрирует обязательный конфигурационный параметр.
При отсутствии переменной в источнике во время #run! будет выброшено исключение.
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/bbk/utils/config.rb', line 248 def require(env, desc: nil, bool: false, type: nil, key: nil, rewrite: true, secure: false, category: nil, warning: nil) raise ArgumentError.new('Specified type and bool') if bool && type.present? type = BBK::Utils::Config::BooleanCaster.singleton_method(:cast) if bool conf_key = full_prefixed_key(env) return if @store.key?(conf_key) && !rewrite @store[conf_key] = { env: full_prefixed_key(key || env), file: nil, required: true, desc: desc, bool: bool, type: type, secure: secure, category: category, warning: warning } end |
#required!(item) ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Выбрасывает ошибку об отсутствии обязательного параметра.
651 652 653 |
# File 'lib/bbk/utils/config.rb', line 651 def required!(item) raise "ENV [#{item[:env]}] is required!" end |
#root? ⇒ Boolean
Проверяет, является ли конфигурация корневой (не имеет родителя).
501 502 503 |
# File 'lib/bbk/utils/config.rb', line 501 def root? @parent.nil? end |
#run!(source = ENV) ⇒ nil
Запускает обработку всех зарегистрированных параметров.
Проходит по всем элементам хранилища и читает значения из источника. Рекурсивно обрабатывает все подконфигурации.
327 328 329 330 331 332 333 |
# File 'lib/bbk/utils/config.rb', line 327 def run!(source = ENV) @store.each_value do |item| process(source, item) end @subconfigs.each {|sub| sub.run!(source) } nil end |
#subconfig(prefix:, name: nil) {|sub| ... } ⇒ Config
Создаёт подконфигурацию с собственным префиксом.
Переменные окружения подконфигурации получают дополнительный префикс. Например, при префиксе родителя 'APP' и префиксе подконфигурации 'DB', переменная 'HOST' будет читаться как 'APP_DB_HOST'.
355 356 357 358 359 360 361 362 |
# File 'lib/bbk/utils/config.rb', line 355 def subconfig(prefix:, name: nil) raise ArgumentError.new("Subconfig with prefix #{prefix} already exists") if @subconfigs.any? {|sub| sub.prefix == prefix.to_s } sub = self.class.new(name: name, prefix: prefix, parent: self) @subconfigs << sub yield sub if block_given? sub end |
#to_json(*_args) ⇒ String
Если в конфигурации используются кастомные type-кастеры (Method, Proc, Class),
поле type будет сериализовано как строковое представление объекта (например, "#<Method: Object(Kernel)#Integer(*)>").
Возвращает конфигурацию в формате JSON.
484 485 486 |
# File 'lib/bbk/utils/config.rb', line 484 def to_json(*_args) JSON.pretty_generate(as_json) end |
#to_s ⇒ String
Возвращает человекочитаемое представление конфигурации.
Выводит все параметры с их значениями, описаниями и статусом обязательности.
Безопасные параметры отображаются как [FILTERED].
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
# File 'lib/bbk/utils/config.rb', line 444 def to_s result = StringIO.new result.puts "Environment variables#{@name ? " for #{@name}" : ''}:" padding = ' ' * 3 sorted = store_with_subconfigs.values.sort_by do |item| [item[:file].present? ? 0 : 1, item[:required] ? 0 : 1] end sorted.each do |item| if item[:file] result.puts print_file_item(item, padding) else result.puts print_item(item, padding) end end result.string end |
#to_yaml(*_args) ⇒ String
Если в конфигурации используются кастомные type-кастеры (Method, Proc, Class),
поле type будет сериализовано как строковое представление объекта (например, "#<Method: Object(Kernel)#Integer(*)>").
Возвращает конфигурацию в формате YAML.
494 495 496 |
# File 'lib/bbk/utils/config.rb', line 494 def to_yaml(*_args) JSON.parse(to_json).to_yaml end |
#wrap_required(item) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Оборачивает имя переменной в скобки в зависимости от обязательности.
Обязательные параметры выводятся в угловых скобках: <ИМЯ> Опциональные параметры выводятся в квадратных скобках: [ИМЯ]
714 715 716 717 718 719 720 |
# File 'lib/bbk/utils/config.rb', line 714 def wrap_required(item) if item[:required] "<#{item[:env]}>" else "[#{item[:env]}]" end end |