mismatch-inspectable 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 671248717fed7d33e53f9e167f8e58d7344189fc8f2a77ab2b4fbcad603db188
4
- data.tar.gz: 3c45218dd7adbbde45baddfa827ee71de4f2781d1e50aa44ce465567eca98f92
3
+ metadata.gz: 0f2e711a465a93a6cf4a13d5f4536df7633f1d42e0a445c4e01558aa9c33e25e
4
+ data.tar.gz: 3c8439ca0fb0937d4c969d023b40a5bf2571933dc137469755e284c258100b96
5
5
  SHA512:
6
- metadata.gz: 365ef02e25f9e3d697d887ddab05c9fb14aeedaf9190a57d9ca2c6e197dcfeed5276c7e6d77f4282893aa5ca380035b6e967f43daf9899b451ca7ae3e8b5668c
7
- data.tar.gz: eefe5088fdfc55365c237f5b5fa84472b50352b61a21f7f88302e7bf156f52f4d40b9021da44cb93d152a3918d57ecf262781b77f39c093a3d15490864dcf8a9
6
+ metadata.gz: ca368e42844554edf57a7a450f4aadf8d2ac799b8ce711be465d2146c022456d379502dda5d030849ac0a8768254c67dbb7688a5db695e64de76aeb689240ab0
7
+ data.tar.gz: 309644a355739a5a97d9b0e6dd58683514698be2020e9deeab76863ac1f504901a1e44d395107cbd58d1b952d870b70281e5fa450fa21f4b2906c7b31cdfb413
data/README.md ADDED
@@ -0,0 +1,217 @@
1
+ The `MismatchInspectable` module provides a way to compare two objects and find the differences in their attributes. The module can be included in any class to compare objects of that class, and it provides options for the output format, recursion, and inclusion of class names in the output.
2
+
3
+
4
+ - [Usage](#usage)
5
+ - [Options](#options)
6
+ - [format](#format)
7
+ - [include\_class (default: true)](#include_class-default-true)
8
+ - [recursive (default: false)](#recursive-default-false)
9
+ - [Examples](#examples)
10
+ - [Example 1: Basic usage](#example-1-basic-usage)
11
+ - [Example 2: Using different formats](#example-2-using-different-formats)
12
+ - [Example 3: Comparing nested objects](#example-3-comparing-nested-objects)
13
+ - [Example 4: Excluding class from output](#example-4-excluding-class-from-output)
14
+ - [Example 5: Comparing objects with recursive set to false](#example-5-comparing-objects-with-recursive-set-to-false)
15
+ - [Example 6: Comparing objects when one is null](#example-6-comparing-objects-when-one-is-null)
16
+
17
+ ***
18
+ ## Usage
19
+
20
+ To use `MismatchInspectable`, first include it in your class and list the attributes you want to compare using the `inspect_mismatch_for` method.
21
+
22
+ ```ruby
23
+ class Thing
24
+ include MismatchInspectable
25
+
26
+ inspect_mismatch_for :color, :shape, :is_cool
27
+
28
+ def initialize(color, shape, is_cool)
29
+ @color = color
30
+ @shape = shape
31
+ @is_cool = is_cool
32
+ end
33
+
34
+ # Your class implementation
35
+ end
36
+ ```
37
+
38
+ Then, to compare two objects of the class, call the inspect_mismatch method on one of the objects and pass the other object as an argument.
39
+
40
+ ```ruby
41
+ thing1 = Thing.new("red", "circle", true)
42
+ thing2 = Thing.new("blue", "circle", false)
43
+
44
+ mismatch = thing1.inspect_mismatch(thing2)
45
+ ```
46
+
47
+ ***
48
+ ## Options
49
+
50
+ ### format
51
+ You can choose from three different output formats: :array, :hash, or :object. The default format is :array.
52
+
53
+
54
+ • :array format example:
55
+
56
+ ```ruby
57
+ [
58
+ ['Thing#color', 'red', 'blue'],
59
+ ['Thing#is_cool', true, false]
60
+ ]
61
+ ```
62
+
63
+ • :hash format example:
64
+ ```ruby
65
+ {
66
+ 'Thing#color' => ['red', 'blue'],
67
+ 'Thing#is_cool' => [true, false]
68
+ }
69
+ ```
70
+
71
+ :object format example:
72
+ ```ruby
73
+ {
74
+ Thing: {
75
+ color: ['red', 'blue'],
76
+ is_cool: [true, false]
77
+ }
78
+ }
79
+ ```
80
+
81
+ ### include_class (default: true)
82
+
83
+ When this option is set to true, the comparison will include the class in the
84
+ output for the objects being compared. If the objects being compared have
85
+ different classes, a mismatch will always be reported, regardless of this flag
86
+ being set. If set to false, you will simply not see the class names in the
87
+ output.
88
+
89
+
90
+ ### recursive (default: false)
91
+ When this option is set to true, the comparison will be performed recursively on
92
+ all instance variables which are passed to `inspect_mismatch_for`. If any of the
93
+ instance variables are also objects that include the `MismatchInspectable`
94
+ module, their `inspect_mismatch` method will be called with the same options. If
95
+ set to false,the comparison will only be performed on the top-level instance
96
+ variables which are passed to `inspect_mismatch_for` and will not delve deeper
97
+ into the objects. Instead it will do a simple comparison of said instance
98
+ variables.
99
+
100
+ ***
101
+ ## Examples
102
+ ### Example 1: Basic usage
103
+
104
+ ```ruby
105
+ require "mismatch_inspectable"
106
+
107
+ class Car
108
+ include MismatchInspectable
109
+
110
+ inspect_mismatch_for :make, :model, :year
111
+
112
+ def initialize(make, model, year)
113
+ @make = make
114
+ @model = model
115
+ @year = year
116
+ end
117
+
118
+ attr_accessor :make, :model, :year
119
+ end
120
+
121
+ car1 = Car.new("Toyota", "Camry", 2020)
122
+ car2 = Car.new("Toyota", "Corolla", 2020)
123
+ mismatches = car1.inspect_mismatch(car2)
124
+
125
+ # Output with default format: array
126
+ # [["Car#model", "Camry", "Corolla"]]
127
+ ```
128
+
129
+
130
+ ### Example 2: Using different formats
131
+
132
+ ```ruby
133
+ mismatches_hash = car1.inspect_mismatch(car2, format: :hash)
134
+
135
+ # Output with format: hash
136
+ # {"Car#model" => ["Camry", "Corolla"]}
137
+
138
+ mismatches_object = car1.inspect_mismatch(car2, format: :object)
139
+
140
+ # Output with format: object
141
+ # {Car: {model: ["Camry", "Corolla"]}}
142
+ ```
143
+
144
+ ### Example 3: Comparing nested objects
145
+
146
+ ```ruby
147
+ class Owner
148
+ include MismatchInspectable
149
+
150
+ inspect_mismatch_for :name, :age
151
+
152
+ def initialize(name, age)
153
+ @name = name
154
+ @age = age
155
+ end
156
+
157
+ attr_accessor :name, :age
158
+ end
159
+
160
+ class Pet
161
+ include MismatchInspectable
162
+
163
+ inspect_mismatch_for :name, :species, :owner
164
+
165
+ def initialize(name, species, owner)
166
+ @name = name
167
+ @species = species
168
+ @owner = owner
169
+ end
170
+
171
+ attr_accessor :name, :species, :owner
172
+ end
173
+
174
+ owner1 = Owner.new("Alice", 30)
175
+ owner2 = Owner.new("Bob", 35)
176
+ pet1 = Pet.new("Fluffy", "cat", owner1)
177
+ pet2 = Pet.new("Fluffy", "cat", owner2)
178
+
179
+ mismatches = pet1.inspect_mismatch(pet2, recursive: true)
180
+
181
+ # Output with recursive: true
182
+ # [["Pet#owner.Owner#name", "Alice", "Bob"], ["Pet#owner.Owner#age", 30, 35]]
183
+ ```
184
+
185
+
186
+ ### Example 4: Excluding class from output
187
+ ```ruby
188
+ mismatches_no_class = pet1.inspect_mismatch(pet2, recursive: true, include_class: false)
189
+
190
+ # Output with include_class: false
191
+ # [["owner.name", "Alice", "Bob"], ["owner.age", 30, 35]]
192
+ ```
193
+
194
+ ### Example 5: Comparing objects with recursive set to false
195
+
196
+ ```ruby
197
+ pet5 = Pet.new("Max", "dog", owner1)
198
+ pet6 = Pet.new("Max", "dog", owner2)
199
+
200
+ mismatches_non_recursive = pet5.inspect_mismatch(pet6, recursive: false)
201
+
202
+ # Output with recursive: false and non-nil owners
203
+ # [["Pet#owner", #<Owner:0x00007fe3d206d3c8 @name="Alice", @age=30>, #<Owner:0x00007fe3d207d3c8 @name="Bob", @age=35>]]
204
+ ```
205
+
206
+ ### Example 6: Comparing objects when one is null
207
+
208
+ ```ruby
209
+ owner3 = Owner.new("Charlie", 40)
210
+ pet3 = Pet.new("Buddy", "dog", owner3)
211
+ pet4 = Pet.new("Buddy", "dog", nil)
212
+
213
+ mismatches_owner_nil = pet3.inspect_mismatch(pet4, recursive: true)
214
+
215
+ # Output with recursive: true and one nil owner
216
+ # [["Pet#owner", #<Owner:0x00007fe3d206d3c8 @name="Charlie", @age=40>, nil]]
217
+ ```
@@ -1,14 +1,10 @@
1
- require_relative 'hash_formatter'
2
- require_relative 'array_formatter'
3
- require_relative 'object_formatter'
1
+ require_relative "hash_formatter"
2
+ require_relative "array_formatter"
3
+ require_relative "object_formatter"
4
4
 
5
5
  module MismatchInspectable
6
- class << self
7
- def included(base)
8
- class << base
9
- include ClassMethods
10
- end
11
- end
6
+ def self.included(target_class)
7
+ target_class.extend ClassMethods
12
8
  end
13
9
 
14
10
  module ClassMethods
@@ -19,15 +15,16 @@ module MismatchInspectable
19
15
  end
20
16
  end
21
17
 
22
- def inspect_mismatch(other, recursive: false, include_class: true, prefix: '', format: :array)
18
+ class MissingCompareMethodsError < StandardError
19
+ def initialize(klass)
20
+ super("The class #{klass} does not have methods to compare. Define methods with `inspect_mismatch_for`.")
21
+ end
22
+ end
23
+
24
+ def inspect_mismatch(other, recursive: false, include_class: true, prefix: "", format: :array)
23
25
  return if self.class != other.class
24
26
 
25
- formatter = case format
26
- when :hash then HashFormatter.new
27
- when :array then ArrayFormatter.new
28
- when :object then ObjectFormatter.new
29
- else raise ArgumentError, "Invalid format: #{format}"
30
- end
27
+ formatter = select_formatter(format)
31
28
 
32
29
  process_attributes(formatter, other, recursive, include_class, prefix, format)
33
30
  formatter.mismatches
@@ -39,8 +36,20 @@ module MismatchInspectable
39
36
 
40
37
  private
41
38
 
39
+ def select_formatter(format)
40
+ case format
41
+ when :hash then HashFormatter.new
42
+ when :array then ArrayFormatter.new
43
+ when :object then ObjectFormatter.new
44
+ else raise ArgumentError, "Invalid format: #{format}"
45
+ end
46
+ end
47
+
48
+ # rubocop:disable Metrics/ParameterLists
42
49
  def process_attributes(formatter, other, recursive, include_class, prefix, format)
43
- self.class.compare_methods.each do |attribute|
50
+ raise MissingCompareMethodsError if compare_methods.nil?
51
+
52
+ compare_methods.each do |attribute|
44
53
  curr_val = __send__(attribute)
45
54
  other_val = other.__send__(attribute)
46
55
 
@@ -61,19 +70,26 @@ module MismatchInspectable
61
70
  nested_mismatches = curr_val.inspect_mismatch(
62
71
  other_val,
63
72
  recursive: true,
64
- include_class: include_class,
65
- prefix: prefix + "#{attribute}.",
66
- format: format
73
+ include_class:,
74
+ prefix: "#{prefix}#{attribute}.",
75
+ format:
67
76
  )
68
77
 
69
- formatter.merge_mismatches(nested_mismatches) unless nested_mismatches?(nested_mismatches)
78
+ formatter.merge_mismatches(nested_mismatches) unless no_nested_mismatches?(nested_mismatches)
70
79
  end
80
+ # rubocop:enable Metrics/ParameterLists
71
81
 
72
82
  def update_prefix(include_class, prefix)
73
- include_class ? "#{prefix}#{self.class}#" : prefix
83
+ comparable_prefix = get_comparable_prefix(prefix)
84
+ include_class && comparable_prefix != "#{self.class}#" ? "#{prefix}#{self.class}#" : prefix
74
85
  end
75
86
 
76
- def nested_mismatches?(mismatches)
87
+ def no_nested_mismatches?(mismatches)
77
88
  mismatches.nil? || mismatches.empty?
78
89
  end
90
+
91
+ def get_comparable_prefix(prefix)
92
+ prefixes = prefix.split(".")
93
+ prefixes.length >= 2 ? prefixes[-1] : prefix
94
+ end
79
95
  end
@@ -1,4 +1,4 @@
1
- require_relative 'deep_merge'
1
+ require_relative "deep_merge"
2
2
 
3
3
  class ObjectFormatter
4
4
  def initialize
@@ -8,7 +8,7 @@ class ObjectFormatter
8
8
  attr_reader :mismatches
9
9
 
10
10
  def add_mismatch(prefix, attribute, curr_val, other_val)
11
- prefix_parts = prefix.split('.').flat_map { |part| part.split('#') }.collect(&:to_sym)
11
+ prefix_parts = prefix.split(".").flat_map { |part| part.split("#") }.collect(&:to_sym)
12
12
  curr = mismatches
13
13
 
14
14
  prefix_parts.each do |part|
@@ -0,0 +1,3 @@
1
+ module MismatchInspectable
2
+ VERSION = "0.1.2".freeze
3
+ end
@@ -1 +1 @@
1
- require_relative 'mismatch_inspectable/mismatch_inspectable'
1
+ require_relative "mismatch_inspectable/mismatch_inspectable"
data/spec/examples.txt CHANGED
@@ -1,22 +1,24 @@
1
1
  example_id | status | run_time |
2
2
  ---------------------------------------------------------- | ------ | --------------- |
3
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:1:1] | passed | 0.0009 seconds |
4
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:1] | passed | 0.00075 seconds |
5
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:2:1] | passed | 0.00015 seconds |
6
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:1:1] | passed | 0.0002 seconds |
7
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:1:2:1] | passed | 0.00016 seconds |
8
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:2:1] | passed | 0.00015 seconds |
9
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:2:2:1] | passed | 0.00016 seconds |
10
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:2:1] | passed | 0.00013 seconds |
11
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:1:1] | passed | 0.00011 seconds |
12
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:2:1] | passed | 0.00072 seconds |
13
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:3:1] | passed | 0.00036 seconds |
14
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:1:1:1] | passed | 0.00023 seconds |
15
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:1:2:1] | passed | 0.00018 seconds |
16
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:1:3:1] | passed | 0.00015 seconds |
17
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:2:1:1] | passed | 0.00023 seconds |
18
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:2:2:1] | passed | 0.00026 seconds |
19
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:2:3:1] | passed | 0.00061 seconds |
20
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:2:1:1] | passed | 0.00016 seconds |
21
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:2:2:1] | passed | 0.00034 seconds |
22
- ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:2:3:1] | passed | 0.00025 seconds |
3
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:1:1] | passed | 0.00088 seconds |
4
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:1] | passed | 0.00045 seconds |
5
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:2:1] | passed | 0.00012 seconds |
6
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:1:1] | passed | 0.00146 seconds |
7
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:1:2:1] | passed | 0.00014 seconds |
8
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:2:1] | passed | 0.00012 seconds |
9
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:2:2:1] | passed | 0.00013 seconds |
10
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:2:1] | passed | 0.0001 seconds |
11
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:1:1] | passed | 0.00009 seconds |
12
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:2:1] | passed | 0.0001 seconds |
13
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:3:1] | passed | 0.00011 seconds |
14
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:1:1:1] | passed | 0.00012 seconds |
15
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:1:2:1] | passed | 0.00011 seconds |
16
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:1:3:1] | passed | 0.00033 seconds |
17
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:2:1:1] | passed | 0.00012 seconds |
18
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:2:2:1] | passed | 0.00013 seconds |
19
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:2:3:1] | passed | 0.00015 seconds |
20
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:2:1:1] | passed | 0.0001 seconds |
21
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:2:2:1] | passed | 0.00011 seconds |
22
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:2:3:1] | passed | 0.00011 seconds |
23
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:3:1:1] | passed | 0.00017 seconds |
24
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:3:2:1] | passed | 0.0002 seconds |
@@ -1,6 +1,6 @@
1
- require 'rspec'
2
- require 'spec_helper'
3
- require 'mismatch_inspectable/mismatch_inspectable'
1
+ require "rspec"
2
+ require "spec_helper"
3
+ require "mismatch_inspectable"
4
4
 
