konfig-config 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []