dry-configurable 0.7.0 → 0.8.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.
@@ -1,9 +1,6 @@
1
- require 'concurrent'
2
- require 'dry/configurable/config'
1
+ require 'dry/core/constants'
2
+ require 'dry/configurable/settings'
3
3
  require 'dry/configurable/error'
4
- require 'dry/configurable/nested_config'
5
- require 'dry/configurable/argument_parser'
6
- require 'dry/configurable/config/value'
7
4
  require 'dry/configurable/version'
8
5
 
9
6
  # A collection of micro-libraries, each intended to encapsulate
@@ -11,7 +8,7 @@ require 'dry/configurable/version'
11
8
  module Dry
12
9
  # A simple configuration mixin
13
10
  #
14
- # @example
11
+ # @example class-level configuration
15
12
  #
16
13
  # class App
17
14
  # extend Dry::Configurable
@@ -21,30 +18,123 @@ module Dry
21
18
  # end
22
19
  # end
23
20
  #
24
- # App.configure do |config|
25
- # config.database.dsn = 'jdbc:sqlite:memory'
21
+ # App.config.database.dsn = 'jdbc:sqlite:memory'
22
+ # App.config.database.dsn
23
+ # # => "jdbc:sqlite:memory"
24
+ #
25
+ # @example instance-level configuration
26
+ #
27
+ # class App
28
+ # include Dry::Configurable
29
+ #
30
+ # setting :database
26
31
  # end
27
32
  #
28
- # App.config.database.dsn
29
- # # => "jdbc:sqlite:memory'"
33
+ # production = App.new
34
+ # production.config.database = ENV['DATABASE_URL']
35
+ # production.finalize!
36
+ #
37
+ # development = App.new
38
+ # development.config.database = 'jdbc:sqlite:memory'
39
+ # development.finalize!
30
40
  #
31
41
  # @api public
32
42
  module Configurable
33
- # @private
34
- def self.extended(base)
35
- base.class_eval do
36
- @_config_mutex = ::Mutex.new
37
- @_settings = ::Concurrent::Array.new
38
- @_reader_attributes = ::Concurrent::Array.new
43
+ include Dry::Core::Constants
44
+
45
+ module ClassMethods
46
+ # @private
47
+ def self.extended(base)
48
+ base.instance_exec do
49
+ @settings = Settings.new
50
+ end
51
+ end
52
+
53
+ # Add a setting to the configuration
54
+ #
55
+ # @param [Mixed] key
56
+ # The accessor key for the configuration value
57
+ # @param [Mixed] default
58
+ # The default config value
59
+ #
60
+ # @yield
61
+ # If a block is given, it will be evaluated in the context of
62
+ # and new configuration class, and bound as the default value
63
+ #
64
+ # @return [Dry::Configurable::Config]
65
+ #
66
+ # @api public
67
+ def setting(key, value = Undefined, options = Undefined, &block)
68
+ extended = singleton_class < Configurable
69
+ raise_already_defined_config(key) if _settings.config_defined?
70
+
71
+ setting = _settings.add(key, value, options, &block)
72
+
73
+ if setting.reader?
74
+ readers = extended ? singleton_class : self
75
+ readers.send(:define_method, setting.name) { config[setting.name] }
76
+ end
77
+ end
78
+
79
+ # Return an array of setting names
80
+ #
81
+ # @return [Set]
82
+ #
83
+ # @api public
84
+ def settings
85
+ _settings.names
86
+ end
87
+
88
+ # @private no, really...
89
+ def _settings
90
+ @settings
91
+ end
92
+
93
+ private
94
+
95
+ # @private
96
+ def raise_already_defined_config(key)
97
+ raise AlreadyDefinedConfig,
98
+ "Cannot add setting +#{key}+, #{self} is already configured"
99
+ end
100
+
101
+ # @private
102
+ def inherited(subclass)
103
+ parent = self
104
+ subclass.instance_exec do
105
+ @settings = parent._settings.dup
106
+ end
107
+
108
+ if singleton_class < Configurable
109
+ parent_config = @config
110
+ subclass.instance_exec do
111
+ @config = _settings.create_config
112
+ @config.define!(parent_config.to_h) if parent_config.defined?
113
+ end
114
+ end
115
+
116
+ super
117
+ end
118
+ end
119
+
120
+ class << self
121
+ # @private
122
+ def extended(base)
123
+ base.extend(ClassMethods)
124
+ base.class_eval do
125
+ @config = _settings.create_config
126
+ end
127
+ end
128
+
129
+ # @private
130
+ def included(base)
131
+ base.extend(ClassMethods)
39
132
  end
40
133
  end
41
134
 
