konfig-config 1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 75a53b2df0604086dbbec0e28d797572149247f999d16875948c0ceec940ccb5
4
+ data.tar.gz: 5dcc54bb9a313c53c26ce7b3070ef69e3a9ea9a805aa9a3513d5406be7297c64
5
+ SHA512:
6
+ metadata.gz: 6536edc14535469c53978a368ddc3fb8e73dbf8058c7c79cb4737894e73645d30275e86551a16fcfc5374acc8eb40e94c1c0af64ab5c45ab8775d341c8971273
7
+ data.tar.gz: c0f45ef59ea643d7d1a1d1342d6bda7ff88edb4e50530e53a009eb46efdbf6f3d64c42ece69d98cf959150fc74f0abedf6422d8f249815e5ff8370c5635cbec8
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konfig
4
+ class Config
5
+
6
+ class << self
7
+
8
+ def build(schema, sources: [])
9
+ @schema = schema
10
+ @sources = sources
11
+
12
+ values = Hashie::Mash.new(@schema.create_hash(nil))
13
+
14
+ # Override all defaults with values from the sources
15
+ # in the reverse order they are given.
16
+ sources.reverse.each do |source|
17
+ values.deep_merge!(@schema.create_hash(source))
18
+ end
19
+
20
+ values
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hashie'
4
+
5
+ module Konfig
6
+ class ConfigHash < ::Hash
7
+
8
+ include Hashie::Extensions::MethodAccess
9
+ include Hashie::Extensions::DeepMerge
10
+ include Hashie::Extensions::MergeInitializer
11
+ include Hashie::Extensions::IndifferentAccess
12
+
13
+ end
14
+ end
@@ -0,0 +1 @@
1
+ # frozen_string_literal: true
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konfig
4
+
5
+ class Error < StandardError
6
+ end
7
+
8
+ class GroupNotFoundError < Error
9
+ end
10
+
11
+ class AttributeNotFoundError < Error
12
+ end
13
+
14
+ class InvalidAttributeTypeError < Error
15
+ end
16
+
17
+ class ValueNotPresentError < Error
18
+ end
19
+
20
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'konfig/error'
4
+ require 'konfig/schema_group'
5
+ require 'konfig/schema_dsl'
6
+ require 'konfig/config_hash'
7
+
8
+ module Konfig
9
+ class Schema
10
+
11
+ attr_reader :groups
12
+
13
+ def initialize
14
+ @groups = {}
15
+ end
16
+
17
+ def group(name)
18
+ @groups[name] || raise(GroupNotFoundError, "Group '#{name}' not found in schema")
19
+ end
20
+
21
+ def group?(name)
22
+ @groups.key?(name)
23
+ end
24
+
25
+ def add_group(name)
26
+ group = SchemaGroup.new
27
+ @groups[name] = group
28
+ yield group if block_given?
29
+ group
30
+ end
31
+
32
+ def create_hash(source)
33
+ @groups.each_with_object({}) do |(name, group), hash|
34
+ hash[name] = group.create_hash([name], source)
35
+ end
36
+ end
37
+
38
+ class << self
39
+
40
+ def draw(&block)
41
+ schema = new
42
+ if block
43
+ dsl = SchemaDSL.new(schema)
44
+ dsl.instance_eval(&block)
45
+ end
46
+ schema
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konfig
4
+ class SchemaAttribute
5
+
6
+ TYPES = [:string, :integer, :float, :boolean].freeze
7
+
8
+ attr_reader :name
9
+ attr_reader :type
10
+ attr_reader :default
11
+ attr_reader :transformer
12
+
13
+ def initialize(name, type: :string, array: false, default: nil, transformer: nil)
14
+ @name = name
15
+ @type = type
16
+ @array = array
17
+ @default = default
18
+ @transformer = transformer
19
+
20
+ raise InvalidAttributeTypeError, "Invalid type #{type} for attribute #{name}" unless TYPES.include?(type)
21
+ end
22
+
23
+ def array?
24
+ @array == true
25
+ end
26
+
27
+ def cast(value)
28
+ return value.map { |v| cast(v) } if value.is_a?(Array)
29
+
30
+ return nil if value.nil?
31
+ return nil if value.is_a?(String) && value.empty?
32
+
33
+ send("cast_#{type}", value)
34
+ end
35
+
36
+ def transform(value)
37
+ casted = cast(value)
38
+ return casted if transformer.nil?
39
+
40
+ transformer.call(casted)
41
+ end
42
+
43
+ private
44
+
45
+ def cast_string(input)
46
+ input.to_s
47
+ end
48
+
49
+ def cast_integer(input)
50
+ return 1 if input == true
51
+ return 0 if input == false
52
+
53
+ input.to_i
54
+ end
55
+
56
+ def cast_float(input)
57
+ return 1.0 if input == true
58
+ return 0.0 if input == false
59
+
60
+ input.to_f
61
+ end
62
+
63
+ def cast_boolean(input)
64
+ return true if input == true
65
+ return false if input == false
66
+
67
+ %w[true 1 1.0].include?(input.to_s)
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'konfig/schema_group_dsl'
4
+
5
+ module Konfig
6
+ class SchemaDSL
7
+
8
+ def initialize(schema)
9
+ @schema = schema
10
+ end
11
+
12
+ def group(name, &block)
13
+ group = @schema.add_group(name)
14
+ if block
15
+ group_dsl = SchemaGroupDSL.new(group)
16
+ group_dsl.instance_eval(&block)
17
+ end
18
+ group
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'konfig/config_hash'
4
+ require 'konfig/schema_attribute'
5
+
6
+ module Konfig
7
+ class SchemaGroup
8
+
9
+ def initialize
10
+ @attributes = {}
11
+ end
12
+
13
+ def attribute(name)
14
+ @attributes[name] || raise(AttributeNotFoundError, "Attribute '#{name}' not found in schema")
15
+ end
16
+
17
+ def attribute?(name)
18
+ @attributes.key?(name)
19
+ end
20
+
21
+ def add_attribute(name, **kwargs, &block)
22
+ kwargs[:transformer] = block if block && !kwargs.key?(:transformer)
23
+ @attributes[name] = SchemaAttribute.new(name, **kwargs)
24
+ end
25
+
26
+ # Create a hash of the all the values for this group from the given source.
27
+ def create_hash(path, source = nil)
28
+ @attributes.each_with_object({}) do |(name, attribute), hash|
29
+ attribute_path = path + [name]
30
+ if source.nil?
31
+ hash[name] = attribute.cast(attribute.default)
32
+ else
33
+ begin
34
+ source_value = source.get(attribute_path, attribute: attribute)
35
+ hash[name] = attribute.transform(source_value)
36
+ rescue ValueNotPresentError
37
+ # This is OK
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'konfig/schema_attribute'
4
+
5
+ module Konfig
6
+ class SchemaGroupDSL
7
+
8
+ def initialize(group)
9
+ @group = group
10
+ end
11
+
12
+ SchemaAttribute::TYPES.each do |type|
13
+ define_method type do |name, **kwargs, &block|
14
+ @group.add_attribute(name, type: type, **kwargs, &block)
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konfig
4
+ module Sources
5
+ class Abstract
6
+
7
+ def initialize
8
+ end
9
+
10
+ # The get method will return the give value for the given path.
11
+ # The path will contain the full path to the value, including the
12
+ # groups which lead up to it.
13
+ def get(path, attribute: nil)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'konfig/error'
4
+ require 'konfig/sources/abstract'
5
+
6
+ module Konfig
7
+ module Sources
8
+ class Directory < Abstract
9
+
10
+ def initialize(root, strip_contents: true, array_separator: /\n/)
11
+ super()
12
+ @root = root
13
+ @strip_contents = strip_contents
14
+ @array_separator = array_separator
15
+ end
16
+
17
+ def get(path, attribute: nil)
18
+ file_path = File.join(@root, path.join('.'))
19
+ raise ValueNotPresentError unless File.exist?(file_path)
20
+
21
+ result = File.read(file_path)
22
+ result = result.strip if @strip_contents
23
+ result = handle_array(result) if attribute&.array?
24
+ result
25
+ end
26
+
27
+ private
28
+
29
+ def handle_array(value)
30
+ value.split(@array_separator).map(&:strip)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'konfig/error'
4
+ require 'konfig/sources/abstract'
5
+
6
+ module Konfig
7
+ module Sources
8
+ class Environment < Abstract
9
+
10
+ def initialize(env, array_separator: /\s*,\s*/)
11
+ super()
12
+ @env = env
13
+ @array_separator = array_separator
14
+ end
15
+
16
+ def get(path, attribute: nil)
17
+ key = path.map { |p| p.to_s.upcase }.join('_')
18
+ raise ValueNotPresentError unless @env.key?(key)
19
+
20
+ value = @env[key]
21
+
22
+ value = handle_array(value) if attribute&.array?
23
+ value
24
+ end
25
+
26
+ private
27
+
28
+ def handle_array(value)
29
+ value.split(@array_separator).map(&:strip)
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'konfig/error'
4
+ require 'konfig/sources/abstract'
5
+ require 'yaml'
6
+
7
+ module Konfig
8
+ module Sources
9
+ class YAML < Abstract
10
+
11
+ def initialize(source)
12
+ super()
13
+ @source = ::YAML.safe_load(source)
14
+ end
15
+
16
+ def get(path, _attribute: nil)
17
+ source = @source
18
+ path.each do |p|
19
+ raise ValueNotPresentError unless source.key?(p.to_s)
20
+
21
+ source = source[p.to_s]
22
+ end
23
+ source
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konfig
4
+
5
+ VERSION_FILE_ROOT = File.expand_path('../../VERSION', __dir__)
6
+ if File.file?(VERSION_FILE_ROOT)
7
+ VERSION = File.read(VERSION_FILE_ROOT).strip.sub(/\Av/, '')
8
+ else
9
+ VERSION = '0.0.0.dev'
10
+ end
11
+
12
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'konfig'
data/lib/konfig.rb ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'konfig/schema'
4
+ require 'konfig/config'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: konfig-config
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Cooke
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-03-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hashie
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: A config schema generator
28
+ email:
29
+ - adam@krystal.uk
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/konfig-config.rb
35
+ - lib/konfig.rb
36
+ - lib/konfig/config.rb
37
+ - lib/konfig/config_hash.rb
38
+ - lib/konfig/deep_merge.rb
39
+ - lib/konfig/error.rb
40
+ - lib/konfig/schema.rb
41
+ - lib/konfig/schema_attribute.rb
42
+ - lib/konfig/schema_dsl.rb
43
+ - lib/konfig/schema_group.rb
44
+ - lib/konfig/schema_group_dsl.rb
45
+ - lib/konfig/sources/abstract.rb
46
+ - lib/konfig/sources/directory.rb
47
+ - lib/konfig/sources/environment.rb
48
+ - lib/konfig/sources/yaml.rb
49
+ - lib/konfig/version.rb
50
+ homepage: https://github.com/krystal/konfig
51
+ licenses:
52
+ - MIT
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '2.6'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubygems_version: 3.3.26
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: A config schema generator
73
+ test_files: []