dry-configurable 0.16.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 323f300c1cf0cf407272352b81dd0775ac7f855713cd6145fccc9d11dbdae8cf
4
- data.tar.gz: b8258cfa9a30108831384e575a00e5afd93b9d94942d52799d23013e3753ceaf
3
+ metadata.gz: 5d067b9cb78be3e3904b22ab5e73a6903cecb387c241d08c875f2a1eaf1002a4
4
+ data.tar.gz: c7f9be3367d11ea23c10a8a73d0a63d0fab54a1cf6cd2f5053e8845b808bd801
5
5
  SHA512:
6
- metadata.gz: d37acc7d39adb1ed1f0da218c5f16ac0e6d09832e70e73220770fa28ad8eff4f21f440a6285b22785de82f76a8df6f444d37024d5059fdc2ba3f3b3006fd19c9
7
- data.tar.gz: c7888430539dfa30f4518ca159cae0675d2766ec8c27870a5e74c64f04521aa517fea1772bb766f5c9f364d83e24610c7bf7f72c65739adb05b2e9a0ca1230f0
6
+ metadata.gz: 45d5b855ac62fa249080b1c40ed9faf0644cf9daa3d3a18c8c3cc0780723006dd510d3b0cb156d2251962775f95396b591e96136d951e08e4679dd684118601d
7
+ data.tar.gz: fc7420686647324a2c671f183aff7b1fc69131f80db292d0847e53dc5744e16904f56dc74a35b6dadb6cce1f4927409a632d026d7127ccb5844d9b8ba67b4e98
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
2
 
