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
@@ -4,41 +4,39 @@ class Sinclair
4
4
  module Matchers
5
5
  # @api private
6
6
  #
7
- # Commone methods for matchers
8
- module AddMethod
9
- # @api public
10
- #
11
- # Builds final matcher
12
- #
13
- # The matcher checks if a method was added
14
- # to a class or instance
15
- #
16
- # @param [target] target where the method will be added
17
- #
18
- # @return [Sinclair::Matchers::Base]
19
- #
20
- # @example
21
- # RSpec.configure do |config|
22
- # config.include Sinclair::Matchers
23
- # end
24
- #
25
- # class MyModel
26
- # end
27
- #
28
- # RSpec.describe 'my test' do
29
- # let(:klass) { Class.new(MyModel) }
30
- # let(:builder) { Sinclair.new(klass) }
31
- #
32
- # before do
33
- # builder.add_method(:class_name, 'self.class.name')
34
- # end
35
- #
36
- # it do
37
- # expect { builder.build }.to add_method(:class_name).to(klass)
38
- # end
39
- # end
40
- def to(target = nil)
41
- add_method_to_class.new(target, method_name)
7
+ # Common methods for matchers
8
+ class AddMethod < Base
9
+ class << self
10
+ private
11
+
12
+ # @api private
13
+ # @private
14
+ #
15
+ # Add a method to generate the final matcher
16
+ #
17
+ # @param name [String,Symbol] the name of the method
18
+ # @param matcher_class [Class<AddMethodTo>] The matcher class to be returned
19
+ #
20
+ # @return (see Sinclair#build)
21
+ #
22
+ # @!macro with_final_matcher
23
+ # @!method $1(target = nil)
24
+ # @api public
25
+ #
26
+ # Builds final matcher
27
+ #
28
+ # The matcher checks if a method was added
29
+ # to a class or instance
30
+ #
31
+ # @param [Class,Object] target where the method will be added
32
+ #
33
+ # @return [$2]
34
+ def with_final_matcher(name, matcher_class)
35
+ matcher = matcher_class
36
+ Sinclair.new(self).tap do |builder|
37
+ builder.add_method(name) { |target| matcher.new(target, method_name) }
38
+ end.build
39
+ end
42
40
  end
43
41
 
44
42
  # @abstract
@@ -7,15 +7,30 @@ class Sinclair
7
7
  #
8
8
  # AddInstanceMethod is able to build an instance of
9
9
  # {Sinclair::Matchers::ChangeClassMethodOn}
10
- class ChangeClassMethod < Base
11
- include AddMethod
12
-
13
- # @api public
10
+ class ChangeClassMethod < AddMethod
11
+ # @example Checking if a class method has changed
12
+ # RSpec.configure do |config|
13
+ # config.include Sinclair::Matchers
14
+ # end
15
+ #
16
+ # class MyModel
17
+ # end
14
18
  #
15
- # Builds final matcher
19
+ # RSpec.describe 'my test' do
20
+ # let(:builder) { Sinclair.new(klass) }
21
+ # let(:klass) { Class.new(MyModel) }
16
22
  #
17
- # @return [Sinclair::Matchers::ChangeClassMethodOn]
18
- alias on to
23
+ # before do
24
+ # builder.add_class_method(:the_method) { 10 }
25
+ # builder.build
26
+ # builder.add_class_method(:the_method) { 20 }
27
+ # end
28
+ #
29
+ # it do
30
+ # expect{ builder.build }.to change_class_method(:the_method).on(klass)
31
+ # end
32
+ # end
33
+ with_final_matcher :on, ChangeClassMethodOn
19
34
 
20
35
  private
21
36
 
@@ -28,15 +43,6 @@ class Sinclair
28
43
  'You should specify which class the method is being changed on' \
29
44
  "change_class_method(:#{method_name}).on(klass)"
30
45
  end
31
-
32
- # @private
33
- #
34
- # Class of the real matcher
35
- #
36
- # @return [Class<ChangeClassMethodOn>]
37
- def add_method_to_class
38
- ChangeClassMethodOn
39
- end
40
46
  end
41
47
  end
42
48
  end
@@ -7,15 +7,54 @@ class Sinclair
7
7
  #
8
8
  # AddInstanceMethod is able to build an instance of
9
9
  # {Sinclair::Matchers::ChangeInstanceMethodOn}
10
- class ChangeInstanceMethod < Base
11
- include AddMethod
12
-
13
- # @api public
10
+ class ChangeInstanceMethod < AddMethod
11
+ # @example Checking if an instance method has changed
12
+ # RSpec.configure do |config|
13
+ # config.include Sinclair::Matchers
14
+ # end
15
+ #
16
+ # class MyModel
17
+ # end
18
+ #
19
+ # RSpec.describe 'my test' do
20
+ # let(:builder) { Sinclair.new(klass) }
21
+ # let(:klass) { Class.new(MyModel) }
22
+ #
23
+ # before do
24
+ # builder.add_method(:the_method) { 10 }
25
+ # builder.build
26
+ # builder.add_method(:the_method) { 20 }
27
+ # end
28
+ #
29
+ # it do
30
+ # expect{ builder.build }.to change_method(:the_method).on(klass)
31
+ # end
32
+ # end
33
+ #
34
+ # @example Checking if an instance method has changed on an instance
35
+ # RSpec.configure do |config|
36
+ # config.include Sinclair::Matchers
37
+ # end
38
+ #
39
+ # class MyModel
40
+ # end
14
41
  #
