mismatch-inspectable 0.1.0 → 0.1.1

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: 5d1a63392c8810b446da003f2a520b13ed9c820a9e38d825d2b1d90a9b56ac55
4
+ data.tar.gz: f5e50d3286ab7bcd0d75f584902272cbed5ddef4cc55b82e0709b4ab9228f25d
5
5
  SHA512:
6
- metadata.gz: 365ef02e25f9e3d697d887ddab05c9fb14aeedaf9190a57d9ca2c6e197dcfeed5276c7e6d77f4282893aa5ca380035b6e967f43daf9899b451ca7ae3e8b5668c
7
- data.tar.gz: eefe5088fdfc55365c237f5b5fa84472b50352b61a21f7f88302e7bf156f52f4d40b9021da44cb93d152a3918d57ecf262781b77f39c093a3d15490864dcf8a9
6
+ metadata.gz: 898f167833de14ad907bb1038d326b5b356fd3d7c0a7388f172f72b77f8a9aee8b084b8cf502ea42ddbb2f13a67aaa9b7b56f897eb294c7894a9d6cbc165695e
7
+ data.tar.gz: 550bfa28af86449bcd1931f520d99a1075cc3447baf9f00a9254e6f23113a0c5247eeaf3e6bc047c849bca35e9544e20768f084716b58f9604aa1dbc904efdd5
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
+ ```
@@ -19,15 +19,16 @@ module MismatchInspectable
19
19
  end
20
20
  end
21
21
 
22
+ class MissingCompareMethodsError < StandardError
23
+ def initialize(klass)
24
+ super("The class #{klass} does not have methods to compare. Define methods with `inspect_mismatch_for`.")
25
+ end
26
+ end
27
+
22
28
  def inspect_mismatch(other, recursive: false, include_class: true, prefix: '', format: :array)
23
29
  return if self.class != other.class
24
30
 
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
31
+ formatter = select_formatter(format)
31
32
 
32
33
  process_attributes(formatter, other, recursive, include_class, prefix, format)
33
34
  formatter.mismatches
@@ -39,8 +40,19 @@ module MismatchInspectable
39
40
 
40
41
  private
41
42
 
43
+ def select_formatter(format)
44
+ case format
45
+ when :hash then HashFormatter.new
46
+ when :array then ArrayFormatter.new
47
+ when :object then ObjectFormatter.new
48
+ else raise ArgumentError, "Invalid format: #{format}"
49
+ end
50
+ end
51
+
42
52
  def process_attributes(formatter, other, recursive, include_class, prefix, format)
43
- self.class.compare_methods.each do |attribute|
53
+ raise MissingCompareMethodsError if compare_methods.nil?
54
+
55
+ compare_methods.each do |attribute|
44
56
  curr_val = __send__(attribute)
45
57
  other_val = other.__send__(attribute)
46
58
 
@@ -62,18 +74,24 @@ module MismatchInspectable
62
74
  other_val,
63
75
  recursive: true,
64
76
  include_class: include_class,
65
- prefix: prefix + "#{attribute}.",
77
+ prefix: "#{prefix}#{attribute}.",
66
78
  format: format
67
79
  )
68
80
 
69
- formatter.merge_mismatches(nested_mismatches) unless nested_mismatches?(nested_mismatches)
81
+ formatter.merge_mismatches(nested_mismatches) unless no_nested_mismatches?(nested_mismatches)
70
82
  end
71
83
 
72
84
  def update_prefix(include_class, prefix)
73
- include_class ? "#{prefix}#{self.class}#" : prefix
85
+ comparable_prefix = get_comparable_prefix(prefix)
86
+ include_class && comparable_prefix != "#{self.class}#" ? "#{prefix}#{self.class}#" : prefix
74
87
  end
75
88
 
76
- def nested_mismatches?(mismatches)
89
+ def no_nested_mismatches?(mismatches)
77
90
  mismatches.nil? || mismatches.empty?
78
91
  end
92
+
93
+ def get_comparable_prefix(prefix)
94
+ prefixes = prefix.split('.')
95
+ prefixes.length >= 2 ? prefixes[-1] : prefix
96
+ end
79
97
  end
@@ -0,0 +1,3 @@
1
+ module MismatchInspectable
2
+ VERSION = '0.1.1'
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.00118 seconds |
4
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:1] | passed | 0.00041 seconds |
5
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:2:1] | passed | 0.0001 seconds |
6
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:1:1] | passed | 0.00015 seconds |
7
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:1:2:1] | passed | 0.00013 seconds |
8
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:2:1] | passed | 0.00014 seconds |
9
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:1:2:2:1] | passed | 0.00014 seconds |
10
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:1:3:2:1] | passed | 0.00011 seconds |
11
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:1:1] | passed | 0.0001 seconds |
12
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:2:1] | passed | 0.00025 seconds |
13
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:3:1] | passed | 0.00014 seconds |
14
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:1:1:1] | passed | 0.00014 seconds |
15
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:1:2:1] | passed | 0.00013 seconds |
16
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:1:3:1] | passed | 0.00013 seconds |
17
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:2:1:1] | passed | 0.00031 seconds |
18
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:2:2:1] | passed | 0.0002 seconds |
19
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:1:2:3:1] | passed | 0.00017 seconds |
20
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:2:1:1] | passed | 0.00014 seconds |
21
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:2:2:1] | passed | 0.00012 seconds |
22
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:2:3:1] | passed | 0.00013 seconds |
23
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:3:1:1] | passed | 0.00028 seconds |
24
+ ./spec/lib/mismatch_inspectable_spec.rb[1:1:2:2:4:3:2:1] | passed | 0.00049 seconds |
@@ -1,6 +1,6 @@
1
1
  require 'rspec'
2
2
  require 'spec_helper'
3
- require 'mismatch_inspectable/mismatch_inspectable'
3
+ require 'mismatch_inspectable'
4
4
 
5
5
  class TestClass
6
6
  include MismatchInspectable
@@ -306,6 +306,104 @@ 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
+ class Thing
312
+ include MismatchInspectable
313
+
314
+ inspect_mismatch_for :color, :shape, :is_cool, :nested_thing
315
+
316
+ def initialize(color: nil, shape: nil, is_cool: nil, nested_thing: nil)
317
+ @color = color
318
+ @shape = shape
319
+ @is_cool = is_cool
320
+ @nested_thing = nested_thing
321
+ end
322
+
323
+ def inspect
324
+ '<Thing>'
325
+ end
326
+
327
+ attr_accessor :color, :shape, :is_cool, :nested_thing
328
+ end
329
+
330
+ let(:thing1) do
331
+ Thing.new(
332
+ color: 'blue',
333
+ shape: 'square',
334
+ is_cool: false,
335
+ nested_thing: Thing.new(
336
+ color: 'blue',
337
+ shape: 'oval',
338
+ is_cool: true,
339
+ nested_thing: Thing.new(
340
+ color: 'silver',
341
+ shape: 'oval',
342
+ is_cool: true
343
+ )
344
+ )
345
+ )
346
+ end
347
+
348
+ let(:thing2) do
349
+ Thing.new(
350
+ color: 'green',
351
+ shape: 'oval',
352
+ is_cool: false,
353
+ nested_thing: Thing.new(
354
+ color: 'red',
355
+ shape: 'another shape',
356
+ is_cool: true,
357
+ nested_thing: Thing.new(
358
+ color: 'blue',
359
+ shape: 'oval',
360
+ is_cool: false
361
+ )
362
+ )
363
+ )
364
+ end
365
+ context 'with recursive flag disabled' do
366
+ it 'returns the mismatched top-level attributes' do
367
+ expect(thing1.inspect_mismatch(thing2, format: :object)).to eq(
368
+ {
369
+ Thing: {
370
+ color: %w[blue green],
371
+ shape: %w[square oval],
372
+ nested_thing: [thing1.nested_thing, thing2.nested_thing] # [<Thing>, <Thing>]
373
+ }
374
+ }
375
+ )
376
+ end
377
+ end
378
+
379
+ context 'with recursive flag enabled' do
380
+ let(:recursive) { true }
381
+ it 'returns the mismatched attributes with the appropriate nesting' do
382
+ expect(thing1.inspect_mismatch(thing2, recursive: recursive, format: :object)).to eq(
383
+ {
384
+ Thing: {
385
+ color: %w[blue green],
386
+ shape: %w[
387
+ square oval
388
+ ],
389
+ nested_thing: {
390
+ Thing: {
391
+ color: %w[blue red],
392
+ shape: ['oval', 'another shape'],
393
+ nested_thing: {
394
+ Thing: {
395
+ color: %w[silver blue],
396
+ is_cool: [true, false]
397
+ }
398
+ }
399
+ }
400
+ }
401
+ }
402
+ }
403
+ )
404
+ end
405
+ end
406
+ end
309
407
  end
310
408
  end
311
409
  end
metadata CHANGED
@@ -1,14 +1,14 @@
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.1
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
11
+ date: 2023-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -33,12 +33,14 @@ extensions: []
33
33
  extra_rdoc_files: []
34
34
  files:
35
35
  - LICENSE.md
36
+ - README.md
36
37
  - lib/mismatch_inspectable.rb
37
38
  - lib/mismatch_inspectable/array_formatter.rb
38
39
  - lib/mismatch_inspectable/deep_merge.rb
39
40
  - lib/mismatch_inspectable/hash_formatter.rb
40
41
  - lib/mismatch_inspectable/mismatch_inspectable.rb
41
42
  - lib/mismatch_inspectable/object_formatter.rb
43
+ - lib/mismatch_inspectable/version.rb
42
44
  - spec/examples.txt
43
45
  - spec/lib/mismatch_inspectable_spec.rb
44
46
  - spec/spec_helper.rb