dry-configurable 0.9.0 → 0.11.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -21
  3. data/LICENSE +1 -1
  4. data/README.md +15 -27
  5. data/dry-configurable.gemspec +27 -18
  6. data/lib/dry-configurable.rb +2 -0
  7. data/lib/dry/configurable.rb +21 -146
  8. data/lib/dry/configurable/class_methods.rb +103 -0
  9. data/lib/dry/configurable/compiler.rb +45 -0
  10. data/lib/dry/configurable/config.rb +78 -136
  11. data/lib/dry/configurable/constants.rb +12 -0
  12. data/lib/dry/configurable/dsl.rb +62 -0
  13. data/lib/dry/configurable/dsl/args.rb +58 -0
  14. data/lib/dry/configurable/{error.rb → errors.rb} +5 -1
  15. data/lib/dry/configurable/instance_methods.rb +46 -0
  16. data/lib/dry/configurable/methods.rb +32 -0
  17. data/lib/dry/configurable/setting.rb +91 -18
  18. data/lib/dry/configurable/settings.rb +42 -87
  19. data/lib/dry/configurable/test_interface.rb +3 -5
  20. data/lib/dry/configurable/version.rb +3 -1
  21. metadata +30 -25
  22. data/.codeclimate.yml +0 -12
  23. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
  24. data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -34
  25. data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
  26. data/.github/workflows/ci.yml +0 -70
  27. data/.github/workflows/docsite.yml +0 -34
  28. data/.github/workflows/sync_configs.yml +0 -30
  29. data/.gitignore +0 -9
  30. data/.rspec +0 -4
  31. data/.rubocop.yml +0 -89
  32. data/CODE_OF_CONDUCT.md +0 -13
  33. data/CONTRIBUTING.md +0 -29
  34. data/Gemfile +0 -20
  35. data/Rakefile +0 -12
  36. data/docsite/source/index.html.md +0 -55
  37. data/docsite/source/testing.html.md +0 -27
  38. data/lib/dry/configurable/settings/argument_parser.rb +0 -50
  39. data/rakelib/rubocop.rake +0 -18
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/configurable/config'
4
+ require 'dry/configurable/methods'
5
+
6
+ module Dry
7
+ module Configurable
8
+ # Instance-level API when `Dry::Configurable` is included in a class
9
+ #
10
+ # @api public
11
+ module InstanceMethods
12
+ include Methods
13
+
14
+ # Return object's configuration
15
+ #
16
+ # @return [Config]
17
+ #
18
+ # @api public
19
+ attr_reader :config
20
+
21
+ # @api private
22
+ def initialize(*)
23
+ @config = Config.new(self.class._settings.dup)
24
+ super
25
+ end
26
+
27
+ # Finalize the config and freeze the object
28
+ #
29
+ # @api public
30
+ def finalize!
31
+ return self if frozen?
32
+
33
+ super
34
+ freeze
35
+ end
36
+
37
+ private
38
+
39
+ # @api public
40
+ def initialize_copy(source)
41
+ super
42
+ @config = source.config.dup
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/configurable/errors'
4
+
5
+ module Dry
6
+ module Configurable
7
+ # Common API for both classes and instances
8
+ #
9
+ # @api public
10
+ module Methods
11
+ # @api public
12
+ def configure(&block)
13
+ raise FrozenConfig, 'Cannot modify frozen config' if frozen?
14
+
15
+ yield(config) if block
16
+ self
17
+ end
18
+
19
+ # Finalize and freeze configuration
20
+ #
21
+ # @return [Dry::Configurable::Config]
22
+ #
23
+ # @api public
24
+ def finalize!
25
+ return self if config.frozen?
26
+
27
+ config.finalize!
28
+ self
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,45 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ require 'dry/equalizer'
6
+
7
+ require 'dry/configurable/constants'
8
+ require 'dry/configurable/config'
9
+
1
10
  module Dry
2
11
  module Configurable
3
12
  # This class represents a setting and is used internally.
4
13
  #
5
- # @private
14
+ # @api private
6
15
  class Setting