42
135
  # @private
43
- def inherited(subclass)
44
- subclass.instance_variable_set(:@_config_mutex, ::Mutex.new)
45
- subclass.instance_variable_set(:@_settings, @_settings.clone)
46
- subclass.instance_variable_set(:@_reader_attributes, @_reader_attributes.clone)
47
- subclass.instance_variable_set(:@_config, @_config.clone) if defined?(@_config)
136
+ def initialize(*)
137
+ @config = self.class._settings.create_config
48
138
  super
49
139
  end
50
140
 
@@ -54,8 +144,8 @@ module Dry
54
144
  #
55
145
  # @api public
56
146
  def config
57
- return @_config if defined?(@_config)
58
- create_config
147
+ return @config if @config.defined?
148
+ @config.define!
59
149
  end
60
150
 
61
151
  # Return configuration
@@ -66,8 +156,9 @@ module Dry
66
156
  #
67
157
  # @api public
68
158
  def configure
69
- raise_frozen_config if frozen?
70
- yield(config) if block_given?
159
+ raise FrozenConfig, 'Cannot modify frozen config' if frozen?
160
+ yield(config)
161
+ self
71
162
  end
72
163
 
73
164
  # Finalize and freeze configuration
@@ -79,107 +170,5 @@ module Dry
79
170
  freeze
80
171
  config.finalize!
81
172
  end
82
-
83
- # Add a setting to the configuration
84
- #
85
- # @param [Mixed] key
86
- # The accessor key for the configuration value
87
- # @param [Mixed] default
88
- # The default config value
89
- #
90
- # @yield
91
- # If a block is given, it will be evaluated in the context of
92
- # and new configuration class, and bound as the default value
93
- #
94
- # @return [Dry::Configurable::Config]
95
- #
96
- # @api public
97
- def setting(key, *args, &block)
98
- raise_already_defined_config(key) if defined?(@_config)
99
- value, options = ArgumentParser.call(args)
100
- if block
101
- if block.parameters.empty?
102
- value = _config_for(&block)
103
- else
104
- processor = block
105
- end
106
- end
107
-
108
- _settings << ::Dry::Configurable::Config::Value.new(
109
- key,
110
- !value.nil? ? value : ::Dry::Configurable::Config::Value::NONE,
111
- processor || ::Dry::Configurable::Config::DEFAULT_PROCESSOR
112
- )
113
- store_reader_options(key, options) if options.any?
114
- end
115
-
116
- # Return an array of setting names
117
- #
118
- # @return [Array]
119
- #
120
- # @api public
121
- def settings
122
- _settings.map(&:name)
123
- end
124
-
125
- # @private no, really...
126
- def _settings
127
- @_settings
128
- end
129
-
130
- def _reader_attributes
131
- @_reader_attributes
132
- end
133
-
134
- private
135
-
136
- # @private
137
- def _config_for(&block)
138
- ::Dry::Configurable::NestedConfig.new(&block)
139
- end
140
-
141
- # @private
142
- def create_config
143
- @_config_mutex.synchronize do
144
- create_config_for_nested_configurations
145
- @_config = ::Dry::Configurable::Config.create(_settings) unless _settings.empty?
146
- end
147
- end
148
-
149
- # @private
150
- def create_config_for_nested_configurations
151
- nested_configs.map(&:create_config)
152
- end
153
-
154
- # @private
155
- def nested_configs
156
- _settings.select { |setting| setting.value.is_a?(::Dry::Configurable::NestedConfig) }.map(&:value)
157
- end
158
-
159
- # @private
160
- def raise_already_defined_config(key)
161
- raise AlreadyDefinedConfig,
162
- "Cannot add setting +#{key}+, #{self} is already configured"
163
- end
164
-
165
- # @private
166
- def raise_frozen_config
167
- raise FrozenConfig, 'Cannot modify frozen config'
168
- end
169
-
170
- # @private
171
- def store_reader_options(key, options)
172
- _reader_attributes << key if options.fetch(:reader, false)
173
- end
174
-
175
- # @private
176
- def method_missing(method, *args, &block)
177
- _reader_attributes.include?(method) ? config.public_send(method, *args, &block) : super
178
- end
179
-
180
- # @private
181
- def respond_to_missing?(method, _include_private = false)
182
- _reader_attributes.include?(method) || super
183
- end
184
173
  end
185
174
  end
metadata CHANGED
@@ -1,69 +1,89 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-configurable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.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: 2017-04-25 00:00:00.000000000 Z
11
+ date: 2019-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-core
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
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
27
47
  - !ruby/object:Gem::Dependency
28
48
  name: bundler
29
49
  requirement: !ruby/object:Gem::Requirement
