dry-configurable 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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