sinclair 1.9.0 → 1.11.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -1
  3. data/README.md +414 -324
  4. data/config/check_specs.yml +3 -4
  5. data/config/yardstick.yml +9 -1
  6. data/lib/sinclair/configurable.rb +2 -0
  7. data/lib/sinclair/equals_checker.rb +2 -0
  8. data/lib/sinclair/matchers/add_class_method.rb +26 -36
  9. data/lib/sinclair/matchers/add_instance_method.rb +59 -59
  10. data/lib/sinclair/matchers/add_method.rb +33 -35
  11. data/lib/sinclair/matchers/change_class_method.rb +22 -16
  12. data/lib/sinclair/matchers/change_instance_method.rb +46 -16
  13. data/lib/sinclair/matchers.rb +2 -8
  14. data/lib/sinclair/method_builder/base.rb +17 -2
  15. data/lib/sinclair/method_builder/call_method_builder.rb +49 -0
  16. data/lib/sinclair/method_builder.rb +5 -17
  17. data/lib/sinclair/method_definition/block_definition.rb +2 -0
  18. data/lib/sinclair/method_definition/call_definition.rb +49 -0
  19. data/lib/sinclair/method_definition/string_definition.rb +2 -2
  20. data/lib/sinclair/method_definition.rb +80 -26
  21. data/lib/sinclair/method_definitions.rb +25 -7
  22. data/lib/sinclair/options/builder.rb +8 -0
  23. data/lib/sinclair/options.rb +1 -13
  24. data/lib/sinclair/version.rb +1 -1
  25. data/lib/sinclair.rb +149 -62
  26. data/spec/integration/readme/sinclair/types_of_definition_spec.rb +47 -0
  27. data/spec/integration/yard/sinclair/add_class_method_spec.rb +51 -0
  28. data/spec/integration/yard/sinclair/add_method_spec.rb +60 -0
  29. data/spec/integration/yard/sinclair/eval_and_add_method_spec.rb +26 -0
  30. data/spec/integration/yard/sinclair/matchers/change_class_method_spec.rb +24 -0
  31. data/spec/integration/yard/sinclair/matchers/change_instance_method_spec.rb +40 -0
  32. data/spec/integration/yard/sinclair_spec.rb +0 -83
  33. data/spec/lib/sinclair/method_builder/base_spec.rb +15 -0
  34. data/spec/lib/sinclair/method_builder/call_method_builder_spec.rb +76 -0
  35. data/spec/lib/sinclair/method_builder_spec.rb +63 -20
  36. data/spec/lib/sinclair/method_definition/call_definition_spec.rb +33 -0
  37. data/spec/lib/sinclair/method_definition_spec.rb +129 -0
  38. data/spec/lib/sinclair/method_definitions_spec.rb +79 -1
  39. data/spec/lib/sinclair/options/builder_spec.rb +13 -0
  40. data/spec/lib/sinclair/options/class_methods_spec.rb +23 -8
  41. data/spec/lib/sinclair_spec.rb +6 -160
  42. data/spec/support/models/dummy_builder.rb +4 -1
  43. data/spec/support/models/dummy_class_builder.rb +3 -0
  44. data/spec/support/models/person.rb +1 -1
  45. data/spec/support/shared_examples/attribute_accessor.rb +103 -0
  46. data/spec/support/shared_examples/sinclair.rb +112 -0
  47. metadata +15 -2
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'yard Sinclair#add_method' do
6
+ describe 'Using string code to add a string defined method' do
7
+ let(:klass) { Class.new(Person) }
8
+
9
+ it 'creates new method' do
10
+ builder = Sinclair.new(klass)
11
+ builder.add_method(:full_name, '[first_name, last_name].join(" ")')
12
+ builder.build
13
+
14
+ expect(klass.new('john', 'wick').full_name).to eq('john wick')
15
+ end
16
+ end
17
+
18
+ describe 'Using block to add a block method' do
19
+ let(:klass) { Class.new(Person) }
20
+
21
+ it 'creates new method' do
22
+ builder = Sinclair.new(klass)
23
+ builder.add_method(:bond_name) { "#{last_name}, #{first_name} #{last_name}" }
24
+ builder.build
25
+
26
+ expect(klass.new('john', 'wick').bond_name).to eq('wick, john wick')
27
+ end
28
+ end
29
+
30
+ describe 'Passing type block' do
31
+ let(:klass) { Class.new(Person) }
32
+
33
+ it 'creates new method' do
34
+ builder = Sinclair.new(klass)
35
+ builder.add_method(:bond_name, type: :block, cached: true) do
36
+ "#{last_name}, #{first_name} #{last_name}"
37
+ end
38
+ builder.build
39
+ person = klass.new('john', 'wick')
40
+
41
+ expect(person.bond_name).to eq('wick, john wick')
42
+ person.first_name = 'Johny'
43
+ expect(person.bond_name).to eq('wick, john wick')
44
+ end
45
+ end
46
+
47
+ describe 'Passing type call' do
48
+ let(:klass) { Class.new }
49
+
50
+ it 'creates new method' do
51
+ builder = Sinclair.new(klass)
52
+ builder.add_method(:attr_accessor, :bond_name, type: :call)
53
+ builder.build
54
+ person = klass.new
55
+
56
+ person.bond_name = 'Bond, James Bond'
57
+ expect(person.bond_name).to eq('Bond, James Bond')
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'yard Sinclair#eval_and_add_method' do
6
+ describe 'Building a initial value class method' do
7
+ let(:klass) do
8
+ Class.new do
9
+ include InitialValuer
10
+ attr_writer :age
11
+
12
+ initial_value_for :age, 20
13
+ end
14
+ end
15
+
16
+ context 'when it has not been initialized' do
17
+ it do
18
+ object = klass.new
19
+
20
+ expect(object.age).to eq(20)
21
+ object.age = 30
22
+ expect(object.age).to eq(30)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Sinclair::Matchers::ChangeClassMethod do
6
+ describe 'yard' do
7
+ describe '#on' do
8
+ context 'when checking against Class' do
9
+ let(:klass) { Class.new(MyModel) }
10
+ let(:builder) { Sinclair.new(klass) }
11
+
12
+ before do
13
+ builder.add_class_method(:the_method) { 10 }
14
+ builder.build
15
+ builder.add_class_method(:the_method) { 20 }
16
+ end
17
+
18
+ it do
19
+ expect { builder.build }.to change_class_method(:the_method).on(klass)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Sinclair::Matchers::ChangeInstanceMethod do
6
+ describe 'yard' do
7
+ describe '#on' do
8
+ context 'when checking against Class' do
9
+ let(:klass) { Class.new(MyModel) }
10
+ let(:builder) { Sinclair.new(klass) }
11
+
12
+ before do
13
+ builder.add_method(:the_method) { 10 }
14
+ builder.build
15
+ builder.add_method(:the_method) { 20 }
16
+ end
17
+
18
+ it do
19
+ expect { builder.build }.to change_method(:the_method).on(klass)
20
+ end
21
+ end
22
+
23
+ context 'when checking against an intance' do
24
+ let(:klass) { Class.new(MyModel) }
25
+ let(:instance) { klass.new }
26
+ let(:builder) { Sinclair.new(klass) }
27
+
28
+ before do
29
+ builder.add_method(:the_method) { 10 }
30
+ builder.build
31
+ builder.add_method(:the_method) { 20 }
32
+ end
33
+
34
+ it do
35
+ expect { builder.build }.to change_method(:the_method).on(instance)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -119,88 +119,5 @@ describe Sinclair do
119
119
  end
