ucfg 0.1.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: 8bbf3f2b08b59481c9ec0f4575399514acac9e446b99440c4599d6fa70f4cf76
4
+ data.tar.gz: f17dbf0b390389951bb295f1a23e42a79e0a4bd5efc641e3c6f2d6aa32f9a1eb
5
+ SHA512:
6
+ metadata.gz: b098e6e239820fd2f75e108d7173dd29c5054adf103b31d84879c3503df6b23979d9c26722457f8029f95927b3d00f5dc09d28d321daab35e99519431a93c76d
7
+ data.tar.gz: c725cb25678c117cfc0b839b4ef20cf6305b67e3809e420369e1bca07d0061e026948b054a233e072b406c720629690c5e07dba783b17ccdd77093ff46a78293
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.5
7
+ before_install: gem install bundler -v 2.0.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ucfg.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ucfg (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ rake (10.5.0)
11
+ rspec (3.8.0)
12
+ rspec-core (~> 3.8.0)
13
+ rspec-expectations (~> 3.8.0)
14
+ rspec-mocks (~> 3.8.0)
15
+ rspec-core (3.8.2)
16
+ rspec-support (~> 3.8.0)
17
+ rspec-expectations (3.8.4)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.8.0)
20
+ rspec-mocks (3.8.1)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.8.0)
23
+ rspec-support (3.8.2)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ bundler (~> 2.0)
30
+ rake (~> 10.0)
31
+ rspec (~> 3.0)
32
+ ucfg!
33
+
34
+ BUNDLED WITH
35
+ 2.0.2
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ucfg"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/ucfg.rb ADDED
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ucfg/version"
4
+ require "ucfg/variable_substitution"
5
+ require "yaml"
6
+
7
+ module Ucfg
8
+ class Error < StandardError; end
9
+ class InvalidYAMLSyntax < Error; end
10
+ class InvalidSubstitution < Error; end
11
+
12
+ # This is a modified version of Hash#deep_merge! from ActiveSupport: https://github.com/rails/rails/blob/b9ca94caea2ca6a6cc09abaffaad67b447134079/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
13
+ module DeepMergeableHash
14
+ refine Hash do
15
+ def deep_merge!(other_hash, &block)
16
+ merge!(other_hash) do |key, this_val, other_val|
17
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
18
+ this_val.dup.deep_merge!(other_val, &block)
19
+ elsif block_given?
20
+ block.call(key, this_val, other_val)
21
+ else
22
+ other_val
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ using DeepMergeableHash
30
+
31
+ class << self
32
+ def parse(raw, options = ENV)
33
+ value = YAML.safe_load(raw)
34
+ dot_nested_value = dot_to_nested(value)
35
+ VariableSubstitution.new(dot_nested_value, options).substitute
36
+ rescue Psych::SyntaxError
37
+ raise InvalidYAMLSyntax, "Invalid YAML"
38
+ end
39
+
40
+ def dot_to_nested(value)
41
+ case value
42
+ when Array
43
+ value.map { |v| dot_to_nested(v) }
44
+ when Hash
45
+ value.each_with_object({}) do |(k, v), out|
46
+ keys_in_path = k.split(".")
47
+ transformed_value = dot_to_nested(v)
48
+ dot_nested_hash = keys_in_path.reverse_each.reduce(transformed_value) { |memo, part| { part => memo } }
49
+ out.deep_merge!(dot_nested_hash)
50
+ end
51
+ else
52
+ value
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Ucfg
6
+ class VariableSubstitution
7
+ PREFIX = "${"
8
+ SUFFIX = "}"
9
+
10
+ def initialize(full_config, options = {})
11
+ @full_config = full_config
12
+ @options = options
13
+ end
14
+
15
+ def substitute
16
+ substitute_variables(full_config)
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :full_config, :options
22
+
23
+ def substitute_variables(config)
24
+ case config
25
+ when Hash
26
+ config.transform_values { |val| substitute_variables(val) }
27
+ when Array
28
+ config.map { |val| substitute_variables(val) }
29
+ when String
30
+ final_value = variable_substitutions(config)
31
+ Psych::ScalarScanner.new(Psych::ClassLoader.new).tokenize(final_value)
32
+ else
33
+ config
34
+ end
35
+ end
36
+
37
+ def variable_substitutions(string, visited_placeholders = Set.new)
38
+ while (start_index = string.index(PREFIX))
39
+ break unless (end_index = find_placeholder_end_index(string, start_index))
40
+
41
+ placeholder = string[(start_index + PREFIX.length)..(end_index - SUFFIX.length)]
42
+ if visited_placeholders.include?(placeholder)
43
+ raise InvalidSubstitution, "Circular placeholder reference '#{placeholder}' in property definitions"
44
+ end
45
+
46
+ visited_placeholders.add(placeholder)
47
+
48
+ placeholder = variable_substitutions(placeholder, visited_placeholders)
49
+ placeholder, _, default_value = placeholder.partition(":")
50
+ if !options.key?(placeholder) && default_value.to_s.empty?
51
+ raise InvalidSubstitution, "Could not resolve placeholder '#{placeholder}'"
52
+ end
53
+
54
+ unquoted_default_value =
55
+ if default_value.start_with?('"') && default_value.end_with?('"')
56
+ default_value[1..-2]
57
+ elsif default_value.start_with?("'") && default_value.end_with?("'")
58
+ default_value[1..-2]
59
+ else
60
+ default_value
61
+ end
62
+ property_value = options.key?(placeholder) ? options.fetch(placeholder) : unquoted_default_value
63
+ final_value = variable_substitutions(property_value, visited_placeholders)
64
+ string = string[0...start_index] + final_value + string[(end_index + 1)..-1]
65
+
66
+ visited_placeholders.delete(placeholder)
67
+ end
68
+
69
+ string
70
+ end
71
+
72
+ def find_placeholder_end_index(config, start_index)
73
+ index = start_index + PREFIX.length
74
+ within_nested_placedholder = 0
75
+ while index < config.length
76
+ if config[index...(index + SUFFIX.length)] == SUFFIX
77
+ return index unless within_nested_placedholder > 0
78
+
79
+ within_nested_placedholder -= 1
80
+ index += SUFFIX.length
81
+ elsif config[index...(index + PREFIX.length)] == PREFIX
82
+ within_nested_placedholder += 1
83
+ index += PREFIX.length
84
+ else
85
+ index += 1
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ucfg
4
+ VERSION = "0.1.0"
5
+ end
data/ucfg.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "ucfg/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "ucfg"
7
+ spec.version = Ucfg::VERSION
8
+ spec.authors = ["Orhan Toy"]
9
+ spec.email = ["toyorhan@gmail.com"]
10
+
11
+ spec.summary = %q{Ruby universal configuration}
12
+ spec.description = %q{This library is the Ruby variant of https://github.com/elastic/go-ucfg}
13
+ spec.homepage = "https://github.com/orhantoy/ucfg"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.com/orhantoy/ucfg"
17
+ spec.metadata["changelog_uri"] = "https://github.com/orhantoy/ucfg"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_development_dependency "bundler", "~> 2.0"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ucfg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Orhan Toy
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-08-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: This library is the Ruby variant of https://github.com/elastic/go-ucfg
56
+ email:
57
+ - toyorhan@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - Gemfile.lock
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/setup
70
+ - lib/ucfg.rb
71
+ - lib/ucfg/variable_substitution.rb
72
+ - lib/ucfg/version.rb
73
+ - ucfg.gemspec
74
+ homepage: https://github.com/orhantoy/ucfg
75
+ licenses: []
76
+ metadata:
77
+ homepage_uri: https://github.com/orhantoy/ucfg
78
+ source_code_uri: https://github.com/orhantoy/ucfg
79
+ changelog_uri: https://github.com/orhantoy/ucfg
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubygems_version: 3.0.4
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Ruby universal configuration
99
+ test_files: []