7
- VALID_NAME = /\A[a-z_]\w*\z/i
16
+ include Dry::Equalizer(:name, :value, :options)
17
+
18
+ OPTIONS = %i[input default reader constructor settings].freeze
19
+
20
+ DEFAULT_CONSTRUCTOR = -> v { v }.freeze
8
21
 
22
+ CLONABLE_VALUE_TYPES = [Array, Hash, Set, Config].freeze
23
+
24
+ # @api private
9
25
  attr_reader :name
10
26
 
27
+ # @api private
28
+ attr_reader :writer_name
29
+
30
+ # @api private
31
+ attr_reader :input
32
+
33
+ # @api private
34
+ attr_reader :default
35
+
36
+ # @api private
37
+ attr_reader :value
38
+
39
+ # @api private
11
40
  attr_reader :options
12
41
 
13
- attr_reader :processor
42
+ # Specialized Setting which includes nested settings
43
+ #
44
+ # @api private
45
+ class Nested < Setting
46
+ CONSTRUCTOR = Config.method(:new)
47
+
48
+ # @api private
49
+ def pristine
50
+ with(input: input.pristine)
51
+ end
14
52
 
15
- def initialize(name, value, processor, options = EMPTY_HASH)
16
- unless VALID_NAME =~ name.to_s
17
- raise ArgumentError, "+#{name}+ is not a valid setting name"
53
+ # @api private
54
+ def constructor
55
+ CONSTRUCTOR
18
56
  end
19
- @name = name.to_sym
20
- @value = value
21
- @processor = processor
57
+ end
58
+
59
+ # @api private
60
+ def initialize(name, input: Undefined, default: Undefined, **options)
61
+ @name = name
62
+ @writer_name = :"#{name}="
63
+ @input = input.equal?(Undefined) ? default : input
64
+ @default = default
22
65
  @options = options
66
+ set!
67
+ end
68
+
69
+ # @api private
70
+ def nested(settings)
71
+ Nested.new(name, input: settings, **options)
72
+ end
73
+
74
+ # @api private
75
+ def pristine
76
+ with(input: Undefined)
23
77
  end
24
78
 
25
- def value
26
- Undefined.default(@value, nil)
79
+ # @api private
80
+ def with(new_opts)
81
+ self.class.new(name, input: input, default: default, **options, **new_opts)
27
82
  end
28
83
 
29
- def undefined?
30
- Undefined.equal?(@value)
84
+ # @api private
85
+ def constructor
86
+ options[:constructor] || DEFAULT_CONSTRUCTOR
31
87
  end
32
88
 
89
+ # @api private
33
90
  def reader?
34
- options[:reader]
91
+ options[:reader].equal?(true)
92
+ end
93
+
94
+ # @api private
95
+ def writer?(meth)
96
+ writer_name.equal?(meth)
35
97
  end
36
98
 
37
- def node?
38
- Settings === @value
99
+ # @api private
100
+ def clonable_value?
101
+ CLONABLE_VALUE_TYPES.any? { |type| value.is_a?(type) }
102
+ end
103
+
104
+ private
105
+
106
+ # @api private
107
+ def initialize_copy(source)
108
+ super
109
+ @value = source.value.dup if source.clonable_value?
110
+ @options = source.options.dup
39
111
  end
40
112
 
41
- def reserved?
42
- options[:reserved]
113
+ # @api private
114
+ def set!
115
+ @value = constructor[input.equal?(Undefined) ? nil : input]
43
116
  end
44
117
  end
45
118
  end
@@ -1,116 +1,71 @@
1
- require 'set'
2
- require 'concurrent/array'
3
- require 'dry/configurable/settings/argument_parser'
4
- require 'dry/configurable/setting'
5
- require 'dry/configurable/config'
1
+ # frozen_string_literal: true
2
+
3
+ require 'concurrent/map'
4
+
5
+ require 'dry/equalizer'
6
+ require 'dry/configurable/constants'
6
7
 
7
8
  module Dry
8
9
  module Configurable