120
120
  end
121
121
  end
122
-
123
- describe '#add_method' do
124
- let(:klass) { Class.new(Person) }
125
- let(:instance) { klass.new('john', 'wick') }
126
-
127
- before do
128
- builder.add_method(:full_name, '[first_name, last_name].join(" ")')
129
- builder.add_method(:bond_name) { "#{last_name}, #{first_name} #{last_name}" }
130
- builder.build
131
- end
132
-
133
- describe '#full_name' do
134
- it 'returns the full name' do
135
- expect(instance.full_name).to eq('john wick')
136
- end
137
- end
138
-
139
- describe '#bond_name' do
140
- it 'returns the full name, bond style' do
141
- expect(instance.bond_name).to eq('wick, john wick')
142
- end
143
- end
144
- end
145
-
146
- describe '#add_class_method' do
147
- let(:klass) { env_fetcher }
148
- let(:env_fetcher) { Class.new }
149
-
150
- describe '#hostname' do
151
- before do
152
- builder.add_class_method(:hostname, 'ENV["HOSTNAME"]')
153
- builder.build
154
- ENV['HOSTNAME'] = 'myhost'
155
- end
156
-
157
- it 'returns the hostname' do
158
- expect(env_fetcher.hostname).to eq('myhost')
159
- end
160
- end
161
-
162
- describe '#timeout' do
163
- before do
164
- builder.add_class_method(:timeout) { ENV['TIMEOUT'] }
165
- builder.build
166
- ENV['TIMEOUT'] = '300'
167
- end
168
-
169
- it 'returns the timeout' do
170
- expect(env_fetcher.timeout).to eq('300')
171
- end
172
- end
173
- end
174
-
175
- describe '#eval_and_add_method' do
176
- subject(:builder) { klass.new }
177
-
178
- let(:klass) do
179
- Class.new do
180
- include InitialValuer
181
- attr_writer :age
182
- initial_value_for :age, 20
183
- end
184
- end
185
-
186
- describe '#age' do
187
- context 'when it has not been initialized' do
188
- it do
189
- expect(builder.age).to eq(20)
190
- end
191
- end
192
-
193
- context 'when it has been initialized' do
194
- before do
195
- builder.age
196
- builder.age = 30
197
- end
198
-
199
- it do
200
- expect(builder.age).to eq(30)
201
- end
202
- end
203
- end
204
- end
205
122
  end
