sinclair 2.0.0 → 2.1.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/check_specs.yml +2 -0
  4. data/config/yardstick.yml +5 -1
  5. data/lib/sinclair/chain_settable.rb +83 -0
  6. data/lib/sinclair/class_methods.rb +2 -2
  7. data/lib/sinclair/core_ext/object.rb +39 -0
  8. data/lib/sinclair/env_settable.rb +35 -17
  9. data/lib/sinclair/settable/builder.rb +140 -0
  10. data/lib/sinclair/settable/caster.rb +61 -0
  11. data/lib/sinclair/settable/class_methods.rb +80 -0
  12. data/lib/sinclair/settable.rb +115 -0
  13. data/lib/sinclair/version.rb +1 -1
  14. data/lib/sinclair.rb +2 -0
  15. data/spec/integration/yard/sinclair/env_settable_spec.rb +6 -25
  16. data/spec/integration/yard/sinclair/settable/caster_spec.rb +41 -0
  17. data/spec/integration/yard/sinclair/settable/class_methods_spec.rb +11 -0
  18. data/spec/integration/yard/sinclair/settable_spec.rb +26 -0
  19. data/spec/lib/sinclair/chain_settable_spec.rb +76 -0
  20. data/spec/lib/sinclair/class_methods_spec.rb +24 -4
  21. data/spec/lib/sinclair/env_settable_spec.rb +4 -2
  22. data/spec/lib/sinclair/settable/builder_spec.rb +47 -0
  23. data/spec/lib/sinclair/settable_spec.rb +46 -0
  24. data/spec/support/files/config.yml +1 -0
  25. data/spec/support/models/app_client.rb +1 -0
  26. data/spec/support/models/complex_builder.rb +17 -0
  27. data/spec/support/models/hash_app_client.rb +14 -0
  28. data/spec/support/models/hash_settable.rb +10 -0
  29. data/spec/support/models/json_env_settings.rb +18 -0
  30. data/spec/support/models/math_env_settings.rb +17 -0
  31. data/spec/support/models/my_app_client.rb +1 -0
  32. data/spec/support/models/non_default_app_client.rb +8 -0
  33. data/spec/support/models/yaml_file_settings.rb +26 -0
  34. data/spec/support/shared_examples/settable.rb +86 -0
  35. metadata +22 -5
  36. data/lib/sinclair/env_settable/builder.rb +0 -61
  37. data/spec/lib/sinclair/env_settable/builder_spec.rb +0 -35
  38. data/spec/support/shared_examples/env_settable.rb +0 -43
data/lib/sinclair.rb CHANGED
@@ -85,6 +85,7 @@ class Sinclair
85
85
  autoload :VERSION, 'sinclair/version'
86
86
  autoload :Caster, 'sinclair/caster'
87
87
  autoload :ClassMethods, 'sinclair/class_methods'
88
+ autoload :ChainSettable, 'sinclair/chain_settable'
88
89
  autoload :Config, 'sinclair/config'
89
90
  autoload :ConfigBuilder, 'sinclair/config_builder'
90
91
  autoload :ConfigClass, 'sinclair/config_class'
@@ -100,6 +101,7 @@ class Sinclair
100
101
  autoload :MethodDefinitions, 'sinclair/method_definitions'
101
102
  autoload :Model, 'sinclair/model'
102
103
  autoload :Options, 'sinclair/options'
104
+ autoload :Settable, 'sinclair/settable'
103
105
 
104
106
  include OptionsParser
105
107
  extend ClassMethods
@@ -4,42 +4,23 @@ require 'spec_helper'
4
4
 
5
5
  describe Sinclair::EnvSettable do
6
6
  describe '#yard' do
7
- subject(:settable) { Class.new(MyAppClient) }
7
+ subject(:settable) { MyAppClient }
8
8
 
9
9
  before do
10
10
  ENV['MY_APP_USERNAME'] = 'my_login'
11
+ ENV['MY_APP_PORT'] = '8080'
11
12
  end
12
13
 
13
14
  after do
14
15
  ENV.delete('MY_APP_USERNAME')
16
+ ENV.delete('MY_APP_PORT')
15
17
  end
16
18
 