5
5
  class TestClass
6
6
  include MismatchInspectable
@@ -24,9 +24,9 @@ RSpec.describe MismatchInspectable do
24
24
  let(:format) { :array }
25
25
  let(:recursive) { false }
26
26
 
27
- let(:name) { 'Tyler' }
27
+ let(:name) { "Tyler" }
28
28
  let(:age) { 29 }
29
- let(:address) { '123 Cool St' }
29
+ let(:address) { "123 Cool St" }
30
30
 
31
31
  before do
32
32
  object1.name = name
@@ -38,38 +38,38 @@ RSpec.describe MismatchInspectable do
38
38
  object2.address = address
39
39
  end
40
40
 
41
- describe '#inspect_mismatch' do
42
- context 'when objects are of different classes' do
41
+ describe "#inspect_mismatch" do
42
+ context "when objects are of different classes" do
43
43
  let(:object3) { Object.new }
44
44
 
45
- it 'returns nil' do
45
+ it "returns nil" do
46
46
  expect(object1.inspect_mismatch(object3)).to be_nil
47
47
  end
48
48
  end
49
49
 
50
- context 'when objects are of the same class' do
51
- context 'with top level attributes that match' do
52
- it 'returns an empty array' do
50
+ context "when objects are of the same class" do
51
+ context "with top level attributes that match" do
52
+ it "returns an empty array" do
53
53
  expect(object1.inspect_mismatch(object1)).to eq([])