30
50
  requirements:
31
- - - '>='
51
+ - - ">="
32
52
  - !ruby/object:Gem::Version
33
53
  version: '0'
34
54
  type: :development
35
55
  prerelease: false
36
56
  version_requirements: !ruby/object:Gem::Requirement
37
57
  requirements:
38
- - - '>='
58
+ - - ">="
39
59
  - !ruby/object:Gem::Version
40
60
  version: '0'
41
61
  - !ruby/object:Gem::Dependency
42
62
  name: rake
43
63
  requirement: !ruby/object:Gem::Requirement
44
64
  requirements:
45
- - - '>='
65
+ - - ">="
46
66
  - !ruby/object:Gem::Version
47
67
  version: '0'
48
68
  type: :development
49
69
  prerelease: false
50
70
  version_requirements: !ruby/object:Gem::Requirement
51
71
  requirements:
52
- - - '>='
72
+ - - ">="
53
73
  - !ruby/object:Gem::Version
54
74
  version: '0'
55
75
  - !ruby/object:Gem::Dependency
56
76
  name: rspec
57
77
  requirement: !ruby/object:Gem::Requirement
58
78
  requirements:
59
- - - '>='
79
+ - - ">="
60
80
  - !ruby/object:Gem::Version
61
81
  version: '0'
62
82
  type: :development
63
83
  prerelease: false
64
84
  version_requirements: !ruby/object:Gem::Requirement
65
85
  requirements:
66
- - - '>='
86
+ - - ">="
67
87
  - !ruby/object:Gem::Version
68
88
  version: '0'
69
89
  description:
@@ -73,12 +93,12 @@ executables: []
73
93
  extensions: []
74
94
  extra_rdoc_files: []
75
95
  files:
76
- - .codeclimate.yml
77
- - .gitignore
78
- - .rspec
79
- - .ruby-version
80
- - .travis.yml
96
+ - ".codeclimate.yml"
97
+ - ".gitignore"
98
+ - ".rspec"
99
+ - ".travis.yml"
81
100
  - CHANGELOG.md
101
+ - CONTRIBUTING.md
82
102
  - Gemfile
83
103
  - LICENSE
84
104
  - README.md
@@ -86,21 +106,15 @@ files:
86
106
  - dry-configurable.gemspec
87
107
  - lib/dry-configurable.rb
88
108
  - lib/dry/configurable.rb
89
- - lib/dry/configurable/argument_parser.rb
90
109
  - lib/dry/configurable/config.rb
91
- - lib/dry/configurable/config/value.rb
92
110
  - lib/dry/configurable/error.rb
93
- - lib/dry/configurable/nested_config.rb
111
+ - lib/dry/configurable/setting.rb
112
+ - lib/dry/configurable/settings.rb
113
+ - lib/dry/configurable/settings/argument_parser.rb
94
114
  - lib/dry/configurable/test_interface.rb
95
115
  - lib/dry/configurable/version.rb
96
116
  - rakelib/rubocop.rake
97
- - spec/integration/configurable_spec.rb
98
- - spec/spec_helper.rb
99
- - spec/support/shared_examples/configurable.rb
100
- - spec/unit/dry/configurable/argument_parser_spec.rb
101
- - spec/unit/dry/configurable/config/value_spec.rb
102
- - spec/unit/dry/configurable/config_spec.rb
103
- homepage: https://github.com/dryrb/dry-configurable
117
+ homepage: https://github.com/dry-rb/dry-configurable
104
118
  licenses:
105
119
  - MIT
106
120
  metadata: {}
@@ -110,24 +124,17 @@ require_paths:
110
124
  - lib
111
125
  required_ruby_version: !ruby/object:Gem::Requirement
112
126
  requirements:
113
- - - '>='
127
+ - - ">="
114
128
  - !ruby/object:Gem::Version
115
- version: '0'
129
+ version: 2.3.0
116
130
  required_rubygems_version: !ruby/object:Gem::Requirement
117
131
  requirements:
118
- - - '>='
132
+ - - ">="
119
133
  - !ruby/object:Gem::Version
120
134
  version: '0'
121
135
  requirements: []
122
- rubyforge_project:
123
- rubygems_version: 2.4.8
136
+ rubygems_version: 3.0.1
124
137
  signing_key:
125
138
  specification_version: 4
126
139
  summary: A mixin to add configuration functionality to your classes