17
- it 'retrieves username from env' do
19
+ it 'retrieves data from env' do
18
20
  expect(settable.username).to eq('my_login')
19
- end
20
-
21
- it 'retrieves password from env' do
22
21
  expect(settable.password).to be_nil
23
- end
24
-
25
- context 'when defining defaults' do
26
- it 'returns default value' do
27
- expect(settable.host).to eq('my-host.com')
28
- end
29
-
30
- context 'when setting the env variable' do
31
- before do
32
- ENV['MY_APP_HOST'] = 'other-host.com'
33
- end
34
-
35
- after do
36
- ENV.delete('MY_APP_HOST')
37
- end
38
-
39
- it 'retrieves host from env' do
40
- expect(settable.host).to eq('other-host.com')
41
- end
42
- end
22
+ expect(settable.host).to eq('my-host.com')
23
+ expect(settable.port).to eq(8080)
43
24
  end
44
25
  end
45
26
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Settable::Caster do
6
+ describe '#yard' do
7
+ describe 'altering base caster' do
8
+ subject(:settable) { MathEnvSettings }
9
+
10
+ before do
11
+ ENV['MATH_VALUE'] = '80'
12
+ end
13
+
14
+ after do
15
+ ENV.delete('MATH_VALUE')
16
+ end
17
+
18
+ it 'retrieves data from env' do
19
+ expect(settable.value).to eq(6400.0)
20
+ end
21
+ end
22
+
23
+ describe 'creating a new caster' do
24
+ subject(:settable) { JsonEnvSettings }
25
+
26
+ let(:hash) { { key: 'value' } }
27
+
28
+ before do
29
+ ENV['JSON_CONFIG'] = hash.to_json
30
+ end
31
+
32
+ after do
33
+ ENV.delete('JSON_CONFIG')
34
+ end
35
+
36
+ it 'retrieves data from env' do
37
+ expect(settable.config).to eq(hash.stringify_keys)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Settable::ClassMethods do
6
+ describe 'yard #read_with' do
7
+ it 'reads the settings from the file' do
8
+ expect(YamlFileSettings.timeout).to eq(10)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Settable do
6
+ describe '#yard' do
7
+ subject(:settable) { HashAppClient }
8
+
9
+ before do
10
+ HashAppClient::HASH[:username] = 'my_login'
11
+ HashAppClient::HASH[:port] = '8080'
12
+ end
13
+
14
+ after do
15
+ HashAppClient::HASH.delete(:username)
16
+ HashAppClient::HASH.delete(:port)
17
+ end
18
+
19
+ it 'retrieves data from env' do
20
+ expect(settable.username).to eq('my_login')
21
+ expect(settable.password).to be_nil
22
+ expect(settable.host).to eq('my-host.com')
23
+ expect(settable.port).to eq(8080)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::ChainSettable do
6
+ subject(:settable) do
7
+ options = options_hash
8
+
9
+ Class.new do
10
+ extend Sinclair::ChainSettable
11
+
12
+ source :app_client, Class.new(NonDefaultAppClient)
13
+ source :my_app_client, Class.new(MyAppClient)
14
+
15
+ setting_with_options :username, :password, :host, :port, **options
16
+ end
17
+ end
18
+
19
+ let(:options_hash) { {} }
20
+ let(:username) { 'my_login' }
21
+ let(:password) { Random.rand(10_000).to_s }
22
+
23
+ context 'when the first setting finds the data' do
24
+ let(:username_key) { 'USERNAME' }
25
+ let(:password_key) { 'PASSWORD' }
26
+ let(:host_key) { 'HOST' }
27
+ let(:port_key) { 'PORT' }
28
+
29
+ it_behaves_like 'settings reading'
30
+ end
31
+
32
+ context 'when the second setting finds the data' do
33
+ let(:username_key) { 'MY_APP_USERNAME' }
34
+ let(:password_key) { 'MY_APP_PASSWORD' }
35
+ let(:host_key) { 'MY_APP_HOST' }
36
+ let(:port_key) { 'MY_APP_PORT' }
37
+
38
+ it_behaves_like 'settings reading'
39
+ end
40
+
41
+ context 'when both have a value' do
42
+ let(:first_username) { 'first_username' }
43
+ let(:second_username) { 'second_username' }
44
+
45
+ before do
46
+ ENV['USERNAME'] = first_username
47
+ ENV['MY_APP_USERNAME'] = second_username
48
+ end
49
+
50
+ after do
51
+ ENV.delete('USERNAME')
52
+ ENV.delete('MY_APP_USERNAME')
53
+ end
54
+
55
+ it 'returns the first value' do
56
+ expect(settable.username).to eq(first_username)
57
+ end
58
+
59
+ context 'when passing a different source as options' do
60
+ let(:options_hash) { { sources: %i[my_app_client app_client] } }
61
+
62
+ it 'returns the second value' do
63
+ expect(settable.username).to eq(second_username)
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'when none has value' do
69
+ let(:default) { 'some_default_username' }
70
+ let(:options_hash) { { default: default } }
71
+
72
+ it 'returns the first value' do
73
+ expect(settable.username).to eq(default)
74
+ end
75
+ end
76
+ end
@@ -10,7 +10,7 @@ describe Sinclair::ClassMethods do
10
10
  let(:dummy_class) { Class.new }