9
- # A collection of settings. This is not part of the public API.
10
+ # A settings map
10
11
  #
11
- # @private
12
+ # @api private
12
13
  class Settings
13
- Parser = ArgumentParser.new.freeze
14
-
15
- class DSL
16
- def self.call(&block)
17
- new.instance_exec do
18
- instance_exec(&block)
19
- @settings
20
- end
21
- end
22
-
23
- def initialize
24
- @settings = Settings.new
25
- end
26
-
27
- def setting(*args, &block)
28
- @settings.add(*args, &block)
29
- end
30
- end
31
-
32
- # Capture nested config definition
33
- #
34
- # @return [Dry::Configurable::Setting]
35
- def self.capture(&block)
36
- DSL.(&block)
37
- end
38
-
39
- attr_reader :settings
14
+ include Dry::Equalizer(:elements)
40
15
 
41
- attr_reader :config_class
16
+ include Enumerable
42
17
 
43
- attr_reader :index
44
- private :index
18
+ # @api private
19
+ attr_reader :elements
45
20
 
46
- def initialize(settings = ::Concurrent::Array.new)
47
- @settings = settings
48
- @config_class = Config[self]
49
- @index = settings.map { |s| [s.name, s] }.to_h
50
- yield(self) if block_given?
21
+ # @api private
22
+ def initialize(elements = EMPTY_ARRAY)
23
+ initialize_elements(elements)
51
24
  end
52
25
 
53
- def add(key, value = Undefined, options = Undefined, &block)
54
- extended = singleton_class < Configurable
55
- raise_already_defined_config(key) if extended && configured?
56
-
57
- *args, opts = Parser.(value, options, block)
58
-
59
- Setting.new(key, *args, { **opts, reserved: reserved?(key) }).tap do |s|
60
- settings.delete_if { |e| e.name.eql?(s.name) }
61
- settings << s
62
- index[s.name] = s
63
- @names = nil
64
- end
65
- end
66
-
67
- def each
68
- settings.each { |s| yield(s) }
69
- end
70
-
71
- def names
72
- @names ||= index.keys.to_set
26
+ # @api private
27
+ def <<(setting)
28
+ elements[setting.name] = setting
29
+ self
73
30
  end
74
31
 
32
+ # @api private
75
33
  def [](name)
76
- index[name]
77
- end
78
-
79
- def empty?
80
- settings.empty?
34
+ elements[name]
81
35
  end
82
36
 
83
- def name?(name)
84
- index.key?(name)
37
+ # @api private
38
+ def key?(name)
39
+ keys.include?(name)
85
40
  end
86
41
 
87
- def dup
88
- Settings.new(settings.dup)
42
+ # @api private
43
+ def keys
44
+ elements.keys
89
45
  end
90
46
 
91
- def freeze
92
- settings.freeze
93
- super
47
+ # @api private
48
+ def each(&block)
49
+ elements.values.each(&block)
94
50
  end
95
51
 
96
- def create_config
97
- config_class.new
52
+ # @api private
53
+ def pristine
54
+ self.class.new(map(&:pristine))
98
55
  end
99
56
 
100
- def config_defined?
101
- config_class.config_defined?
102
- end
57
+ private
103
58
 
104
- def reserved?(name)
105
- reserved_names.include?(name)
59
+ # @api private
60
+ def initialize_copy(source)
61
+ initialize_elements(source.map(&:dup))
106
62
  end
107
63
 
108
- def reserved_names
109
- @reserved_names ||= [
110
- config_class.instance_methods(false),
111
- config_class.superclass.instance_methods(false),
112
- %i(class public_send)
113
- ].reduce(:+)
64
+ # @api private
65
+ def initialize_elements(elements)
66
+ @elements = elements.each_with_object(Concurrent::Map.new) { |s, m|
67
+ m[s.name] = s
68
+ }
114
69
  end
115
70
  end
116
71
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Configurable
3
5
  # Methods meant to be used in a testing scenario
@@ -8,11 +10,7 @@ module Dry
8
10
  #
9
11
  # @api public
