dry-configurable 0.7.0 → 0.8.3
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 +5 -5
- data/.gitignore +1 -1
- data/.travis.yml +8 -14
- data/CHANGELOG.md +70 -2
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +1 -0
- data/README.md +20 -32
- data/dry-configurable.gemspec +10 -4
- data/lib/dry/configurable.rb +125 -118
- data/lib/dry/configurable/config.rb +117 -40
- data/lib/dry/configurable/error.rb +0 -2
- data/lib/dry/configurable/setting.rb +37 -0
- data/lib/dry/configurable/settings.rb +92 -0
- data/lib/dry/configurable/settings/argument_parser.rb +50 -0
- data/lib/dry/configurable/test_interface.rb +5 -1
- data/lib/dry/configurable/version.rb +1 -1
- metadata +47 -38
- data/.ruby-version +0 -1
- data/lib/dry/configurable/argument_parser.rb +0 -91
- data/lib/dry/configurable/config/value.rb +0 -27
- data/lib/dry/configurable/nested_config.rb +0 -33
- data/spec/integration/configurable_spec.rb +0 -25
- data/spec/spec_helper.rb +0 -92
- data/spec/support/shared_examples/configurable.rb +0 -391
- data/spec/unit/dry/configurable/argument_parser_spec.rb +0 -114
- data/spec/unit/dry/configurable/config/value_spec.rb +0 -55
- data/spec/unit/dry/configurable/config_spec.rb +0 -186
@@ -1,61 +1,87 @@
|
|
1
|
+
require 'concurrent/hash'
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Configurable
|
3
5
|
# @private
|
4
6
|
class Config
|
5
|
-
|
7
|
+
class << self
|
8
|
+
# @private
|
9
|
+
def [](settings)
|
10
|
+
::Class.new(Config) do
|
11
|
+
@settings = settings
|
12
|
+
singleton_class.send(:attr_reader, :settings)
|
13
|
+
|
14
|
+
@lock = ::Mutex.new
|
15
|
+
@config_defined = false
|
16
|
+
end
|
17
|
+
end
|
6
18
|
|
7
|
-
|
8
|
-
|
19
|
+
# @private
|
20
|
+
def define_accessors!
|
21
|
+
@lock.synchronize do
|
22
|
+
break if config_defined?
|
9
23
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
24
|
+
settings.each do |setting|
|
25
|
+
define_method(setting.name) do
|
26
|
+
@config[setting.name]
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method("#{setting.name}=") do |value|
|
30
|
+
raise FrozenConfig, 'Cannot modify frozen config' if frozen?
|
31
|
+
@config[setting.name] = setting.processor.(value)
|
32
|
+
end
|
33
|
+
end
|
14
34
|
|
15
|
-
|
16
|
-
raise_frozen_config if frozen?
|
17
|
-
@config[setting.name] = setting.processor.call(value)
|
35
|
+
@config_defined = true
|
18
36
|
end
|
19
37
|
end
|
20
38
|
|
21
|
-
|
39
|
+
# @private
|
40
|
+
def config_defined?
|
41
|
+
@config_defined
|
42
|
+
end
|
22
43
|
end
|
23
44
|
|
24
|
-
def initialize
|
45
|
+
def initialize
|
25
46
|
@config = ::Concurrent::Hash.new
|
26
|
-
|
27
|
-
|
28
|
-
if setting.none?
|
29
|
-
@config[setting.name] = nil
|
30
|
-
else
|
31
|
-
public_send("#{setting.name}=", setting.value)
|
32
|
-
end
|
33
|
-
end
|
47
|
+
@lock = ::Mutex.new
|
48
|
+
@defined = false
|
34
49
|
end
|
35
50
|
|
36
|
-
def
|
37
|
-
|
38
|
-
dup.instance_variable_set(:@config, @config.dup)
|
39
|
-
dup
|
51
|
+
def defined?
|
52
|
+
@defined
|
40
53
|
end
|
41
54
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
55
|
+
# @private
|
56
|
+
def define!(parent_config = EMPTY_HASH)
|
57
|
+
@lock.synchronize do
|
58
|
+
break if self.defined?
|
59
|
+
|
60
|
+
self.class.define_accessors!
|
61
|
+
set_values!(parent_config)
|
62
|
+
|
63
|
+
@defined = true
|
64
|
+
end
|
65
|
+
|
66
|
+
self
|
46
67
|
end
|
47
68
|
|
69
|
+
# @private
|
48
70
|
def finalize!
|
71
|
+
define!
|
49
72
|
@config.freeze
|
50
73
|
freeze
|
51
74
|
end
|
52
75
|
|
76
|
+
# Serialize config to a Hash
|
77
|
+
#
|
78
|
+
# @return [Hash]
|
79
|
+
#
|
80
|
+
# @api public
|
53
81
|
def to_h
|
54
|
-
@config.each_with_object({}) do |
|
55
|
-
key, value = tuple
|
56
|
-
|
82
|
+
@config.each_with_object({}) do |(key, value), hash|
|
57
83
|
case value
|
58
|
-
when
|
84
|
+
when Config
|
59
85
|
hash[key] = value.to_h
|
60
86
|
else
|
61
87
|
hash[key] = value
|
@@ -64,29 +90,80 @@ module Dry
|
|
64
90
|
end
|
65
91
|
alias to_hash to_h
|
66
92
|
|
93
|
+
# Get config value by a key
|
94
|
+
#
|
95
|
+
# @param [String,Symbol] name
|
96
|
+
#
|
97
|
+
# @return Config value
|
67
98
|
def [](name)
|
68
|
-
raise_unknown_setting_error(name) unless
|
99
|
+
raise_unknown_setting_error(name) unless key?(name.to_sym)
|
69
100
|
public_send(name)
|
70
101
|
end
|
71
102
|
|
103
|
+
# Set config value.
|
104
|
+
# Note that finalized configs cannot be changed.
|
105
|
+
#
|
106
|
+
# @param [String,Symbol] name
|
107
|
+
# @param [Object] value
|
72
108
|
def []=(name, value)
|
73
|
-
raise_unknown_setting_error(name) unless
|
109
|
+
raise_unknown_setting_error(name) unless key?(name.to_sym)
|
74
110
|
public_send("#{name}=", value)
|
75
111
|
end
|
76
112
|
|
113
|
+
# Whether config has a key
|
114
|
+
#
|
115
|
+
# @param [Symbol] key
|
116
|
+
# @return [Bool]
|
117
|
+
def key?(name)
|
118
|
+
self.class.settings.name?(name)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Recursively update values from a hash
|
122
|
+
#
|
123
|
+
# @param [Hash] values to set
|
124
|
+
# @return [Config]
|
125
|
+
def update(values)
|
126
|
+
values.each do |key, value|
|
127
|
+
if self[key].is_a?(Config)
|
128
|
+
self[key].update(value)
|
129
|
+
else
|
130
|
+
self[key] = value
|
131
|
+
end
|
132
|
+
end
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
def dup
|
137
|
+
if self.defined?
|
138
|
+
self.class.new.define!(to_h)
|
139
|
+
else
|
140
|
+
self.class.new
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
77
144
|
private
|
78
145
|
|
79
|
-
|
80
|
-
|
146
|
+
# @private
|
147
|
+
def set_values!(parent)
|
148
|
+
self.class.settings.each do |setting|
|
149
|
+
if parent.key?(setting.name) && !setting.node?
|
150
|
+
@config[setting.name] = parent[setting.name]
|
151
|
+
elsif setting.undefined?
|
152
|
+
@config[setting.name] = nil
|
153
|
+
elsif setting.node?
|
154
|
+
value = setting.value.create_config
|
155
|
+
value.define!(parent.fetch(setting.name, EMPTY_HASH))
|
156
|
+
self[setting.name] = value
|
157
|
+
else
|
158
|
+
self[setting.name] = setting.value
|
159
|
+
end
|
160
|
+
end
|
81
161
|
end
|
82
162
|
|
163
|
+
# @private
|
83
164
|
def raise_unknown_setting_error(name)
|
84
165
|
raise ArgumentError, "+#{name}+ is not a setting name"
|
85
166
|
end
|
86
|
-
|
87
|
-
def setting?(name)
|
88
|
-
@config.key?(name.to_sym)
|
89
|
-
end
|
90
167
|
end
|
91
168
|
end
|
92
169
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Dry
|
2
|
+
module Configurable
|
3
|
+
# This class represents a setting and is used internally.
|
4
|
+
#
|
5
|
+
# @private
|
6
|
+
class Setting
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
attr_reader :processor
|
12
|
+
|
13
|
+
def initialize(name, value, processor, options = EMPTY_HASH)
|
14
|
+
@name = name.to_sym
|
15
|
+
@value = value
|
16
|
+
@processor = processor
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
def value
|
21
|
+
Undefined.default(@value, nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
def undefined?
|
25
|
+
Undefined.equal?(@value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def reader?
|
29
|
+
options[:reader]
|
30
|
+
end
|
31
|
+
|
32
|
+
def node?
|
33
|
+
Settings === @value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'concurrent/array'
|
3
|
+
require 'dry/configurable/settings/argument_parser'
|
4
|
+
require 'dry/configurable/setting'
|
5
|
+
require 'dry/configurable/config'
|
6
|
+
|
7
|
+
module Dry
|
8
|
+
module Configurable
|
9
|
+
# A collection of settings. This is not part of the public API.
|
10
|
+
#
|
11
|
+
# @private
|
12
|
+
class Settings
|
13
|
+
Parser = ArgumentParser.new.freeze
|
14
|
+
|
15
|
+
class DSL
|
16
|
+
def self.call(&block)
|
17
|
+
new.instance_exec do
|
18
|
+
instance_exec(&block)
|
19
|
+
@settings
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@settings = Settings.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def setting(*args, &block)
|
28
|
+
@settings.add(*args, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Capture nested config definition
|
33
|
+
#
|
34
|
+
# @return [Dry::Configurable::Setting]
|
35
|
+
def self.capture(&block)
|
36
|
+
DSL.(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :settings
|
40
|
+
|
41
|
+
attr_reader :config_class
|
42
|
+
|
43
|
+
attr_reader :names
|
44
|
+
|
45
|
+
def initialize(settings = ::Concurrent::Array.new)
|
46
|
+
@settings = settings
|
47
|
+
@config_class = Config[self]
|
48
|
+
@names = Set.new(settings.map(&:name))
|
49
|
+
yield(self) if block_given?
|
50
|
+
end
|
51
|
+
|
52
|
+
def add(key, value = Undefined, options = Undefined, &block)
|
53
|
+
extended = singleton_class < Configurable
|
54
|
+
raise_already_defined_config(key) if extended && configured?
|
55
|
+
|
56
|
+
Setting.new(key, *Parser.(value, options, block)).tap do |s|
|
57
|
+
settings << s
|
58
|
+
names << s.name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def each
|
63
|
+
settings.each { |s| yield(s) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def empty?
|
67
|
+
settings.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
def name?(name)
|
71
|
+
names.include?(name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def dup
|
75
|
+
Settings.new(settings.dup)
|
76
|
+
end
|
77
|
+
|
78
|
+
def freeze
|
79
|
+
settings.freeze
|
80
|
+
super
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_config
|
84
|
+
config_class.new
|
85
|
+
end
|
86
|
+
|
87
|
+
def config_defined?
|
88
|
+
config_class.config_defined?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Dry
|
2
|
+
# Argument parser
|
3
|
+
#
|
4
|
+
# Passing and array or arguments, it will decide which one are arguments
|
5
|
+
# and which one are options.
|
6
|
+
#
|
7
|
+
# We have a limitation if setting the value without options, as a hash
|
8
|
+
# having the same key as one of the valid options, will parse the value
|
9
|
+
# as options. In this case, all unknown options will be reject with an exception.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# p = Dry::Configurable::ArgumentParser.new.('db:sqlite', reader: true)
|
13
|
+
#
|
14
|
+
# p[0] # => 'db:sqlite'
|
15
|
+
# p[1] # => ArgumentParser::DEFAULT_PROCESSOR
|
16
|
+
# p[2] # => { reader: true }
|
17
|
+
module Configurable
|
18
|
+
class Settings
|
19
|
+
# @private
|
20
|
+
class ArgumentParser
|
21
|
+
DEFAULT_PROCESSOR = ->(v) { v }
|
22
|
+
|
23
|
+
# @private
|
24
|
+
def call(val, opts, block)
|
25
|
+
if block && block.parameters.empty?
|
26
|
+
raise ArgumentError unless Undefined.equal?(opts)
|
27
|
+
|
28
|
+
processor = DEFAULT_PROCESSOR
|
29
|
+
|
30
|
+
value, options = Settings.capture(&block), val
|
31
|
+
else
|
32
|
+
processor = block || DEFAULT_PROCESSOR
|
33
|
+
|
34
|
+
if Undefined.equal?(opts) && val.is_a?(Hash) && val.key?(:reader)
|
35
|
+
value, options = Undefined, val
|
36
|
+
else
|
37
|
+
value, options = val, opts
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
[value, processor, options(Undefined.default(options, EMPTY_HASH))]
|
42
|
+
end
|
43
|
+
|
44
|
+
def options(reader: false)
|
45
|
+
{ reader: reader }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
CHANGED
@@ -1,69 +1,89 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-configurable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Holland
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-core
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.4'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 0.4.7
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0.4'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.4.7
|
27
47
|
- !ruby/object:Gem::Dependency
|
28
48
|
name: bundler
|
29
49
|
requirement: !ruby/object:Gem::Requirement
|
30
50
|
requirements:
|
31
|
-
- -
|
51
|
+
- - ">="
|
32
52
|
- !ruby/object:Gem::Version
|
33
53
|
version: '0'
|
34
54
|
type: :development
|
35
55
|
prerelease: false
|
36
56
|
version_requirements: !ruby/object:Gem::Requirement
|
37
57
|
requirements:
|
38
|
-
- -
|
58
|
+
- - ">="
|
39
59
|
- !ruby/object:Gem::Version
|
40
60
|
version: '0'
|
41
61
|
- !ruby/object:Gem::Dependency
|
42
62
|
name: rake
|
43
63
|
requirement: !ruby/object:Gem::Requirement
|
44
64
|
requirements:
|
45
|
-
- -
|
65
|
+
- - ">="
|
46
66
|
- !ruby/object:Gem::Version
|
47
67
|
version: '0'
|
48
68
|
type: :development
|
49
69
|
prerelease: false
|
50
70
|
version_requirements: !ruby/object:Gem::Requirement
|
51
71
|
requirements:
|
52
|
-
- -
|
72
|
+
- - ">="
|
53
73
|
- !ruby/object:Gem::Version
|
54
74
|
version: '0'
|
55
75
|
- !ruby/object:Gem::Dependency
|
56
76
|
name: rspec
|
57
77
|
requirement: !ruby/object:Gem::Requirement
|
58
78
|
requirements:
|
59
|
-
- -
|
79
|
+
- - ">="
|
60
80
|
- !ruby/object:Gem::Version
|
61
81
|
version: '0'
|
62
82
|
type: :development
|
63
83
|
prerelease: false
|
64
84
|
version_requirements: !ruby/object:Gem::Requirement
|
65
85
|
requirements:
|
66
|
-
- -
|
86
|
+
- - ">="
|
67
87
|
- !ruby/object:Gem::Version
|
68
88
|
version: '0'
|
69
89
|
description:
|
@@ -73,12 +93,12 @@ executables: []
|
|
73
93
|
extensions: []
|
74
94
|
extra_rdoc_files: []
|
75
95
|
files:
|
76
|
-
- .codeclimate.yml
|
77
|
-
- .gitignore
|
78
|
-
- .rspec
|
79
|
-
- .
|
80
|
-
- .travis.yml
|
96
|
+
- ".codeclimate.yml"
|
97
|
+
- ".gitignore"
|
98
|
+
- ".rspec"
|
99
|
+
- ".travis.yml"
|
81
100
|
- CHANGELOG.md
|
101
|
+
- CONTRIBUTING.md
|
82
102
|
- Gemfile
|
83
103
|
- LICENSE
|
84
104
|
- README.md
|
@@ -86,48 +106,37 @@ files:
|
|
86
106
|
- dry-configurable.gemspec
|
87
107
|
- lib/dry-configurable.rb
|
88
108
|
- lib/dry/configurable.rb
|
89
|
-
- lib/dry/configurable/argument_parser.rb
|
90
109
|
- lib/dry/configurable/config.rb
|
91
|
-
- lib/dry/configurable/config/value.rb
|
92
110
|
- lib/dry/configurable/error.rb
|
93
|
-
- lib/dry/configurable/
|
111
|
+
- lib/dry/configurable/setting.rb
|
112
|
+
- lib/dry/configurable/settings.rb
|
113
|
+
- lib/dry/configurable/settings/argument_parser.rb
|
94
114
|
- lib/dry/configurable/test_interface.rb
|
95
115
|
- lib/dry/configurable/version.rb
|
96
116
|
- rakelib/rubocop.rake
|
97
|
-
|
98
|
-
- spec/spec_helper.rb
|
99
|
-
- spec/support/shared_examples/configurable.rb
|
100
|
-
- spec/unit/dry/configurable/argument_parser_spec.rb
|
101
|
-
- spec/unit/dry/configurable/config/value_spec.rb
|
102
|
-
- spec/unit/dry/configurable/config_spec.rb
|
103
|
-
homepage: https://github.com/dryrb/dry-configurable
|
117
|
+
homepage: https://github.com/dry-rb/dry-configurable
|
104
118
|
licenses:
|
105
119
|
- MIT
|
106
|
-
metadata:
|
120
|
+
metadata:
|
121
|
+
source_code_uri: https://github.com/dry-rb/dry-configurable
|
122
|
+
changelog_uri: https://github.com/dry-rb/dry-configurable/blob/master/CHANGELOG.md
|
107
123
|
post_install_message:
|
108
124
|
rdoc_options: []
|
109
125
|
require_paths:
|
110
126
|
- lib
|
111
127
|
required_ruby_version: !ruby/object:Gem::Requirement
|
112
128
|
requirements:
|
113
|
-
- -
|
129
|
+
- - ">="
|
114
130
|
- !ruby/object:Gem::Version
|
115
|
-
version:
|
131
|
+
version: 2.3.0
|
116
132
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
133
|
requirements:
|
118
|
-
- -
|
134
|
+
- - ">="
|
119
135
|
- !ruby/object:Gem::Version
|
120
136
|
version: '0'
|
121
137
|
requirements: []
|
122
|
-
|
123
|
-
rubygems_version: 2.4.8
|
138
|
+
rubygems_version: 3.0.3
|
124
139
|
signing_key:
|
125
140
|
specification_version: 4
|
126
141
|
summary: A mixin to add configuration functionality to your classes
|
127
|
-
test_files:
|
128
|
-
- spec/integration/configurable_spec.rb
|
129
|
-
- spec/spec_helper.rb
|
130
|
-
- spec/support/shared_examples/configurable.rb
|
131
|
-
- spec/unit/dry/configurable/argument_parser_spec.rb
|
132
|
-
- spec/unit/dry/configurable/config/value_spec.rb
|
133
|
-
- spec/unit/dry/configurable/config_spec.rb
|
142
|
+
test_files: []
|