54
54
  end
55
55
 
56
- context 'when format is set to :hash' do
56
+ context "when format is set to :hash" do
57
57
  let(:format) { :hash }
58
58
 
59
- it 'returns an empty hash' do
60
- expect(object1.inspect_mismatch(object1, format: format)).to eq({})
59
+ it "returns an empty hash" do
60
+ expect(object1.inspect_mismatch(object1, format:)).to eq({})
61
61
  end
62
62
  end
63
63
 
64
- context 'with nested inspectable attributes' do
64
+ context "with nested inspectable attributes" do
65
65
  let(:recursive) { true }
66
66
 
67
- context 'when objects have nested attributes with the same class' do
67
+ context "when objects have nested attributes with the same class" do
68
68
  let(:nested1) { NestedTestClass.new }
69
69
  let(:nested2) { NestedTestClass.new }
70
70
 
71
- let(:city) { 'ATX' }
72
- let(:country) { 'USA' }
71
+ let(:city) { "ATX" }
72
+ let(:country) { "USA" }
73
73
 
74
74
  before do
75
75
  nested1.city = city
@@ -82,42 +82,42 @@ RSpec.describe MismatchInspectable do
82
82
  object2.nested = nested2
83
83
  end
84
84
 
85
- context 'when nested attributes match' do
86
- it 'returns an empty array' do
87
- expect(object1.inspect_mismatch(object2, recursive: recursive)).to eq([])
85
+ context "when nested attributes match" do
86
+ it "returns an empty array" do
87
+ expect(object1.inspect_mismatch(object2, recursive:)).to eq([])
88
88
  end