3
+ ## 0.16.1 2022-10-13
4
+
5
+
6
+ ### Changed
7
+
8
+ - Restored performance of config value reads (direct reader methods as well as aggregate methods like `#values` and `#to_h`) to pre-0.16.0 levels (#149 by @timriley)
9
+
10
+ [Compare v0.16.0...v0.16.1](https://github.com/dry-rb/dry-configurable/compare/v0.16.0...v0.16.1)
11
+
3
12
  ## 0.16.0 2022-10-08
4
13
 
5
14
 
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.required_ruby_version = ">= 2.7.0"
30
30
 
31
31
  # to update dependencies edit project.yml
32
- spec.add_runtime_dependency "dry-core", "~> 0.6"
32
+ spec.add_runtime_dependency "dry-core", "~> 1.0", "< 2"
33
33
  spec.add_runtime_dependency "zeitwerk", "~> 2.6"
34
34
 
35
35
  spec.add_development_dependency "bundler"
@@ -13,7 +13,7 @@ module Dry
13
13
 
14
14
  subclass.instance_variable_set(:@__config_extension__, __config_extension__)
15
15
 
16
- new_settings = _settings.dup
16
+ new_settings = settings.dup
17
17
  subclass.instance_variable_set(:@_settings, new_settings)
18
18
 
19
19
  # Only classes **extending** Dry::Configurable have class-level config. When
@@ -43,28 +43,19 @@ module Dry
43
43
  def setting(*args, **options, &block)
44
44
  setting = __config_dsl__.setting(*args, **options, &block)
45
45
 
46
- _settings << setting
46
+ settings << setting
47
47
 
48
48
  __config_reader__.define(setting.name) if setting.reader?
49
49
 
50
50
  self
51
51
  end
52
52
 
53
- # Return declared settings
54
- #
55
- # @return [Set<Symbol>]
56
- #
57
- # @api public
58
- def settings
59
- Set[*_settings.map(&:name)]
60
- end
61
-
62
- # Return declared settings
53
+ # Returns the defined settings for the class.
63
54
  #
64
55
  # @return [Settings]
65
56
  #
66
57
  # @api public
67
- def _settings
58
+ def settings
68
59
  @_settings ||= Settings.new
69
60
  end
70
61
 
@@ -78,7 +69,7 @@ module Dry
78
69
  end
79
70
 
80
71
  # @api private
81
- def __config_build__(settings = _settings)
72
+ def __config_build__(settings = self.settings)
82
73
  __config_extension__.config_class.new(settings)
83
74
  end
84
75
 
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/constants"
4
-
5
- require "dry/core/equalizer"
3
+ require "set"
6
4
 
7
5
  module Dry
8
6
  module Configurable
@@ -18,15 +16,27 @@ module Dry
18
16
  # @api private
19
17
  attr_reader :_values
20
18
 
19
+ # @api private
20
+ attr_reader :_configured
21
+ protected :_configured
22
+
21
23
  # @api private
22
24
  def initialize(settings, values: {})
23
25
  @_settings = settings
24
26
  @_values = values
27
+ @_configured = Set.new
28
+ end
29
+
30
+ # @api private
31
+ private def initialize_copy(source)
32
+ super
33
+ @_values = source.__send__(:dup_values)
34
+ @_configured = source._configured.dup
25
35
  end
26
36
 
27
37
  # @api private
28
38
  def dup_for_settings(settings)
29
- self.class.new(settings, values: dup_values)
39
+ dup.tap { |config| config.instance_variable_set(:@_settings, settings) }
30
40
  end
31
41
 
32
42
  # Get config value by a key
@@ -42,10 +52,11 @@ module Dry
42
52
  end
43
53
 
44
54
  _values.fetch(name) {
45
- # When reading values, only capture cloneable (i.e. mutable) values in local state, making
46
- # it easier to determine which values have actually been changed vs just read
55
+ # Mutable settings may be configured after read
56
+ _configured.add(name) if setting.cloneable?
57
+
47
58
  setting.to_value.tap { |value|
48
- _values[name] = value if setting.cloneable?
59
+ _values[name] = value
49
60
  }
50
61
  }
51
62
  end
@@ -56,7 +67,7 @@ module Dry
56
67
  # @param [String,Symbol] name
57
68
  # @param [Object] value
58
69
  def []=(name, value)
59
- raise FrozenConfig, "Cannot modify frozen config" if frozen?
70
+ raise FrozenConfigError, "Cannot modify frozen config" if frozen?
60
71
 
61
72
  name = name.to_sym
62
73
 
@@ -64,6 +75,8 @@ module Dry
64
75
  raise ArgumentError, "+#{name}+ is not a setting name"
65
76
  end
66
77
 
78
+ _configured.add(name)
79
+
67
80
  _values[name] = setting.constructor.(value)
68
81
  end
69
82
 
@@ -101,11 +114,11 @@ module Dry
101
114
  #
102
115
  # @api public
103
116
  def configured?(key)
104
- if _settings[key].cloneable? && _values.key?(key)
117
+ if _configured.include?(key) && _settings[key].cloneable?
105
118
  return _values[key] != _settings[key].to_value
106
119
  end
107
120
 
108
- _values.key?(key)
121
+ _configured.include?(key)
109
122
  end
110
123
 
111
124
  # Returns the current config values.
@@ -116,7 +129,10 @@ module Dry
116
129
  #
117
130
  # @api public
118
131
  def values
119
- _settings.to_h { |setting| [setting.name, self[setting.name]] }
132
+ # Ensure all settings are represented in values
133
+ _settings.each { |setting| self[setting.name] unless _values.key?(setting.name) }
134
+
135
+ _values
120
136
  end
121
137
 
122
138
  # Returns config values as a hash, with nested values also converted from {Config} instances
@@ -130,7 +146,19 @@ module Dry
130
146
  end
131
147
 
132
148
  # @api private
149
+ alias_method :_dry_equalizer_hash, :hash
150
+
151
+ # @api public
152
+ def hash
153
+ return @__hash__ if instance_variable_defined?(:@__hash__)
154
+
155
+ _dry_equalizer_hash
156
+ end
157
+
158
+ # @api public
133
159
  def finalize!(freeze_values: false)
160
+ return self if frozen?
161
+
134
162
  values.each_value do |value|
135
163
  if value.is_a?(self.class)
136
164
  value.finalize!(freeze_values: freeze_values)
@@ -139,6 +167,13 @@ module Dry
139
167
  end
140
168
  end
141
169
 
170
+ # Memoize the hash for the object when finalizing (regardless of whether values themselves
171
+ # are to be frozen; the intention of finalization is that no further changes should be
172
+ # made). The benefit of freezing the hash at this point is that it saves repeated expensive
173
+ # computation (through Dry::Equalizer's hash implementation) if that hash is to be used
174
+ # later in performance-sensitive situations, such as when serving as a cache key or similar.
175
+ @__hash__ = _dry_equalizer_hash
176
+
142
177
  freeze
143
178
  end
144
179
 
@@ -175,11 +210,6 @@ module Dry
175
210
  dup_hsh[key] = _settings[key].cloneable? ? val.dup : val
176
211
  }
177
212
  end
178
-
179
- def initialize_copy(source)
180
- super
181
- @_values = source.__send__(:dup_values)
182
- end
183
213
  end
184
214
  end
185
215
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/constants"
4
-
5
3
  module Dry
6
4
  # Shared constants
7
5
  #
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/deprecations"
4
-
5
3
  module Dry
6
4
  module Configurable
7
5
  # Setting DSL used by the class API
@@ -6,7 +6,8 @@ module Dry
6
6
  # @api public
7
7
  module Configurable
8
8
  Error = Class.new(::StandardError)
9
- AlreadyIncluded = ::Class.new(Error)
10
- FrozenConfig = ::Class.new(Error)
9
+
10
+ AlreadyIncludedError = Class.new(Error)
11
+ FrozenConfigError = Class.new(Error)
11
12
  end
12
13
  end
@@ -26,7 +26,7 @@ module Dry
26
26
 
27
27
  # @api private
28
28
  def included(klass)
29
- raise AlreadyIncluded if klass.include?(InstanceMethods)
29
+ raise AlreadyIncludedError if klass.include?(InstanceMethods)
30
30
 
31
31
  super
32
32
 
@@ -12,7 +12,7 @@ module Dry
12
12
  module Initializer
13
13
  # @api private
14
14
  def initialize(*)
15
- @config = self.class.__config_build__(self.class._settings)
15
+ @config = self.class.__config_build__(self.class.settings)
16
16
 
17
17
  super
18
18
  end
@@ -8,7 +8,7 @@ module Dry
8
8
  module Methods
9
9
  # @api public
10
10
  def configure(&block)
11
- raise FrozenConfig, "Cannot modify frozen config" if frozen?
11
+ raise FrozenConfigError, "Cannot modify frozen config" if config.frozen?
12
12
 
13
13
  yield(config) if block
14
14
  self
@@ -2,40 +2,41 @@
2
2
 
3
3
  require "set"
4
4
 
5
- require "dry/core/equalizer"
6
-
7
5
  module Dry
8
6
  module Configurable
9
- # This class represents a setting and is used internally.
7
+ # A defined setting.
10
8
  #
11
- # @api private
9
+ # @api public
12
10
  class Setting
13
11
  include Dry::Equalizer(:name, :default, :constructor, :children, :options, inspect: false)
14
12
 
15
- OPTIONS = %i[default reader constructor cloneable settings config_class].freeze
13
+ OPTIONS = %i[default reader constructor mutable cloneable settings config_class].freeze
16
14
 
17
15
  DEFAULT_CONSTRUCTOR = -> v { v }.freeze
18
16
 
19
- CLONEABLE_VALUE_TYPES = [Array, Hash, Set, Config].freeze
17
+ MUTABLE_VALUE_TYPES = [Array, Hash, Set, Config].freeze
20
18
 
21
- # @api private
19
+ # @api public
22
20
  attr_reader :name
23
21
 
24
- # @api private
22
+ # @api public
25
23
  attr_reader :default
26
24
 
27
- # @api private
25
+ # @api public
26
+ attr_reader :mutable
27
+
28
+ # @api public
28
29
  attr_reader :constructor
29
30
 
30
- # @api private
31
+ # @api public
31
32
  attr_reader :children
32
33
 
33
- # @api private
34
+ # @api public
34
35
  attr_reader :options
35
36
 
36
37
  # @api private
37
- def self.cloneable_value?(value)
38
- CLONEABLE_VALUE_TYPES.any? { |type| value.is_a?(type) }
38
+ def self.mutable_value?(value)
39
+ MUTABLE_VALUE_TYPES.any? { |type| value.is_a?(type) }
39
40
  end
40
41
 
41
42
  # @api private
@@ -48,6 +49,10 @@ module Dry
48
49
  )