206
123
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::MethodBuilder::Base do
6
+ describe '.build' do
7
+ let(:klass) { Class.new }
8
+ let(:type) { Sinclair::MethodBuilder::CLASS_METHOD }
9
+
10
+ it do
11
+ expect { described_class.build(klass, instance_of(described_class), type: type) }
12
+ .to raise_error(NotImplementedError)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::MethodBuilder::CallMethodBuilder do
6
+ describe '#build' do
7
+ subject(:builder) do
8
+ described_class.new(klass, definition, type: type)
9
+ end
10
+
11
+ let(:call_name) { "attr_#{accessor_type}" }
12
+
13
+ let(:definition) do
14
+ Sinclair::MethodDefinition::CallDefinition.new(call_name, *attributes)
15
+ end
16
+
17
+ context 'when method called is attr_accessor' do
18
+ let(:accessor_type) { :accessor }
19
+
20
+ it_behaves_like 'a method builder that adds attribute reader'
21
+ it_behaves_like 'a method builder that adds attribute writer'
22
+ end
23
+
24
+ context 'when method called is attr_reader' do
25
+ let(:accessor_type) { :reader }
26
+
27
+ it_behaves_like 'a method builder that adds attribute reader' do
28
+ context 'when type is instance' do
29
+ let(:type) { Sinclair::MethodBuilder::INSTANCE_METHOD }
30
+
31
+ it 'does not add a reader' do
32
+ expect { builder.build }
33
+ .not_to add_method("#{method_name}=")
34
+ .to(instance)
35
+ end
36
+ end
37
+
38
+ context 'when type is class' do
39
+ let(:type) { Sinclair::MethodBuilder::CLASS_METHOD }
40
+
41
+ it 'does not add a reader' do
42
+ expect { builder.build }
43
+ .not_to add_class_method("#{method_name}=")
44
+ .to(klass)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ context 'when method called is attr_writter' do
51
+ let(:accessor_type) { :writer }
52
+
53
+ it_behaves_like 'a method builder that adds attribute writer' do
54
+ context 'when type is instance' do
55
+ let(:type) { Sinclair::MethodBuilder::INSTANCE_METHOD }
56
+
57
+ it 'does not add a reader' do
58
+ expect { builder.build }
59
+ .not_to add_method(method_name)
60
+ .to(instance)
61
+ end
62
+ end
63
+
64
+ context 'when type is class' do
65
+ let(:type) { Sinclair::MethodBuilder::CLASS_METHOD }
66
+
67
+ it 'does not add a reader' do
68
+ expect { builder.build }
69
+ .not_to add_class_method(method_name)
70
+ .to(klass)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -11,41 +11,84 @@ describe Sinclair::MethodBuilder do
11
11
  let(:method_name) { :the_method }
12
12
  let(:instance) { klass.new }
13
13
 
