dry-configurable 0.11.6 → 0.13.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: 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