89
89
 
90
- context 'when format is set to :hash' do
90
+ context "when format is set to :hash" do
91
91
  let(:format) { :hash }
92
- it 'returns an empty hash' do
93
- expect(object1.inspect_mismatch(object2, format: format, recursive: recursive)).to eq({})
92
+ it "returns an empty hash" do
93
+ expect(object1.inspect_mismatch(object2, format:, recursive:)).to eq({})
94
94
  end
95
95
  end
96
96
  end
97
97
 
98
- context 'when nested attributes have different values' do
99
- before { nested2.city = 'Phoenix' }
98
+ context "when nested attributes have different values" do
99
+ before { nested2.city = "Phoenix" }
100
100
 
101
- it 'returns an array of mismatched nested attributes with recursive flag enabled' do
101
+ it "returns an array of mismatched nested attributes with recursive flag enabled" do
102
102
  expected = [
103
- ['nested.NestedTestClass#city', 'ATX', 'Phoenix']
103
+ ["nested.NestedTestClass#city", "ATX", "Phoenix"]
104
104
  ]
105
- expect(object1.inspect_mismatch(object2, recursive: recursive)).to eq(expected)
105
+ expect(object1.inspect_mismatch(object2, recursive:)).to eq(expected)
106
106
  end
107
107
 
108
- context 'when format is set to :hash' do
108
+ context "when format is set to :hash" do
109
109
  let(:format) { :hash }