14
- before do
15
- definitions.add(method_name, value.to_s)
16
- end
17
-
18
14
  describe '#build_methods' do
19
- context 'when building an instance method' do
20
- let(:type) { described_class::INSTANCE_METHOD }
15
+ context 'when the method is a string definition' do
16
+ before do
17
+ definitions.add(method_name, value.to_s)
18
+ end
21
19
 
22
- it do
23
- expect { builder.build_methods(definitions, type) }
24
- .to add_method(method_name).to(instance)
20
+ context 'when building an instance method' do
21
+ let(:type) { described_class::INSTANCE_METHOD }
22
+
23
+ it do
24
+ expect { builder.build_methods(definitions, type) }
25
+ .to add_method(method_name).to(instance)
26
+ end
27
+
28
+ context 'when the method is called' do
29
+ before { builder.build_methods(definitions, type) }
30
+
31
+ it do
32
+ expect(instance.the_method).to eq(value)
33
+ end
34
+ end
25
35
  end
26
36
 
27
- context 'when the method is called' do
28
- before { builder.build_methods(definitions, type) }
37
+ context 'when building a class method' do
38
+ let(:type) { described_class::CLASS_METHOD }
29
39
 
30
40
  it do
31
- expect(instance.the_method).to eq(value)
41
+ expect { builder.build_methods(definitions, type) }
42
+ .to add_class_method(method_name).to(klass)
43
+ end
44
+
45
+ context 'when the method is called' do
46
+ before { builder.build_methods(definitions, type) }
47
+
48
+ it do
49
+ expect(klass.the_method).to eq(value)
50
+ end
32
51
  end
33
52
  end
34
53
  end
35
54
 
36
- context 'when building a class method' do
37
- let(:type) { described_class::CLASS_METHOD }
55
+ context 'when the method is a block definition' do
56
+ before do
57
+ result = value
58
+ definitions.add(method_name) { result }
59
+ end
60
+
61
+ context 'when building an instance method' do
62
+ let(:type) { described_class::INSTANCE_METHOD }
63
+
64
+ it do
65
+ expect { builder.build_methods(definitions, type) }
66
+ .to add_method(method_name).to(instance)
67
+ end
38
68
 
39
- it do
40
- expect { builder.build_methods(definitions, type) }
41
- .to add_class_method(method_name).to(klass)
69
+ context 'when the method is called' do
70
+ before { builder.build_methods(definitions, type) }
71
+
72
+ it do
73
+ expect(instance.the_method).to eq(value)
74
+ end
75
+ end
42
76
  end
43
77
 
44
- context 'when the method is called' do
45
- before { builder.build_methods(definitions, type) }
78
+ context 'when building a class method' do
79
+ let(:type) { described_class::CLASS_METHOD }
46
80
 
47
81
  it do
48
- expect(klass.the_method).to eq(value)
82
+ expect { builder.build_methods(definitions, type) }
83
+ .to add_class_method(method_name).to(klass)
84
+ end
85
+
86
+ context 'when the method is called' do
87
+ before { builder.build_methods(definitions, type) }
88
+
89
+ it do
90
+ expect(klass.the_method).to eq(value)
91
+ end
49
92
  end
50
93
  end
51
94
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::MethodDefinition::CallDefinition do
6
+ subject(:definition) do
7
+ described_class.new(call_name, *attributes)
8
+ end
9
+
10
+ let(:call_name) { :method_call }
11
+ let(:attributes) { %i[key1 value2] }
12
+
13
+ describe '#code_block' do
14
+ let(:instance) { klass.new }
15
+ let(:klass) do
16
+ Class.new do
17
+ def method_call(*args)
18
+ args
19
+ end
20
+ end
21
+ end
22
+
23
+ it do
24
+ expect(definition.code_block)
25
+ .to be_a(Proc)
26
+ end
27
+
28
+ it 'returns a proc with the method call' do
29
+ expect(instance.instance_eval(&definition.code_block))
30
+ .to eq(attributes)
31
+ end
32
+ end
33
+ end
@@ -5,6 +5,48 @@ require 'spec_helper'
5
5
  describe Sinclair::MethodDefinition do
6
6
  let(:method_name) { :the_method }
7
7
 