10
12
  def reset_config
11
- @config = if self.is_a?(Module)
12
- _settings.create_config
13
- else
14
- self.class._settings.create_config
15
- end
13
+ @config = config.pristine
16
14
  end
17
15
  end
18
16
 
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Configurable
3
5
  # @api public
4
- VERSION = '0.9.0'.freeze
6
+ VERSION = '0.11.0'
5
7
  end
6
8
  end
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.9.0
4
+ version: 0.11.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: 2019-11-06 00:00:00.000000000 Z
11
+ date: 2020-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -44,6 +44,20 @@ dependencies:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
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'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.2'
47
61
  - !ruby/object:Gem::Dependency
48
62
  name: bundler
49
63
  requirement: !ruby/object:Gem::Requirement
@@ -86,49 +100,40 @@ dependencies:
86
100
  - - ">="
87
101
  - !ruby/object:Gem::Version
88
102
  version: '0'
89
- description:
103
+ description: A mixin to add configuration functionality to your classes
90
104
  email:
91
105
  - andyholland1991@aol.com
92
106
  executables: []
93
107
  extensions: []
94
108
  extra_rdoc_files: []
95
109
  files:
96
- - ".codeclimate.yml"
97
- - ".github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md"
98
- - ".github/ISSUE_TEMPLATE/---bug-report.md"
99
- - ".github/ISSUE_TEMPLATE/---feature-request.md"
100
- - ".github/workflows/ci.yml"
101
- - ".github/workflows/docsite.yml"
102
- - ".github/workflows/sync_configs.yml"
103
- - ".gitignore"
104
- - ".rspec"
105
- - ".rubocop.yml"
106
110
  - CHANGELOG.md
107
- - CODE_OF_CONDUCT.md
108
- - CONTRIBUTING.md
109
- - Gemfile
110
111
  - LICENSE
111
112
  - README.md
112
- - Rakefile
113
- - docsite/source/index.html.md
114
- - docsite/source/testing.html.md
115
113
  - dry-configurable.gemspec
116
114
  - lib/dry-configurable.rb
117
115
  - lib/dry/configurable.rb
116
+ - lib/dry/configurable/class_methods.rb
117
+ - lib/dry/configurable/compiler.rb
118
118
  - lib/dry/configurable/config.rb
119
- - lib/dry/configurable/error.rb
119
+ - lib/dry/configurable/constants.rb
120
+ - lib/dry/configurable/dsl.rb
121
+ - lib/dry/configurable/dsl/args.rb
122
+ - lib/dry/configurable/errors.rb
123
+ - lib/dry/configurable/instance_methods.rb
124
+ - lib/dry/configurable/methods.rb
120
125
  - lib/dry/configurable/setting.rb
121
126
  - lib/dry/configurable/settings.rb
122
- - lib/dry/configurable/settings/argument_parser.rb
123
127
  - lib/dry/configurable/test_interface.rb
124
128
  - lib/dry/configurable/version.rb
125
- - rakelib/rubocop.rake
126
- homepage: https://github.com/dry-rb/dry-configurable
129
+ homepage: https://dry-rb.org/gems/dry-configurable
127
130
  licenses:
128
131
  - MIT
129
132
  metadata:
130
- source_code_uri: https://github.com/dry-rb/dry-configurable
133
+ allowed_push_host: https://rubygems.org
131
134
  changelog_uri: https://github.com/dry-rb/dry-configurable/blob/master/CHANGELOG.md
135
+ source_code_uri: https://github.com/dry-rb/dry-configurable
136
+ bug_tracker_uri: https://github.com/dry-rb/dry-configurable/issues
132
137
  post_install_message:
133
138
  rdoc_options: []
134
139
  require_paths:
@@ -144,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
149
  - !ruby/object:Gem::Version
145
150
  version: '0'
146
151
  requirements: []
147
- rubygems_version: 3.0.6
152
+ rubygems_version: 3.0.3
148
153
  signing_key:
149
154
  specification_version: 4
150
155
  summary: A mixin to add configuration functionality to your classes