easy-settings 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: 28262b3447381ef370224446b8d64871b2391aa420f3addfde0417a9b4a3c909
4
+ data.tar.gz: 44c4d788c7f2b49e4748062b3f48029ce331da8c55a7b920543eadd3f2da6023
5
+ SHA512:
6
+ metadata.gz: cb3b83e76da3c4ae3afa86525b35cec52f6dfe4b8809d2e080c21acad84b30cce0e3975a285daa1994d9907b629e89798ad867e6e1b8320bebd6e43ac7f16ea6
7
+ data.tar.gz: e4900f399e2b9d7089f5999b5b908beb5e05586a6312a8587d0f85ebab661e9a2a35e855a629a690be0b0e1263dac6fb1a5031c4fc92215583424f1963e86e53
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+
6
+ .idea
7
+
8
+ .byebug_history
data/Dockerfile ADDED
@@ -0,0 +1,7 @@
1
+ FROM ruby:2.6-stretch
2
+
3
+ RUN groupadd --gid 1000 web && useradd --create-home --uid 1000 --gid 1000 --shell /bin/bash web
4
+
5
+ WORKDIR /home/web/app
6
+
7
+ USER web
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in easy-settings.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 - 2019 Corin Langosch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,61 @@
1
+ = easy-settings
2
+
3
+ Inspired by the mc-settings and config gems, but with a simpler code base and more flexible.
4
+
5
+ == Installation
6
+
7
+ Just include the screwdriver gem in your Bundler Gemfile
8
+
9
+ gem "easy-settings"
10
+
11
+ == Usage
12
+
13
+ Just create an instance of EasySettings (you can have as many as you need) and specify the sources, where this instance should load its settings/ configuration from. The sources are loaded in order and later settings override previous ones. This ways it's easy to for example put some defaults in a defaults.yml but override the settings using ENV variables or docker swarm secrets. Example:
14
+
15
+ Settings = EasySettings.new(
16
+ sources: [
17
+ EasySettings::YamlSource.new("config/settings/defaults.yml")),
18
+ EasySettings::YamlSource.new("config/settings/#{Rails.env}.yml")),
19
+ EasySettings::PathSource.new("/run/secrets"), # nice when using docker swarm secrets (the name of the file is the name of the setting, similar to the ENV source)
20
+ EasySettings::EnvSource.new("APP"), # APP__BUSINESS_TIME__ENABLED=true => Settings.business_time.enabled
21
+ ],
22
+ fail_on_missing: true,
23
+ )
24
+
25
+ MyConfig = EasySettings.new(
26
+ sources: [
27
+ EasySettings::YamlSource.new("config.yml"),
28
+ EasySettings::EnvSource.new("APP"),
29
+ ],
30
+ )
31
+
32
+ You can also easily implement your own `Source` in case you have special requirements. Just have a look at the ones defined in this gem to see how.
33
+
34
+ To access your settings:
35
+
36
+ Settings.apis.netskin.url # raises exception if setting cannot be found if fail_on_missing is true, nil otherwise
37
+ Settings.apis.netskin.try("url") # returns nil if setting cannot be found
38
+ Settings.business_time[2019].holidays # raises exception if setting cannot be found if fail_on_missing is true, nil otherwise
39
+
40
+ To set/ change some settings programmatically:
41
+
42
+ Settings.apis.netskin.url = "https://www.netskin.com"
43
+ Settings.apis.netskin["url"] = "https://www.netskin.com"
44
+
45
+ == Todo/ next steps
46
+
47
+ * Add tests
48
+
49
+ == Contributing
50
+
51
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
52
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
53
+ * Fork the project
54
+ * Start a feature/bugfix branch
55
+ * Commit and push until you are happy with your contribution
56
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
57
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
58
+
59
+ == Copyright
60
+
61
+ Copyright (c) by Corin Langosch. Released unter the MIT license.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ version: "2.1"
2
+
3
+ services:
4
+ web:
5
+ build: .
6
+ command: bash -c 'sleep 100000'
7
+ volumes:
8
+ - ./:/home/web/app
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "easy-settings/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "easy-settings"
7
+ s.version = EasySettings::VERSION
8
+ s.license = "MIT"
9
+ s.authors = ["Corin Langosch"]
10
+ s.email = ["info@corinlangosch.com"]
11
+ s.homepage = "https://github.com/gucki/easy-settings"
12
+ s.summary = %q{flexible yet easy handling of application settings}
13
+ s.description = %q{flexible yet easy handling of application settings}
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # specify any dependencies here; for example:
21
+ s.add_development_dependency "rspec"
22
+ s.add_development_dependency "byebug"
23
+ s.add_runtime_dependency "activesupport", ">=3.0.0"
24
+ end
@@ -0,0 +1,31 @@
1
+ require "easy-settings/version"
2
+ require "easy-settings/struct"
3
+ require "easy-settings/env_source"
4
+ require "easy-settings/path_source"
5
+ require "easy-settings/yaml_source"
6
+
7
+ class EasySettings
8
+ delegate :respond_to?, to: :@data
9
+
10
+ def initialize(sources:, fail_on_missing: true)
11
+ @sources = sources
12
+ @fail_on_missing = fail_on_missing
13
+ reload!
14
+ end
15
+
16
+ def reload!
17
+ config = {}
18
+ @sources.each do |source|
19
+ data = source.load
20
+ config.deep_merge!(data)
21
+ end
22
+ @data = EasySettings::Struct.import(config)
23
+ end
24
+
25
+ def method_missing(method_name, *args)
26
+ @data.send(method_name, *args)
27
+ rescue EasySettings::Struct::UnknownPropertyError => e
28
+ return unless @fail_on_missing
29
+ raise e
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ require "json"
2
+
3
+ class EasySettings::Coercion
4
+ attr_reader :value
5
+
6
+ def initialize(value)
7
+ @value = value
8
+ end
9
+
10
+ def run
11
+ case value
12
+ when "false"
13
+ false
14
+ when "true"
15
+ true
16
+ when /^json:/
17
+ JSON.parse(value.gsub(/^json:/, ""))
18
+ when /^\+/ # don't treat +41791234567 as a number
19
+ value
20
+ else
21
+ Integer(value) rescue Float(value) rescue value
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ require "easy-settings/coercion"
2
+
3
+ class EasySettings::EnvSource
4
+ attr_reader :prefix, :separator, :converter, :parse_values
5
+
6
+ def initialize(prefix, separator: "__", converter: :downcase, parse_values: true)
7
+ @prefix = prefix
8
+ @separator = separator
9
+ @converter = converter
10
+ @parse_values = parse_values
11
+ end
12
+
13
+ def load
14
+ {}.tap do |data|
15
+ ENV.each do |variable, value|
16
+ keys = variable.to_s.split(separator)
17
+ next unless keys.shift == prefix
18
+
19
+ keys.map!{ |key| key.send(converter) } if converter
20
+
21
+ leaf = keys[0...-1].inject(data){ |h, key| h[key] ||= {} }
22
+ leaf[keys.last] = parse_values ? EasySettings::Coercion.new(value).run : value
23
+ rescue NoMethodError => e
24
+ raise "Invalid name converter: #{converter}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ require "easy-settings/coercion"
2
+
3
+ class EasySettings::PathSource
4
+ attr_reader :base_path, :separator, :converter, :parse_values
5
+
6
+ def initialize(base_path, separator: "__", converter: :downcase, parse_values: true)
7
+ @base_path = base_path.to_s
8
+ @separator = separator
9
+ @converter = converter
10
+ @parse_values = parse_values
11
+ end
12
+
13
+ def load
14
+ {}.tap do |data|
15
+ Dir["#{base_path}/*"].each do |path|
16
+ next unless File.file?(path)
17
+
18
+ variable = path.gsub("#{base_path}/", "")
19
+ value = File.read(path).strip
20
+
21
+ keys = variable.to_s.split(separator)
22
+ keys.map!{ |key| key.send(converter) } if converter
23
+
24
+ leaf = keys[0...-1].inject(data){ |h, key| h[key] ||= {} }
25
+ leaf[keys.last] = parse_values ? EasySettings::Coercion.new(value).run : value
26
+ rescue NoMethodError => e
27
+ raise "Invalid name converter: #{converter}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,58 @@
1
+ class EasySettings::Struct
2
+ include Enumerable
3
+
4
+ UnknownPropertyError = Class.new(StandardError)
5
+
6
+ delegate :each, to: :@properties
7
+
8
+ def self.import(hash)
9
+ new.tap do |struct|
10
+ hash.each do |k, v|
11
+ case v
12
+ when Hash
13
+ v = import(v)
14
+ when Array
15
+ v = v.collect{ |i| i.is_a?(Hash) ? import(i) : i }
16
+ end
17
+ struct[k] = v
18
+ end
19
+ end
20
+ end
21
+
22
+ def initialize(properties = {})
23
+ @properties = properties
24
+ end
25
+
26
+ def [](property)
27
+ @properties.fetch(property){ raise_unknown_property(property) }
28
+ end
29
+
30
+ def []=(property, value)
31
+ @properties[property] = value
32
+ end
33
+
34
+ def try(property)
35
+ @properties[property]
36
+ end
37
+
38
+ def to_h
39
+ @properties.transform_values{ |v| v.is_a?(EasySettings::Struct) ? v.to_h : v }
40
+ end
41
+
42
+ def respond_to?(property, include_private = false)
43
+ @properties.has_key?(property.to_s)
44
+ end
45
+
46
+ def method_missing(method, *args)
47
+ method = method.to_s
48
+ return @properties.fetch(method) unless method.end_with?("=")
49
+ property = method[0..-2]
50
+ @properties[property] = args[0]
51
+ rescue KeyError => e
52
+ raise_unknown_property(method)
53
+ end
54
+
55
+ def raise_unknown_property(property)
56
+ raise UnknownPropertyError, "unknown property #{property.inspect} (#{@properties.keys.map(&:inspect).join(", ")})"
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ class EasySettings
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,16 @@
1
+ require "yaml"
2
+ require "erb"
3
+
4
+ class EasySettings::YamlSource
5
+ attr_reader :path
6
+
7
+ def initialize(path)
8
+ @path = path.to_s
9
+ end
10
+
11
+ def load
12
+ result = File.exist?(path) ? YAML.load(ERB.new(IO.read(path)).result) : {}
13
+ rescue StandardError => e
14
+ raise "Error occurred while parsing #{path}: #{e.message}"
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ require "spec_helper"
2
+
3
+ describe EasySettings do
4
+ end
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+ Bundler.setup
3
+
4
+ RSpec.configure do |config|
5
+ # some (optional) config here
6
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: easy-settings
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Corin Langosch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-09-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 3.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 3.0.0
55
+ description: flexible yet easy handling of application settings
56
+ email:
57
+ - info@corinlangosch.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Dockerfile
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.rdoc
67
+ - Rakefile
68
+ - docker-compose.yml
69
+ - easy-settings.gemspec
70
+ - lib/easy-settings.rb
71
+ - lib/easy-settings/coercion.rb
72
+ - lib/easy-settings/env_source.rb
73
+ - lib/easy-settings/path_source.rb
74
+ - lib/easy-settings/struct.rb
75
+ - lib/easy-settings/version.rb
76
+ - lib/easy-settings/yaml_source.rb
77
+ - spec/easy-settings_spec.rb
78
+ - spec/spec_helper.rb
79
+ homepage: https://github.com/gucki/easy-settings
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubygems_version: 3.0.3
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: flexible yet easy handling of application settings
102
+ test_files: []