sinclair 1.9.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/README.md +2 -2
  4. data/config/yardstick.yml +7 -1
  5. data/lib/sinclair/configurable.rb +2 -0
  6. data/lib/sinclair/equals_checker.rb +2 -0
  7. data/lib/sinclair/matchers/add_class_method.rb +27 -36
  8. data/lib/sinclair/matchers/add_instance_method.rb +59 -59
  9. data/lib/sinclair/matchers/add_method.rb +33 -35
  10. data/lib/sinclair/matchers/change_class_method.rb +22 -16
  11. data/lib/sinclair/matchers/change_instance_method.rb +46 -16
  12. data/lib/sinclair/matchers.rb +2 -8
  13. data/lib/sinclair/method_builder/call_method_builder.rb +49 -0
  14. data/lib/sinclair/method_builder.rb +4 -1
  15. data/lib/sinclair/method_definition/call_definition.rb +52 -0
  16. data/lib/sinclair/method_definition/string_definition.rb +0 -2
  17. data/lib/sinclair/method_definition.rb +40 -24
  18. data/lib/sinclair/method_definitions.rb +21 -1
  19. data/lib/sinclair/options/builder.rb +8 -0
  20. data/lib/sinclair/options.rb +1 -13
  21. data/lib/sinclair/version.rb +1 -1
  22. data/spec/integration/yard/sinclair/matchers/change_class_method_spec.rb +24 -0
  23. data/spec/integration/yard/sinclair/matchers/change_instance_method_spec.rb +40 -0
  24. data/spec/lib/sinclair/method_builder/call_method_builder_spec.rb +76 -0
  25. data/spec/lib/sinclair/method_builder_spec.rb +63 -20
  26. data/spec/lib/sinclair/method_definition/call_definition_spec.rb +36 -0
  27. data/spec/lib/sinclair/method_definition_spec.rb +64 -0
  28. data/spec/lib/sinclair/method_definitions_spec.rb +79 -0
  29. data/spec/lib/sinclair/options/builder_spec.rb +13 -0
  30. data/spec/lib/sinclair/options/class_methods_spec.rb +23 -8
  31. data/spec/support/shared_examples/attribute_accessor.rb +103 -0
  32. metadata +9 -2
@@ -5,8 +5,6 @@ class Sinclair
5
5
  # @api private
6
6
  # @author darthjee
7
7
  #
8
- # @abstract
9
- #
10
8
  # Define an instance method from string
11
9
  class StringDefinition < MethodDefinition
12
10
  # @param name [String,Symbol] name of the method
@@ -11,6 +11,7 @@ class Sinclair
11
11
  autoload :BlockHelper, 'sinclair/method_definition/block_helper'
12
12
  autoload :BlockDefinition, 'sinclair/method_definition/block_definition'
13
13
  autoload :StringDefinition, 'sinclair/method_definition/string_definition'
14
+ autoload :CallDefinition, 'sinclair/method_definition/call_definition'
14
15
 
15
16
  # @method name
16
17
  #
@@ -24,31 +25,46 @@ class Sinclair
24
25
  cached: false
25
26
  }.freeze
26
27
 
27
- # Builds a method that will return the same value always
28
- #
29
- # @return [Symbol]
30
- def self.default_value(method_name, value)
31
- define_method(method_name) { value }
32
- end
28
+ class << self
29
+ # Builds a method that will return the same value always
30
+ #
31
+ # @return [Symbol]
32
+ def default_value(method_name, value)
33
+ define_method(method_name) { value }
34
+ end
33
35
 
34
- # @param name [String,Symbol] name of the method
35
- # @param code [String] code to be evaluated as method
36
- # @param block [Proc] block with code to be added as method
37
- # @param options [Hash] Options of construction
38
- # @option options cached [Boolean] Flag telling to create a block
39
- # with cache
40
- #
41
- # builds a method definition based on arguments
42
- #
43
- # when block is given, returns a {BlockDefinition} and
44
- # returns a {StringDefinition} otherwise
45
- #
46
- # @return [Base]
47
- def self.from(name, code = nil, **options, &block)
48
- if block
49
- BlockDefinition.new(name, **options, &block)
50
- else
51
- StringDefinition.new(name, code, **options)
36
+ # @param name [String,Symbol] name of the method
37
+ # @param code [String] code to be evaluated as method
38
+ # @param block [Proc] block with code to be added as method
39
+ # @param options [Hash] Options of construction
40
+ # @option options cached [Boolean] Flag telling to create a block
41
+ # with cache
42
+ #
43
+ # builds a method definition based on arguments
44
+ #
45
+ # when block is given, returns a {BlockDefinition} and
46
+ # returns a {StringDefinition} otherwise
47
+ #
48
+ # @return [Base]
49
+ def from(name, code = nil, **options, &block)
50
+ if block
51
+ BlockDefinition.new(name, **options, &block)
52
+ else
53
+ StringDefinition.new(name, code, **options)
54
+ end
55
+ end
56
+
57
+ # creates a definition
58
+ #
59
+ # The creation is based on type which will be used to infer
60
+ # which subclass of {Sinclair::MethodDefinition} to be used
61
+ #
62
+ # @param type [Symbol] the method definition type
63
+ #
64
+ # @return [Sinclair::MethodDefinition] an instance of a subclass
65
+ def for(type, *args, **options, &block)
66
+ klass = const_get("#{type}_definition".camelize)
67
+ klass.new(*args, **options, &block)
52
68
  end
