dry-configurable 0.9.0 → 0.11.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 +4 -4
- data/CHANGELOG.md +81 -21
- data/LICENSE +1 -1
- data/README.md +15 -27
- data/dry-configurable.gemspec +27 -18
- data/lib/dry-configurable.rb +2 -0
- data/lib/dry/configurable.rb +21 -146
- data/lib/dry/configurable/class_methods.rb +103 -0
- data/lib/dry/configurable/compiler.rb +45 -0
- data/lib/dry/configurable/config.rb +79 -136
- data/lib/dry/configurable/constants.rb +12 -0
- data/lib/dry/configurable/dsl.rb +62 -0
- data/lib/dry/configurable/dsl/args.rb +58 -0
- data/lib/dry/configurable/{error.rb → errors.rb} +5 -1
- data/lib/dry/configurable/instance_methods.rb +46 -0
- data/lib/dry/configurable/methods.rb +32 -0
- data/lib/dry/configurable/setting.rb +91 -17
- data/lib/dry/configurable/settings.rb +42 -87
- data/lib/dry/configurable/test_interface.rb +3 -5
- data/lib/dry/configurable/version.rb +3 -1
- metadata +30 -25
- data/.codeclimate.yml +0 -12
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
- data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -34
- data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
- data/.github/workflows/ci.yml +0 -70
- data/.github/workflows/docsite.yml +0 -34
- data/.github/workflows/sync_configs.yml +0 -30
- data/.gitignore +0 -9
- data/.rspec +0 -4
- data/.rubocop.yml +0 -89
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -20
- data/Rakefile +0 -12
- data/docsite/source/index.html.md +0 -55
- data/docsite/source/testing.html.md +0 -27
- data/lib/dry/configurable/settings/argument_parser.rb +0 -50
- data/rakelib/rubocop.rake +0 -18
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/configurable/constants'
|
4
|
+
require 'dry/configurable/setting'
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module Configurable
|
8
|
+
class DSL
|
9
|
+
# @api private
|
10
|
+
class Args
|
11
|
+
# @api private
|
12
|
+
attr_reader :args
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
attr_reader :size
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
attr_reader :opts
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
def initialize(args)
|
22
|
+
@args = args
|
23
|
+
@size = args.size
|
24
|
+
@opts = Setting::OPTIONS
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def ensure_valid_options
|
29
|
+
return unless options
|
30
|
+
|
31
|
+
keys = options.keys - opts
|
32
|
+
raise ArgumentError, "Invalid options: #{keys.inspect}" unless keys.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
def to_ary
|
37
|
+
[default, options || EMPTY_HASH]
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
def default
|
42
|
+
if size.equal?(1) && options.nil?
|
43
|
+
args[0]
|
44
|
+
elsif size > 1 && options
|
45
|
+
args[0]
|
46
|
+
else
|
47
|
+
Undefined
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @api private
|
52
|
+
def options
|
53
|
+
args.detect { |arg| arg.is_a?(Hash) && (opts & arg.keys).any? }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/configurable/config'
|
4
|
+
require 'dry/configurable/methods'
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module Configurable
|
8
|
+
# Instance-level API when `Dry::Configurable` is included in a class
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
module InstanceMethods
|
12
|
+
include Methods
|
13
|
+
|
14
|
+
# Return object's configuration
|
15
|
+
#
|
16
|
+
# @return [Config]
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
attr_reader :config
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
def initialize(*)
|
23
|
+
@config = Config.new(self.class._settings.dup)
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# Finalize the config and freeze the object
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def finalize!
|
31
|
+
return self if frozen?
|
32
|
+
|
33
|
+
super
|
34
|
+
freeze
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# @api public
|
40
|
+
def initialize_copy(source)
|
41
|
+
super
|
42
|
+
@config = source.config.dup
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/configurable/errors'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Configurable
|
7
|
+
# Common API for both classes and instances
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
module Methods
|
11
|
+
# @api public
|
12
|
+
def configure(&block)
|
13
|
+
raise FrozenConfig, 'Cannot modify frozen config' if frozen?
|
14
|
+
|
15
|
+
yield(config) if block
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
# Finalize and freeze configuration
|
20
|
+
#
|
21
|
+
# @return [Dry::Configurable::Config]
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def finalize!
|
25
|
+
return self if config.frozen?
|
26
|
+
|
27
|
+
config.finalize!
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,45 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
require 'dry/equalizer'
|
6
|
+
|
7
|
+
require 'dry/configurable/constants'
|
8
|
+
require 'dry/configurable/config'
|
9
|
+
|
1
10
|
module Dry
|
2
11
|
module Configurable
|
3
12
|
# This class represents a setting and is used internally.
|
4
13
|
#
|
5
|
-
# @private
|
14
|
+
# @api private
|
6
15
|
class Setting
|
7
|
-
|
16
|
+
include Dry::Equalizer(:name, :value, :options)
|
17
|
+
|
18
|
+
OPTIONS = %i[input default reader constructor settings].freeze
|
19
|
+
|
20
|
+
DEFAULT_CONSTRUCTOR = -> v { v }.freeze
|
21
|
+
|
22
|
+
CLONABLE_VALUE_TYPES = [Array, Hash, Set, Config].freeze
|
8
23
|
|
24
|
+
# @api private
|
9
25
|
attr_reader :name
|
10
26
|
|
27
|
+
# @api private
|
28
|
+
attr_reader :writer_name
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
attr_reader :input
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
attr_reader :default
|
35
|
+
|
36
|
+
# @api private
|
11
37
|
attr_reader :options
|
12
38
|
|
13
|
-
|
39
|
+
# Specialized Setting which includes nested settings
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
class Nested < Setting
|
43
|
+
CONSTRUCTOR = Config.method(:new)
|
14
44
|
|
15
|
-
|
16
|
-
|
17
|
-
|
45
|
+
# @api private
|
46
|
+
def pristine
|
47
|
+
with(input: input.pristine)
|
18
48
|
end
|
19
|
-
|
20
|
-
@
|
21
|
-
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
def constructor
|
52
|
+
CONSTRUCTOR
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @api private
|
57
|
+
def initialize(name, input: Undefined, default: Undefined, **options)
|
58
|
+
@name = name
|
59
|
+
@writer_name = :"#{name}="
|
60
|
+
@input = input.equal?(Undefined) ? default : input
|
61
|
+
@default = default
|
22
62
|
@options = options
|
23
63
|
end
|
24
64
|
|
65
|
+
# @api private
|
25
66
|
def value
|
26
|
-
|
67
|
+
@value ||= evaluate
|
68
|
+
end
|
69
|
+
|
70
|
+
# @api private
|
71
|
+
def nested(settings)
|
72
|
+
Nested.new(name, input: settings, **options)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @api private
|
76
|
+
def pristine
|
77
|
+
with(input: Undefined)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @api private
|
81
|
+
def with(new_opts)
|
82
|
+
self.class.new(name, input: input, default: default, **options, **new_opts)
|
27
83
|
end
|
28
84
|
|
29
|
-
|
30
|
-
|
85
|
+
# @api private
|
86
|
+
def constructor
|
87
|
+
options[:constructor] || DEFAULT_CONSTRUCTOR
|
31
88
|
end
|
32
89
|
|
90
|
+
# @api private
|
33
91
|
def reader?
|
34
|
-
options[:reader]
|
92
|
+
options[:reader].equal?(true)
|
93
|
+
end
|
94
|
+
|
95
|
+
# @api private
|
96
|
+
def writer?(meth)
|
97
|
+
writer_name.equal?(meth)
|
35
98
|
end
|
36
99
|
|
37
|
-
|
38
|
-
|
100
|
+
# @api private
|
101
|
+
def clonable_value?
|
102
|
+
CLONABLE_VALUE_TYPES.any? { |type| value.is_a?(type) }
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# @api private
|
108
|
+
def initialize_copy(source)
|
109
|
+
super
|
110
|
+
@value = source.value.dup if source.clonable_value?
|
111
|
+
@options = source.options.dup
|
39
112
|
end
|
40
113
|
|
41
|
-
|
42
|
-
|
114
|
+
# @api private
|
115
|
+
def evaluate
|
116
|
+
@value = constructor[input.equal?(Undefined) ? nil : input]
|
43
117
|
end
|
44
118
|
end
|
45
119
|
end
|
@@ -1,116 +1,71 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require '
|
4
|
-
|
5
|
-
require 'dry/
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent/map'
|
4
|
+
|
5
|
+
require 'dry/equalizer'
|
6
|
+
require 'dry/configurable/constants'
|
6
7
|
|
7
8
|
module Dry
|
8
9
|
module Configurable
|
9
|
-
# A
|
10
|
+
# A settings map
|
10
11
|
#
|
11
|
-
# @private
|
12
|
+
# @api private
|
12
13
|
class Settings
|
13
|
-
|
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
|
14
|
+
include Dry::Equalizer(:elements)
|
40
15
|
|
41
|
-
|
16
|
+
include Enumerable
|
42
17
|
|
43
|
-
|
44
|
-
|
18
|
+
# @api private
|
19
|
+
attr_reader :elements
|
45
20
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@index = settings.map { |s| [s.name, s] }.to_h
|
50
|
-
yield(self) if block_given?
|
21
|
+
# @api private
|
22
|
+
def initialize(elements = EMPTY_ARRAY)
|
23
|
+
initialize_elements(elements)
|
51
24
|
end
|
52
25
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
*args, opts = Parser.(value, options, block)
|
58
|
-
|
59
|
-
Setting.new(key, *args, { **opts, reserved: reserved?(key) }).tap do |s|
|
60
|
-
settings.delete_if { |e| e.name.eql?(s.name) }
|
61
|
-
settings << s
|
62
|
-
index[s.name] = s
|
63
|
-
@names = nil
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def each
|
68
|
-
settings.each { |s| yield(s) }
|
69
|
-
end
|
70
|
-
|
71
|
-
def names
|
72
|
-
@names ||= index.keys.to_set
|
26
|
+
# @api private
|
27
|
+
def <<(setting)
|
28
|
+
elements[setting.name] = setting
|
29
|
+
self
|
73
30
|
end
|
74
31
|
|
32
|
+
# @api private
|
75
33
|
def [](name)
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
def empty?
|
80
|
-
settings.empty?
|
34
|
+
elements[name]
|
81
35
|
end
|
82
36
|
|
83
|
-
|
84
|
-
|
37
|
+
# @api private
|
38
|
+
def key?(name)
|
39
|
+
keys.include?(name)
|
85
40
|
end
|
86
41
|
|
87
|
-
|
88
|
-
|
42
|
+
# @api private
|
43
|
+
def keys
|
44
|
+
elements.keys
|
89
45
|
end
|
90
46
|
|
91
|
-
|
92
|
-
|
93
|
-
|
47
|
+
# @api private
|
48
|
+
def each(&block)
|
49
|
+
elements.values.each(&block)
|
94
50
|
end
|
95
51
|
|
96
|
-
|
97
|
-
|
52
|
+
# @api private
|
53
|
+
def pristine
|
54
|
+
self.class.new(map(&:pristine))
|
98
55
|
end
|
99
56
|
|
100
|
-
|
101
|
-
config_class.config_defined?
|
102
|
-
end
|
57
|
+
private
|
103
58
|
|
104
|
-
|
105
|
-
|
59
|
+
# @api private
|
60
|
+
def initialize_copy(source)
|
61
|
+
initialize_elements(source.map(&:dup))
|
106
62
|
end
|
107
63
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
].reduce(:+)
|
64
|
+
# @api private
|
65
|
+
def initialize_elements(elements)
|
66
|
+
@elements = elements.each_with_object(Concurrent::Map.new) { |s, m|
|
67
|
+
m[s.name] = s
|
68
|
+
}
|
114
69
|
end
|
115
70
|
end
|
116
71
|
end
|