110
- it 'returns a hash of mismatched nested attributes with recursive flag enabled' do
110
+ it "returns a hash of mismatched nested attributes with recursive flag enabled" do
111
111
  expected = {
112
- 'nested.NestedTestClass#city' => %w[ATX Phoenix]
112
+ "nested.NestedTestClass#city" => %w[ATX Phoenix]
113
113
  }
114
- expect(object1.inspect_mismatch(object2, format: format, recursive: recursive)).to eq(expected)
114
+ expect(object1.inspect_mismatch(object2, format:, recursive:)).to eq(expected)
115
115
  end
116
116
  end
117
117
  end
118
118
  end
119
119
 
120
- context 'when nested attributes have different classes' do
120
+ context "when nested attributes have different classes" do
121
121
  let(:nested1) { NestedTestClass.new }
122
122
  let(:nested2) { TestClass.new }
123
123
 
@@ -126,8 +126,8 @@ RSpec.describe MismatchInspectable do
126
126
  object2.nested = nested2
127
127
  end
128
128
 
129
- it 'returns nil for mismatched nested attribute with recursive flag enabled' do
130
- expect(object1.inspect_mismatch(object2, recursive: recursive)).to eq([])
129
+ it "returns nil for mismatched nested attribute with recursive flag enabled" do
130
+ expect(object1.inspect_mismatch(object2, recursive:)).to eq([])
131
131
  end
132
132
  end
133
133
  end
@@ -136,117 +136,117 @@ RSpec.describe MismatchInspectable do
136
136
  context "with top level attributes that don't match" do
137
137
  before { object2.age = 30 }
138
138
 
139
- context 'when format is set to :array (default)' do
140
- it 'returns an array of mismatched attributes' do
139
+ context "when format is set to :array (default)" do
140
+ it "returns an array of mismatched attributes" do
141
141
  expected = [
142
- ['TestClass#age', 29, 30]
142
+ ["TestClass#age", 29, 30]
143
143
  ]
144
144
  expect(object1.inspect_mismatch(object2)).to eq(expected)
145
145
  end
146
146
  end
147
147
 
148
- context 'when format is set to :hash' do
148
+ context "when format is set to :hash" do
149
149
  let(:format) { :hash }
150
- it 'returns a hash of mismatched attributes' do
150
+ it "returns a hash of mismatched attributes" do
151
151
  expected = {
152
- 'TestClass#age' => [29, 30]
152
+ "TestClass#age" => [29, 30]
153
153
  }
