dry-configurable 0.16.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []