dry-configurable 0.8.3 → 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 +98 -25
- data/LICENSE +1 -1
- data/README.md +15 -27
- data/dry-configurable.gemspec +28 -19
- data/lib/dry-configurable.rb +2 -0
- data/lib/dry/configurable.rb +21 -147
- data/lib/dry/configurable/class_methods.rb +103 -0
- data/lib/dry/configurable/compiler.rb +45 -0
- data/lib/dry/configurable/config.rb +80 -121
- 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 +95 -12
- data/lib/dry/configurable/settings.rb +43 -63
- data/lib/dry/configurable/test_interface.rb +3 -5
- data/lib/dry/configurable/version.rb +3 -1
- metadata +30 -16
- data/.codeclimate.yml +0 -23
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/.travis.yml +0 -28
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -17
- data/Rakefile +0 -12
- 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,36 +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
|
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
|
23
|
+
|
24
|
+
# @api private
|
7
25
|
attr_reader :name
|
8
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
|
9
37
|
attr_reader :options
|
10
38
|
|
11
|
-
|
39
|
+
# Specialized Setting which includes nested settings
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
class Nested < Setting
|
43
|
+
CONSTRUCTOR = Config.method(:new)
|
44
|
+
|
45
|
+
# @api private
|
46
|
+
def pristine
|
47
|
+
with(input: input.pristine)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
def constructor
|
52
|
+
CONSTRUCTOR
|
53
|
+
end
|
54
|
+
end
|
12
55
|
|
13
|
-
|
14
|
-
|
15
|
-
@
|
16
|
-
@
|
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
|
17
62
|
@options = options
|
18
63
|
end
|
19
64
|
|
65
|
+
# @api private
|
20
66
|
def value
|
21
|
-
|
67
|
+
@value ||= evaluate
|
22
68
|
end
|
23
69
|
|
24
|
-
|
25
|
-
|
70
|
+
# @api private
|
71
|
+
def nested(settings)
|
72
|
+
Nested.new(name, input: settings, **options)
|
26
73
|
end
|
27
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)
|
83
|
+
end
|
84
|
+
|
85
|
+
# @api private
|
86
|
+
def constructor
|
87
|
+
options[:constructor] || DEFAULT_CONSTRUCTOR
|
88
|
+
end
|
89
|
+
|
90
|
+
# @api private
|
28
91
|
def reader?
|
29
|
-
options[:reader]
|
92
|
+
options[:reader].equal?(true)
|
93
|
+
end
|
94
|
+
|
95
|
+
# @api private
|
96
|
+
def writer?(meth)
|
97
|
+
writer_name.equal?(meth)
|
98
|
+
end
|
99
|
+
|
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
|
30
112
|
end
|
31
113
|
|
32
|
-
|
33
|
-
|
114
|
+
# @api private
|
115
|
+
def evaluate
|
116
|
+
@value = constructor[input.equal?(Undefined) ? nil : input]
|
34
117
|
end
|
35
118
|
end
|
36
119
|
end
|
@@ -1,91 +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
|
+
include Dry::Equalizer(:elements)
|
14
15
|
|
15
|
-
|
16
|
-
def self.call(&block)
|
17
|
-
new.instance_exec do
|
18
|
-
instance_exec(&block)
|
19
|
-
@settings
|
20
|
-
end
|
21
|
-
end
|
16
|
+
include Enumerable
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
18
|
+
# @api private
|
19
|
+
attr_reader :elements
|
26
20
|
|
27
|
-
|
28
|
-
|
29
|
-
|
21
|
+
# @api private
|
22
|
+
def initialize(elements = EMPTY_ARRAY)
|
23
|
+
initialize_elements(elements)
|
30
24
|
end
|
31
25
|
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
DSL.(&block)
|
26
|
+
# @api private
|
27
|
+
def <<(setting)
|
28
|
+
elements[setting.name] = setting
|
29
|
+
self
|
37
30
|
end
|
38
31
|
|
39
|
-
|
40
|
-
|
41
|
-
|
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?
|
32
|
+
# @api private
|
33
|
+
def [](name)
|
34
|
+
elements[name]
|
50
35
|
end
|
51
36
|
|
52
|
-
|
53
|
-
|
54
|
-
|
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) }
|
37
|
+
# @api private
|
38
|
+
def key?(name)
|
39
|
+
keys.include?(name)
|
64
40
|
end
|
65
41
|
|
66
|
-
|
67
|
-
|
42
|
+
# @api private
|
43
|
+
def keys
|
44
|
+
elements.keys
|
68
45
|
end
|
69
46
|
|
70
|
-
|
71
|
-
|
47
|
+
# @api private
|
48
|
+
def each(&block)
|
49
|
+
elements.values.each(&block)
|
72
50
|
end
|
73
51
|
|
74
|
-
|
75
|
-
|
52
|
+
# @api private
|
53
|
+
def pristine
|
54
|
+
self.class.new(map(&:pristine))
|
76
55
|
end
|
77
56
|
|
78
|
-
|
79
|
-
settings.freeze
|
80
|
-
super
|
81
|
-
end
|
57
|
+
private
|
82
58
|
|
83
|
-
|
84
|
-
|
59
|
+
# @api private
|
60
|
+
def initialize_copy(source)
|
61
|
+
initialize_elements(source.map(&:dup))
|
85
62
|
end
|
86
63
|
|
87
|
-
|
88
|
-
|
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
|
+
}
|
89
69
|
end
|
90
70
|
end
|
91
71
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Configurable
|
3
5
|
# Methods meant to be used in a testing scenario
|
@@ -8,11 +10,7 @@ module Dry
|
|
8
10
|
#
|
9
11
|
# @api public
|
10
12
|
def reset_config
|
11
|
-
@config =
|
12
|
-
_settings.create_config
|
13
|
-
else
|
14
|
-
self.class._settings.create_config
|
15
|
-
end
|
13
|
+
@config = config.pristine
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|