154
- expect(object1.inspect_mismatch(object2, format: format)).to eq(expected)
154
+ expect(object1.inspect_mismatch(object2, format:)).to eq(expected)
155
155
  end
156
156
  end
157
157
 
158
- context 'when format is set to :object' do
158
+ context "when format is set to :object" do
159
159
  let(:format) { :object }
160
- it 'returns an object of mismatched attributes' do
160
+ it "returns an object of mismatched attributes" do
161
161
  expected = {
162
162
  TestClass: {
163
163
  age: [29, 30]
164
164
  }
165
165
  }
166
- expect(object1.inspect_mismatch(object2, format: format)).to eq(expected)
166
+ expect(object1.inspect_mismatch(object2, format:)).to eq(expected)
167
167
  end
168
168
  end
169
169
 
170
- context 'with nested inspectable attributes' do
170
+ context "with nested inspectable attributes" do
171
171
  let(:recursive) { true }
172
- context 'when objects have nested attributes with the same class' do
172
+ context "when objects have nested attributes with the same class" do
173
173
  let(:nested1) { NestedTestClass.new }
174
174
  let(:nested2) { NestedTestClass.new }
175
175
 
176
- let(:city) { 'ATX' }
177
- let(:country) { 'USA' }
176
+ let(:city) { "ATX" }
177
+ let(:country) { "USA" }
178
178
 
179
179
  before do
180
180
  nested1.city = city
181
181
  nested1.country = country
182
182
 
183
- nested2.city = 'Phoenix'
183
+ nested2.city = "Phoenix"
184
184
  nested2.country = country
185
185
 
186
186
  object1.nested = nested1
187
187
  object2.nested = nested1
188
188
  end
189
189
 
190
- context 'when nested attributes match' do
191
- context 'when format is set to :array (default)' do
192
- it 'returns the top-level mismatched attributes' do
190
+ context "when nested attributes match" do
191
+ context "when format is set to :array (default)" do
192
+ it "returns the top-level mismatched attributes" do
193
193
  expected = [
194
- ['TestClass#age', 29, 30]
194
+ ["TestClass#age", 29, 30]
195
195
  ]
196
196
  expect(object1.inspect_mismatch(object2)).to eq(expected)
197
197
  end
198
198
  end
199
199
 
200
- context 'when format is set to :hash' do
200
+ context "when format is set to :hash" do
201
201
  let(:format) { :hash }
202
- it 'returns the top-level mismatched attributes' do
202
+ it "returns the top-level mismatched attributes" do
203
203
  expected = {
204
- 'TestClass#age' => [29, 30]
204
+ "TestClass#age" => [29, 30]
205
205
  }
206
- expect(object1.inspect_mismatch(object2, format: format)).to eq(expected)
206
+ expect(object1.inspect_mismatch(object2, format:)).to eq(expected)
207
207
  end
208
208
  end
209
209
 
210
- context 'when format is set to :object' do
210
+ context "when format is set to :object" do
211
211
  let(:format) { :object }
212
- it 'returns the top-level mismatched attributes' do
212
+ it "returns the top-level mismatched attributes" do
213
213
  expected = {
214
214
  TestClass: {
215
215
  age: [29, 30]
216
216
  }
217
217
  }
218
- expect(object1.inspect_mismatch(object2, format: format)).to eq(expected)
218
+ expect(object1.inspect_mismatch(object2, format:)).to eq(expected)
219
219
  end
220
220
  end
221
221
  end
222
222
 
223
- context 'when nested attributes have different values' do
223
+ context "when nested attributes have different values" do
224
224
  before { object2.nested = nested2 }
225
225
 
226
- context 'when format is set to :array (default)' do
227
- it 'returns an array of mismatched nested attributes with recursive flag enabled' do
226
+ context "when format is set to :array (default)" do
227
+ it "returns an array of mismatched nested attributes with recursive flag enabled" do
228
228
  expected = [
229
- ['TestClass#age', 29, 30],
230
- ['TestClass#nested.NestedTestClass#city', 'ATX', 'Phoenix']
229
+ ["TestClass#age", 29, 30],
230
+ ["TestClass#nested.NestedTestClass#city", "ATX", "Phoenix"]
231
231
  ]
232
232
  expect(object1.inspect_mismatch(object2, recursive: true)).to eq(expected)
233
233
  end
234
234
  end
235
235
 
236
- context 'when format is set to :hash' do
236
+ context "when format is set to :hash" do
237
237
  let(:format) { :hash }
238
- it 'returns a hash of mismatched nested attributes with recursive flag enabled' do
238
+ it "returns a hash of mismatched nested attributes with recursive flag enabled" do
239
239
  expected = {
240
- 'TestClass#age' => [29, 30],
241
- 'TestClass#nested.NestedTestClass#city' => %w[ATX Phoenix]
240
+ "TestClass#age" => [29, 30],
241
+ "TestClass#nested.NestedTestClass#city" => %w[ATX Phoenix]
242
242
  }