49
50
  @name = name
50
51
  @default = default
52
+ @mutable = children.any? || options.fetch(:mutable) {
53
+ # Allow `cloneable` as an option alias for `mutable`
54
+ options.fetch(:cloneable) { Setting.mutable_value?(default) }
55
+ }
51
56
  @constructor = constructor
52
57
  @children = children
53
58
  @options = options
@@ -58,10 +63,11 @@ module Dry
58
63
  options[:reader].equal?(true)
59
64
  end
60
65
 
61
- # @api private
62
- def cloneable?
63
- children.any? || options.fetch(:cloneable) { Setting.cloneable_value?(default) }
66
+ # @api public
67
+ def mutable?
68
+ mutable
64
69
  end
70
+ alias_method :cloneable?, :mutable?
65
71
 
66
72
  # @api private
67
73
  def to_value
@@ -71,7 +77,7 @@ module Dry
71
77
  value = default
72
78
  value = constructor.(value) unless value.eql?(Undefined)
73
79
 
74
- cloneable? ? value.dup : value
80
+ mutable? ? value.dup : value
75
81
  end
76
82
  end
77
83
  end
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/equalizer"
4
-
5
3
  module Dry
6
4
  module Configurable
7
- # A settings map
5
+ # A collection of defined settings on a given class.
8
6
  #
