sinclair 1.3.4 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e1275a6e3f8459ed0176557d4d9580c7cac9bc537b36dd3a96179d43803035b
4
- data.tar.gz: baba86f5fe29bddf70c3d440e0539c4d0aa4a7b892a9548f73dbaf8945d51be7
3
+ metadata.gz: 4826b55d94281092e5a0ba4d397366c39ec3494ec6f84743480c9e6ef69a030e
4
+ data.tar.gz: fbd63c4a98eacaf569a56c95411e71043f5b400bd7f98b593c73fba1a68d5bd6
5
5
  SHA512:
6
- metadata.gz: ca4c580be409552638fb442d63b5036cac1d45134839f00551ea2cdcf1cada5f8736d6e1f2abdda97bd9d850a25d92599791efa15f423496796f30c834db053f
7
- data.tar.gz: 604d5a4c072fff368b8bd6d062ab6730880bef1f29d6d197e00780cf7d8e98d3d6d64a34c87c8d93a1a367ec81a9a3ab98860111d5d425599d2777fb831db7dd
6
+ metadata.gz: 531c6f6db55bed97a3bfcf8f8c6001ae35cd5226aa5e386b0bb9442afcea7984bdf77ef4b16774e6e7c44c4b51c8ec15eac74431042f26a12d069909a2fa6a1f
7
+ data.tar.gz: 5820b843eaddf7838d430c4713645fbad5a8bf46850bc9cce5dbc1cc9f7d0005dc949969c06ba6ed3ca088a9790fdf32121e2e795dcc09935092636594809ed2
data/README.md CHANGED
@@ -14,7 +14,7 @@ methods
14
14
 
15
15
  Yard Documentation
16
16
  -------------------
17
- https://www.rubydoc.info/gems/sinclair/1.3.4
17
+ https://www.rubydoc.info/gems/sinclair/1.4.0
18
18
 
19
19
  Installation
20
20
  ---------------
@@ -335,7 +335,9 @@ the `configurable#configure` method
335
335
  Configurations can also be done through custom classes
336
336
 