11
11
  let(:builder_class) { Sinclair }
12
12
 
13
- describe '#build' do
13
+ describe '.build' do
14
14
  let(:block) do
15
15
  method_name = :some_method
16
16
  value = 1
@@ -21,13 +21,13 @@ describe Sinclair::ClassMethods do
21
21
  end
22
22
 
23
23
  it 'executes the block and builds' do
24
- expect { builder_class.build(dummy_class, options, &block) }
24
+ expect { builder_class.build(dummy_class, **options, &block) }
25
25
  .to add_method(:some_method).to(dummy_class)
26
26
  end
27
27
 
28
28
  context 'when the method is built and called' do
29
29
  before do
30
- builder_class.build(dummy_class, options, &block)
30
+ builder_class.build(dummy_class, **options, &block)
31
31
  end
32
32
 
33
33
  it 'returns the value' do
@@ -46,9 +46,29 @@ describe Sinclair::ClassMethods do
46
46
  end
47
47
 
48
48
  it 'executes the block and builds' do
49
- expect { builder_class.build(dummy_class, options) }
49
+ expect { builder_class.build(dummy_class, **options) }
50
50
  .to add_method(:some_method).to(dummy_class)
51
51
  end
52
52
  end
53
+
54
+ context 'when a block is given and initialization contains more arguments' do
55
+ let(:builder_class) { ComplexBuilder }
56
+ let(:value) { Random.rand(10..30) }
57
+ let(:power) { Random.rand(3..5) }
58
+ let(:result) { value**power }
59
+
60
+ it 'executes the block and builds' do
61
+ expect { builder_class.build(dummy_class, value, power, &:add_default) }
62
+ .to add_method(:result).to(dummy_class)
63
+ end
64
+
65
+ context 'when the method is called' do
66
+ before { builder_class.build(dummy_class, value, power, &:add_default) }
67
+
68
+ it 'returns the evaluated value' do
69
+ expect(instance.result).to eq(result)
70
+ end
71
+ end
72
+ end
53
73
  end
54
74
  end
@@ -11,8 +11,9 @@ describe Sinclair::EnvSettable do
11
11
  let(:username_key) { 'USERNAME' }
12
12
  let(:password_key) { 'PASSWORD' }
13
13
  let(:host_key) { 'HOST' }
14
+ let(:port_key) { 'PORT' }
14
15
 
15
- it_behaves_like 'settings reading from env'
16
+ it_behaves_like 'settings reading'
16
17
 
17
18
  context 'when defining a prefix' do
18
19
  subject(:settable) { Class.new(MyAppClient) }
@@ -20,7 +21,8 @@ describe Sinclair::EnvSettable do
20
21
  let(:username_key) { 'MY_APP_USERNAME' }
21
22
  let(:password_key) { 'MY_APP_PASSWORD' }
22
23
  let(:host_key) { 'MY_APP_HOST' }
24
+ let(:port_key) { 'MY_APP_PORT' }
23
25
 
24
- it_behaves_like 'settings reading from env'
26
+ it_behaves_like 'settings reading'
25
27
  end