53
69
  end
54
70
 
@@ -10,6 +10,8 @@ class Sinclair
10
10
 
11
11
  # Builds and adds new definition
12
12
  #
13
+ # The type is decided based in the arguments
14
+ #
13
15
  # @param name [String,Symbol] method name
14
16
  # @param options [Hash] Options of construction
15
17
  # @option options cached [Boolean] Flag telling to create
@@ -21,11 +23,29 @@ class Sinclair
21
23
  # @overload add(definition_class, name, **options, &block)
22
24
  # @param block [Proc] block to be ran as method
23
25
  #
24
- # @return MethodDefinitions
26
+ # @return [Array<MethodDefinition>]
25
27
  def add(name, code = nil, **options, &block)
26
28
  definitions << MethodDefinition.from(name, code, **options, &block)
27
29
  end
28
30
 
31
+ # Builds and adds new definition
32
+ #
33
+ # The type is decided based on the argument +type+
34
+ #
35
+ # @param type [Symbol] type of definition
36
+ # - :string -> {MethodDefinition::StringDefinition}
37
+ # - :block -> {MethodDefinition::BlockDefinition}
38
+ # - :call -> {MethodDefinition::CallDefinition}
39
+ # @param options [Hash] Options of construction
40
+ # @option options cached [Boolean] Flag telling to create
41
+ # a method with cache
42
+ # @param block [Proc] block to be ran as method
43
+ #
44
+ # @return [Array<MethodDefinition>]
45
+ def add_definition(type, *args, **options, &block)
46
+ definitions << MethodDefinition.for(type, *args, **options, &block)
47
+ end
48
+
29
49
  private
30
50
 
31
51
  # @private
@@ -36,6 +36,7 @@ class Sinclair
36
36
  # @return (see Sinclair#build)
37
37
  def build
38
38
  add_all_methods
39
+ add_filds_to_equals
39
40
 
40
41
  super
41
42
  end
@@ -60,6 +61,13 @@ class Sinclair
60
61
  klass.allow(option)
61
62
  end
62
63
  end
64
+
65
+ # Add the fields to equals comparation
66
+ #
67
+ # @return (see Sinclair::EqualsChecker#add)
68
+ def add_filds_to_equals
69
+ klass.comparable_by(*attributes.keys)
70
+ end
63
71
  end
64
72
  end
65
73
  end
@@ -24,6 +24,7 @@ class Sinclair
24
24
  autoload :ClassMethods, 'sinclair/options/class_methods'
25
25
 
26
26
  extend ClassMethods
27
+ include Comparable
27
28
 
28
29
  # @param options [Hash] hash with options (see {.options}, {.with_options})
29
30
  # @example (see Options)
@@ -59,19 +60,6 @@ class Sinclair
59
60
  end
60
61
  end
61
62
 
62
- # returns if other equals to self
63
- #
64
- # @param other [Object] object to be compared
65
- #
66
- # @return [TrueClass,FalseClass]
67
- def ==(other)
68
- return false unless self.class == other.class
69
-
70
- allowed_options.all? do |name|
71
- public_send(name) == other.public_send(name)
72
- end
73
- end
74
-
75
63
  private
76
64
 
77
65
  delegate :allowed_options, :skip_validation?, :invalid_options_in, to: :class
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Sinclair
4
- VERSION = '1.9.0'
4
+ VERSION = '1.10.0'
5
5
  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,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
@@ -42,4 +42,68 @@ describe Sinclair::MethodDefinition do
42
42
  end
43
43
  end
44
44
  end