337
337
  ```ruby
338
- class MyServerConfig
338
+ class MyServerConfig < Sinclair::Config
339
+ config_attributes :host, :port
340
+
339
341
  def url
340
342
  if @port
341
343
  "http://#{@host}:#{@port}"
@@ -348,7 +350,7 @@ Configurations can also be done through custom classes
348
350
  class Client
349
351
  extend Sinclair::Configurable
350
352
 
351
- configurable_by MyServerConfig, with: %i[host port]
353
+ configurable_by MyServerConfig
352
354
  end
353
355
 
354
356
  Client.configure do
data/WARNINGS.md ADDED
@@ -0,0 +1,69 @@
1
+ # Warnings
2
+ This documents tries to bring more information on warnings
3
+ and depreations on current versions of the gem.
4
+
5
+ ## Usage of custom config classes
6
+ Introduced: 1.3.0
7
+ Changed in: 1.4.0
8
+
9
+ In the past, you could use any custom configuration classes
10
+ to configure your configurable. Those classes could be
11
+ extended through the regular usage of ```.configurable_with```
12
+ or have it's configuration attributes declared by
13
+ ```with``` option on ```configurable_with```
14
+
15
+ ```ruby
16
+ class MyConfig
17
+ def url
18
+ if @port
19
+ "http://#{@host}:#{@port}"
20
+ else
21
+ "http://#{@host}"
22
+ end
23
+ end
24
+ end
25
+
26
+ class MyClient
27
+ configutable_by MyConfig, with: [:host, :port]
28
+ configurable_with timeout: 5.seconds
29
+ end
30
+
31
+ MyClient.configure do
32
+ port 5555
33
+ host 'myhost.com'
34
+ timeout 10.seconds
35
+ end
36
+ ```
37
+
38
+ Now, the configuration class should extend ```Sinclair::ConfigClass```
39
+ and the attributes defined within the class itself, still
40
+ being able to extend it through the usage of
41
+ ```.configurable_with``` call after ```configurable_by```
42
+
43
+ ```ruby
44
+ class MyConfig
45
+ extend Sinclair::ConfigClass
46
+
47
+ config_attributes :host, :port
48
+
49
+ def url
50
+ if @port
51
+ "http://#{@host}:#{@port}"
52
+ else
53
+ "http://#{@host}"
54
+ end
55
+ end
56
+ end
57
+
58
+ class MyClient
59
+ configutable_by MyConfig
60
+
61
+ configurable_with timeout: 5.seconds
62
+ end
63
+
64
+ MyClient.configure do
65
+ port 5555
66
+ host 'myhost.com'
67
+ timeout 10.seconds
68
+ end
69
+ ```
data/config/yardstick.yml CHANGED
@@ -37,8 +37,8 @@ rules:
37
37
  - Sinclair::ConfigFactory#initialize
38
38
  - Sinclair::ConfigFactory#config_class
39
39
  - Sinclair::ConfigFactory#config_attributes
40
- - Sinclair::ConfigFactory::MethodsBuilder#names
41
- - Sinclair::ConfigFactory::MethodsBuilder#defaults
40
+ - Sinclair::Config::MethodsBuilder#names
41
+ - Sinclair::Config::MethodsBuilder#defaults
42
42
  - Sinclair::Configurable#config
43
43
  - Sinclair::Configurable#reset_config
44
44
  - Sinclair::Configurable#configure
@@ -60,9 +60,9 @@ rules:
60
60
  - Sinclair::ConfigFactory#initialize
61
61
  - Sinclair::ConfigFactory#config_class
62
62
  - Sinclair::ConfigFactory#config_attributes
63
- - Sinclair::ConfigFactory::MethodsBuilder#initialize
64
- - Sinclair::ConfigFactory::MethodsBuilder#names
65
- - Sinclair::ConfigFactory::MethodsBuilder#defaults
63
+ - Sinclair::Config::MethodsBuilder#initialize
64
+ - Sinclair::Config::MethodsBuilder#names
65
+ - Sinclair::Config::MethodsBuilder#defaults
66
66
  Summary::Length:
67
67
  enabled: true
68
68
  exclude: []
data/lib/sinclair.rb CHANGED
@@ -86,6 +86,7 @@ class Sinclair
86
86
  autoload :MethodDefinition, 'sinclair/method_definition'
87
87
  autoload :Config, 'sinclair/config'
88
88
  autoload :ConfigBuilder, 'sinclair/config_builder'
89
+ autoload :ConfigClass, 'sinclair/config_class'
89
90
  autoload :ConfigFactory, 'sinclair/config_factory'
90
91
  autoload :Configurable, 'sinclair/configurable'
91
92
 
@@ -7,5 +7,32 @@ class Sinclair
7
7
  #
8
8
  # The instance variables will be set by {ConfigBuilder}
9
9
  class Config
10
+ autoload :MethodsBuilder, 'sinclair/config/methods_builder'
11
+ extend ConfigClass
12
+
13
+ # Return all the current configurations in a hash
14
+ #
15
+ # @return [Hash]
16
+ #
17
+ # @example Checking all hash/json formats
18
+ # class LoginConfig < Sinclair::Config
19
+ # add_configs :password, username: 'bob'
20
+ # end
21
+ #
22
+ # config = LoginConfig.new
23
+ #
24
+ # config.to_hash
25
+ # # returns { 'password' => nil, 'username' => 'bob' }
26
+ #
27
+ # config.as_json
28
+ # # returns { 'password' => nil, 'username' => 'bob' }
29
+ #
30
+ # config.to_json
31
+ # # returns '{"password":null,"username":"bob"}'
32
+ def to_hash
33
+ self.class.config_attributes.each_with_object({}) do |attribute, hash|
34
+ hash[attribute.to_s] = public_send(attribute)
35
+ end
36
+ end
10
37
  end
11
38
  end
@@ -1,19 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Sinclair
4
- class ConfigFactory
4
+ class Config
5
5
  # @api private
6
6
  #
7
7
  # Class responsible for adding method to configuration
8
8
  # classes
9
9
  class MethodsBuilder < Sinclair
10
+ # Instantiate method builder and build the methods
11
+ #
12
+ # @param (see #initialize)
13
+ #
14
+ # @overload build(klass, *names, default)
15
+ # @param names [Array<Symbol,String>] List of configuration names
16
+ # to be added
17
+ # @param default [Hash] Configurations that will receive a default
18
+ # value when not configured
19
+ #
20
+ # @return [MethodsBuilder]
21
+ def self.build(klass, *names)
22
+ new(klass, *names).tap(&:build)
23
+ end
24
+
10
25
  # @param klass [Class] class inheriting from {Sinclair::Config}
11
26
  # that will receive the methods
12
27
  # @overload initialize(klass, *names, default)
13
28
  # @param names [Array<Symbol,String>] List of configuration names
14
- # to be added
29
+ # to be added
15
30
  # @param default [Hash] Configurations that will receive a default
16
- # value when not configured
31
+ # value when not configured
17
32
  def initialize(klass, *names)
18
33
  super(klass)
19
34
 
@@ -7,6 +7,8 @@ class Sinclair
7
7
  #
8
8
  # @example
9
9
  # class MyConfig
10
+ # extend Sinclair::ConfigClass
11
+ #
10
12
  # attr_reader :name, :config
11
13
  # end
12
14
  #
@@ -47,7 +49,7 @@ class Sinclair
47
49
  #
48
50
  # @return [Object]
49
51
  def method_missing(method_name, *args)
50
- return super unless @config_attributes.include?(method_name)
52
+ return super unless method_included?(method_name)
51
53
 
52
54
  @config.instance_variable_set("@#{method_name}", *args)
53
55
  end
@@ -60,7 +62,21 @@ class Sinclair
60
62
  #
61
63
  # @see #method_missing
62
64
  def respond_to_missing?(method_name, include_private)
63
- @config_attributes.include?(method_name) || super
65
+ method_included?(method_name) || super
66
+ end
67
+
68
+ # Checks if a method is included in the methods defined
69
+ #
70
+ # @param method_name [Symbol] name of the method called
71
+ #
72
+ # @return [TrueClass,FalseClass]
73
+ #
74
+ # @todo get rid of @config_attributes when only
75
+ # Sinclair::ConfigClass are accepted
76
+ def method_included?(method_name)
77
+ @config_attributes.include?(method_name) ||
78
+ @config.class.is_a?(Sinclair::ConfigClass) &&
79
+ @config.class.config_attributes.include?(method_name)
64
80
  end
65
81
  end
66
82
  end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ # @api public
5
+ #
6
+ # Module with all class methods for {Config}
7
+ #
8
+ # Any class that will be used as configuration class
9
+ # should extend {ConfigClass} as {#config_attributes}
10
+ # is used to check what configurations have been added
11
+ #
12
+ # @example (see #add_configs)
13
+ module ConfigClass
14
+ # @api private
15
+ #
16
+ # Adds an attribute to the list of attributes
17
+ #
18
+ # The list of attributes represent attribute
19
+ # readers that class instances can respond to
20
+ #
21
+ # Subclasses will respond to the same attributes as the
22
+ # parent class plus it's own
23
+ #
24
+ # This method does not add the method or .attr_reader
25
+ #
26
+ # @param attributes [Array<Symbol,String>] list of
27
+ # attributes the instances should respond to
28
+ #
29
+ # @return [Array<Symbol>] all attributes the class have
30
+ #
31
+ # @see #attributes
32
+ def config_attributes(*attributes)
33
+ @config_attributes ||= []
34
+
35
+ if attributes.present?
36
+ new_attributes = attributes.map(&:to_sym) - @config_attributes
37
+ @config_attributes.concat(new_attributes)
38
+ end
39
+
40
+ if superclass.is_a?(ConfigClass)
41
+ (superclass.config_attributes.dup + @config_attributes).uniq
42
+ else
43
+ @config_attributes
44
+ end
45
+ end
46
+
47
+ # Add a config attribute
48
+ #
49
+ # This method adds an attribute (see {#config_attributes})
50
+ # and the method readers
51
+ #
52
+ # @overload add_configs(*names, default)
53
+ # @param names [Array<Symbol,String>] List of configuration names
54
+ # to be added
55
+ # @param default [Hash] Configurations that will receive a default
56
+ # value when not configured
57
+ #
58
+ # @return [MethodsBuilder]
59
+ #
60
+ # @see MethodsBuilder#build
61
+ #
62
+ # @example Adding configurations to config class
63
+ # class AppConfig
64
+ # extend Sinclair::ConfigClass
65
+ #
66
+ # add_configs :secret, app_name: 'MyApp'
67
+ # end
68
+ #
69
+ # config = AppConfig.new
70
+ #
71
+ # config.secret
72
+ # # return nil
73
+ #
74
+ # config.app_name
75
+ # # return 'MyApp'
76
+ #
77
+ # config_builder = Sinclair::ConfigBuilder.new(config)
78
+ #
79
+ # config_builder.secret '123abc'
80
+ # config_builder.app_name 'MySuperApp'
81
+ #
82
+ # config.secret
83
+ # # return '123abc'
84
+ #
85
+ # config.app_name
86
+ # # return 'MySuperApp'
87
+ def add_configs(*args)
88
+ Config::MethodsBuilder.new(self, *args).tap do |builder|
89
+ builder.build
90
+
91
+ config_attributes(*builder.config_names)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -16,13 +16,20 @@ class Sinclair
16
16
  # factory.config.equal?(config) # returns true
17
17
  # config.name # returns 'John'
18
18
  class ConfigFactory
19
- autoload :MethodsBuilder, 'sinclair/config_factory/methods_builder'
20
-
19
+ # @api private
20
+ # Deprecation warning message
21
+ # @see https://github.com/darthjee/sinclair/blob/master/WARNINGS.md#usage-of-custom-config-classes
22
+ CONFIG_CLASS_WARNING = 'Config class is expected to be ConfigClass. ' \
23
+ "In future releases this will be enforced.\n" \
24
+ 'see more on https://github.com/darthjee/sinclair/blob/master/WARNINGS.md#usage-of-custom-config-classes'
21
25
  # @param config_class [Class] configuration class to be used
22
26
  # @param config_attributes [Array<Symbol,String>] list of possible configurations
23
27
  def initialize(config_class: Class.new(Config), config_attributes: [])
24
28
  @config_class = config_class
25
29
  @config_attributes = config_attributes.dup
30
+
31
+ return if config_class.is_a?(ConfigClass)
32
+ warn CONFIG_CLASS_WARNING
26
33
  end
27
34
 
28
35
  # @api public
@@ -71,6 +78,8 @@ class Sinclair
71
78
  # {ConfigBuilder} is able to set those values when invoked
72
79
  #
73
80
  # @return [Array<Symbol>] all known config attributes
81
+ # @todo remove class check once only
82
+ # ConfigClass are accepted
74
83
  #
75
84
  # @example Adding configuration name
76
85
  # factory = Sinclair::ConfigFactory.new
@@ -84,9 +93,11 @@ class Sinclair
84
93
  # config.respond_to? :active
85
94
  # # returns true
86
95
  def add_configs(*args)
87
- builder = MethodsBuilder.new(config_class, *args)
88
-
89
- builder.build
96
+ builder = if config_class.is_a?(Sinclair::ConfigClass)
97
+ config_class.add_configs(*args)
98
+ else
99
+ Config::MethodsBuilder.build(config_class, *args)
100
+ end
90
101
 
91
102
  config_attributes.concat(builder.config_names.map(&:to_sym))
92
103
  end
@@ -107,6 +118,8 @@ class Sinclair
107
118
  #
108
119
  # @example Setting name on config
109
120
  # class MyConfig
121
+ # extend Sinclair::ConfigClass
122
+ #
110
123
  # attr_reader :name
111
124
  # end
112
125
  #
@@ -17,6 +17,14 @@ class Sinclair
17
17
  # @example (see #configurable_with)
18
18
  # @example (see #configurable_by)
19
19
  module Configurable
20
+ # @api private
21
+ # Deprecation warning message
22
+ # @see https://github.com/darthjee/sinclair/blob/master/WARNINGS.md#usage-of-custom-config-classes
23
+ CONFIG_CLASS_WARNING = 'Config classes attributes should ' \
24
+ 'be defined inside the class or through the usage of ' \
25
+ "configurable_with.\n" \
26
+ "In future releases this will be enforced.\n" \
27
+ 'see more on https://github.com/darthjee/sinclair/blob/master/WARNINGS.md#usage-of-custom-config-classes'
20
28
  # (see ConfigFactory#config)
21
29
  # @see ConfigFactory#config
22
30
  def config
@@ -116,7 +124,9 @@ class Sinclair
116
124
  # @return [ConfigFactory]
117
125
  #
118
126
  # @example Configured by custom config class
119
- # class MyServerConfig
127
+ # class MyServerConfig < Sinclair::Config
128
+ # config_attributes :host, :port
129
+ #
120
130
  # def url
121
131
  # if @port
122
132
  # "http://#{@host}:#{@port}"
@@ -129,7 +139,7 @@ class Sinclair
129
139
  # class Client
130
140
  # extend Sinclair::Configurable
131
141
  #
132
- # configurable_by MyServerConfig, with: %i[host port]
142
+ # configurable_by MyServerConfig
133
143
  # end
134
144
  #
135
145
  # Client.configure do
@@ -144,6 +154,8 @@ class Sinclair
144
154
  #
145
155
  # Client.config.url # returns 'http://interstella.com:8080'
146
156
  def configurable_by(config_class, with: [])
157
+ warn CONFIG_CLASS_WARNING if with.present?
158
+
147
159
  @config_factory = ConfigFactory.new(
148
160
  config_class: config_class,
149
161
  config_attributes: with.map(&:to_sym)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Sinclair
4
- VERSION = '1.3.4'
4
+ VERSION = '1.4.0'
5
5
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'spec_helper'
4
+
3
5
  describe Sinclair::ConfigBuilder do
4
6
  describe 'yard' do
5
7
  describe '#instance_eval' do
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::ConfigClass do
6
+ describe 'yard' do
7
+ describe '.add_configs' do
8
+ subject(:config) { AppConfig.new }
9
+
10
+ it 'has a secret configuration method' do
11
+ expect(config.secret).to be_nil
12
+ end
13
+
14
+ it 'has a app_name configuration method' do
15
+ expect(config.app_name).to eq('MyApp')
16
+ end
17
+
18
+ context 'when configured' do
19
+ let(:config_builder) do
20
+ Sinclair::ConfigBuilder.new(config)
21
+ end
22
+
23
+ before do
24
+ config_builder.secret '123abc'
25
+ config_builder.app_name 'MySuperApp'
26
+ end
27
+
28
+ it 'has a secret configuration method' do
29
+ expect(config.secret).to eq('123abc')
30
+ end
31
+
32
+ it 'has a app_name configuration method' do
33
+ expect(config.app_name).to eq('MySuperApp')
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'spec_helper'
4
+
3
5
  describe Sinclair::ConfigFactory do
4
6
  describe '#yard' do
5
7
  subject(:factory) do
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Config do
6
+ describe 'yard' do
7
+ describe '#to_hash' do
8
+ subject(:config) { config_class.new }
9
+
10
+ let(:config_class) { LoginConfig }
11
+
12
+ it 'returns all configs hash' do
13
+ expect(config.to_hash)
14
+ .to eq('password' => nil, 'username' => 'bob')
15
+ end
16
+
17
+ it 'returns all configs hash on as_json calls' do
18
+ expect(config.as_json)
19
+ .to eq('password' => nil, 'username' => 'bob')
20
+ end
21
+
22
+ it 'returns all configs json on to_json calls' do
23
+ expect(config.to_json)
24
+ .to eq('{"password":null,"username":"bob"}')
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'spec_helper'
4
+
3
5
  describe Sinclair::MethodDefinition do
4
6
  describe 'yard' do
5
7
  describe '#build' do
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Sinclair::ConfigFactory::MethodsBuilder do
5
+ describe Sinclair::Config::MethodsBuilder do
6
6
  describe '#build' do
7
7
  let(:config_class) { Class.new(Sinclair::Config) }
8
8
  let(:config) { config_class.new }
@@ -1,51 +1,88 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'spec_helper'
4
+
3
5
  describe Sinclair::ConfigBuilder do
4
6
  subject(:builder) { described_class.new(config, *config_attributes) }
5
7
 
6
- let(:config) { MyConfig.new }
8
+ let(:config_class) { MyConfig }
9
+ let(:config) { config_class.new }
7
10
  let(:config_attributes) { [:name] }
8
11
 
9
- it 'changes responds to given config' do
10
- expect(builder).to respond_to(:name)
11
- end
12
+ describe '#responds_to' do
13
+ it 'changes responds to given config' do
14
+ expect(builder).to respond_to(:name)
15
+ end
12
16
 
13
- it 'does not respond to othr configs' do
14
- expect(builder).not_to respond_to(:other_method)
15
- end
17
+ it 'does not respond to other configs' do
18
+ expect(builder).not_to respond_to(:other_method)
19
+ end
16
20
 
17
- it 'responds to other parent methods' do
18
- expect(builder).to respond_to(:to_s)
19
- end
21
+ it 'responds to other parent methods' do
22
+ expect(builder).to respond_to(:to_s)
23
+ end
24
+
25
+ context 'when it was instatiated without the attribute' do
26
+ let(:config_attributes) { [] }
27
+
28
+ it 'does not respond to given attribute' do
29
+ expect(builder).not_to respond_to(:name)
30
+ end
20
31
 
21
- context 'when builder was configuratd with the method called' do
22
- let(:config_attributes) { [:name] }
32
+ context 'when class is a ConfigClass and has been configured' do
33
+ let(:config_class) { Class.new(Sinclair::Config) }
23
34
 
24
- it 'sets the instance variable' do
25
- expect { builder.name 'John' }
26
- .to change(config, :name)
27
- .from(nil).to('John')
35
+ before { config_class.config_attributes(:name) }
36
+
37
+ it 'changes responds to given config' do
38
+ expect(builder).to respond_to(:name)
39
+ end
40
+ end
28
41
  end
29
42
  end
30
43
 
31
- context 'when builder was configuratd without the method called' do
32
- let(:config_attributes) { [] }
44
+ describe '#method_missing' do
45
+ context 'when builder was configuratd with the method called' do
46
+ let(:config_attributes) { [:name] }
33
47
 
34
- it 'does not set the instance variable and raises error' do
35
- expect { builder.name 'John' }
36
- .to raise_error(NoMethodError)
37
- .and not_change(config, :name)
48
+ it 'sets the instance variable' do
49
+ expect { builder.name 'John' }
50
+ .to change(config, :name)
51
+ .from(nil).to('John')
52
+ end
53
+ end
54
+
55
+ context 'when builder was configuratd without the method called' do
56
+ let(:config_attributes) { [] }
57
+
58
+ it 'does not set the instance variable and raises error' do
59
+ expect { builder.name 'John' }
60
+ .to raise_error(NoMethodError)
61
+ .and not_change(config, :name)
62
+ end
63
+
64
+ context 'with config class that has been configured with it' do
65
+ let(:config_class) { Class.new(Sinclair::Config) }
66
+
67
+ before { config_class.add_configs(:name) }
68
+
69
+ it 'sets the instance variable' do
70
+ expect { builder.name 'John' }
71
+ .to change(config, :name)
72
+ .from(nil).to('John')
73
+ end
74
+ end
38
75
  end
39
- end
40
76
 
41
- context 'when using a variable name from builder variables' do
42
- let(:config_attributes) { [:config] }
77
+ context 'when using a variable name from builder variables' do
78
+ let(:config_attributes) { [:config] }
43
79
 
44
- it 'sets the instance variable without changing builder instance variable' do
45
- expect { builder.config(key: 'value') }
46
- .to not_change { builder.instance_variable_get(:@config) }
47
- .and change(config, :config)
48
- .from(nil).to(key: 'value')
80
+ it 'sets the instance variable without changing builder instance variable' do
81
+ expect { builder.config(key: 'value') }
82
+ .to not_change { builder.instance_variable_get(:@config) }
83
+ .and change(config, :config)
84
+ .from(nil).to(key: 'value')
85
+ end
49
86
  end
50
87
  end
51
88
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::ConfigClass do
6
+ subject(:klass) do
7
+ Class.new { extend Sinclair::ConfigClass }
8
+ end
9
+
10
+ let(:child_klass) { Class.new(klass) }
11
+
12
+ let(:config) { klass.new }
13
+
14
+ describe '.config_attributes' do
15
+ it_behaves_like 'a config class with .config_attributes method'
16
+ end
17
+
18
+ describe '.add_configs' do
19
+ let(:setter_block) do
20
+ proc { |value| config.instance_variable_set(:@name, value) }
21
+ end
22
+
23
+ it_behaves_like 'a config methods builder adding config' do
24
+ let(:code_block) { proc { klass.add_configs(:name) } }
25
+
26
+ it 'sets nil value by default' do
27
+ code_block.call
28
+ expect(config.name).to be_nil
29
+ end
30
+
31
+ it 'adds attributes to class' do
32
+ expect(&code_block).to change(klass, :config_attributes)
33
+ .from([]).to(%i[name])
34
+ end
35
+ end
36
+
37
+ context 'when giving defaults' do
38
+ it_behaves_like 'a config methods builder adding config' do
39
+ let(:code_block) { proc { klass.add_configs(name: 'Bob') } }
40
+
41
+ it 'sets default value' do
42
+ code_block.call
43
+ expect(config.name).to eq('Bob')
44
+ end
45
+
46
+ it 'adds attributes to class' do
47
+ expect(&code_block).to change(klass, :config_attributes)
48
+ .from([]).to(%i[name])
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -28,13 +28,43 @@ describe Sinclair::ConfigFactory do
28
28
  end
29
29
  end
30
30
 
31
- context 'when initializing with custom config class' do
31
+ context 'when initializing with custom config class that extends config_class' do
32
+ subject(:factory) { described_class.new(config_class: MyConfig) }
33
+
34
+ it do
35
+ expect(factory.config).to be_a(MyConfig)
36
+ end
37
+
38
+ context 'when calling after reset_config' do
39
+ before { factory.reset_config }
40
+
41
+ it do
42
+ expect(factory.config).to be_a(MyConfig)
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'when initializing with custom config class that does not extend config_class' do
32
48
  subject(:factory) { described_class.new(config_class: DummyConfig) }
33
49
 
50
+ # rubocop:disable RSpec/AnyInstance
51
+ before do
52
+ allow_any_instance_of(described_class)
53
+ .to receive(:warn)
54
+ end
55
+ # rubocop:enable RSpec/AnyInstance
56
+
34
57
  it do
35
58
  expect(factory.config).to be_a(DummyConfig)
36
59
  end
37
60
 
61
+ it 'warns about class use' do
62
+ factory.config
63
+
64
+ expect(factory).to have_received(:warn)
65
+ .with described_class::CONFIG_CLASS_WARNING
66
+ end
67
+
38
68
  context 'when calling after reset_config' do
39
69
  before { factory.reset_config }
40
70
 
@@ -60,7 +90,7 @@ describe Sinclair::ConfigFactory do
60
90
  end
61
91
 
62
92
  context 'when initializing with custom config class' do
63
- subject(:factory) { described_class.new(config_class: DummyConfig) }
93
+ subject(:factory) { described_class.new(config_class: MyConfig) }
64
94
 
65
95
  it 'reset_configs instance' do
66
96
  expect { factory.reset_config }
@@ -119,6 +149,55 @@ describe Sinclair::ConfigFactory do
119
149
  end
120
150
  end
121
151
  end
152
+
153
+ context 'when config class was set from common class' do
154
+ subject(:factory) { described_class.new(config_class: config_class) }
155
+
156
+ let(:config_class) { Class.new }
157
+
158
+ # rubocop:disable RSpec/AnyInstance
159
+ before do
160
+ allow_any_instance_of(described_class)
161
+ .to receive(:warn)
162
+ end
163
+ # rubocop:enable RSpec/AnyInstance
164
+
165
+ it_behaves_like 'a config factory adding config' do
166
+ let(:method_call) { proc { add_configs(:name) } }
167
+
168
+ it 'does not set a default value' do
169
+ code_block.call
170
+
171
+ expect(factory.config.name).to be_nil
172
+ end
173
+
174
+ it 'warns about class use' do
175
+ code_block.call
176
+
177
+ expect(factory).to have_received(:warn)
178
+ .with described_class::CONFIG_CLASS_WARNING
179
+ end
180
+ end
181
+
182
+ context 'when passing a hash' do
183
+ it_behaves_like 'a config factory adding config' do
184
+ let(:method_call) { proc { add_configs(name: 'Bobby') } }
185
+
186
+ it 'sets a default value' do
187
+ code_block.call
188
+
189
+ expect(factory.config.name).to eq('Bobby')
190
+ end
191
+
192
+ it 'warns about class use' do
193
+ code_block.call
194
+
195
+ expect(factory).to have_received(:warn)
196
+ .with described_class::CONFIG_CLASS_WARNING
197
+ end
198
+ end
199
+ end
200
+ end
122
201
  end
123
202
 
124
203
  describe '#configure' do
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Config do
6
+ subject(:config) { klass.new }
7
+
8
+ let(:child_klass) { Class.new(klass) }
9
+
10
+ let(:klass) { Class.new(described_class) }
11
+
12
+ describe '.config_attributes' do
13
+ it_behaves_like 'a config class with .config_attributes method'
14
+ end
15
+
16
+ describe '#to_hash' do
17
+ it 'returns empty hash' do
18
+ expect(config.as_json).to eq({})
19
+ end
20
+
21
+ context 'when attributes have been defined' do
22
+ before do
23
+ klass.config_attributes(:username, :password)
24
+ klass.attr_reader(:username, :password)
25
+ end
26
+
27
+ it 'uses given attributes to create json' do
28
+ expect(config.as_json)
29
+ .to eq('username' => nil, 'password' => nil)
30
+ end
31
+
32
+ context 'when the method called sets instance variable' do
33
+ before do
34
+ klass.add_configs(name: 'John')
35
+ end
36
+
37
+ it 'returns the value' do
38
+ expect(config.as_json).to eq(
39
+ 'name' => 'John', 'username' => nil, 'password' => nil
40
+ )
41
+ end
42
+ end
43
+ end
44
+
45
+ context 'when passing options' do
46
+ before do
47
+ klass.add_configs(username: 'john', password: '123456')
48
+ end
49
+
50
+ it 'accpes only option' do
51
+ expect(config.as_json(only: 'username'))
52
+ .to eq('username' => 'john')
53
+ end
54
+
55
+ it 'accepts except options' do
56
+ expect(config.as_json(except: 'password'))
57
+ .to eq('username' => 'john')
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#to_json' do
63
+ it 'returns empty json' do
64
+ expect(config.to_json).to eq('{}')
65
+ end
66
+
67
+ context 'when attributes have been defined' do
68
+ before do
69
+ klass.config_attributes(:username, :password)
70
+ klass.attr_reader(:username, :password)
71
+ end
72
+
73
+ it 'uses given attributes to create json' do
74
+ expect(config.to_json)
75
+ .to eq('{"username":null,"password":null}')
76
+ end
77
+
78
+ context 'when the method called sets instance variable' do
79
+ before do
80
+ klass.add_configs(name: 'John')
81
+ end
82
+
83
+ it 'returns the value' do
84
+ expect(config.to_json).to eq(
85
+ '{"username":null,"password":null,"name":"John"}'
86
+ )
87
+ end
88
+ end
89
+ end
90
+
91
+ context 'when passing options' do
92
+ before do
93
+ klass.add_configs(username: 'john', password: '123456')
94
+ end
95
+
96
+ it 'accpes only option' do
97
+ expect(config.to_json(only: 'username'))
98
+ .to eq('{"username":"john"}')
99
+ end
100
+
101
+ it 'accepts except options' do
102
+ expect(config.to_json(except: 'password'))
103
+ .to eq('{"username":"john"}')
104
+ end
105
+ end
106
+ end
107
+ end
@@ -123,6 +123,14 @@ describe Sinclair::Configurable do
123
123
  end
124
124
  end
125
125
 
126
+ before do
127
+ # rubocop:disable RSpec/SubjectStub
128
+ allow(configurable)
129
+ .to receive(:warn)
130
+ .with(described_class::CONFIG_CLASS_WARNING)
131
+ # rubocop:enable RSpec/SubjectStub
132
+ end
133
+
126
134
  it 'does not add symbol methods config object' do
127
135
  expect(&block)
128
136
  .not_to add_method(:host).to(configurable.config)
@@ -133,6 +141,11 @@ describe Sinclair::Configurable do
133
141
  .not_to add_method(:port).to(configurable.config)
134
142
  end
135
143
 
144
+ it 'sends deprecation warning' do
145
+ block.call
146
+ expect(configurable).to have_received(:warn)
147
+ end
148
+
136
149
  it 'does not raises error on configuration of given symbol attributes' do
137
150
  block.call
138
151
 
@@ -177,6 +190,11 @@ describe Sinclair::Configurable do
177
190
  let(:config_class) { ServerConfig }
178
191
 
179
192
  before do
193
+ # rubocop:disable RSpec/SubjectStub
194
+ allow(configurable)
195
+ .to receive(:warn)
196
+ .with(described_class::CONFIG_CLASS_WARNING)
197
+ # rubocop:enable RSpec/SubjectStub
180
198
  configurable.send(
181
199
  :configurable_by, config_class, with: [:host, 'port']
182
200
  )
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AppConfig
4
+ extend Sinclair::ConfigClass
5
+
6
+ add_configs :secret, app_name: 'MyApp'
7
+ end
@@ -5,5 +5,5 @@ require './spec/support/models/my_server_config'
5
5
  class Client
6
6
  extend Sinclair::Configurable
7
7
 
8
- configurable_by MyServerConfig, with: %i[host port]
8
+ configurable_by MyServerConfig
9
9
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LoginConfig < Sinclair::Config
4
+ add_configs :password, username: 'bob'
5
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class MyConfig
4
+ extend Sinclair::ConfigClass
5
+
4
6
  attr_reader :name, :config
5
7
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class MyServerConfig
3
+ class MyServerConfig < Sinclair::Config
4
+ config_attributes :host, :port
5
+
4
6
  def url
5
7
  if @port
6
8
  "http://#{@host}:#{@port}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class ServerConfig
3
+ class ServerConfig < Sinclair::Config
4
4
  attr_reader :host, :port
5
5
  end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_examples 'a config class with .config_attributes method' do
4
+ let(:attributes) { [:username, 'password', :key] }
5
+
6
+ describe 'setting the attributes' do
7
+ it 'adds symbol attributes to class attributes' do
8
+ expect { klass.config_attributes(*attributes) }
9
+ .to change(klass, :config_attributes)
10
+ .from([]).to(%i[username password key])
11
+ end
12
+
13
+ context 'when class already has attributes' do
14
+ before do
15
+ klass.config_attributes('email')
16
+ end
17
+
18
+ it 'adds new attributes' do
19
+ expect { klass.config_attributes(*attributes) }
20
+ .to change(klass, :config_attributes)
21
+ .from([:email]).to(%i[email username password key])
22
+ end
23
+ end
24
+
25
+ context 'when attributes have already been added' do
26
+ before do
27
+ klass.config_attributes('username', :password)
28
+ end
29
+
30
+ it 'adds new attributes' do
31
+ expect { klass.config_attributes(*attributes) }
32
+ .to change(klass, :config_attributes)
33
+ .from(%i[username password])
34
+ .to(%i[username password key])
35
+ end
36
+ end
37
+
38
+ context 'when there is a child class' do
39
+ it 'adds attributes to child class' do
40
+ expect { klass.config_attributes(*attributes) }
41
+ .to change(child_klass, :config_attributes)
42
+ .from([]).to(%i[username password key])
43
+ end
44
+
45
+ context 'when child class already has attributes' do
46
+ before do
47
+ child_klass.config_attributes('email')
48
+ end
49
+
50
+ it 'adds new attributes to child class' do
51
+ expect { klass.config_attributes(*attributes) }
52
+ .to change(child_klass, :config_attributes)
53
+ .from([:email]).to(%i[username password key email])
54
+ end
55
+ end
56
+
57
+ context 'when child class already has one of the attributes' do
58
+ before do
59
+ child_klass.config_attributes(:email, 'username')
60
+ end
61
+
62
+ it 'adds new attributes to child class' do
63
+ expect { klass.config_attributes(*attributes) }
64
+ .to change(child_klass, :config_attributes)
65
+ .from(%i[email username]).to(%i[username password key email])
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'when there is a parent class' do
71
+ it 'does not add attributes to parent class' do
72
+ expect { child_klass.config_attributes(*attributes) }
73
+ .not_to change(klass, :config_attributes)
74
+ end
75
+
76
+ context 'when parent already has attributes' do
77
+ before do
78
+ klass.config_attributes(:email, 'username')
79
+ end
80
+
81
+ it 'adds only attributes that had not been defined before' do
82
+ expect { child_klass.config_attributes(*attributes) }
83
+ .to change(child_klass, :config_attributes)
84
+ .from(%i[email username]).to(%i[email username password key])
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ describe 'retrieving the attributes' do
91
+ let(:attributes) { [:username, 'password', :key] }
92
+
93
+ context 'when attributes have been added' do
94
+ before { klass.config_attributes(*attributes) }
95
+
96
+ it 'returns added attributes' do
97
+ expect(klass.config_attributes).to eq(%i[username password key])
98
+ end
99
+ end
100
+
101
+ context 'when parent class has attributes' do
102
+ before { klass.config_attributes(*attributes) }
103
+
104
+ it 'returns parents attributes also' do
105
+ expect(child_klass.config_attributes).to eq(%i[username password key])
106
+ end
107
+ end
108
+
109
+ context 'when parent class changes its attributes' do
110
+ it 'returns adds new attributes to self' do
111
+ expect { klass.config_attributes(*attributes) }
112
+ .to change(child_klass, :config_attributes)
113
+ .from([]).to(%i[username password key])
114
+ end
115
+ end
116
+ end
117
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinclair
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.4
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DarthJee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-21 00:00:00.000000000 Z
11
+ date: 2019-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -181,15 +181,17 @@ files:
181
181
  - LICENSE
182
182
  - README.md
183
183
  - Rakefile
184
+ - WARNINGS.md
184
185
  - config/rubycritc.rb
185
186
  - config/yardstick.rb
186
187
  - config/yardstick.yml
187
188
  - docker-compose.yml
188
189
  - lib/sinclair.rb
189
190
  - lib/sinclair/config.rb
191
+ - lib/sinclair/config/methods_builder.rb
190
192
  - lib/sinclair/config_builder.rb
193
+ - lib/sinclair/config_class.rb
191
194
  - lib/sinclair/config_factory.rb
192
- - lib/sinclair/config_factory/methods_builder.rb
193
195
  - lib/sinclair/configurable.rb
194
196
  - lib/sinclair/matchers.rb
195
197
  - lib/sinclair/matchers/add_method.rb
@@ -211,7 +213,9 @@ files:
211
213
  - spec/integration/sinclair/matchers_spec.rb
212
214
  - spec/integration/yard/my_builder_spec.rb
213
215
  - spec/integration/yard/sinclair/config_builder_spec.rb
216
+ - spec/integration/yard/sinclair/config_class_spec.rb
214
217
  - spec/integration/yard/sinclair/config_factory_spec.rb
218
+ - spec/integration/yard/sinclair/config_spec.rb
215
219
  - spec/integration/yard/sinclair/configurable_spec.rb
216
220
  - spec/integration/yard/sinclair/matchers/add_method_spec.rb
217
221
  - spec/integration/yard/sinclair/matchers/add_method_to_spec.rb
@@ -220,9 +224,11 @@ files:
220
224
  - spec/integration/yard/sinclair/method_definition_spec.rb
221
225
  - spec/integration/yard/sinclair/options_parser_spec.rb
222
226
  - spec/integration/yard/sinclair_spec.rb
227
+ - spec/lib/sinclair/config/methods_builder_spec.rb
223
228
  - spec/lib/sinclair/config_builder_spec.rb
224
- - spec/lib/sinclair/config_factory/methods_builder_spec.rb
229
+ - spec/lib/sinclair/config_class_spec.rb
225
230
  - spec/lib/sinclair/config_factory_spec.rb
231
+ - spec/lib/sinclair/config_spec.rb
226
232
  - spec/lib/sinclair/configurable_spec.rb
227
233
  - spec/lib/sinclair/matchers/add_method_spec.rb
228
234
  - spec/lib/sinclair/matchers/add_method_to_spec.rb
@@ -234,6 +240,7 @@ files:
234
240
  - spec/lib/sinclair_spec.rb
235
241
  - spec/spec_helper.rb
236
242
  - spec/support/fixture_helpers.rb
243
+ - spec/support/models/app_config.rb
237
244
  - spec/support/models/client.rb
238
245
  - spec/support/models/default_value.rb
239
246
  - spec/support/models/default_value_builder.rb
@@ -245,6 +252,7 @@ files:
245
252
  - spec/support/models/http_json_model.rb
246
253
  - spec/support/models/http_person.rb
247
254
  - spec/support/models/initial_valuer.rb
255
+ - spec/support/models/login_config.rb
248
256
  - spec/support/models/my_builder.rb
249
257
  - spec/support/models/my_class.rb
250
258
  - spec/support/models/my_concern.rb
@@ -257,6 +265,7 @@ files:
257
265
  - spec/support/models/server.rb
258
266
  - spec/support/models/server_config.rb
259
267
  - spec/support/models/validator_builder.rb
268
+ - spec/support/shared_examples/config.rb
260
269
  - spec/support/shared_examples/config_factory.rb
261
270
  - spec/support/shared_examples/method_definition.rb
262
271
  homepage: https://github.com/darthjee/sinclair