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.
@@ -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
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
4
+ # Shared errors
5
+ #
6
+ # @api public
2
7
  module Configurable
3
8
  Error = Class.new(::StandardError)
4
- AlreadyDefinedConfig = ::Class.new(Error)
5
9
  FrozenConfig = ::Class.new(Error)
6
10
  end
7
11
  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
- attr_reader :processor
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
- def initialize(name, value, processor, options = EMPTY_HASH)
14
- @name = name.to_sym
15
- @value = value
16
- @processor = processor
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
- Undefined.default(@value, nil)
67
+ @value ||= evaluate
22
68
  end
23
69
 
24
- def undefined?
25
- Undefined.equal?(@value)
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
- def node?
33
- Settings === @value
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
- require 'set'
2
- require 'concurrent/array'
3
- require 'dry/configurable/settings/argument_parser'
4
- require 'dry/configurable/setting'
5
- require 'dry/configurable/config'
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 collection of settings. This is not part of the public API.
10
+ # A settings map
10
11
  #
11
- # @private
12
+ # @api private
12
13
  class Settings
13
- Parser = ArgumentParser.new.freeze
14
+ include Dry::Equalizer(:elements)
14
15
 
15
- class DSL
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
- def initialize
24
- @settings = Settings.new
25
- end
18
+ # @api private
19
+ attr_reader :elements
26
20
 
27
- def setting(*args, &block)
28
- @settings.add(*args, &block)
29
- end
21
+ # @api private
22
+ def initialize(elements = EMPTY_ARRAY)
23
+ initialize_elements(elements)
30
24
  end
31
25
 
32
- # Capture nested config definition
33
- #
34
- # @return [Dry::Configurable::Setting]
35
- def self.capture(&block)
36
- DSL.(&block)
26
+ # @api private
27
+ def <<(setting)
28
+ elements[setting.name] = setting
29
+ self
37
30
  end
38
31
 
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?
32
+ # @api private
33
+ def [](name)
34
+ elements[name]
50
35
  end
51
36
 
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) }
37
+ # @api private
38
+ def key?(name)
39
+ keys.include?(name)
64
40
  end
65
41
 
66
- def empty?
67
- settings.empty?
42
+ # @api private
43
+ def keys
44
+ elements.keys
68
45
  end
69
46
 
70
- def name?(name)
71
- names.include?(name)
47
+ # @api private
48
+ def each(&block)
49
+ elements.values.each(&block)
72
50
  end
73
51
 
74
- def dup
75
- Settings.new(settings.dup)
52
+ # @api private
53
+ def pristine
54
+ self.class.new(map(&:pristine))
76
55
  end
77
56
 
78
- def freeze
79
- settings.freeze
80
- super
81
- end
57
+ private
82
58
 
83
- def create_config
84
- config_class.new
59
+ # @api private
60
+ def initialize_copy(source)
61
+ initialize_elements(source.map(&:dup))
85
62
  end
86
63
 
87
- def config_defined?
88
- config_class.config_defined?
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 = if self.is_a?(Module)
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
 
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Configurable
3
5
  # @api public
4
- VERSION = '0.8.3'.freeze
6
+ VERSION = '0.11.3'
5
7
  end
6
8
  end