243
- expect(object1.inspect_mismatch(object2, recursive: recursive, format: format)).to eq(expected)
243
+ expect(object1.inspect_mismatch(object2, recursive:, format:)).to eq(expected)
244
244
  end
245
245
  end
246
246
 
247
- context 'when format is set to :object' do
247
+ context "when format is set to :object" do
248
248
  let(:format) { :object }
249
- it 'returns an object of mismatched nested attributes with recursive flag enabled' do
249
+ it "returns an object of mismatched nested attributes with recursive flag enabled" do
250
250
  expected = {
251
251
  TestClass: {
252
252
  age: [29, 30],
@@ -257,13 +257,13 @@ RSpec.describe MismatchInspectable do
257
257
  }
258
258
  }
259
259
  }
260
- expect(object1.inspect_mismatch(object2, recursive: recursive, format: format)).to eq(expected)
260
+ expect(object1.inspect_mismatch(object2, recursive:, format:)).to eq(expected)
261
261
  end
262
262
  end
263
263
  end
264
264
  end
265
265
 
266
- context 'when nested attributes have different classes' do
266
+ context "when nested attributes have different classes" do
267
267
  let(:nested1) { NestedTestClass.new }
268
268
  let(:nested2) { TestClass.new }
269
269
 
@@ -272,31 +272,31 @@ RSpec.describe MismatchInspectable do
272
272
  object2.nested = nested2
273
273
  end
274
274
 