9
7
  # @api private
10
8
  class Settings
@@ -20,37 +18,48 @@ module Dry
20
18
  @settings = settings.each_with_object({}) { |s, m| m[s.name] = s }
21
19
  end
22
20
 
21
+ # @api private
22
+ private def initialize_copy(source)
23
+ @settings = source.settings.dup
24
+ end
25
+
23
26
  # @api private
24
27
  def <<(setting)
25
28
  settings[setting.name] = setting
26
29
  self
27
30
  end
28
31
 
29
- # @api private
32
+ # Returns the setting for the given name, if found.
33
+ #
34
+ # @return [Setting, nil] the setting, or nil if not found
35
+ #
36
+ # @api public
30
37
  def [](name)
31
38
  settings[name]
32
39
  end
33
40
 
34
- # @api private
41
+ # Returns true if a setting for the given name is defined.
42
+ #
43
+ # @return [Boolean]
44
+ #
45
+ # @api public
35
46
  def key?(name)
36
47
  keys.include?(name)
37
48
  end
38
49
 
39
- # @api private
50
+ # Returns the list of defined setting names.
51
+ #
52
+ # @return [Array<Symbol>]
53
+ #
54
+ # @api public
40
55
  def keys
41
56
  settings.keys
42
57
  end
43
58
 
44
- # @api private
59
+ # @api public
45
60
  def each(&block)
46
61
  settings.each_value(&block)
47
62
  end
48
-
49
- private
50
-
51
- def initialize_copy(source)
52
- @settings = source.settings.dup
53
- end
54
63
  end
55
64
  end
56
65
  end
@@ -3,6 +3,6 @@
3
3
  module Dry
4
4
  module Configurable
5
5
  # @api public
6
- VERSION = "0.16.0"
6
+ VERSION = "1.0.0"
7
7
  end
8
8
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "zeitwerk"
4
4
 
5
- require "dry/core/constants"
5
+ require "dry/core"
6
6
  require "dry/configurable/constants"
7
7
  require "dry/configurable/errors"
8
8
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-configurable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Holland
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-07 00:00:00.000000000 Z
11
+ date: 2022-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-core
@@ -16,14 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.6'
19
+ version: '1.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - "~>"
25
28
  - !ruby/object:Gem::Version
26
- version: '0.6'
29
+ version: '1.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: zeitwerk
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -114,7 +120,7 @@ metadata:
114
120
  changelog_uri: https://github.com/dry-rb/dry-configurable/blob/main/CHANGELOG.md
115
121
  source_code_uri: https://github.com/dry-rb/dry-configurable
116
122
  bug_tracker_uri: https://github.com/dry-rb/dry-configurable/issues
117
- post_install_message:
123
+ post_install_message:
118
124
  rdoc_options: []
119
125
  require_paths:
120
126
  - lib
@@ -129,8 +135,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
135
  - !ruby/object:Gem::Version
130
136
  version: '0'
131
137
  requirements: []
132
- rubygems_version: 3.3.7
133
- signing_key:
138
+ rubygems_version: 3.1.6
139
+ signing_key:
134
140
  specification_version: 4
135
141
  summary: A mixin to add configuration functionality to your classes
136
142
  test_files: []