15
- # Builds final matcher
42
+ # RSpec.describe 'my test' do
43
+ # let(:builder) { Sinclair.new(klass) }
44
+ # let(:instance) { klass.new }
45
+ # let(:klass) { Class.new(MyModel) }
16
46
  #
17
- # @return [Sinclair::Matchers::ChangeInstanceMethodOn]
18
- alias on to
47
+ # before do
48
+ # builder.add_method(:the_method) { 10 }
49
+ # builder.build
50
+ # builder.add_method(:the_method) { 20 }
51
+ # end
52
+ #
53
+ # it do
54
+ # expect{ builder.build }.to change_method(:the_method).on(instance)
55
+ # end
56
+ # end
57
+ with_final_matcher :on, ChangeInstanceMethodOn
19
58
 
20
59
  private
21
60
 
@@ -28,15 +67,6 @@ class Sinclair
28
67
  'You should specify which instance the method is being changed on' \
29
68
  "change_method(:#{method_name}).on(instance)"
30
69
  end
31
-
32
- # @private
33
- #
34
- # Class of the real matcher
35
- #
36
- # @return [Class<Sinclair::Matchers::Base>]
37
- def add_method_to_class
38
- ChangeInstanceMethodOn
39
- end
40
70
  end
41
71
  end
42
72
  end
@@ -6,8 +6,6 @@ class Sinclair
6
6
  #
7
7
  # Matchers module will have the DSL to be included in RSpec in order to have
8
8
  # access to the matchers
9
- #
10
- # @example (see Sinclair::Matchers::AddMethod#to)
11
9
  module Matchers
12
10
  autoload :Base, 'sinclair/matchers/base'
13
11
  autoload :AddInstanceMethod, 'sinclair/matchers/add_instance_method'
@@ -25,7 +23,6 @@ class Sinclair
25
23
 
26
24
  # DSL to AddInstanceMethod
27
25
  #
28
- # @example (see Sinclair::Matchers)
29
26
  # @example (see Sinclair::Matchers::AddInstanceMethod#to)
30
27
  #
31
28
  # @return [AddInstanceMethod] RSpec Matcher
@@ -35,7 +32,6 @@ class Sinclair
35
32
 
36
33
  # DSL to AddClassMethod
37
34
  #
38
- # @example (see Sinclair::Matchers)
39
35
  # @example (see Sinclair::Matchers::AddClassMethod#to)
40
36
  #
41
37
  # @return [AddClassMethod] RSpec Matcher
@@ -45,8 +41,7 @@ class Sinclair
45
41
 
46
42
  # DSL to ChangeInstanceMethod
47
43
  #
48
- # @example (see Sinclair::Matchers)
49
- # @example (see Sinclair::Matchers::ChangeInstanceMethod#to)
44
+ # @example (see Sinclair::Matchers::ChangeInstanceMethod#on)
50
45
  #
51
46
  # @return [ChangeInstanceMethod] RSpec Matcher
52
47
  def change_method(method_name)
@@ -55,8 +50,7 @@ class Sinclair
55
50
 
56
51
  # DSL to ChangeClassMethod
57
52
  #
58
- # @example (see Sinclair::Matchers)
59
- # @example (see Sinclair::Matchers::ChangeClassMethod#to)
53
+ # @example (see Sinclair::Matchers::ChangeClassMethod#on)
60
54
  #
61
55
  # @return [ChangeClassMethod] RSpec Matcher
62
56
  def change_class_method(method_name)
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ class MethodBuilder
5
+ # @api private
6
+ # @author darthjee
7
+ #
8
+ # Build a method based on a {MethodDefinition::CallDefinition}
9
+ class CallMethodBuilder < Base
10
+ # Builds the method
11
+ #
12
+ # @return [NilClass]
13
+ def build
14
+ klass.module_eval(code_line, __FILE__, __LINE__ + 1)
15
+ end
16
+
17
+ private
18
+
19
+ # @api private
20
+ # @private
21
+ #
22
+ # String to be evaluated when building the method
23
+ #
24
+ # This can be {code_string} or {class_code_string}
25
+ # @return (see MethodDefinition::CallDefinition#code_string)
26
+ def code_line
27
+ instance? ? code_string : class_code_string
28
+ end
29
+
30
+ delegate :code_string, :class_code_string, to: :definition
31
+
32
+ # @method code_string
33
+ # @private
34
+ # @api private
35
+ #
36
+ # Delegated from {MethodDefinition::CallDefinition}
37
+ #
38
+ # @see MethodDefinition::CallDefinition#code_string
39
+ # @return [String]
40
+
41
+ # @method class_code_string
42
+ # @private
43
+ # @api private
44
+ #
45
+ # @see MethodDefinition::CallDefinition#class_code_string
46
+ # @return [String]
47
+ end
48
+ end
49
+ end
@@ -9,6 +9,7 @@ class Sinclair
9
9
  autoload :Base, 'sinclair/method_builder/base'
