const_conf 0.0.0

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.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # vim: set filetype=ruby et sw=2 ts=2:
2
+
3
+ require 'gem_hadar'
4
+
5
+ GemHadar do
6
+ name 'const_conf'
7
+ module_type :module
8
+ author 'Florian Frank'
9
+ email 'flori@ping.de'
10
+ homepage 'https://github.com/flori/const_conf'
11
+ summary 'Clean DSL for config settings with validation and Rails integration'
12
+ description <<~EOT
13
+ ConstConf is a Ruby configuration library that manages settings
14
+ through environment variables, files, and directories with comprehensive
15
+ validation and Rails integration.
16
+ EOT
17
+ test_dir 'spec'
18
+ ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', '.AppleDouble', '.bundle',
19
+ '.yardoc', 'doc', 'tags', 'coverage'
20
+ package_ignore '.all_images.yml', '.gitignore', 'VERSION', '.utilsrc',
21
+ '.github', *Dir['.contexts/*']
22
+ readme 'README.md'
23
+
24
+ dependency 'tins', '~> 1.42'
25
+ dependency 'rails', '~> 8'
26
+ dependency 'json', '~> 2.0'
27
+ dependency 'complex_config', '~> 0.22'
28
+ development_dependency 'debug'
29
+ development_dependency 'rspec', '~> 3.13'
30
+ development_dependency 'context_spook', '~> 0.3'
31
+ development_dependency 'all_images', '~> 0.6'
32
+ development_dependency 'simplecov', '~> 0.22'
33
+
34
+ licenses << 'MIT'
35
+ end
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # stub: const_conf 0.0.0 ruby lib
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "const_conf".freeze
6
+ s.version = "0.0.0".freeze
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
+ s.require_paths = ["lib".freeze]
10
+ s.authors = ["Florian Frank".freeze]
11
+ s.date = "1980-01-02"
12
+ s.description = "ConstConf is a Ruby configuration library that manages settings\nthrough environment variables, files, and directories with comprehensive\nvalidation and Rails integration.\n".freeze
13
+ s.email = "flori@ping.de".freeze
14
+ s.extra_rdoc_files = ["README.md".freeze, "lib/const_conf.rb".freeze, "lib/const_conf/dir_plugin.rb".freeze, "lib/const_conf/env_dir_extension.rb".freeze, "lib/const_conf/errors.rb".freeze, "lib/const_conf/file_plugin.rb".freeze, "lib/const_conf/json_plugin.rb".freeze, "lib/const_conf/railtie.rb".freeze, "lib/const_conf/setting.rb".freeze, "lib/const_conf/setting_accessor.rb".freeze, "lib/const_conf/tree.rb".freeze, "lib/const_conf/version.rb".freeze, "lib/const_conf/yaml_plugin.rb".freeze]
15
+ s.files = ["Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "const_conf.gemspec".freeze, "lib/const_conf.rb".freeze, "lib/const_conf/dir_plugin.rb".freeze, "lib/const_conf/env_dir_extension.rb".freeze, "lib/const_conf/errors.rb".freeze, "lib/const_conf/file_plugin.rb".freeze, "lib/const_conf/json_plugin.rb".freeze, "lib/const_conf/railtie.rb".freeze, "lib/const_conf/setting.rb".freeze, "lib/const_conf/setting_accessor.rb".freeze, "lib/const_conf/tree.rb".freeze, "lib/const_conf/version.rb".freeze, "lib/const_conf/yaml_plugin.rb".freeze, "spec/assets/.env/API_KEY".freeze, "spec/assets/config.json".freeze, "spec/assets/config.yml".freeze, "spec/assets/config_env.yml".freeze, "spec/const_conf/dir_plugin_spec.rb".freeze, "spec/const_conf/env_dir_extension_spec.rb".freeze, "spec/const_conf/file_plugin_spec.rb".freeze, "spec/const_conf/json_plugin_spec.rb".freeze, "spec/const_conf/setting_accessor_spec.rb".freeze, "spec/const_conf/setting_spec.rb".freeze, "spec/const_conf/tree_spec.rb".freeze, "spec/const_conf/yaml_plugin_spec.rb".freeze, "spec/const_conf_spec.rb".freeze, "spec/spec_helper.rb".freeze]
16
+ s.homepage = "https://github.com/flori/const_conf".freeze
17
+ s.licenses = ["MIT".freeze]
18
+ s.rdoc_options = ["--title".freeze, "ConstConf - Clean DSL for config settings with validation and Rails integration".freeze, "--main".freeze, "README.md".freeze]
19
+ s.rubygems_version = "3.6.9".freeze
20
+ s.summary = "Clean DSL for config settings with validation and Rails integration".freeze
21
+ s.test_files = ["spec/const_conf/dir_plugin_spec.rb".freeze, "spec/const_conf/env_dir_extension_spec.rb".freeze, "spec/const_conf/file_plugin_spec.rb".freeze, "spec/const_conf/json_plugin_spec.rb".freeze, "spec/const_conf/setting_accessor_spec.rb".freeze, "spec/const_conf/setting_spec.rb".freeze, "spec/const_conf/tree_spec.rb".freeze, "spec/const_conf/yaml_plugin_spec.rb".freeze, "spec/const_conf_spec.rb".freeze, "spec/spec_helper.rb".freeze]
22
+
23
+ s.specification_version = 4
24
+
25
+ s.add_development_dependency(%q<gem_hadar>.freeze, ["~> 2.1".freeze])
26
+ s.add_development_dependency(%q<debug>.freeze, [">= 0".freeze])
27
+ s.add_development_dependency(%q<rspec>.freeze, ["~> 3.13".freeze])
28
+ s.add_development_dependency(%q<context_spook>.freeze, ["~> 0.3".freeze])
29
+ s.add_development_dependency(%q<all_images>.freeze, ["~> 0.6".freeze])
30
+ s.add_development_dependency(%q<simplecov>.freeze, ["~> 0.22".freeze])
31
+ s.add_runtime_dependency(%q<tins>.freeze, ["~> 1.42".freeze])
32
+ s.add_runtime_dependency(%q<rails>.freeze, ["~> 8".freeze])
33
+ s.add_runtime_dependency(%q<json>.freeze, ["~> 2.0".freeze])
34
+ s.add_runtime_dependency(%q<complex_config>.freeze, ["~> 0.22".freeze])
35
+ end
@@ -0,0 +1,175 @@
1
+ # A module that provides functionality for reading file contents from
2
+ # configuration directories, supporting XDG Base Directory Specification
3
+ # compliance.
4
+ #
5
+ # The DirPlugin extends the ConstConf::Setting class to enable configuration
6
+ # settings that are sourced from files within named directories. This allows
7
+ # for more complex and secure configuration management, particularly useful for
8
+ # storing sensitive data or structured configs in files rather than environment
9
+ # variables.
10
+ #
11
+ # @example Basic usage with file reading
12
+ # module APP
13
+ # include ConstConf
14
+ # plugin ConstConf::DirPlugin
15
+ #
16
+ # CONFIG_FILE = set do
17
+ # description 'Configuration from file'
18
+ # default dir('myapp', 'config.yaml')
19
+ # decoding { |s| YAML.load(s) }
20
+ # end
21
+ # end
22
+ #
23
+ # @example XDG-compliant directory structure
24
+ # module APP
25
+ # include ConstConf
26
+ # plugin ConstConf::DirPlugin
27
+ #
28
+ # XDG_CONFIG_HOME = set do
29
+ # description 'XDG Config HOME'
30
+ # prefix ''
31
+ # end
32
+ #
33
+ # API_KEY = set do
34
+ # description 'API Key from XDG config'
35
+ # default dir('myapp', 'api_key.txt', env_var: APP::XDG_CONFIG_HOME?)
36
+ # end
37
+ # end
38
+ #
39
+ # @see ConstConf::Setting
40
+ module ConstConf::DirPlugin
41
+ # A configuration directory handler that provides methods for deriving
42
+ # directory paths, joining paths, and reading file contents from a specified
43
+ # directory structure.
44
+ #
45
+ # The ConfigDir class is designed to work with the XDG Base Directory
46
+ # Specification and supports reading files from directories with optional
47
+ # environment variable configuration for the root path. It provides a clean
48
+ # interface for accessing configuration files in a structured way.
49
+ class ConfigDir
50
+ # Initializes a new instance with a name and environment variable
51
+ # configuration.
52
+ #
53
+ # @param name [String] the name used to derive the directory path
54
+ # @param root_path [String, nil] the root path to use for deriving the directory path
55
+ # @param env_var [String, nil] the environment variable value to use
56
+ # @param env_var_name [String, nil] the name of the environment variable to look up
57
+ #
58
+ # @raise [ArgumentError] if env_var and env_var_name were given.
59
+ def initialize(name, root_path: nil, env_var:, env_var_name: nil)
60
+ !env_var.nil? && !env_var_name.nil? and
61
+ raise ArgumentError,
62
+ "need either the value of an env_var or the name env_var_name of an env_var"
63
+ if env_var.nil? && !env_var_name.nil?
64
+ env_var = ENV[env_var_name]
65
+ end
66
+ root_path ||= env_var
67
+ @directory_path = derive_directory_path(name, root_path)
68
+ end
69
+
70
+ # Returns the string representation of the configuration directory path.
71
+ #
72
+ # @return [ String ] the path of the configuration directory as a string
73
+ def to_s
74
+ @directory_path.to_s
75
+ end
76
+
77
+ # Joins the directory path with the given path and returns the combined
78
+ # result.
79
+ #
80
+ # @param path [ String ] the path to be joined with the directory path
81
+ #
82
+ # @return [ Pathname ] the combined path as a Pathname object
83
+ def join(path)
84
+ @directory_path + path
85
+ end
86
+ alias + join
87
+
88
+ # Reads the content of a file at the given path within the configuration
89
+ # directory.
90
+ #
91
+ # If the file exists, it returns the file's content as a string encoded in
92
+ # UTF-8. If a block is given and the file exists, it opens the file and
93
+ # yields to the block.
94
+ # If the file does not exist and a default value is provided, it returns
95
+ # the default. If a block is given and the file does not exist, it yields a
96
+ # StringIO object containing
97
+ # the default value to the block.
98
+ #
99
+ # @param path [ String ] the path to the file relative to the configuration
100
+ # directory
101
+ # @param default [ String, nil ] the default value to return if the file
102
+ # does not exist
103
+ #
104
+ # @yield [ io ]
105
+ #
106
+ # @return [ String, nil ] the content of the file or the default value if
107
+ # the file does not exist
108
+ def read(path, default: nil, required: false, &block)
109
+ full_path = join(path)
110
+ if File.exist?(full_path)
111
+ if block
112
+ File.open(full_path, &block)
113
+ else
114
+ File.read(full_path, encoding: 'UTF-8')
115
+ end
116
+ else
117
+ required and raise ConstConf::RequiredValueNotConfigured,
118
+ "require file at #{full_path.to_s.inspect}"
119
+ if default && block
120
+ block.(StringIO.new(default))
121
+ else
122
+ default
123
+ end
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ # Derives the full directory path by combining the root path and the given
130
+ # name.
131
+ #
132
+ # @param name [ String ] the name of the directory to be appended to the root path
133
+ # @param root_path [ String, nil ] the root path to use; if nil, the default root path is used
134
+ #
135
+ # @return [ Pathname ] the combined directory path as a Pathname object
136
+ def derive_directory_path(name, root_path)
137
+ root = if path = root_path
138
+ Pathname.new(path)
139
+ else
140
+ Pathname.new(default_root_path)
141
+ end
142
+ root + name
143
+ end
144
+
145
+ # Returns the default configuration directory path based on the HOME
146
+ # environment variable.
147
+ #
148
+ # This method constructs and returns a Pathname object pointing to the
149
+ # standard configuration directory location, which is typically
150
+ # $HOME/.config.
151
+ #
152
+ # @return [ Pathname ] the default configuration directory path
153
+ def default_root_path
154
+ Pathname.new(ENV.fetch('HOME')) + '.config'
155
+ end
156
+ end
157
+
158
+ # The dir method creates and reads a configuration directory setting.
159
+ #
160
+ # This method initializes a ConfigDir instance with the provided name and
161
+ # environment variable configuration, then reads the directory path with
162
+ # optional default and required validation settings.
163
+ #
164
+ # @param name [String] the name of the configuration directory
165
+ # @param path [String] the filesystem path to the directory
166
+ # @param env_var [String, nil] the environment variable name to use for configuration
167
+ # @param env_var_name [String, nil] the full environment variable name to use
168
+ # @param default [Object] the default value to use when no configuration is provided
169
+ # @param required [Boolean] whether the directory path is required to exist
170
+ #
171
+ # @return [Object] the result of reading path from the directory name
172
+ def dir(name, path, env_var: nil, env_var_name: nil, default: nil, required: false)
173
+ ConfigDir.new(name, env_var:, env_var_name:).read(path, default:, required:)
174
+ end
175
+ end
@@ -0,0 +1,83 @@
1
+ # An extension that enables loading environment variable values from files in
2
+ # a directory structure, where each file's basename becomes an environment
3
+ # variable name.
4
+ #
5
+ # This extension is particularly useful for implementing a pattern, where each
6
+ # environment variable is stored in its own file with the same name as the
7
+ # variable. For example:
8
+ #
9
+ # ./env/DATABASE_URL # contains: postgresql://localhost/myapp
10
+ # ./env/API_KEY # contains: sk-1234567890abcdef1234567890abcdef
11
+ #
12
+ # Usage:
13
+ # module AppConfig
14
+ # include ConstConf
15
+ #
16
+ # description 'Application Configuration'
17
+ #
18
+ # module EnvDir
19
+ # extend ConstConf::EnvDirExtension
20
+ #
21
+ # description 'All variables loaded from .env directory'
22
+ #
23
+ # # Load all environment variables from .env/*
24
+ # load_dotenv_dir('.env/*') {}
25
+ # end
26
+ # end
27
+ #
28
+ # The loaded settings are automatically:
29
+ # - Marked as sensitive (since they might contain secrets)
30
+ # - Required (files must exist to be loaded)
31
+ # - Chomped to remove trailing whitespace
32
+ # - Named using the file basename in uppercase and without a prefix.
33
+ #
34
+ # This extension works well alongside manually-defined settings, if you put it
35
+ # into a nested module at the end of your ConstConf config. Settings defined
36
+ # explicitly with `set do` blocks before this module take precedence over
37
+ # auto-loaded ones, allowing for a hybrid approach where critical configuration
38
+ # is documented and validated, while additional environment variables can be
39
+ # loaded automatically from files.
40
+ #
41
+ # The loaded settings appear in AppConfig.view() with proper descriptions
42
+ # showing their source file locations, making it easy to see what configuration
43
+ # is being used.
44
+ module ConstConf::EnvDirExtension
45
+ include ConstConf::FilePlugin
46
+
47
+ # Loads environment variable values from dotenv-style files into
48
+ # configuration constants.
49
+ #
50
+ # This method processes glob patterns to find files, reads their content
51
+ # using the FilePlugin, and defines configuration settings for each file's
52
+ # content. It automatically creates constants with uppercase names based on
53
+ # the filename and configures them as required and sensitive settings with
54
+ # chomped values.
55
+ #
56
+ # @param globs [Array<String>] glob patterns to match files containing environment variables
57
+ # @yield [ binding ] yields the binding of the caller to allow evaluation in the correct context
58
+ # @yieldparam binding [Binding] the binding to evaluate constants in
59
+ def load_dotenv_dir(*globs, &block)
60
+ block or raise ArgumentError, '&block argument is required'
61
+ globs.each do |glob|
62
+ Dir[glob].each do |path|
63
+ my_file = file(path, required: true)
64
+ eval('self', block.__send__(:binding)).class_eval do
65
+ name = File.basename(path).upcase
66
+ const_set(
67
+ name,
68
+ set do
69
+ prefix ''
70
+ description "Value of #{name.inspect} from #{File.dirname(path).inspect}"
71
+ default my_file
72
+ decode(&:chomp)
73
+ required true
74
+ sensitive true
75
+ end
76
+ )
77
+ rescue ConstConf::SettingAlreadyDefined
78
+ end
79
+ end
80
+ end
81
+ self
82
+ end
83
+ end
@@ -0,0 +1,93 @@
1
+ # A module that defines custom exception classes for configuration-related
2
+ # errors in the ConstConf system. ConstConf::Errors is then included in
3
+ # ConstConf.
4
+ #
5
+ # This module provides a hierarchy of exception classes that are used
6
+ # throughout the ConstConf configuration management library to signal various
7
+ # types of configuration issues, such as missing required values or
8
+ # descriptions. These exceptions help ensure that applications using ConstConf
9
+ # are properly configured and can handle configuration errors gracefully.
10
+ #
11
+ # @example Handling configuration errors
12
+ # begin
13
+ # # Code that may raise a ConstConf::ConfigurationError
14
+ # rescue ConstConf::ConfigurationError => e
15
+ # # Handle the configuration error appropriately
16
+ # end
17
+ module ConstConf::Errors
18
+ # A base exception class for configuration-related errors in the ConstConf module.
19
+ #
20
+ # This class serves as the parent class for all custom exceptions raised within
21
+ # the ConstConf configuration management system. It provides a common ancestor
22
+ # for exception handling and allows for specific configuration error types to
23
+ # be distinguished from general errors.
24
+ #
25
+ # @example Handling a configuration error
26
+ # begin
27
+ # # Code that may raise a ConstConf::ConfigurationError
28
+ # rescue ConstConf::ConfigurationError => e
29
+ # # Handle the configuration error appropriately
30
+ # end
31
+ class ConfigurationError < StandardError; end
32
+
33
+ # A custom exception class used to signal that a required configuration value
34
+ # has not been provided.
35
+ #
36
+ # This exception is raised when a configuration setting marked as required
37
+ # has not been configured with a valid value, helping to ensure that
38
+ # essential application settings are properly defined before runtime.
39
+ #
40
+ # @example Handling a required configuration error
41
+ # begin
42
+ # # Code that may raise RequiredValueNotConfigured
43
+ # rescue RequiredValueNotConfigured => e
44
+ # # Handle the missing required configuration
45
+ # end
46
+ class RequiredValueNotConfigured < ConfigurationError ; end
47
+
48
+ # A custom exception class used to signal that a required configuration
49
+ # description has not been provided.
50
+ #
51
+ # This exception is raised when essential application settings are not
52
+ # properly documented before runtime.
53
+ #
54
+ # @example Handling a required description error
55
+ # begin
56
+ # # Code that may raise RequiredDescriptionNotConfigured
57
+ # rescue RequiredDescriptionNotConfigured => e
58
+ # # Handle the missing required configuration description
59
+ # end
60
+ class RequiredDescriptionNotConfigured < ConfigurationError ; end
61
+
62
+ # A custom exception class raised when a configuration setting is defined
63
+ # more than once in the ConstConf system.
64
+ #
65
+ # This exception is used to prevent duplicate configuration settings from
66
+ # being registered, ensuring that each environment variable name maps to a
67
+ # single configuration value within a given module hierarchy.
68
+ #
69
+ # @example Handling a duplicate setting definition
70
+ # begin
71
+ # # Code that would cause a SettingAlreadyDefined error
72
+ # rescue ConstConf::SettingAlreadyDefined => e
73
+ # # Handle the duplicate setting appropriately
74
+ # end
75
+ class SettingAlreadyDefined < ConfigurationError; end
76
+
77
+ # A custom exception class raised when a configuration setting's confirmation
78
+ # check fails.
79
+ #
80
+ # This exception is used to signal that a configuration setting has failed its
81
+ # confirmation check, which is a custom validation logic defined for the setting.
82
+ # It inherits from ConfigurationError and is part of the error handling mechanism
83
+ # within the ConstConf system to ensure settings meet specific criteria beyond
84
+ # basic required and default value checks.
85
+ #
86
+ # @example Handling a setting check failure
87
+ # begin
88
+ # # Code that may raise SettingCheckFailed
89
+ # rescue ConstConf::SettingCheckFailed => e
90
+ # # Handle the failed validation check appropriately
91
+ # end
92
+ class SettingCheckFailed < ConfigurationError; end
93
+ end
@@ -0,0 +1,33 @@
1
+ # A module that provides functionality for reading file contents as
2
+ # configuration values.
3
+ #
4
+ # The FilePlugin module extends the ConstConf::Setting class to enable
5
+ # configuration settings that are sourced from file contents rather than
6
+ # environment variables. This allows for more complex configuration data, such
7
+ # as SSL certificates or API keys, to be loaded from files and used within the
8
+ # application's configuration system.
9
+ module ConstConf::FilePlugin
10
+ # Reads the content of a file and returns it as a string.
11
+ #
12
+ # This method attempts to read the contents of a file specified by the given
13
+ # path. If the file exists, its content is returned as a string. If the file
14
+ # does not exist and the required flag is set to true, a
15
+ # RequiredValueNotConfigured exception is raised.
16
+ #
17
+ # @param path [String] the filesystem path to the file to be read
18
+ # @param required [Boolean] whether the file is required to exist, defaults to false
19
+ #
20
+ # @return [String, nil] the content of the file if it exists, or nil if it
21
+ # doesn't and required is false
22
+ #
23
+ # @raise [ConstConf::RequiredValueNotConfigured] if the file does not exist
24
+ # and required is true
25
+ def file(path, required: false)
26
+ if File.exist?(path)
27
+ File.read(path)
28
+ elsif required
29
+ raise ConstConf::RequiredValueNotConfigured,
30
+ "file required at path #{path.to_s.inspect}"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ require 'json'
2
+
3
+ module ConstConf::JSONPlugin
4
+ def json(path, required: false, object_class: JSON::GenericObject)
5
+ if File.exist?(path)
6
+ JSON.load_file(path, object_class:)
7
+ elsif required
8
+ raise ConstConf::RequiredValueNotConfigured,
9
+ "JSON file required at path #{path.to_s.inspect}"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # A Railtie implementation that integrates ConstConf with Rails application
2
+ # initialization.
3
+ #
4
+ # This class ensures that configuration settings defined through ConstConf are
5
+ # properly reloaded and synchronized when the Rails application prepares its
6
+ # configuration, maintaining thread safety during the process.
7
+ #
8
+ # @example
9
+ # This Railtie is automatically included in a Rails application when the
10
+ # ConstConf gem is loaded, requiring no explicit usage in application code.
11
+ class ConstConf::Railtie < Rails::Railtie
12
+ config.to_prepare { ConstConf.reload }
13
+ end