lib/dry/configurable/dsl.rb in dry-configurable-0.15.0 vs lib/dry/configurable/dsl.rb in dry-configurable-0.16.0

- old
+ new

@@ -1,148 +1,60 @@ # frozen_string_literal: true -require "dry/configurable/constants" -require "dry/configurable/flags" -require "dry/configurable/setting" -require "dry/configurable/settings" -require "dry/configurable/compiler" require "dry/core/deprecations" module Dry module Configurable # Setting DSL used by the class API # # @api private class DSL VALID_NAME = /\A[a-z_]\w*\z/i.freeze - # @api private attr_reader :compiler - # @api private attr_reader :ast - # @api private - def initialize(&block) + attr_reader :options + + def initialize(**options, &block) @compiler = Compiler.new @ast = [] + @options = options instance_exec(&block) if block end # Registers a new setting node and compile it into a setting object # # @see ClassMethods.setting # @api private # @return Setting - def setting(name, default = Undefined, **options, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + def setting(name, **options, &block) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity unless VALID_NAME.match?(name.to_s) raise ArgumentError, "#{name} is not a valid setting name" end - if default != Undefined - if Dry::Configurable.warn_on_setting_positional_default - Dry::Core::Deprecations.announce( - "default value as positional argument to settings", - "Provide a `default:` keyword argument instead", - tag: "dry-configurable", - uplevel: 2 - ) - end - - options = options.merge(default: default) - end - - if RUBY_VERSION < "3.0" && - default == Undefined && - (valid_opts, invalid_opts = valid_and_invalid_options(options)) && - invalid_opts.any? && - valid_opts.none? - # In Ruby 2.6 and 2.7, when a hash is given as the second positional argument - # (i.e. the hash is intended to be the setting's default value), and there are - # no other keyword arguments given, the hash is assigned to the `options` - # variable instead of `default`. - # - # For example, for this setting: - # - # setting :hash_setting, {my_hash: true} - # - # We'll have a `default` of `Undefined` and an `options` of `{my_hash: true}` - # - # If any additional keyword arguments are provided, e.g.: - # - # setting :hash_setting, {my_hash: true}, reader: true - # - # Then we'll have a `default` of `{my_hash: true}` and an `options` of `{reader: - # true}`, which is what we want. - # - # To work around that first case and ensure our (deprecated) backwards - # compatibility holds for Ruby 2.6 and 2.7, we extract all invalid options from - # `options`, and if there are no remaining valid options (i.e. if there were no - # keyword arguments given), then we can infer the invalid options to be a - # default hash value for the setting. - # - # This approach also preserves the behavior of raising an ArgumentError when a - # distinct hash is _not_ intentionally provided as the second positional - # argument (i.e. it's not enclosed in braces), and instead invalid keyword - # arguments are given alongside valid ones. So this setting: - # - # setting :some_setting, invalid_option: true, reader: true - # - # Would raise an ArgumentError as expected. - # - # However, the one case we can't catch here is when invalid options are supplied - # without hash literal braces, but there are no other keyword arguments - # supplied. In this case, a setting like: - # - # setting :hash_setting, my_hash: true - # - # Is parsed identically to the first case described above: - # - # setting :hash_setting, {my_hash: true} - # - # So in both of these cases, the default value will become `{my_hash: true}`. We - # consider this unlikely to be a problem in practice, since users are not likely - # to be providing invalid options to `setting` and expecting them to be ignored. - # Additionally, the deprecation messages will make the new behavior obvious, and - # encourage the users to upgrade their setting definitions. - - if Dry::Configurable.warn_on_setting_positional_default - Dry::Core::Deprecations.announce( - "default value as positional argument to settings", - "Provide a `default:` keyword argument instead", - tag: "dry-configurable", - uplevel: 2 - ) - end - - options = {default: invalid_opts} - end - - if block && !block.arity.zero? - if Dry::Configurable.warn_on_setting_constructor_block - Dry::Core::Deprecations.announce( - "passing a constructor as a block", - "Provide a `constructor:` keyword argument instead", - tag: "dry-configurable", - uplevel: 2 - ) - end - - options = options.merge(constructor: block) - block = nil - end - ensure_valid_options(options) + options = {default: default, config_class: config_class, **options} + node = [:setting, [name.to_sym, options]] if block ast << [:nested, [node, DSL.new(&block).ast]] else ast << node end compiler.visit(ast.last) + end + + def config_class + options[:config_class] + end + + def default + options[:default_undefined] ? Undefined : nil end private def ensure_valid_options(options)