26
28
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Settable::Builder do
6
+ subject(:settable) do
7
+ setting_prefix = prefix
8
+
9
+ Class.new do
10
+ extend Sinclair::EnvSettable
11
+
12
+ settings_prefix setting_prefix
13
+ end
14
+ end
15
+
16
+ let(:username) { 'my_login' }
17
+ let(:password) { Random.rand(10_000).to_s }
18
+ let(:settings) { %i[username password] }
19
+ let(:options) { { prefix: prefix } }
20
+
21
+ let(:builder) do
22
+ described_class.new(settable, *settings, **options)
23
+ end
24
+
25
+ before { builder.build }
26
+
27
+ context 'when not using prefix' do
28
+ let(:prefix) { nil }
29
+
30
+ let(:username_key) { 'USERNAME' }
31
+ let(:password_key) { 'PASSWORD' }
32
+ let(:host_key) { 'HOST' }
33
+ let(:port_key) { 'PORT' }
34
+
35
+ it_behaves_like 'settings reading'
36
+ end
37
+
38
+ context 'when defining a prefix' do
39
+ let(:prefix) { 'MY_APP' }
40
+ let(:username_key) { 'MY_APP_USERNAME' }
41
+ let(:password_key) { 'MY_APP_PASSWORD' }
42
+ let(:host_key) { 'MY_APP_HOST' }
43
+ let(:port_key) { 'MY_APP_PORT' }
44
+
45
+ it_behaves_like 'settings reading'
46
+ end
47
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Settable do
6
+ context 'when reading from env' do
7
+ subject(:settable) { Class.new(AppClient) }
8
+
9
+ let(:username) { 'my_login' }
10
+ let(:password) { Random.rand(10_000).to_s }
11
+
12
+ let(:username_key) { 'USERNAME' }
13
+ let(:password_key) { 'PASSWORD' }
14
+ let(:host_key) { 'HOST' }
15
+ let(:port_key) { 'PORT' }
16
+
17
+ it_behaves_like 'settings reading'
18
+
19
+ context 'when defining a prefix' do
20
+ subject(:settable) { Class.new(MyAppClient) }
21
+
22
+ let(:username_key) { 'MY_APP_USERNAME' }
23
+ let(:password_key) { 'MY_APP_PASSWORD' }
24
+ let(:host_key) { 'MY_APP_HOST' }
25
+ let(:port_key) { 'MY_APP_PORT' }
26
+
27
+ it_behaves_like 'settings reading'
28
+ end
29
+ end
30
+
31
+ context 'when reading from a hash constant' do
32
+ subject(:settable) { Class.new(HashAppClient) }
33
+
34
+ let(:username) { 'my_login' }
35
+ let(:password) { Random.rand(10_000).to_s }
36
+
37
+ let(:username_key) { :username }
38
+ let(:password_key) { :password }
39
+ let(:host_key) { :host }
40
+ let(:port_key) { :port }
41
+
42
+ it_behaves_like 'settings reading' do
43
+ let(:env_hash) { HashAppClient::HASH }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1 @@
1
+ timeout: 10
@@ -4,4 +4,5 @@ class AppClient
4
4
  extend Sinclair::EnvSettable
5
5
 
6
6
  with_settings :username, :password, host: 'my-host.com'
7
+ setting_with_options :port, type: :integer
7
8
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ComplexBuilder < Sinclair
4
+ def initialize(klass, value, power)
5
+ @value = value
6
+ @power = power
7
+
8
+ super(klass)
9
+ end
10
+
11
+ def add_default
12
+ val = @value
13
+ pow = @power
14
+
15
+ add_method(:result) { val**pow }
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require './spec/support/models/hash_settable'
4
+
5
+ class HashAppClient
6
+ extend HashSettable
7
+
8
+ # rubocop:disable Style/MutableConstant
9
+ HASH = {}
10
+ # rubocop:enable Style/MutableConstant
11
+
12
+ with_settings :username, :password, host: 'my-host.com'
13
+ setting_with_options :port, type: :integer
14
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HashSettable
4
+ extend Sinclair::Settable::ClassMethods
5
+ include Sinclair::Settable
6
+
7
+ read_with do |key|
8
+ self::HASH[key]
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JsonEnvSettable
4
+ include Sinclair::EnvSettable
5
+ extend Sinclair::Settable::ClassMethods
6
+
7
+ class Caster < Sinclair::Settable::Caster
8
+ cast_with(:json) { |value| JSON.parse(value) }
9
+ end
10
+ end
11
+
12
+ class JsonEnvSettings
13
+ extend JsonEnvSettable
14
+
15
+ settings_prefix 'JSON'
16
+
17
+ setting_with_options :config, type: :json
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ module Settable
5
+ class Caster
6
+ cast_with(:squared) { |value| value.to_f**2 }
7
+ end
8
+ end
9
+ end
10
+
11
+ class MathEnvSettings
12
+ extend Sinclair::EnvSettable
13
+
14
+ settings_prefix 'MATH'
15
+
16
+ setting_with_options :value, type: :squared
17
+ end
@@ -6,4 +6,5 @@ class MyAppClient
6
6
  settings_prefix 'MY_APP'