45
+
46
+ describe '.for' do
47
+ context 'when there are no options nor block' do
48
+ let(:type) { :call }
49
+ let(:arguments) { %i[attr_reader some_attribute other_attribute] }
50
+
51
+ it do
52
+ expect(described_class.for(type, *arguments))
53
+ .to be_a(described_class)
54
+ end
55
+
56
+ it 'Returns an instance of the given type' do
57
+ expect(described_class.for(type, *arguments))
58
+ .to be_a(described_class::CallDefinition)
59
+ end
60
+
61
+ it 'initializes it correctly' do
62
+ expect(described_class.for(type, *arguments).code_string)
63
+ .to eq('attr_reader :some_attribute, :other_attribute')
64
+ end
65
+ end
66
+
67
+ context 'when a block is given' do
68
+ let(:type) { :block }
69
+ let(:method_name) { :the_method }
70
+ let(:block) { proc { 10 } }
71
+
72
+ it do
73
+ expect(described_class.for(type, method_name, &block))
74
+ .to be_a(described_class)
75
+ end
76
+
77
+ it 'Returns an instance of the given type' do
78
+ expect(described_class.for(type, method_name, &block))
79
+ .to be_a(described_class::BlockDefinition)
80
+ end
81
+
82
+ it 'initializes it correctly' do
83
+ expect(described_class.for(type, method_name, &block).name)
84
+ .to eq(method_name)
85
+ end
86
+ end
87
+
88
+ context 'when options are given' do
89
+ let(:type) { :string }
90
+ let(:method_name) { :the_method }
91
+ let(:code) { '10' }
92
+
93
+ it do
94
+ expect(described_class.for(type, method_name, code))
95
+ .to be_a(described_class)
96
+ end
97
+
98
+ it 'Returns an instance of the given type' do
99
+ expect(described_class.for(type, method_name, code))
100
+ .to be_a(described_class::StringDefinition)
101
+ end
102
+
103
+ it 'initializes it correctly' do
104
+ expect(described_class.for(type, method_name, code).name)
105
+ .to eq(method_name)
106
+ end
107
+ end
108
+ end
45
109
  end
@@ -42,4 +42,83 @@ describe Sinclair::MethodDefinitions do
42
42
  end
43
43
  end
44
44
  end
45
+
46
+ describe '#add_definition' do
47
+ context 'when there are no options nor block' do
48
+ let(:type) { :call }
49
+ let(:arguments) { %i[attr_reader some_attribute other_attribute] }
50
+
51
+ it do
52
+ expect(definitions.add_definition(type, *arguments))
53
+ .to be_a(Array)
54
+ end
55
+
56
+ it 'creates a new definition' do
57
+ expect(definitions.add_definition(type, *arguments).last)
58
+ .to be_a(Sinclair::MethodDefinition)
59
+ end
60
+
61
+ it 'creates a new definition of the chosen type' do
62
+ expect(definitions.add_definition(type, *arguments).last)
63
+ .to be_a(Sinclair::MethodDefinition::CallDefinition)
64
+ end
65
+
66
+ it 'initializes it correctly' do
67
+ expect(definitions.add_definition(type, *arguments).last.code_string)
68
+ .to eq('attr_reader :some_attribute, :other_attribute')
69
+ end
70
+ end
71
+
72
+ context 'when a block is given' do
73
+ let(:type) { :block }
74
+ let(:method_name) { :the_method }
75
+ let(:block) { proc { 10 } }
76
+
77
+ it do
78
+ expect(definitions.add_definition(type, method_name, &block))
79
+ .to be_a(Array)
80
+ end
81
+
82
+ it 'creates a new definition' do
83
+ expect(definitions.add_definition(type, method_name, &block).last)
84
+ .to be_a(Sinclair::MethodDefinition)
85
+ end
86
+
87
+ it 'creates a new definition of the chosen type' do
88
+ expect(definitions.add_definition(type, method_name, &block).last)
89
+ .to be_a(Sinclair::MethodDefinition::BlockDefinition)
90
+ end
91
+
92
+ it 'initializes it correctly' do
93
+ expect(definitions.add_definition(type, method_name, &block).last.name)
94
+ .to eq(method_name)
95
+ end
96
+ end
97
+
98
+ context 'when options are given' do
99
+ let(:type) { :string }
100
+ let(:method_name) { :the_method }
101
+ let(:code) { '10' }
102
+
103
+ it do
104
+ expect(definitions.add_definition(type, method_name, code))
105
+ .to be_a(Array)
106
+ end
107
+
108
+ it 'creates a new definition' do
109
+ expect(definitions.add_definition(type, method_name, code).last)
110
+ .to be_a(Sinclair::MethodDefinition)
111
+ end
112
+
113
+ it 'creates a new definition of the chosen type' do
114
+ expect(definitions.add_definition(type, method_name, code).last)
115
+ .to be_a(Sinclair::MethodDefinition::StringDefinition)
116
+ end
117
+
118
+ it 'initializes it correctly' do
119
+ expect(definitions.add_definition(type, method_name, code).last.name)
120
+ .to eq(method_name)
121
+ end
122
+ end
123
+ end
45
124
  end
@@ -40,6 +40,19 @@ describe Sinclair::Options::Builder do
40
40
  .not_to change { Sinclair::Options.invalid_options_in(test_keys) }
41
41
  end
42
42
 
43
+ context 'when the object is compared with others' do
44
+ before { klass.skip_validation }
45
+
46
+ let(:options1) { klass.new(timeout: 10) }
47
+ let(:options2) { klass.new(timeout: 15) }
48
+
49
+ it 'adds the field to the equals check' do
50
+ expect { builder.build }
51
+ .to change { options1 == options2 }
52
+ .from(true).to(false)
53
+ end
54
+ end
55
+
43
56
  context 'when when calling method after building' do
44
57
  before { builder.build }
45
58