8
+ describe '.build' do
9
+ subject(:definition) do
10
+ definition_class.new(method_name, **options)
11
+ end
12
+
13
+ let(:definition_class) { Class.new(described_class) }
14
+ let(:options) { {} }
15
+ let(:klass) { Class.new }
16
+ let(:type) { Sinclair::MethodBuilder::CLASS_METHOD }
17
+
18
+ context 'when the builder has not been defined' do
19
+ it do
20
+ expect { definition.build(klass, type) }
21
+ .to raise_error(NotImplementedError)
22
+ end
23
+ end
24
+
25
+ context 'when the builder has been defined' do
26
+ let(:definition_class) do
27
+ Class.new(described_class) do
28
+ def code_line
29
+ '10'
30
+ end
31
+ end
32
+ end
33
+
34
+ before do
35
+ definition_class.build_with(Sinclair::MethodBuilder::StringMethodBuilder)
36
+ end
37
+
38
+ it do
39
+ expect { definition.build(klass, type) }
40
+ .not_to raise_error
41
+ end
42
+
43
+ it 'builds the method using the builder' do
44
+ expect { definition.build(klass, type) }
45
+ .to add_class_method(method_name).to(klass)
46
+ end
47
+ end
48
+ end
49
+
8
50
  describe '.default_value' do
9
51
  subject(:klass) { Class.new(described_class) }
10
52
 
@@ -42,4 +84,91 @@ describe Sinclair::MethodDefinition do
42
84
  end
43
85
  end
44
86
  end
87
+
88
+ describe '.for' do
89
+ let(:klass) { Class.new }
90
+
91
+ context 'when there are no options nor block' do
92
+ let(:type) { :call }
93
+ let(:arguments) { %i[attr_reader some_attribute other_attribute] }
94
+
95
+ it do
96
+ expect(described_class.for(type, *arguments))
97
+ .to be_a(described_class)
98
+ end
99
+
100
+ it 'Returns an instance of the given type' do
101
+ expect(described_class.for(type, *arguments))
102
+ .to be_a(described_class::CallDefinition)
103
+ end
104
+
105
+ it 'initializes it correctly' do
106
+ expect { klass.module_eval(&described_class.for(type, *arguments).code_block) }
107
+ .to add_method(:some_attribute).to(klass)
108
+ end
109
+ end
110
+
111
+ context 'when type is not given' do
112
+ let(:type) { nil }
113
+ let(:method_name) { :the_method }
114
+ let(:block) { proc { 10 } }
115
+
116
+ it do
117
+ expect(described_class.for(type, method_name, &block))
118
+ .to be_a(described_class)
119
+ end
120
+
121
+ it 'infers the definition from arguments' do
122
+ expect(described_class.for(type, method_name, &block))
123
+ .to be_a(described_class::BlockDefinition)
124
+ end
125
+
126
+ it 'initializes it correctly' do
127
+ expect(described_class.for(type, method_name, &block).name)
128
+ .to eq(method_name)
129
+ end
130
+ end
131
+
132
+ context 'when a block is given' do
133
+ let(:type) { :block }
134
+ let(:method_name) { :the_method }
135
+ let(:block) { proc { 10 } }
136
+
137
+ it do
138
+ expect(described_class.for(type, method_name, &block))
139
+ .to be_a(described_class)
140
+ end
141
+
142
+ it 'Returns an instance of the given type' do
143
+ expect(described_class.for(type, method_name, &block))
144
+ .to be_a(described_class::BlockDefinition)
145
+ end
146
+
147
+ it 'initializes it correctly' do
148
+ expect(described_class.for(type, method_name, &block).name)
149
+ .to eq(method_name)
150
+ end
151
+ end
152
+
153
+ context 'when options are given' do
154
+ let(:type) { :string }
155
+ let(:method_name) { :the_method }
156
+ let(:code) { '10' }
157
+
158
+ it do
159
+ expect(described_class.for(type, method_name, code))
160
+ .to be_a(described_class)
161
+ end
162
+
163
+ it 'Returns an instance of the given type' do
164
+ expect(described_class.for(type, method_name, code))
165
+ .to be_a(described_class::StringDefinition)
166
+ end
167
+
168
+ it 'initializes it correctly' do
169
+ expect(described_class.for(type, method_name, code).name)
170
+ .to eq(method_name)
171
+ end
172
+ end
173
+ end
45
174
  end