sinclair 1.8.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +20 -6
  3. data/.rubocop.yml +5 -2
  4. data/Dockerfile +1 -1
  5. data/README.md +25 -1
  6. data/config/check_specs.yml +1 -0
  7. data/config/yardstick.yml +9 -1
  8. data/lib/sinclair/comparable/class_methods.rb +33 -0
  9. data/lib/sinclair/comparable.rb +47 -0
  10. data/lib/sinclair/config_builder.rb +4 -2
  11. data/lib/sinclair/configurable.rb +2 -0
  12. data/lib/sinclair/equals_checker.rb +110 -0
  13. data/lib/sinclair/matchers/add_class_method.rb +27 -36
  14. data/lib/sinclair/matchers/add_instance_method.rb +59 -59
  15. data/lib/sinclair/matchers/add_method.rb +33 -35
  16. data/lib/sinclair/matchers/change_class_method.rb +22 -16
  17. data/lib/sinclair/matchers/change_instance_method.rb +46 -16
  18. data/lib/sinclair/matchers.rb +2 -8
  19. data/lib/sinclair/method_builder/call_method_builder.rb +49 -0
  20. data/lib/sinclair/method_builder.rb +4 -1
  21. data/lib/sinclair/method_definition/call_definition.rb +52 -0
  22. data/lib/sinclair/method_definition/string_definition.rb +0 -2
  23. data/lib/sinclair/method_definition.rb +40 -24
  24. data/lib/sinclair/method_definitions.rb +21 -1
  25. data/lib/sinclair/options/builder.rb +8 -0
  26. data/lib/sinclair/options.rb +1 -13
  27. data/lib/sinclair/version.rb +1 -1
  28. data/lib/sinclair.rb +2 -0
  29. data/sinclair.gemspec +1 -1
  30. data/spec/integration/readme/sinclair/comparable_spec.rb +24 -0
  31. data/spec/integration/yard/sinclair/comparable_spec.rb +24 -0
  32. data/spec/integration/yard/sinclair/equals_checker_spec.rb +51 -0
  33. data/spec/integration/yard/sinclair/matchers/change_class_method_spec.rb +24 -0
  34. data/spec/integration/yard/sinclair/matchers/change_instance_method_spec.rb +40 -0
  35. data/spec/lib/sinclair/comparable_spec.rb +202 -0
  36. data/spec/lib/sinclair/equals_checker_spec.rb +148 -0
  37. data/spec/lib/sinclair/method_builder/call_method_builder_spec.rb +76 -0
  38. data/spec/lib/sinclair/method_builder_spec.rb +63 -20
  39. data/spec/lib/sinclair/method_definition/call_definition_spec.rb +36 -0
  40. data/spec/lib/sinclair/method_definition_spec.rb +64 -0
  41. data/spec/lib/sinclair/method_definitions_spec.rb +79 -0
  42. data/spec/lib/sinclair/options/builder_spec.rb +13 -0
  43. data/spec/lib/sinclair/options/class_methods_spec.rb +23 -8
  44. data/spec/support/sample_model.rb +19 -0
  45. data/spec/support/shared_examples/attribute_accessor.rb +103 -0
  46. metadata +21 -5
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::EqualsChecker do
6
+ describe '#yard' do
7
+ describe '#match?' do
8
+ it 'regular usage' do
9
+ checker = Sinclair::EqualsChecker.new(:name, :age)
10
+
11
+ model1 = SampleModel.new(name: 'jack', age: 21)
12
+ model2 = SampleModel.new(name: 'rose', age: 23)
13
+
14
+ expect(checker.match?(model1, model2)).to be_falsey
15
+ end
16
+
17
+ it 'similar models' do
18
+ checker = Sinclair::EqualsChecker.new(:name, :age)
19
+
20
+ model1 = SampleModel.new(name: 'jack', age: 21)
21
+ model2 = SampleModel.new(name: 'jack', age: 21)
22
+
23
+ expect(checker.match?(model1, model2)).to be_truthy
24
+ end
25
+
26
+ it 'different classes' do
27
+ checker = Sinclair::EqualsChecker.new(:name, :age)
28
+
29
+ model1 = SampleModel.new(name: 'jack', age: 21)
30
+ model2 = OtherModel.new(name: 'jack', age: 21)
31
+
32
+ expect(checker.match?(model1, model2)).to be_falsey
33
+ end
34
+ end
35
+
36
+ describe '#add' do
37
+ it 'adding fields to equal checker' do
38
+ checker = Sinclair::EqualsChecker.new(:name)
39
+
40
+ model1 = SampleModel.new(name: 'jack', age: 21)
41
+ model2 = SampleModel.new(name: 'jack', age: 22)
42
+
43
+ expect(checker.match?(model1, model2)).to be_truthy
44
+
45
+ checker.add(:age)
46
+
47
+ expect(checker.match?(model1, model2)).to be_falsey
48
+ end
49
+ end
50
+ end
51
+ 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
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Comparable do
6
+ let(:model_class) do
7
+ Class.new(SampleModel) do
8
+ include Sinclair::Comparable
9
+ end
10
+ end
11
+
12
+ let(:attributes) { %i[] }
13
+ let(:model1_class) { model_class }
14
+ let(:model2_class) { model_class }
15
+ let(:model1) { model1_class.new(**model1_attributes) }
16
+ let(:model2) { model2_class.new(**model2_attributes) }
17
+
18
+ let(:model1_attributes) { { name: name1, age: age1 } }
19
+ let(:model2_attributes) { { name: name2, age: age2 } }
20
+ let(:name1) { SecureRandom.hex(10) }
21
+ let(:name2) { SecureRandom.hex(10) }
22
+ let(:age1) { Random.rand(10..20) }
23
+ let(:age2) { Random.rand(21..50) }
24
+
25
+ describe '.comparable_by' do
26
+ let(:model2_class) { model1_class }
27
+
28
+ context 'when no field was present' do
29
+ it 'adds the field for comparison' do
30
+ expect { model1_class.comparable_by(:name) }
31
+ .to change { model1 == model2 }
32
+ .from(true).to(false)
33
+ end
34
+ end
35
+
36
+ context 'when there was a field present' do
37
+ let(:name2) { name1 }
38
+
39
+ before { model1_class.comparable_by(:name) }
40
+
41
+ it 'adds the field for comparison' do
42
+ expect { model1_class.comparable_by(:age) }
43
+ .to change { model1 == model2 }
44
+ .from(true).to(false)
45
+ end
46
+ end
47
+
48
+ context 'when there was a field present and it made a non match' do
49
+ let(:age2) { age1 }
50
+
51
+ before { model1_class.comparable_by(:name) }
52
+
53
+ it 'adds the field for comparison without forgeting the previous' do
54
+ expect { model1_class.comparable_by(:age) }
55
+ .not_to change { model1 == model2 }
56
+ .from(false)
57
+ end
58
+ end
59
+
60
+ context 'when the class parent class adds a field' do
61
+ let(:model1_class) { Class.new(model_class) }
62
+ let(:model2_class) { model1_class }
63
+
64
+ it 'takes the field into consideration' do
65
+ expect { model_class.comparable_by(:name) }
66
+ .to change { model1 == model2 }
67
+ .from(true).to(false)
68
+ end
69
+
70
+ context 'when we add a field to the class itself' do
71
+ let(:name2) { name1 }
72
+
73
+ before { model_class.comparable_by(:name) }
74
+
75
+ it 'takes all fields into consideration' do
76
+ expect { model1_class.comparable_by(:age) }
77
+ .to change { model1 == model2 }
78
+ .from(true).to(false)
79
+ end
80
+ end
81
+
82
+ context 'when we add a field to the parent class after' do
83
+ let(:name2) { name1 }
84
+
85
+ before { model1_class.comparable_by(:name) }
86
+
87
+ it 'takes all fields into consideration' do
88
+ expect { model_class.comparable_by(:age) }
89
+ .to change { model1 == model2 }
90
+ .from(true).to(false)
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ describe '#==' do
97
+ before do
98
+ model1_class.comparable_by(attributes)
99
+ model2_class.comparable_by(attributes)
100
+ end
101
+
102
+ context 'when the attributes is empty' do
103
+ context 'when they are different classes and attributes are the same' do
104
+ let(:model2_class) { Class.new(model1_class) }
105
+ let(:name2) { name1 }
106
+ let(:age2) { age1 }
107
+
108
+ it do
109
+ expect(model1).not_to eq(model2)
110
+ end
111
+ end
112
+
113
+ context 'when the models have the same attributes' do
114
+ let(:name2) { name1 }
115
+ let(:age2) { age1 }
116
+
117
+ it do
118
+ expect(model1).to eq(model2)
119
+ end
120
+ end
121
+
122
+ context 'when the models have very different attributes' do
123
+ it do
124
+ expect(model1).to eq(model2)
125
+ end
126
+ end
127
+ end
128
+
129
+ context 'when the attributes is missing just one attribute' do
130
+ let(:attributes) { %i[name] }
131
+
132
+ context 'when they are different classes and attributes are the same' do
133
+ let(:model2_class) { Class.new(model1_class) }
134
+ let(:name2) { name1 }
135
+ let(:age2) { age1 }
136
+
137
+ it do
138
+ expect(model1).not_to eq(model2)
139
+ end
140
+ end
141
+
142
+ context 'when the models have a non listed different attribute' do
143
+ let(:name2) { name1 }
144
+
145
+ it do
146
+ expect(model1).to eq(model2)
147
+ end
148
+ end
149
+
150
+ context 'when the models have a listed different attribute' do
151
+ let(:age2) { age1 }
152
+
153
+ it do
154
+ expect(model1).not_to eq(model2)
155
+ end
156
+ end
157
+
158
+ context 'when the models have very different attributes' do
159
+ it do
160
+ expect(model1).not_to eq(model2)
161
+ end
162
+ end
163
+ end
164
+
165
+ context 'when all attributes are included' do
166
+ let(:attributes) { %i[name age] }
167
+
168
+ context 'when they are different classes and attributes are the same' do
169
+ let(:model2_class) { Class.new(model1_class) }
170
+ let(:name2) { name1 }
171
+ let(:age2) { age1 }
172
+
173
+ it do
174
+ expect(model1).not_to eq(model2)
175
+ end
176
+ end
177
+
178
+ context 'when the models have the same attributes' do
179
+ let(:name2) { name1 }
180
+ let(:age2) { age1 }
181
+
182
+ it do
183
+ expect(model1).to eq(model2)
184
+ end
185
+ end
186
+
187
+ context 'when the models have a listed different attribute' do
188
+ let(:name) { name1 }
189
+
190
+ it do
191
+ expect(model1).not_to eq(model2)
192
+ end
193
+ end
194
+
195
+ context 'when the models have very different attributes' do
196
+ it do
197
+ expect(model1).not_to eq(model2)
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::EqualsChecker do
6
+ subject(:checker) { described_class.new(attributes) }
7
+
8
+ let(:attributes) { %i[] }
9
+
10
+ let(:model1_class) { SampleModel }
11
+ let(:model2_class) { SampleModel }
12
+ let(:model1) { model1_class.new(**model1_attributes) }
13
+ let(:model2) { model2_class.new(**model2_attributes) }
14
+
15
+ let(:model1_attributes) { { name: name1, age: age1 } }
16
+ let(:model2_attributes) { { name: name2, age: age2 } }
17
+ let(:name1) { SecureRandom.hex(10) }
18
+ let(:name2) { SecureRandom.hex(10) }
19
+ let(:age1) { Random.rand(10..20) }
20
+ let(:age2) { Random.rand(21..50) }
21
+
22
+ describe 'match?' do
23
+ context 'when the attributes is empty' do
24
+ context 'when they are different classes and attributes are the same' do
25
+ let(:model2_class) { Class.new(SampleModel) }
26
+ let(:name2) { name1 }
27
+ let(:age2) { age1 }
28
+
29
+ it do
30
+ expect(checker).not_to be_match(model1, model2)
31
+ end
32
+ end
33
+
34
+ context 'when the models have the same attributes' do
35
+ let(:name2) { name1 }
36
+ let(:age2) { age1 }
37
+
38
+ it do
39
+ expect(checker).to be_match(model1, model2)
40
+ end
41
+ end
42
+
43
+ context 'when the models have very different attributes' do
44
+ it do
45
+ expect(checker).to be_match(model1, model2)
46
+ end
47
+ end
48
+ end
49
+
50
+ context 'when the attributes is missing just one attribute' do
51
+ let(:attributes) { %i[name] }
52
+
53
+ context 'when they are different classes and attributes are the same' do
54
+ let(:model2_class) { Class.new(SampleModel) }
55
+ let(:name2) { name1 }
56
+ let(:age2) { age1 }
57
+
58
+ it do
59
+ expect(checker).not_to be_match(model1, model2)
60
+ end
61
+ end
62
+
63
+ context 'when the models have a non listed different attribute' do
64
+ let(:name2) { name1 }
65
+
66
+ it do
67
+ expect(checker).to be_match(model1, model2)
68
+ end
69
+ end
70
+
71
+ context 'when the models have a listed different attribute' do
72
+ let(:age2) { age1 }
73
+
74
+ it do
75
+ expect(checker).not_to be_match(model1, model2)
76
+ end
77
+ end
78
+
79
+ context 'when the models have very different attributes' do
80
+ it do
81
+ expect(checker).not_to be_match(model1, model2)
82
+ end
83
+ end
84
+ end
85
+
86
+ context 'when all attributes are included' do
87
+ let(:attributes) { %i[name age] }
88
+
89
+ context 'when they are different classes and attributes are the same' do
90
+ let(:model2_class) { Class.new(SampleModel) }
91
+ let(:name2) { name1 }
92
+ let(:age2) { age1 }
93
+
94
+ it do
95
+ expect(checker).not_to be_match(model1, model2)
96
+ end
97
+ end
98
+
99
+ context 'when the models have the same attributes' do
100
+ let(:name2) { name1 }
101
+ let(:age2) { age1 }
102
+
103
+ it do
104
+ expect(checker).to be_match(model1, model2)
105
+ end
106
+ end
107
+
108
+ context 'when the models have a listed different attribute' do
109
+ let(:name) { name1 }
110
+
111
+ it do
112
+ expect(checker).not_to be_match(model1, model2)
113
+ end
114
+ end
115
+
116
+ context 'when the models have very different attributes' do
117
+ it do
118
+ expect(checker).not_to be_match(model1, model2)
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ describe '#add' do
125
+ let(:attributes) { [:name] }
126
+ let(:new_attributes) { [:age] }
127
+
128
+ context 'when the new field has different values' do
129
+ let(:name2) { name1 }
130
+
131
+ it 'uses the new field to the match' do
132
+ expect { checker.add(new_attributes) }
133
+ .to change { checker.match?(model1, model2) }
134
+ .from(true).to(false)
135
+ end
136
+ end
137
+
138
+ context 'when the old field has different values' do
139
+ let(:age2) { age1 }
140
+
141
+ it 'uses the new field to the match' do
142
+ expect { checker.add(new_attributes) }
143
+ .not_to change { checker.match?(model1, model2) }
144
+ .from(false)
145
+ end
146
+ end
147
+ end
148
+ 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,36 @@
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_string' do
14
+ let(:expected) { 'method_call :key1, :value2' }
15
+
16
+ it 'returns the code string' do
17
+ expect(definition.code_string)
18
+ .to eq(expected)
19
+ end
20
+ end
21
+
22
+ describe '#class_code_string' do
23
+ let(:expected) do
24
+ <<-RUBY
25
+ class << self
26
+ method_call :key1, :value2
27
+ end
28
+ RUBY
29
+ end
30
+
31
+ it 'returns the code string' do
32
+ expect(definition.class_code_string.gsub(/^ */, ''))
33
+ .to eq(expected.gsub(/^ */, ''))
34
+ end
35
+ end
36
+ end