10
10
  autoload :StringMethodBuilder, 'sinclair/method_builder/string_method_builder'
11
11
  autoload :BlockMethodBuilder, 'sinclair/method_builder/block_method_builder'
12
+ autoload :CallMethodBuilder, 'sinclair/method_builder/call_method_builder'
12
13
 
13
14
  CLASS_METHOD = :class
14
15
  INSTANCE_METHOD = :instance
@@ -53,8 +54,10 @@ class Sinclair
53
54
  def build_from_definition(definition, type)
54
55
  if definition.string?
55
56
  StringMethodBuilder.new(klass, definition, type: type).build
56
- else
57
+ elsif definition.block?
57
58
  BlockMethodBuilder.new(klass, definition, type: type).build
59
+ else
60
+ CallMethodBuilder.new(klass, definition, type: type).build
58
61
  end
59
62
  end
60
63
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ class MethodDefinition
5
+ # @api private
6
+ # @author darthjee
7
+ #
8
+ # Define a call of method to e done within the class
9
+ class CallDefinition < MethodDefinition
10
+ # @param method_name [Symbol] method to be called
11
+ # @param arguments [Array<Symbol,String>] parameters to be passed as
12
+ # arguments to the call
13
+ def initialize(method_name, *arguments)
14
+ @arguments = arguments
15
+ super(method_name)
16
+ end
17
+
18
+ default_value :block?, false
19
+ default_value :string?, false
20
+
21
+ # String to be executed within the class
22
+ # @return [String]
23
+ def code_string
24
+ "#{name} :#{arguments.join(', :')}"
25
+ end
26
+
27
+ # String to be executed within the class running code to change the class itself
28
+ #
29
+ # @see code_string
30
+ # @return [String]
31
+ def class_code_string
32
+ <<-CODE
33
+ class << self
34
+ #{code_string}
35
+ end
36
+ CODE
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :arguments
42
+
43
+ # @method arguments
44
+ # @api private
45
+ # @private
46
+ #
47
+ # parameters to be passed as arguments to the call
48
+ #
49
+ # @return [Array<Symbol,String>]
50
+ end
51
+ end
52
+ end
@@ -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.8.0'
4
+ VERSION = '1.10.0'
5
5
  end
data/lib/sinclair.rb CHANGED
@@ -88,8 +88,10 @@ class Sinclair
88
88
  autoload :ConfigClass, 'sinclair/config_class'
89
89
  autoload :ConfigFactory, 'sinclair/config_factory'
90
90
  autoload :Configurable, 'sinclair/configurable'
91
+ autoload :Comparable, 'sinclair/comparable'
91
92
  autoload :EnvSettable, 'sinclair/env_settable'
92
93
  autoload :Exception, 'sinclair/exception'
94
+ autoload :EqualsChecker, 'sinclair/equals_checker'
93
95
  autoload :InputHash, 'sinclair/input_hash'
94
96
  autoload :MethodBuilder, 'sinclair/method_builder'
95
97
  autoload :MethodDefinition, 'sinclair/method_definition'
data/sinclair.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem|
21
21
 
22
22
  gem.add_runtime_dependency 'activesupport', '~> 5.2.0'
23
23
 
24
- gem.add_development_dependency 'bundler', '2.3.20'
24
+ gem.add_development_dependency 'bundler', '2.3.25'
25
25
  gem.add_development_dependency 'pry', '0.14.1'
26
26
  gem.add_development_dependency 'pry-nav', '1.0.0'
27
27
  gem.add_development_dependency 'rake', '13.0.1'
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Comparable do
6
+ describe '#readme' do
7
+ describe '.comparable_by?' do
8
+ let(:model_class) do
9
+ Class.new(SampleModel) do
10
+ include Sinclair::Comparable
11
+
12
+ comparable_by :name
13
+ end
14
+ end
15
+
16
+ it 'regular usage' do
17
+ model1 = model_class.new(name: 'jack', age: 21)
18
+ model2 = model_class.new(name: 'jack', age: 23)
19
+
20
+ expect(model1 == model2).to be_truthy
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Comparable do
6
+ describe '#yard' do
7
+ describe '.comparable_by?' do
8
+ let(:model_class) do
9
+ Class.new(SampleModel) do
10
+ include Sinclair::Comparable
11
+
12
+ comparable_by :name
13
+ end
14
+ end
15
+
16
+ it 'regular usage' do
17
+ model1 = model_class.new(name: 'jack', age: 21)
18
+ model2 = model_class.new(name: 'jack', age: 23)
19
+
20
+ expect(model1 == model2).to be_truthy
21
+ end
22
+ end
23
+ end
24
+ end