275
- context 'when format is set to :array (default)' do
276
- it 'returns nil for mismatched nested attribute with recursive flag enabled' do
277
- expect(object1.inspect_mismatch(object2, recursive: recursive)).to eq(
275
+ context "when format is set to :array (default)" do
276
+ it "returns nil for mismatched nested attribute with recursive flag enabled" do
277
+ expect(object1.inspect_mismatch(object2, recursive:)).to eq(
278
278
  [
279
- ['TestClass#age', 29, 30]
279
+ ["TestClass#age", 29, 30]
280
280
  ]
281
281
  )
282
282
  end
283
283
  end
284
284
 
285
- context 'when format is set to :hash' do
285
+ context "when format is set to :hash" do
286
286
  let(:format) { :hash }
287
- it 'returns nil for mismatched nested attribute with recursive flag enabled' do
288
- expect(object1.inspect_mismatch(object2, recursive: recursive, format: format)).to eq(
287
+ it "returns nil for mismatched nested attribute with recursive flag enabled" do
288
+ expect(object1.inspect_mismatch(object2, recursive:, format:)).to eq(
289
289
  {
290
- 'TestClass#age' => [29, 30]
290
+ "TestClass#age" => [29, 30]
291
291
  }
292
292
  )
293
293
  end
294
294
  end
295
295
 
296
- context 'when format is set to :object' do
296
+ context "when format is set to :object" do
297
297
  let(:format) { :object }
298
- it 'returns nil for mismatched nested attribute with recursive flag enabled' do
299
- expect(object1.inspect_mismatch(object2, recursive: recursive, format: format)).to eq(
298
+ it "returns nil for mismatched nested attribute with recursive flag enabled" do
299
+ expect(object1.inspect_mismatch(object2, recursive:, format:)).to eq(
300
300
  {
301
301
  TestClass: {
302
302
  age: [29, 30]
@@ -306,6 +306,106 @@ RSpec.describe MismatchInspectable do
306
306
  end
307
307
  end
308
308
  end
309
+
310
+ context "with multiple levels of nested inspectable attributes" do
311
+ # rubocop:disable Lint/ConstantDefinitionInBlock
312
+ class Thing
313
+ include MismatchInspectable
314
+
315
+ inspect_mismatch_for :color, :shape, :is_cool, :nested_thing
316
+
317
+ def initialize(color: nil, shape: nil, is_cool: nil, nested_thing: nil)
318
+ @color = color
319
+ @shape = shape
320
+ @is_cool = is_cool
321
+ @nested_thing = nested_thing
322
+ end
323
+
324
+ def inspect
325
+ "<Thing>"
326
+ end
327
+
328
+ attr_accessor :color, :shape, :is_cool, :nested_thing
329
+ end
330
+ # rubocop:enable Lint/ConstantDefinitionInBlock
331
+
332
+ let(:thing1) do
333
+ Thing.new(
334
+ color: "blue",
335
+ shape: "square",
336
+ is_cool: false,
337
+ nested_thing: Thing.new(
338
+ color: "blue",
339
+ shape: "oval",
340
+ is_cool: true,
341
+ nested_thing: Thing.new(
342
+ color: "silver",
343
+ shape: "oval",
344
+ is_cool: true
345
+ )
346
+ )
347
+ )
348
+ end
349
+
350
+ let(:thing2) do
351
+ Thing.new(
352
+ color: "green",
353
+ shape: "oval",
354
+ is_cool: false,
355
+ nested_thing: Thing.new(
356
+ color: "red",
357
+ shape: "another shape",
358
+ is_cool: true,
359
+ nested_thing: Thing.new(
360
+ color: "blue",
361
+ shape: "oval",
362
+ is_cool: false
363
+ )
364
+ )
365
+ )
366
+ end
367
+ context "with recursive flag disabled" do
368
+ it "returns the mismatched top-level attributes" do
369
+ expect(thing1.inspect_mismatch(thing2, format: :object)).to eq(
370
+ {
371
+ Thing: {
372
+ color: %w[blue green],
373
+ shape: %w[square oval],
374
+ nested_thing: [thing1.nested_thing, thing2.nested_thing] # [<Thing>, <Thing>]
375
+ }
376
+ }
377
+ )
378
+ end
379
+ end
380
+
381
+ context "with recursive flag enabled" do
382
+ let(:recursive) { true }
383
+ it "returns the mismatched attributes with the appropriate nesting" do
384
+ expect(thing1.inspect_mismatch(thing2, recursive:, format: :object)).to eq(
385
+ {
386
+ Thing: {
387
+ color: %w[blue green],
388
+ shape: %w[
389
+ square oval
390
+ ],
391
+ nested_thing: {
392
+ Thing: {
393
+ color: %w[blue red],
394
+ shape: ["oval", "another shape"],
395
+ nested_thing: {
396
+ Thing: {
397
+ color: %w[silver blue],
398
+ is_cool: [true, false]
399
+ }
400
+ }
401
+ }
402
+ }
403
+ }
404
+ }
405
+ )
406
+ end
407
+ end
408
+ end
309
409
  end
310
410
  end
311
411
  end
data/spec/spec_helper.rb CHANGED
@@ -56,7 +56,7 @@ RSpec.configure do |config|
56
56
  # # Allows RSpec to persist some state between runs in order to support
57
57
  # # the `--only-failures` and `--next-failure` CLI options. We recommend
58
58
  # # you configure your source control system to ignore this file.
59
- config.example_status_persistence_file_path = 'spec/examples.txt'
59
+ config.example_status_persistence_file_path = "spec/examples.txt"
60
60
  #
61
61
  # # Limits the available syntax to the non-monkey patched syntax that is
62
62
  # # recommended. For more details, see:
metadata CHANGED
@@ -1,31 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mismatch-inspectable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Rhodes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-21 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rspec
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- description: A library that includes a module that can print mismatched values for
28
- any class that includes it. Supports recursive inspection of nested objects.
11
+ date: 2023-06-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ A library that includes a module that can print mismatched
15
+ values for any class that includes it. Supports recursive inspection of nested
16
+ objects.
29
17
  email:
30
18
  - tyler.rhodes@aya.yale.edu
31
19
  executables: []
@@ -33,39 +21,39 @@ extensions: []
33
21
  extra_rdoc_files: []
34
22
  files:
35
23
  - LICENSE.md
24
+ - README.md
36
25
  - lib/mismatch_inspectable.rb
37
26
  - lib/mismatch_inspectable/array_formatter.rb
38
27
  - lib/mismatch_inspectable/deep_merge.rb
39
28
  - lib/mismatch_inspectable/hash_formatter.rb
40
29
  - lib/mismatch_inspectable/mismatch_inspectable.rb
41
30
  - lib/mismatch_inspectable/object_formatter.rb
31
+ - lib/mismatch_inspectable/version.rb
42
32
  - spec/examples.txt
43
33
  - spec/lib/mismatch_inspectable_spec.rb
44
34
  - spec/spec_helper.rb
45
35
  homepage: https://github.com/tyleCaineRhodes/mismatch-inspectable
46
36
  licenses:
47
37
  - MIT
48
- metadata: {}
38
+ metadata:
39
+ rubygems_mfa_required: 'true'
49
40
  post_install_message:
50
41
  rdoc_options: []
51
42
  require_paths:
52
43
  - lib
53
44
  required_ruby_version: !ruby/object:Gem::Requirement
54
45
  requirements:
55
- - - ">="
46
+ - - '='
56
47
  - !ruby/object:Gem::Version
57
- version: '0'
48
+ version: 3.2.1
58
49
  required_rubygems_version: !ruby/object:Gem::Requirement
59
50
  requirements:
60
51
  - - ">="
61
52
  - !ruby/object:Gem::Version
62
53
  version: '0'
63
54
  requirements: []
64
- rubygems_version: 3.2.3
55
+ rubygems_version: 3.4.6
65
56
  signing_key:
66
57
  specification_version: 4
67
58
  summary: A library for easily printing and debugging mismatched values
68
- test_files:
69
- - spec/examples.txt
70
- - spec/lib/mismatch_inspectable_spec.rb
71
- - spec/spec_helper.rb
59
+ test_files: []