7
7
 
8
8
  with_settings :username, :password, host: 'my-host.com'
9
+ setting_with_options :port, type: :integer
9
10
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NonDefaultAppClient
4
+ extend Sinclair::EnvSettable
5
+
6
+ with_settings :username, :password, :host
7
+ setting_with_options :port, type: :integer
8
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YamlFileSettable
4
+ include Sinclair::Settable
5
+ extend Sinclair::Settable::ClassMethods
6
+
7
+ read_with do |key|
8
+ loaded_yaml[key.to_s]
9
+ end
10
+
11
+ def loaded_yaml
12
+ YAML.load_file(setting_file)
13
+ end
14
+
15
+ def setting_file(file_path = @setting_file)
16
+ @setting_file = file_path
17
+ end
18
+ end
19
+
20
+ class YamlFileSettings
21
+ extend YamlFileSettable
22
+
23
+ setting_file './spec/support/files/config.yml'
24
+
25
+ setting_with_options :timeout, default: 30
26
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ shared_examples 'settings reading' do
4
+ let(:env_hash) { ENV }
5
+
6
+ context 'when the key is not set' do
7
+ it 'retrieves username from env' do
8
+ expect(settable.username).to be_nil
9
+ end
10
+
11
+ it 'retrieves password from env' do
12
+ expect(settable.password).to be_nil
13
+ end
14
+ end
15
+
16
+ context 'when the key is set' do
17
+ before do
18
+ env_hash[username_key] = username
19
+ env_hash[password_key] = password
20
+ end
21
+
22
+ after do
23
+ env_hash.delete(username_key)
24
+ env_hash.delete(password_key)
25
+ end
26
+
27
+ it 'retrieves username from env' do
28
+ expect(settable.username).to eq(username)
29
+ end
30
+
31
+ it 'retrieves password from env' do
32
+ expect(settable.password).to eq(password)
33
+ end
34
+ end
35
+
36
+ context 'when defining defaults' do
37
+ let(:settings) { %i[host] }
38
+ let(:options) { { prefix: prefix, default: 'my-host.com' } }
39
+
40
+ it 'returns default value' do
41
+ expect(settable.host).to eq('my-host.com')
42
+ end
43
+
44
+ context 'when setting the env variable' do
45
+ let(:other_host) { 'other-host.com' }
46
+
47
+ before do
48
+ env_hash[host_key] = other_host
49
+ end
50
+
51
+ after do
52
+ env_hash.delete(host_key)
53
+ end
54
+
55
+ it 'retrieves host from env' do
56
+ expect(settable.host).to eq(other_host)
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'when defining a type' do
62
+ let(:settings) { %i[port] }
63
+ let(:options) { { prefix: prefix, type: :integer } }
64
+ let(:port) { Random.rand(10..100) }
65
+
66
+ context 'when the key is not set' do
67
+ it 'retrieves port and cast to string' do
68
+ expect(settable.port).to be_nil
69
+ end
70
+ end
71
+
72
+ context 'when the key is set' do
73
+ before do
74
+ env_hash[port_key] = port.to_s
75
+ end
76
+
77
+ after do
78
+ env_hash.delete(port_key)
79
+ end
80
+
81
+ it 'retrieves port and cast to string' do
82
+ expect(settable.port).to eq(port)
83
+ end
84
+ end
85
+ end
86
+ end