sinclair 1.3.0 → 1.3.1
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 +4 -4
- data/.rubocop.yml +7 -2
- data/Dockerfile +1 -1
- data/README.md +168 -15
- data/config/yardstick.yml +3 -0
- data/lib/sinclair.rb +56 -1
- data/lib/sinclair/method_definition.rb +27 -77
- data/lib/sinclair/method_definition/block_definition.rb +102 -0
- data/lib/sinclair/method_definition/string_definition.rb +82 -0
- data/lib/sinclair/version.rb +1 -1
- data/spec/integration/readme/sinclair/configurable_spec.rb +44 -0
- data/spec/integration/readme/sinclair_spec.rb +70 -0
- data/spec/integration/yard/sinclair/configurable_spec.rb +4 -0
- data/spec/integration/yard/sinclair/method_definition/block_definition_spec.rb +25 -0
- data/spec/integration/yard/sinclair/method_definition/string_definition_spec.rb +32 -0
- data/spec/integration/yard/sinclair/method_definition_spec.rb +2 -2
- data/spec/integration/yard/sinclair_spec.rb +30 -0
- data/spec/lib/sinclair/matchers/add_method_to_spec.rb +10 -0
- data/spec/lib/sinclair/method_definition/block_definition_spec.rb +30 -0
- data/spec/lib/sinclair/method_definition/string_definition_spec.rb +28 -0
- data/spec/lib/sinclair/method_definition_spec.rb +22 -63
- data/spec/support/models/default_value_builder.rb +21 -0
- data/spec/support/models/default_valueable.rb +11 -0
- data/spec/support/models/http_json_model.rb +27 -0
- data/spec/support/models/http_person.rb +11 -0
- data/spec/support/models/server.rb +18 -0
- data/spec/support/shared_examples/method_definition.rb +95 -0
- metadata +15 -2
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Sinclair
|
4
|
+
class MethodDefinition
|
5
|
+
# Define a method from block
|
6
|
+
class BlockDefinition < MethodDefinition
|
7
|
+
# @param name [String,Symbol] name of the method
|
8
|
+
# @param block [Proc] block with code to be added as method
|
9
|
+
# @param options [Hash] Options of construction
|
10
|
+
# @option options cached [Boolean] Flag telling to create
|
11
|
+
# a method with cache
|
12
|
+
def initialize(name, **options, &block)
|
13
|
+
@block = block
|
14
|
+
super(name, **options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Adds the method to given klass
|
18
|
+
#
|
19
|
+
# @param klass [Class] class which will receive the new method
|
20
|
+
#
|
21
|
+
# @see MethodDefinition#build
|
22
|
+
#
|
23
|
+
# @return [Symbol] name of the created method
|
24
|
+
#
|
25
|
+
# @example Using block method with no options
|
26
|
+
# class MyModel
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# instance = MyModel.new
|
30
|
+
#
|
31
|
+
# method_definition = Sinclair::MethodDefinition::BlockDefinition.new(:sequence) do
|
32
|
+
# @x = @x.to_i ** 2 + 1
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# method_definition.build(klass) # adds instance_method :sequence to
|
36
|
+
# # MyModel instances
|
37
|
+
#
|
38
|
+
# instance.instance_variable_get(:@sequence) # returns nil
|
39
|
+
# instance.instance_variable_get(:@x) # returns nil
|
40
|
+
#
|
41
|
+
# instance.sequence # returns 1
|
42
|
+
# instance.sequence # returns 1 (cached value)
|
43
|
+
#
|
44
|
+
# instance.instance_variable_get(:@sequence) # returns 1
|
45
|
+
# instance.instance_variable_get(:@x) # returns 1
|
46
|
+
def build(klass)
|
47
|
+
klass.send(:define_method, name, method_block)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# @private
|
53
|
+
#
|
54
|
+
# Returns the block that will be used for method creattion
|
55
|
+
#
|
56
|
+
# @return [Proc]
|
57
|
+
def method_block
|
58
|
+
return block unless cached?
|
59
|
+
|
60
|
+
case cached
|
61
|
+
when :full
|
62
|
+
full_cached_method_proc(name, block)
|
63
|
+
else
|
64
|
+
cached_method_proc(name, block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @private
|
69
|
+
#
|
70
|
+
# Returns proc block when {#cached?} as simple
|
71
|
+
#
|
72
|
+
# @return [Proc]
|
73
|
+
def cached_method_proc(method_name, inner_block)
|
74
|
+
proc do
|
75
|
+
instance_variable_get("@#{method_name}") ||
|
76
|
+
instance_variable_set(
|
77
|
+
"@#{method_name}",
|
78
|
+
instance_eval(&inner_block)
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @private
|
84
|
+
#
|
85
|
+
# Returns proc block when {#cached?} as full
|
86
|
+
#
|
87
|
+
# @return [Proc]
|
88
|
+
def full_cached_method_proc(method_name, inner_block)
|
89
|
+
proc do
|
90
|
+
if instance_variable_defined?("@#{method_name}")
|
91
|
+
instance_variable_get("@#{method_name}")
|
92
|
+
else
|
93
|
+
instance_variable_set(
|
94
|
+
"@#{method_name}",
|
95
|
+
instance_eval(&inner_block)
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Sinclair
|
4
|
+
class MethodDefinition
|
5
|
+
# Define a method from string
|
6
|
+
class StringDefinition < MethodDefinition
|
7
|
+
# @param name [String,Symbol] name of the method
|
8
|
+
# @param code [String] code to be evaluated as method
|
9
|
+
# @param options [Hash] Options of construction
|
10
|
+
# @option options cached [Boolean] Flag telling to create
|
11
|
+
# a method with cache
|
12
|
+
def initialize(name, code = nil, **options)
|
13
|
+
@code = code
|
14
|
+
super(name, **options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Adds the method to given klass
|
18
|
+
#
|
19
|
+
# @param klass [Class] class which will receive the new method
|
20
|
+
#
|
21
|
+
# @see MethodDefinition#build
|
22
|
+
#
|
23
|
+
# @return [Symbol] name of the created method
|
24
|
+
#
|
25
|
+
# @example With no options
|
26
|
+
# class MyModel
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# instance = MyModel.new
|
30
|
+
#
|
31
|
+
# method_definition = Sinclair::MethodDefinition.from(
|
32
|
+
# :sequence, '@x = @x.to_i ** 2 + 1'
|
33
|
+
# )
|
34
|
+
#
|
35
|
+
# method_definition.build(klass) # adds instance_method :sequence to
|
36
|
+
# # MyModel instances
|
37
|
+
#
|
38
|
+
# instance.instance_variable_get(:@x) # returns nil
|
39
|
+
#
|
40
|
+
# instance.sequence # returns 1
|
41
|
+
# instance.sequence # returns 2
|
42
|
+
# instance.sequence # returns 5
|
43
|
+
#
|
44
|
+
# instance.instance_variable_get(:@x) # returns 5
|
45
|
+
#
|
46
|
+
def build(klass)
|
47
|
+
klass.module_eval(code_definition, __FILE__, __LINE__ + 1)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# @private
|
53
|
+
#
|
54
|
+
# Builds full code of method
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
def code_definition
|
58
|
+
code_line = cached? ? code_with_cache : code
|
59
|
+
|
60
|
+
<<-CODE
|
61
|
+
def #{name}
|
62
|
+
#{code_line}
|
63
|
+
end
|
64
|
+
CODE
|
65
|
+
end
|
66
|
+
|
67
|
+
# @private
|
68
|
+
#
|
69
|
+
# Returns string code when {#cached?}
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
def code_with_cache
|
73
|
+
case cached
|
74
|
+
when :full
|
75
|
+
"defined?(@#{name}) ? @#{name} : (@#{name} = #{code})"
|
76
|
+
else
|
77
|
+
"@#{name} ||= #{code}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/sinclair/version.rb
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Sinclair::Configurable do
|
6
|
+
context 'when it is configured' do
|
7
|
+
before do
|
8
|
+
MyConfigurable.configure do |config|
|
9
|
+
config.host 'interstella.art'
|
10
|
+
config.port 5555
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
MyConfigurable.reset_config
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'sets configuration host' do
|
19
|
+
expect(MyConfigurable.config.host)
|
20
|
+
.to eq('interstella.art')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'sets configuration port' do
|
24
|
+
expect(MyConfigurable.config.port)
|
25
|
+
.to eq(5555)
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when #rest_config is called' do
|
29
|
+
before do
|
30
|
+
MyConfigurable.reset_config
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'resets configuration host' do
|
34
|
+
expect(MyConfigurable.config.host)
|
35
|
+
.to be_nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'resets configuration port' do
|
39
|
+
expect(MyConfigurable.config.port)
|
40
|
+
.to be_nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -22,4 +22,74 @@ describe Sinclair do
|
|
22
22
|
expect("Eighty => #{instance.eighty}").to eq('Eighty => 80')
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
describe 'Stand Alone concern' do
|
27
|
+
subject(:person) { HttpPerson.new(json) }
|
28
|
+
|
29
|
+
let(:json) do
|
30
|
+
<<-JSON
|
31
|
+
{
|
32
|
+
"uid": "12sof511",
|
33
|
+
"personal_information":{
|
34
|
+
"name":"Bob",
|
35
|
+
"age": 21
|
36
|
+
},
|
37
|
+
"digital_information":{
|
38
|
+
"username":"lordbob",
|
39
|
+
"email":"lord@bob.com"
|
40
|
+
}
|
41
|
+
}
|
42
|
+
JSON
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'adds uid method' do
|
46
|
+
expect(person.uid).to eq('12sof511')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'adds name method' do
|
50
|
+
expect(person.name).to eq('Bob')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'adds age method' do
|
54
|
+
expect(person.age).to eq(21)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'adds username method' do
|
58
|
+
expect(person.username).to eq('lordbob')
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'adds email method' do
|
62
|
+
expect(person.email).to eq('lord@bob.com')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe 'DefaultValuable' do
|
67
|
+
subject(:server) { Server.new }
|
68
|
+
|
69
|
+
it 'returns default url' do
|
70
|
+
expect(server.url).to eq('http://server.com:80')
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when new values are set' do
|
74
|
+
before do
|
75
|
+
server.host = 'interstella.com'
|
76
|
+
server.port = 5555
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'returns custom url' do
|
80
|
+
expect(server.url).to eq('http://interstella.com:5555')
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when values are nullified' do
|
84
|
+
before do
|
85
|
+
server.host = nil
|
86
|
+
server.port = nil
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'returns url with default + custom nil values' do
|
90
|
+
expect(server.url).to eq('http://server.com')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
25
95
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Sinclair::MethodDefinition::BlockDefinition do
|
4
|
+
describe 'yard' do
|
5
|
+
describe '#build' do
|
6
|
+
subject(:method_definition) do
|
7
|
+
described_class.from(name, cached: true) do
|
8
|
+
@x = @x.to_i**2 + 1
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:klass) { Class.new }
|
13
|
+
let(:instance) { klass.new }
|
14
|
+
let(:name) { :sequence }
|
15
|
+
|
16
|
+
it 'adds a dynamic method' do
|
17
|
+
expect { method_definition.build(klass) }.to add_method(name).to(instance)
|
18
|
+
expect { instance.sequence }
|
19
|
+
.to change { instance.instance_variable_get(:@x) }.from(nil).to(1)
|
20
|
+
expect { instance.sequence }.not_to change(instance, :sequence)
|
21
|
+
expect(instance.instance_variable_get(:@sequence)).to eq(1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Sinclair::MethodDefinition::StringDefinition do
|
4
|
+
describe 'yard' do
|
5
|
+
describe '#build' do
|
6
|
+
subject(:method_definition) do
|
7
|
+
described_class.new(name, code)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:klass) { Class.new }
|
11
|
+
let(:instance) { klass.new }
|
12
|
+
let(:code) { '@x = @x.to_i ** 2 + 1' }
|
13
|
+
let(:name) { :sequence }
|
14
|
+
|
15
|
+
it 'adds a dynamic method' do
|
16
|
+
expect { method_definition.build(klass) }.to add_method(name).to(instance)
|
17
|
+
expect { instance.sequence }
|
18
|
+
.to change { instance.instance_variable_get(:@x) }.from(nil).to 1
|
19
|
+
expect(instance.sequence).to eq(2)
|
20
|
+
expect(instance.sequence).to eq(5)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'changes instance variable' do
|
24
|
+
method_definition.build(klass)
|
25
|
+
|
26
|
+
expect { instance.sequence }
|
27
|
+
.to change { instance.instance_variable_get(:@x) }
|
28
|
+
.from(nil).to 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -5,7 +5,7 @@ describe Sinclair::MethodDefinition do
|
|
5
5
|
describe '#build' do
|
6
6
|
describe 'using string method with no options' do
|
7
7
|
subject(:method_definition) do
|
8
|
-
described_class.
|
8
|
+
described_class.from(name, code)
|
9
9
|
end
|
10
10
|
|
11
11
|
let(:klass) { Class.new }
|
@@ -32,7 +32,7 @@ describe Sinclair::MethodDefinition do
|
|
32
32
|
|
33
33
|
describe 'using block with cache option' do
|
34
34
|
subject(:method_definition) do
|
35
|
-
described_class.
|
35
|
+
described_class.from(name, cached: true) do
|
36
36
|
@x = @x.to_i**2 + 1
|
37
37
|
end
|
38
38
|
end
|
@@ -9,6 +9,36 @@ describe Sinclair do
|
|
9
9
|
let(:builder) { described_class.new(klass) }
|
10
10
|
let(:default_value) { 10 }
|
11
11
|
|
12
|
+
describe 'Using cache' do
|
13
|
+
subject(:server) { Server.new }
|
14
|
+
|
15
|
+
it 'returns default url' do
|
16
|
+
expect(server.url).to eq('http://server.com:80')
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when new values are set' do
|
20
|
+
before do
|
21
|
+
server.host = 'interstella.com'
|
22
|
+
server.port = 5555
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns custom url' do
|
26
|
+
expect(server.url).to eq('http://interstella.com:5555')
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when values are nullified' do
|
30
|
+
before do
|
31
|
+
server.host = nil
|
32
|
+
server.port = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns url with default + custom nil values' do
|
36
|
+
expect(server.url).to eq('http://server.com')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
12
42
|
describe '#initialize' do
|
13
43
|
describe '#total_price' do
|
14
44
|
before do
|
@@ -39,6 +39,16 @@ describe Sinclair::Matchers::AddMethodTo do
|
|
39
39
|
it { expect(matcher).to be_matches(event_proc) }
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
context 'when a block is given' do
|
44
|
+
it do
|
45
|
+
expect { matcher.matches?(event_proc) { 1 } }
|
46
|
+
.to raise_error(
|
47
|
+
SyntaxError, 'Block not received by the `add_method_to` matcher. ' \
|
48
|
+
'Perhaps you want to use `{ ... }` instead of do/end?'
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
42
52
|
end
|
43
53
|
|
44
54
|
describe '#failure_message_for_should' do
|