dry-configurable 0.7.0 → 0.8.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 +5 -5
- data/.gitignore +0 -1
- data/.travis.yml +5 -12
- data/CHANGELOG.md +46 -2
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +1 -0
- data/README.md +18 -31
- data/dry-configurable.gemspec +5 -4
- data/lib/dry/configurable/config.rb +109 -40
- data/lib/dry/configurable/error.rb +0 -2
- data/lib/dry/configurable/setting.rb +37 -0
- data/lib/dry/configurable/settings/argument_parser.rb +50 -0
- data/lib/dry/configurable/settings.rb +92 -0
- data/lib/dry/configurable/test_interface.rb +5 -1
- data/lib/dry/configurable/version.rb +1 -1
- data/lib/dry/configurable.rb +116 -127
- metadata +44 -37
- 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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b54f274cf2ae374ac89bd1e3342da1ccf4c066ab220bdb8fd19c726be6f5d717
|
|
4
|
+
data.tar.gz: 6045cf60f22c40d231335ec10b84207fda52e888ca6eeba5d953267af782f148
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 02f95a505041d9b8d6ea199ce17aff5fd37e076577be2d5fd690a6350956f46c553a9d9972d447acdf6a97d370a44c6605c2c0c88f59ec821034977d9e33d6e1
|
|
7
|
+
data.tar.gz: af67f8149439b833d1898671ed187a5d1ca1e7d85aa6670723759016188092d8476b0a66574eedd70972c7f52f6534039a58854864c5e913f3d2f724696de9cc
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
|
@@ -8,22 +8,15 @@ script:
|
|
|
8
8
|
after_success:
|
|
9
9
|
- '[ -d coverage ] && bundle exec codeclimate-test-reporter'
|
|
10
10
|
rvm:
|
|
11
|
-
- 2.
|
|
12
|
-
- 2.
|
|
13
|
-
- 2.
|
|
14
|
-
- 2.
|
|
15
|
-
-
|
|
16
|
-
- jruby-9.1.5.0
|
|
17
|
-
- ruby-head
|
|
18
|
-
- jruby-head
|
|
11
|
+
- 2.3.8
|
|
12
|
+
- 2.4.5
|
|
13
|
+
- 2.5.3
|
|
14
|
+
- 2.6.0
|
|
15
|
+
- jruby-9.2.5.0
|
|
19
16
|
env:
|
|
20
17
|
global:
|
|
21
18
|
- JRUBY_OPTS='--dev -J-Xmx1024M'
|
|
22
19
|
- COVERAGE='true'
|
|
23
|
-
matrix:
|
|
24
|
-
allow_failures:
|
|
25
|
-
- rvm: ruby-head
|
|
26
|
-
- rvm: jruby-head
|
|
27
20
|
notifications:
|
|
28
21
|
email: false
|
|
29
22
|
webhooks:
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,48 @@
|
|
|
1
|
-
##
|
|
1
|
+
## 0.8.0 - 2019-02-05
|
|
2
|
+
|
|
3
|
+
## Fixed
|
|
4
|
+
|
|
5
|
+
* A number of bugs related to inheriting settings from parent class were fixed. Ideally, new behavior will be :100: predictable but if you observe any anomaly, please report ([flash-gordon](https://github.com/flash-gordon))
|
|
6
|
+
|
|
7
|
+
## Added
|
|
8
|
+
|
|
9
|
+
* Support for instance-level configuration landed. For usage, `include` the module instead of extending ([flash-gordon](https://github.com/flash-gordon))
|
|
10
|
+
```ruby
|
|
11
|
+
class App
|
|
12
|
+
include Dry::Configurable
|
|
13
|
+
|
|
14
|
+
setting :database
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
production = App.new
|
|
18
|
+
production.config.database = ENV['DATABASE_URL']
|
|
19
|
+
production.finalize!
|
|
20
|
+
|
|
21
|
+
development = App.new
|
|
22
|
+
development.config.database = 'jdbc:sqlite:memory'
|
|
23
|
+
development.finalize!
|
|
24
|
+
```
|
|
25
|
+
* Config values can be set from a hash with `.update`. Nested settings are supported ([flash-gordon](https://github.com/flash-gordon))
|
|
26
|
+
```ruby
|
|
27
|
+
class App
|
|
28
|
+
extend Dry::Configurable
|
|
29
|
+
|
|
30
|
+
setting :db do
|
|
31
|
+
setting :host
|
|
32
|
+
setting :port
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
config.update(YAML.load(File.read("config.yml")))
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Changed
|
|
40
|
+
|
|
41
|
+
* [BREAKING] Minimal supported Ruby version is set to 2.3 ([flash-gordon](https://github.com/flash-gordon))
|
|
42
|
+
|
|
43
|
+
[Compare v0.7.0...v0.8.0](https://github.com/dry-rb/dry-configurable/compare/v0.7.0...v0.8.0)
|
|
44
|
+
|
|
45
|
+
## 0.7.0 - 2017-04-25
|
|
2
46
|
|
|
3
47
|
## Added
|
|
4
48
|
|
|
@@ -10,4 +54,4 @@
|
|
|
10
54
|
* Convert nested configs to nested hashes with `Config#to_h` ([saverio-kantox](https://github.com/saverio-kantox))
|
|
11
55
|
* Disallow modification on frozen config ([qcam](https://github.com/qcam))
|
|
12
56
|
|
|
13
|
-
[Compare v0.6.2...
|
|
57
|
+
[Compare v0.6.2...v0.7.0](https://github.com/dry-rb/dry-configurable/compare/v0.6.2...v0.7.0)
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Issue Guidelines
|
|
2
|
+
|
|
3
|
+
## Reporting bugs
|
|
4
|
+
|
|
5
|
+
If you found a bug, report an issue and describe what's the expected behavior versus what actually happens. If the bug causes a crash, attach a full backtrace. If possible, a reproduction script showing the problem is highly appreciated.
|
|
6
|
+
|
|
7
|
+
## Reporting feature requests
|
|
8
|
+
|
|
9
|
+
Report a feature request **only after discussing it first on [discuss.dry-rb.org](https://discuss.dry-rb.org)** where it was accepted. Please provide a concise description of the feature, don't link to a discussion thread, and instead summarize what was discussed.
|
|
10
|
+
|
|
11
|
+
## Reporting questions, support requests, ideas, concerns etc.
|
|
12
|
+
|
|
13
|
+
**PLEASE DON'T** - use [discuss.dry-rb.org](http://discuss.dry-rb.org) instead.
|
|
14
|
+
|
|
15
|
+
# Pull Request Guidelines
|
|
16
|
+
|
|
17
|
+
A Pull Request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc.
|
|
18
|
+
|
|
19
|
+
Other requirements:
|
|
20
|
+
|
|
21
|
+
1) Do not open a pull request if you can't provide tests along with it. If you have problems writing tests, ask for help in the related issue.
|
|
22
|
+
2) Follow the style conventions of the surrounding code. In most cases, this is standard ruby style.
|
|
23
|
+
3) Add API documentation if it's a new feature
|
|
24
|
+
4) Update API documentation if it changes an existing feature
|
|
25
|
+
5) Bonus points for sending a PR to [github.com/dry-rb/dry-rb.org](github.com/dry-rb/dry-rb.org) which updates user documentation and guides
|
|
26
|
+
|
|
27
|
+
# Asking for help
|
|
28
|
+
|
|
29
|
+
If these guidelines aren't helpful, and you're stuck, please post a message on [discuss.dry-rb.org](https://discuss.dry-rb.org).
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,47 +1,34 @@
|
|
|
1
1
|
[gitter]: https://gitter.im/dry-rb/chat
|
|
2
2
|
[gem]: https://rubygems.org/gems/dry-configurable
|
|
3
3
|
[travis]: https://travis-ci.org/dry-rb/dry-configurable
|
|
4
|
-
[code_climate]: https://codeclimate.com/github/dry-rb/dry-configurable
|
|
5
4
|
[inch]: http://inch-ci.org/github/dry-rb/dry-configurable
|
|
6
5
|
|
|
7
6
|
# dry-configurable [][gitter]
|
|
8
7
|
|
|
9
8
|
[][gem]
|
|
10
9
|
[][travis]
|
|
11
|
-
[](https://codeclimate.com/github/dry-rb/dry-configurable/maintainability)
|
|
11
|
+
[](https://codeclimate.com/github/dry-rb/dry-configurable/test_coverage)
|
|
13
12
|
[][inch]
|
|
14
13
|
|
|
15
|
-
##
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Add this line to your application's Gemfile:
|
|
16
17
|
|
|
17
18
|
```ruby
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
setting :uploader, reader: true do
|
|
32
|
-
setting :bucket, 'dev'
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
App.configure do |config|
|
|
37
|
-
config.database.dsn = 'jdbc:sqlite:memory'
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
App.config.database.dsn
|
|
41
|
-
# => 'jdbc:sqlite:memory'
|
|
42
|
-
App.config.adapter # => nil
|
|
43
|
-
App.pool # => 5
|
|
44
|
-
App.uploader.bucket # => 'dev'
|
|
19
|
+
gem 'dry-configurable'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
And then execute:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
$ bundle
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or install it yourself as:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
$ gem install dry-configurable
|
|
45
32
|
```
|
|
46
33
|
|
|
47
34
|
## Links
|
data/dry-configurable.gemspec
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
1
|
require File.expand_path('../lib/dry/configurable/version', __FILE__)
|
|
3
2
|
|
|
4
3
|
Gem::Specification.new do |spec|
|
|
@@ -7,15 +6,17 @@ Gem::Specification.new do |spec|
|
|
|
7
6
|
spec.authors = ['Andy Holland']
|
|
8
7
|
spec.email = ['andyholland1991@aol.com']
|
|
9
8
|
spec.summary = 'A mixin to add configuration functionality to your classes'
|
|
10
|
-
spec.homepage = 'https://github.com/
|
|
9
|
+
spec.homepage = 'https://github.com/dry-rb/dry-configurable'
|
|
11
10
|
spec.license = 'MIT'
|
|
12
11
|
|
|
13
|
-
spec.files = `git ls-files -z`.split("\x0")
|
|
14
|
-
spec.executables = spec.files.grep(%r{^
|
|
12
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|bin)/}) }
|
|
13
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
15
14
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
16
15
|
spec.require_paths = ['lib']
|
|
17
16
|
|
|
17
|
+
spec.required_ruby_version = ">= 2.3.0"
|
|
18
18
|
spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
|
19
|
+
spec.add_runtime_dependency 'dry-core', '~> 0.4', '>= 0.4.7'
|
|
19
20
|
|
|
20
21
|
spec.add_development_dependency 'bundler'
|
|
21
22
|
spec.add_development_dependency 'rake'
|
|
@@ -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
|
|
14
28
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
34
|
+
|
|
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,72 @@ 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
|
+
|
|
77
136
|
private
|
|
78
137
|
|
|
79
|
-
|
|
80
|
-
|
|
138
|
+
# @private
|
|
139
|
+
def set_values!(parent)
|
|
140
|
+
self.class.settings.each do |setting|
|
|
141
|
+
if parent.key?(setting.name) && !setting.node?
|
|
142
|
+
@config[setting.name] = parent[setting.name]
|
|
143
|
+
elsif setting.undefined?
|
|
144
|
+
@config[setting.name] = nil
|
|
145
|
+
elsif setting.node?
|
|
146
|
+
value = setting.value.create_config
|
|
147
|
+
value.define!(parent.fetch(setting.name, EMPTY_HASH))
|
|
148
|
+
self[setting.name] = value
|
|
149
|
+
else
|
|
150
|
+
self[setting.name] = setting.value
|
|
151
|
+
end
|
|
152
|
+
end
|
|
81
153
|
end
|
|
82
154
|
|
|
155
|
+
# @private
|
|
83
156
|
def raise_unknown_setting_error(name)
|
|
84
157
|
raise ArgumentError, "+#{name}+ is not a setting name"
|
|
85
158
|
end
|
|
86
|
-
|
|
87
|
-
def setting?(name)
|
|
88
|
-
@config.key?(name.to_sym)
|
|
89
|
-
end
|
|
90
159
|
end
|
|
91
160
|
end
|
|
92
161
|
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,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
|
|
@@ -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
|