dry-configurable 0.11.6 → 0.13.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: d76fbe81b58bc0a6d0e5abb5f135e32e6eb7f47533af60864a09e0143b3ea66c
4
- data.tar.gz: 33f74bd69c1f030477088d32e80024969983e8dd793c58c89264828edc207a90
3
+ metadata.gz: 0b1292743d03212bcca735f18e876046b27cb3859a561a9b714704627542a324
4
+ data.tar.gz: 5d9f334008b7d055bbcbadf21feb61dbef8888ed01cc877e54cd7f5959b46f48
5
5
  SHA512:
6
- metadata.gz: aef6696e55e9ec56e46fabc857db9f1c2c8e266efad943844faf574d2d7d224829ca14457f66c8082f016d4cbdcb7f6bc9a1f33f641c743621c3b998d2f604d4
7
- data.tar.gz: 11ab00f69b7c172d23d6de19a2f0a208bbd83ecd91b865cb591d18df0f91fd4578d487fb2acd71a88e4b89477a17e602816999847cad4fc0671139aa68e11d00
6
+ metadata.gz: 1b9bbe538bd93cc27e87e7e97a83bd1f98757d776d215028b88bb8d8b085a2114c53b7f4e3ad1d77ae70b21d99652a7bbe65d978106f897e784374456cb615db
7
+ data.tar.gz: 9fdeb06faf9888965037a364560c10364342731e7c825d56d4fd1cdeeb929992e201ef1f6fb3b14d4fb68ee037588e3a064a8f2236588d0417599e55df7a6a89
data/CHANGELOG.md CHANGED
@@ -1,4 +1,84 @@
1
- ## unreleased
1
+ <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
+
3
+ ## 0.13.0 2021-09-12
4
+
5
+
6
+ ### Added
7
+
8
+ - Added flags to determine whether to warn on the API usage deprecated in this release (see "Changed" section below). Set these to `false` to suppress the warnings. (#124 by @timriley)
9
+
10
+ ```ruby
11
+ Dry::Configurable.warn_on_setting_constructor_block = false
12
+ Dry::Configurable.warn_on_setting_positional_default = false
13
+ ```
14
+
15
+ ### Fixed
16
+
17
+ - Fixed `ArgumentError` for classes including `Dry::Configurable` whose `initializer` has required kwargs. (#113 by @timriley)
18
+
19
+ ### Changed
20
+
21
+ - Deprecated the setting constructor provided as a block. Provide it via the `constructor:` keyword argument instead. (#111 by @waiting-for-dev & @timriley)
22
+
23
+ ```ruby
24
+ setting :path, constructor: -> path { Pathname(path) }
25
+ ```
26
+ - Deprecated the setting default provided as the second positional argument. Provide it via the `default:` keyword argument instead. (#112 and #121 by @waiting-for-dev & @timriley)
27
+
28
+ ```ruby
29
+ setting :path, default: "some/default/path"
30
+ ```
31
+ - [BREAKING] Removed implicit `to_hash` conversion from `Config`. (#114 by @timriley)
32
+
33
+ [Compare v0.12.1...v0.13.0](https://github.com/dry-rb/dry-configurable/compare/v0.12.1...v0.13.0)
34
+
35
+ ## 0.12.1 2021-02-15
36
+
37
+
38
+ ### Added
39
+
40
+ - Settings may be specified with a `cloneable` option, e.g.
41
+
42
+ ```ruby
43
+ setting :component_dirs, Configuration::ComponentDirs.new, cloneable: true
44
+ ```
45
+
46
+ This change makes it possible to provide “rich” config values that carry their own
47
+ configuration interface.
48
+
49
+ In the above example, `ComponentDirs` could provide its own API for adding component
50
+ dirs and configuring aspects of their behavior at the same time. By being passed to
51
+ the setting along with `cloneable: true`, dry-configurable will ensure the setting's
52
+ values are cloned along with the setting at all the appropriate times.
53
+
54
+ A custom cloneable setting value should provide its own `#initialize_copy` (used by
55
+ `Object#dup`) with the appropriate logic. (@timriley in #102)
56
+
57
+ ### Fixed
58
+
59
+ - Only `#initialize` instance method is prepended, leaving the rest of the instance
60
+ methods to be included as normal again. This allows classes including
61
+ `Dry::Configurable` to override instance methods with their own methods as required
62
+ (@adam12 in #103)
63
+
64
+
65
+ [Compare v0.12.0...v0.12.1](https://github.com/dry-rb/dry-configurable/compare/v0.12.0...v0.12.1)
66
+
67
+ ## 0.12.0 2020-12-26
68
+
69
+
70
+ ### Fixed
71
+
72
+ - Setting values provided by defaults and/or pre-processor blocks are no longer accidentally memoized across instances of classes including Dry::Configurable (#99) (@timriley & @esparta)
73
+
74
+ ### Changed
75
+
76
+ - Instance behavior is now prepended, so that if you have your own `initialize`, calling `super` is no longer required (see #98 for more details) (@zabolotnov87)
77
+ - Switched to equalizer from dry-core (@solnic)
78
+
79
+ [Compare v0.11.6...v0.12.0](https://github.com/dry-rb/dry-configurable/compare/v0.11.6...v0.12.0)
80
+
81
+ ## 0.11.6 2020-06-22
2
82
 
3
83
 
4
84
  ### Changed
@@ -6,7 +86,7 @@
6
86
  - A meaningful error is raised when the extension is included more than once (issue #89 fixed via #94) (@landongrindheim)
7
87
  - Evaluate setting input immediately when input is provided. This allows for earlier feedback from constructors designed to raise errors on invalid input (#95) (@timriley)
8
88
 
9
- [Compare v0.11.5...master](https://github.com/dry-rb/dry-configurable/compare/v0.11.5...master)
89
+ [Compare v0.11.5...v0.11.6](https://github.com/dry-rb/dry-configurable/compare/v0.11.5...v0.11.6)
10
90
 
11
91
  ## 0.11.5 2020-03-23
12
92
 
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2020 dry-rb team
3
+ Copyright (c) 2015-2021 dry-rb team
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ <!--- this file is synced from dry-rb/template-gem project -->
1
2
  [gem]: https://rubygems.org/gems/dry-configurable
2
3
  [actions]: https://github.com/dry-rb/dry-configurable/actions
3
4
  [codacy]: https://www.codacy.com/gh/dry-rb/dry-configurable
@@ -7,22 +8,22 @@
7
8
  # dry-configurable [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat]
8
9
 
9
10
  [![Gem Version](https://badge.fury.io/rb/dry-configurable.svg)][gem]
10
- [![CI Status](https://github.com/dry-rb/dry-configurable/workflows/ci/badge.svg)][actions]
11
+ [![CI Status](https://github.com/dry-rb/dry-configurable/workflows/CI/badge.svg)][actions]
11
12
  [![Codacy Badge](https://api.codacy.com/project/badge/Grade/0276a97990e04eb0ac722b3e7f3620b5)][codacy]
12
13
  [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/0276a97990e04eb0ac722b3e7f3620b5)][codacy]
13
14
  [![Inline docs](http://inch-ci.org/github/dry-rb/dry-configurable.svg?branch=master)][inchpages]
14
15
 
15
16
  ## Links
16
17
 
17
- * [User documentation](http://dry-rb.org/gems/dry-configurable)
18
+ * [User documentation](https://dry-rb.org/gems/dry-configurable)
18
19
  * [API documentation](http://rubydoc.info/gems/dry-configurable)
19
20
 
20
21
  ## Supported Ruby versions
21
22
 
22
23
  This library officially supports the following Ruby versions:
23
24
 
24
- * MRI >= `2.4`
25
- * jruby >= `9.2`
25
+ * MRI `>= 2.6.0`
26
+ * ~~jruby~~ `>= 9.3` (we are waiting for [2.6 support](https://github.com/jruby/jruby/issues/6161))
26
27
 
27
28
  ## License
28
29
 
@@ -1,36 +1,36 @@
1
1
  # frozen_string_literal: true
2
- # this file is managed by dry-rb/devtools project
3
2
 
4
- lib = File.expand_path('lib', __dir__)
3
+ # this file is synced from dry-rb/template-gem project
4
+
5
+ lib = File.expand_path("lib", __dir__)
5
6
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
- require 'dry/configurable/version'
7
+ require "dry/configurable/version"
7
8
 
8
9
  Gem::Specification.new do |spec|
9
- spec.name = 'dry-configurable'
10
+ spec.name = "dry-configurable"
10
11
  spec.authors = ["Andy Holland"]
11
12
  spec.email = ["andyholland1991@aol.com"]
12
- spec.license = 'MIT'
13
+ spec.license = "MIT"
13
14
  spec.version = Dry::Configurable::VERSION.dup
14
15
 
15
16
  spec.summary = "A mixin to add configuration functionality to your classes"
16
17
  spec.description = spec.summary
17
- spec.homepage = 'https://dry-rb.org/gems/dry-configurable'
18
+ spec.homepage = "https://dry-rb.org/gems/dry-configurable"
18
19
  spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-configurable.gemspec", "lib/**/*"]
19
- spec.bindir = 'bin'
20
+ spec.bindir = "bin"
20
21
  spec.executables = []
21
- spec.require_paths = ['lib']
22
+ spec.require_paths = ["lib"]
22
23
 
23
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
24
- spec.metadata['changelog_uri'] = 'https://github.com/dry-rb/dry-configurable/blob/master/CHANGELOG.md'
25
- spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-configurable'
26
- spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-configurable/issues'
24
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
25
+ spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-configurable/blob/master/CHANGELOG.md"
26
+ spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-configurable"
27
+ spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-configurable/issues"
27
28
 
28
- spec.required_ruby_version = ">= 2.4.0"
29
+ spec.required_ruby_version = ">= 2.6.0"
29
30
 
30
31
  # to update dependencies edit project.yml
31
32
  spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
32
- spec.add_runtime_dependency "dry-core", "~> 0.4", ">= 0.4.7"
33
- spec.add_runtime_dependency "dry-equalizer", "~> 0.2"
33
+ spec.add_runtime_dependency "dry-core", "~> 0.6"
34
34
 
35
35
  spec.add_development_dependency "bundler"
36
36
  spec.add_development_dependency "rake"
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
3
+ require "set"
4
4
 
5
- require 'dry/configurable/constants'
6
- require 'dry/configurable/dsl'
7
- require 'dry/configurable/methods'
8
- require 'dry/configurable/settings'
5
+ require "dry/configurable/constants"
6
+ require "dry/configurable/dsl"
7
+ require "dry/configurable/methods"
8
+ require "dry/configurable/settings"
9
9
 
10
10
  module Dry
11
11
  module Configurable
@@ -18,25 +18,27 @@ module Dry
18
18
 
19
19
  parent_settings = (respond_to?(:config) ? config._settings : _settings)
20
20
 
21
- klass.instance_variable_set('@_settings', parent_settings)
21
+ klass.instance_variable_set("@_settings", parent_settings)
22
22
  end
23
23
 
24
24
  # Add a setting to the configuration
25
25
  #
26
- # @param [Mixed] key
26
+ # @param [Mixed] name
27
27
  # The accessor key for the configuration value
28
28
  # @param [Mixed] default
29
- # The default config value
30
- #
29
+ # Default value for the setting
30
+ # @param [#call] constructor
31
+ # Transformation given value will go through
32
+ # @param [Boolean] reader
33
+ # Whether a reader accessor must be created
31
34
  # @yield
32
- # If a block is given, it will be evaluated in the context of
33
- # a new configuration class, and bound as the default value
35
+ # A block can be given to add nested settings.
34
36
  #
35
37
  # @return [Dry::Configurable::Config]
36
38
  #
37
39
  # @api public
38
- def setting(*args, &block)
39
- setting = __config_dsl__.setting(*args, &block)
40
+ def setting(*args, **options, &block)
41
+ setting = __config_dsl__.setting(*args, **options, &block)
40
42
 
41
43
  _settings << setting
42
44
 
@@ -74,7 +76,7 @@ module Dry
74
76
 
75
77
  # @api private
76
78
  def __config_dsl__
77
- @dsl ||= DSL.new
79
+ @__config_dsl__ ||= DSL.new
78
80
  end
79
81
 
80
82
  # @api private
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/configurable/setting'
4
- require 'dry/configurable/settings'
3
+ require "dry/configurable/setting"
4
+ require "dry/configurable/settings"
5
5
 
6
6
  module Dry
7
7
  module Configurable
@@ -23,16 +23,10 @@ module Dry
23
23
  public_send(:"visit_#{type}", rest)
24
24
  end
25
25
 
26
- # @api private
27
- def visit_constructor(node)
28
- setting, constructor = node
29
- visit(setting).with(constructor: constructor)
30
- end
31
-
32
26
  # @api private
33
27
  def visit_setting(node)
34
- name, default, opts = node
35
- Setting.new(name, **opts, default: default)
28
+ name, opts = node
29
+ Setting.new(name, **opts)
36
30
  end
37
31
 
38
32
  # @api private
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/map'
3
+ require "concurrent/map"
4
4
 
5
- require 'dry/equalizer'
5
+ require "dry/core/equalizer"
6
6
 
7
- require 'dry/configurable/constants'
8
- require 'dry/configurable/errors'
7
+ require "dry/configurable/constants"
8
+ require "dry/configurable/errors"
9
9
 
10
10
  module Dry
11
11
  module Configurable
@@ -79,7 +79,6 @@ module Dry
79
79
  .to_h
80
80
  end
81
81
  alias_method :to_h, :values
82
- alias_method :to_hash, :values
83
82
 
84
83
  # @api private
85
84
  def finalize!
@@ -106,7 +105,7 @@ module Dry
106
105
  super unless setting
107
106
 
108
107
  if setting.writer?(meth)
109
- raise FrozenConfig, 'Cannot modify frozen config' if frozen?
108
+ raise FrozenConfig, "Cannot modify frozen config" if frozen?
110
109
 
111
110
  _settings << setting.with(input: args[0])
112
111
  else
@@ -116,7 +115,7 @@ module Dry
116
115
 
117
116
  # @api private
118
117
  def resolve(meth)
119
- _resolved.fetch(meth) { _resolved[meth] = meth.to_s.tr('=', '').to_sym }
118
+ _resolved.fetch(meth) { _resolved[meth] = meth.to_s.tr("=", "").to_sym }
120
119
  end
121
120
 
122
121
  # @api private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/core/constants'
3
+ require "dry/core/constants"
4
4
 
5
5
  module Dry
6
6
  # Shared constants
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/configurable/constants'
4
- require 'dry/configurable/setting'
5
- require 'dry/configurable/settings'
6
- require 'dry/configurable/compiler'
7
- require 'dry/configurable/dsl/args'
3
+ require "dry/configurable/constants"
4
+ require "dry/configurable/flags"
5
+ require "dry/configurable/setting"
6
+ require "dry/configurable/settings"
7
+ require "dry/configurable/compiler"
8
+ require "dry/core/deprecations"
8
9
 
9
10
  module Dry
10
11
  module Configurable
@@ -27,36 +28,137 @@ module Dry
27
28
  instance_exec(&block) if block
28
29
  end
29
30
 
30
- # Register a new setting node and compile it into a setting object
31
+ # Registers a new setting node and compile it into a setting object
31
32
  #
32
33
  # @see ClassMethods.setting
33
- # @api public
34
+ # @api private
34
35
  # @return Setting
35
- def setting(name, *args, &block)
36
+ def setting(name, default = Undefined, **options, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
36
37
  unless VALID_NAME.match?(name.to_s)
37
38
  raise ArgumentError, "#{name} is not a valid setting name"
38
39
  end
39
40
 
40
- args = Args.new(args)
41
+ if default != Undefined
42
+ if Dry::Configurable.warn_on_setting_positional_default
43
+ Dry::Core::Deprecations.announce(
44
+ "default value as positional argument to settings",
45
+ "Provide a `default:` keyword argument instead",
46
+ tag: "dry-configurable",
47
+ uplevel: 2
48
+ )
49
+ end
41
50
 
42
- args.ensure_valid_options
51
+ options = options.merge(default: default)
52
+ end
43
53
 
44
- default, opts = args
54
+ if RUBY_VERSION < "3.0" &&
55
+ default == Undefined &&
56
+ (valid_opts, invalid_opts = valid_and_invalid_options(options)) &&
57
+ invalid_opts.any? &&
58
+ valid_opts.none?
59
+ # In Ruby 2.6 and 2.7, when a hash is given as the second positional argument
60
+ # (i.e. the hash is intended to be the setting's default value), and there are
61
+ # no other keyword arguments given, the hash is assigned to the `options`
62
+ # variable instead of `default`.
63
+ #
64
+ # For example, for this setting:
65
+ #
66
+ # setting :hash_setting, {my_hash: true}
67
+ #
68
+ # We'll have a `default` of `Undefined` and an `options` of `{my_hash: true}`
69
+ #
70
+ # If any additional keyword arguments are provided, e.g.:
71
+ #
72
+ # setting :hash_setting, {my_hash: true}, reader: true
73
+ #
74
+ # Then we'll have a `default` of `{my_hash: true}` and an `options` of `{reader:
75
+ # true}`, which is what we want.
76
+ #
77
+ # To work around that first case and ensure our (deprecated) backwards
78
+ # compatibility holds for Ruby 2.6 and 2.7, we extract all invalid options from
79
+ # `options`, and if there are no remaining valid options (i.e. if there were no
80
+ # keyword arguments given), then we can infer the invalid options to be a
81
+ # default hash value for the setting.
82
+ #
83
+ # This approach also preserves the behavior of raising an ArgumentError when a
84
+ # distinct hash is _not_ intentionally provided as the second positional
85
+ # argument (i.e. it's not enclosed in braces), and instead invalid keyword
86
+ # arguments are given alongside valid ones. So this setting:
87
+ #
88
+ # setting :some_setting, invalid_option: true, reader: true
89
+ #
90
+ # Would raise an ArgumentError as expected.
91
+ #
92
+ # However, the one case we can't catch here is when invalid options are supplied
93
+ # without hash literal braces, but there are no other keyword arguments
94
+ # supplied. In this case, a setting like:
95
+ #
96
+ # setting :hash_setting, my_hash: true
97
+ #
98
+ # Is parsed identically to the first case described above:
99
+ #
100
+ # setting :hash_setting, {my_hash: true}
101
+ #
102
+ # So in both of these cases, the default value will become `{my_hash: true}`. We
103
+ # consider this unlikely to be a problem in practice, since users are not likely
104
+ # to be providing invalid options to `setting` and expecting them to be ignored.
105
+ # Additionally, the deprecation messages will make the new behavior obvious, and
106
+ # encourage the users to upgrade their setting definitions.
45
107
 
46
- node = [:setting, [name.to_sym, default, opts == default ? EMPTY_HASH : opts]]
108
+ if Dry::Configurable.warn_on_setting_positional_default
109
+ Dry::Core::Deprecations.announce(
110
+ "default value as positional argument to settings",
111
+ "Provide a `default:` keyword argument instead",
112
+ tag: "dry-configurable",
113
+ uplevel: 2
114
+ )
115
+ end
47
116
 
48
- if block
49
- if block.arity.zero?
50
- ast << [:nested, [node, DSL.new(&block).ast]]
51
- else
52
- ast << [:constructor, [node, block]]
117
+ options = {default: invalid_opts}
118
+ end
119
+
120
+ if block && !block.arity.zero?
121
+ if Dry::Configurable.warn_on_setting_constructor_block
122
+ Dry::Core::Deprecations.announce(
123
+ "passing a constructor as a block",
124
+ "Provide a `constructor:` keyword argument instead",
125
+ tag: "dry-configurable",
126
+ uplevel: 2
127
+ )
53
128
  end
129
+
130
+ options = options.merge(constructor: block)
131
+ block = nil
132
+ end
133
+
134
+ ensure_valid_options(options)
135
+
136
+ node = [:setting, [name.to_sym, options]]
137
+
138
+ if block
139
+ ast << [:nested, [node, DSL.new(&block).ast]]
54
140
  else
55
141
  ast << node
56
142
  end
57
143
 
58
144
  compiler.visit(ast.last)
59
145
  end
146
+
147
+ private
148
+
149
+ def ensure_valid_options(options)
150
+ return if options.none?
151
+
152
+ invalid_keys = options.keys - Setting::OPTIONS
153
+
154
+ raise ArgumentError, "Invalid options: #{invalid_keys.inspect}" unless invalid_keys.empty?
155
+ end
156
+
157
+ # Returns a tuple of valid and invalid options hashes derived from the options hash
158
+ # given to the setting
159
+ def valid_and_invalid_options(options)
160
+ options.partition { |k, _| Setting::OPTIONS.include?(k) }.map(&:to_h)
161
+ end
60
162
  end
61
163
  end
62
164
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/class_attributes"
4
+
5
+ module Dry
6
+ module Configurable
7
+ extend Core::ClassAttributes
8
+
9
+ # Set to false to suppress deprecation warning when a setting default is provided as a
10
+ # positional argument
11
+ defines :warn_on_setting_positional_default
12
+ warn_on_setting_positional_default true
13
+
14
+ # Set to false to suppress deprecation warning when a setting constructor is provided
15
+ # as a block
16
+ defines :warn_on_setting_constructor_block
17
+ warn_on_setting_constructor_block true
18
+ end
19
+ end
@@ -1,10 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/configurable/config'
4
- require 'dry/configurable/methods'
3
+ require "dry/configurable/config"
4
+ require "dry/configurable/methods"
5
5
 
6
6
  module Dry
7
7
  module Configurable
8
+ # Initializer method which is prepended when `Dry::Configurable`
9
+ # is included in a class
10
+ #
11
+ # @api private
12
+ module Initializer
13
+ # @api private
14
+ def initialize(*)
15
+ @config = Config.new(self.class._settings.dup)
16
+ super
17
+ end
18
+ ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
19
+ end
20
+
8
21
  # Instance-level API when `Dry::Configurable` is included in a class
9
22
  #
10
23
  # @api public
@@ -18,12 +31,6 @@ module Dry
18
31
  # @api public
19
32
  attr_reader :config
20
33
 
21
- # @api private
22
- def initialize(*)
23
- @config = Config.new(self.class._settings.dup)
24
- super
25
- end
26
-
27
34
  # Finalize the config and freeze the object
28
35
  #
29
36
  # @api public
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/configurable/errors'
3
+ require "dry/configurable/errors"
4
4
 
5
5
  module Dry
6
6
  module Configurable
@@ -10,7 +10,7 @@ module Dry
10
10
  module Methods
11
11
  # @api public
12
12
  def configure(&block)
13
- raise FrozenConfig, 'Cannot modify frozen config' if frozen?
13
+ raise FrozenConfig, "Cannot modify frozen config" if frozen?
14
14
 
15
15
  yield(config) if block
16
16
  self
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
3
+ require "set"
4
4
 
5
- require 'dry/equalizer'
5
+ require "dry/core/equalizer"
6
6
 
7
- require 'dry/configurable/constants'
8
- require 'dry/configurable/config'
7
+ require "dry/configurable/constants"
8
+ require "dry/configurable/config"
9
9
 
10
10
  module Dry
11
11
  module Configurable
@@ -15,11 +15,11 @@ module Dry
15
15
  class Setting
16
16
  include Dry::Equalizer(:name, :value, :options, inspect: false)
17
17
 
18
- OPTIONS = %i[input default reader constructor settings].freeze
18
+ OPTIONS = %i[input default reader constructor cloneable settings].freeze
19
19
 
20
20
  DEFAULT_CONSTRUCTOR = -> v { v }.freeze
21
21
 
22
- CLONABLE_VALUE_TYPES = [Array, Hash, Set, Config].freeze
22
+ CLONEABLE_VALUE_TYPES = [Array, Hash, Set, Config].freeze
23
23
 
24
24
  # @api private
25
25
  attr_reader :name
@@ -53,11 +53,16 @@ module Dry
53
53
  end
54
54
  end
55
55
 
56
+ # @api private
57
+ def self.cloneable_value?(value)
58
+ CLONEABLE_VALUE_TYPES.any? { |type| value.is_a?(type) }
59
+ end
60
+
56
61
  # @api private
57
62
  def initialize(name, input: Undefined, default: Undefined, **options)
58
63
  @name = name
59
64
  @writer_name = :"#{name}="
60
- @input = input.equal?(Undefined) ? default : input
65
+ @input = input
61
66
  @default = default
62
67
  @options = options
63
68
 
@@ -110,8 +115,16 @@ module Dry
110
115
  end
111
116
 
112
117
  # @api private
113
- def clonable_value?
114
- CLONABLE_VALUE_TYPES.any? { |type| value.is_a?(type) }
118
+ def cloneable?
119
+ if options.key?(:cloneable)
120
+ # Return cloneable option if explicitly set
121
+ options[:cloneable]
122
+ else
123
+ # Otherwise, infer cloneable from any of the input, default, or value
124
+ Setting.cloneable_value?(input) || Setting.cloneable_value?(default) || (
125
+ evaluated? && Setting.cloneable_value?(value)
126
+ )
127
+ end
115
128
  end
116
129
 
117
130
  private
@@ -119,13 +132,19 @@ module Dry
119
132
  # @api private
120
133
  def initialize_copy(source)
121
134
  super
122
- @value = source.value.dup if source.input_defined? && source.clonable_value?
135
+
123
136
  @options = source.options.dup
137
+
138
+ if source.cloneable?
139
+ @input = source.input.dup
140
+ @default = source.default.dup
141
+ @value = source.value.dup if source.evaluated?
142
+ end
124
143
  end
125
144
 
126
145
  # @api private
127
146
  def evaluate
128
- @value = constructor[input.equal?(Undefined) ? nil : input]
147
+ @value = constructor[Undefined.coalesce(input, default, nil)]
129
148
  end
130
149
  end
131
150
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/map'
3
+ require "concurrent/map"
4
4
 
5
- require 'dry/equalizer'
6
- require 'dry/configurable/constants'
5
+ require "dry/core/equalizer"
6
+ require "dry/configurable/constants"
7
7
 
8
8
  module Dry
9
9
  module Configurable
@@ -3,6 +3,6 @@
3
3
  module Dry
4
4
  module Configurable
5
5
  # @api public
6
- VERSION = '0.11.6'
6
+ VERSION = "0.13.0"
7
7
  end
8
8
  end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'concurrent/array'
3
+ require "concurrent/array"
4
4
 
5
- require 'dry/configurable/constants'
6
- require 'dry/configurable/class_methods'
7
- require 'dry/configurable/instance_methods'
8
- require 'dry/configurable/config'
9
- require 'dry/configurable/setting'
10
- require 'dry/configurable/errors'
5
+ require "dry/configurable/constants"
6
+ require "dry/configurable/class_methods"
7
+ require "dry/configurable/instance_methods"
8
+ require "dry/configurable/config"
9
+ require "dry/configurable/setting"
10
+ require "dry/configurable/errors"
11
11
 
12
12
  module Dry
13
13
  # A simple configuration mixin
@@ -58,6 +58,7 @@ module Dry
58
58
  klass.class_eval do
59
59
  extend(ClassMethods)
60
60
  include(InstanceMethods)
61
+ prepend(Initializer)
61
62
 
62
63
  class << self
63
64
  undef :config
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/configurable'
3
+ require "dry/configurable"
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.11.6
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Holland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-22 00:00:00.000000000 Z
11
+ date: 2021-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -30,34 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.4'
34
- - - ">="
35
- - !ruby/object:Gem::Version
36
- version: 0.4.7
37
- type: :runtime
38
- prerelease: false
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - "~>"
42
- - !ruby/object:Gem::Version
43
- version: '0.4'
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 0.4.7
47
- - !ruby/object:Gem::Dependency
48
- name: dry-equalizer
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '0.2'
33
+ version: '0.6'
54
34
  type: :runtime
55
35
  prerelease: false
56
36
  version_requirements: !ruby/object:Gem::Requirement
57
37
  requirements:
58
38
  - - "~>"
59
39
  - !ruby/object:Gem::Version
60
- version: '0.2'
40
+ version: '0.6'
61
41
  - !ruby/object:Gem::Dependency
62
42
  name: bundler
63
43
  requirement: !ruby/object:Gem::Requirement
@@ -118,8 +98,8 @@ files:
118
98
  - lib/dry/configurable/config.rb
119
99
  - lib/dry/configurable/constants.rb
120
100
  - lib/dry/configurable/dsl.rb
121
- - lib/dry/configurable/dsl/args.rb
122
101
  - lib/dry/configurable/errors.rb
102
+ - lib/dry/configurable/flags.rb
123
103
  - lib/dry/configurable/instance_methods.rb
124
104
  - lib/dry/configurable/methods.rb
125
105
  - lib/dry/configurable/setting.rb
@@ -142,14 +122,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
122
  requirements:
143
123
  - - ">="
144
124
  - !ruby/object:Gem::Version
145
- version: 2.4.0
125
+ version: 2.6.0
146
126
  required_rubygems_version: !ruby/object:Gem::Requirement
147
127
  requirements:
148
128
  - - ">="
149
129
  - !ruby/object:Gem::Version
150
130
  version: '0'
151
131
  requirements: []
152
- rubygems_version: 3.0.3
132
+ rubygems_version: 3.1.6
153
133
  signing_key:
154
134
  specification_version: 4
155
135
  summary: A mixin to add configuration functionality to your classes
@@ -1,58 +0,0 @@
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