127
- test_files:
128
- - spec/integration/configurable_spec.rb
129
- - spec/spec_helper.rb
130
- - spec/support/shared_examples/configurable.rb
131
- - spec/unit/dry/configurable/argument_parser_spec.rb
132
- - spec/unit/dry/configurable/config/value_spec.rb
133
- - spec/unit/dry/configurable/config_spec.rb
140
+ test_files: []
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.0.0-p247
@@ -1,91 +0,0 @@
1
- module Dry
2
- # Argument parser
3
- #
4
- # Passing and array or arguments, it will decide wich one are arguments
5
- # and which one are options.
6
- #
7
- # We have a limitation if setting the value without options, as a hash
8
- # having the same key as one of the valid options, will parse the value
9
- # as options.
10
- #
11
- # @example
12
- # p = Dry::Configurable::ArgumentParser.new(['db:sqlite', { reader: true })
13
- #
14
- # p.value # => 'db:sqlite'
15
- # p.options # => { reader: true }
16
- #
17
- # Dry::Configurable::ArgumentParser.call(['db:sqlite', { reader: true })
18
- # # => [ 'db:sqlite', { reader: true } ]
19
- module Configurable
20
- # @private
21
- class ArgumentParser
22
- VALID_OPTIONS = %i(reader).freeze
23
-
24
- def self.call(data)
25
- parsed = new(data)
26
- [parsed.value, parsed.options]
27
- end
28
-
29
- def initialize(data)
30
- @data = data
31
- end
32
-
33
- def value
34
- parse_args[:value]
35
- end
36
-
37
- def options
38
- parse_args[:options]
39
- end
40
-
41
- private
42
-
43
- attr_reader :data
44
-
45
- # @private
46
- def default_args
47
- { value: nil, options: {} }
48
- end
49
-
50
- # @private
51
- def parse_args
52
- return default_args if data.empty?
53
- if data.size > 1
54
- { value: data.first, options: check_options(data.last) }
55
- else
56
- default_args.merge(check_for_value_or_options(data.first))
57
- end
58
- end
59
-
60
- # @private
61
- def check_options(opts)
62
- return {} if opts.empty?
63
- opts.select { |k, _| VALID_OPTIONS.include?(k) }
64
- end
65
-
66
- # @private
67
- def check_for_value_or_options(args)
68
- case args
69
- when Hash
70
- parse_hash(args)
71
- else
72
- { value: args }
73
- end
74
- end
75
-
76
- # @private
77
- def parse_hash(args)
78
- if hash_include_options_key(args)
79
- { options: check_options(args) }
80
- else
81
- { value: args }
82
- end
83
- end
84
-
85
- # @private
86
- def hash_include_options_key(hash)
87
- hash.any? { |k, _| VALID_OPTIONS.include?(k) }
88
- end
89
- end
90
- end
91
- end
@@ -1,27 +0,0 @@
1
- module Dry
2
- module Configurable
3
- class Config
4
- # @private
5
- class Value
6
- # @private
7
- NONE = ::Object.new.freeze
8
-
9
- attr_reader :name, :processor
10
-
11
- def initialize(name, value, processor)
12
- @name = name.to_sym
13
- @value = value
14
- @processor = processor
15
- end
16
-
17
- def value
18
- none? ? nil : @value
19
- end
20
-
21
- def none?
22
- @value.equal?(::Dry::Configurable::Config::Value::NONE)
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,33 +0,0 @@
1
- module Dry
2
- module Configurable
3
- # @private
4
- class NestedConfig
5
- def initialize(&block)
6
- klass = ::Class.new { extend ::Dry::Configurable }
7
- klass.instance_eval(&block)
8
- @klass = klass
9
- end
10
-
11
- # @private no, really...
12
- def create_config
13
- if @klass.instance_variables.include?(:@_config)
14
- @klass.__send__(:create_config)
15
- end
16
- end
17
-
18
- private
19
-
20
- def config
21
- @klass.config
22
- end
23
-
24
- def method_missing(method, *args, &block)
25
- config.respond_to?(method) ? config.public_send(method, *args, &block) : super
26
- end
27
-
28
- def respond_to_missing?(method, _include_private = false)
29
- config.respond_to?(method) || super
30
- end
31
- end
32
- end
33
- end
@@ -1,25 +0,0 @@
1
- RSpec.describe Dry::Configurable do
2
- context 'when extended' do
3
- let(:klass) do
4
- Class.new do
5
- extend Dry::Configurable
6
- end
7
- end
8
-
9
- it_behaves_like 'a configurable class'
10
- end
11
-
12
- context 'when extended then inherited' do
13
- let(:base_klass) do
14
- Class.new do
15
- extend Dry::Configurable
16
- end
17
- end
18
-
19
- let(:klass) do
20
- Class.new(base_klass)
21
- end
22
-
23
- it_behaves_like 'a configurable class'
24
- end
25
- end