sinclair 2.0.0 → 2.1.0

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