Skip to content

Commit

Permalink
Improve diffs for array values
Browse files Browse the repository at this point in the history
  • Loading branch information
flash-gordon committed Mar 11, 2025
1 parent a3311c2 commit 337a403
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 24 deletions.
95 changes: 82 additions & 13 deletions lib/dry/monads/extensions/super_diff.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require "delegate"
require "super_diff"
require "super_diff/rspec"

Expand Down Expand Up @@ -27,23 +28,58 @@ module SuperDiff
}.freeze

TOKEN_MAP = {
Result::Success => "Success(",
Result::Failure => "Failure(",
Maybe::Some => "Some(",
Maybe::None => "None(",
Try::Value => "Value("
Result::Success => "Success",
Result::Failure => "Failure",
Maybe::Some => "Some",
Maybe::None => "None",
Try::Value => "Value"
}.freeze

class WrappedArray < ::SimpleDelegator
def is_a?(klass) = klass <= WrappedArray
end

module OperationTreeFlatteners
class MonadicValues < ::SuperDiff::Basic::OperationTreeFlatteners::CustomObject
def open_token
TOKEN_MAP[operation_tree.underlying_object.class]
private

def initialize(...)
super

obj = operation_tree.underlying_object
@klass = obj.class
@array = EXTRACT_VALUE[@klass].(obj).is_a?(::Array)
end

def close_token = ")"
protected

def array? = @array

def open_token = "#{TOKEN_MAP[@klass]}#{array? ? "[" : "("}"

def close_token = array? ? "]" : ")"

def item_prefix_for(_) = ""
end

class WrappedArray < ::SuperDiff::Basic::OperationTreeFlatteners::Array
protected

def build_tiered_lines = inner_lines

# prevent super_diff from adding a newline after the open token
# for arrays
def build_lines_for_non_change_operation(*)
@indentation_level -= 1
super
ensure
@indentation_level += 1
end

def open_token = ""

def close_token = ""
end
end

module OperationTrees
Expand All @@ -52,6 +88,16 @@ def operation_tree_flattener_class
OperationTreeFlatteners::MonadicValues
end
end

class WrappedArray < ::SuperDiff::Basic::OperationTrees::Array
def self.applies_to?(value)
value.is_a?(::Dry::Monads::SuperDiff::WrappedArray)
end

def operation_tree_flattener_class
OperationTreeFlatteners::WrappedArray
end
end
end

module OperationTreeBuilders
Expand All @@ -67,9 +113,7 @@ def build_operation_tree
OperationTrees::MonadicValues.new([], underlying_object: actual)
end

def attribute_names
[:value]
end
def attribute_names = [:value]

private

Expand All @@ -83,11 +127,26 @@ def get_value(object)

if Unit.equal?(v)
EMPTY_HASH
elsif v.is_a?(::Array)
{value: ::Dry::Monads::SuperDiff::WrappedArray.new(v)}
else
{value: v}
end
end
end

class WrappedArray < ::SuperDiff::Basic::OperationTreeBuilders::Array
def self.applies_to?(expected, actual)
expected.is_a?(::Dry::Monads::SuperDiff::WrappedArray) &&
actual.is_a?(::Dry::Monads::SuperDiff::WrappedArray)
end

private

def operation_tree
@operation_tree ||= OperationTrees::WrappedArray.new([])
end
end
end

module Differs
Expand All @@ -114,16 +173,23 @@ def call
t1.as_lines_when_rendering_to_lines(
collection_bookend: :open
) do |t2|
v = EXTRACT_VALUE[object.class].(object)

t2.add_text(TOKEN_MAP[object.class])

v = EXTRACT_VALUE[object.class].(object)
unless v.is_a?(::Array)
t2.add_text("(")
end

unless Unit.equal?(v)
t2.nested do |t3|
t3.add_inspection_of v
end
end
t2.add_text(")")

unless v.is_a?(::Array)
t2.add_text(")")
end
end
end
end
Expand All @@ -138,4 +204,7 @@ def call
config.prepend_extra_inspection_tree_builder_classes(
Dry::Monads::SuperDiff::InspectionTreeBuilders::MonadicValues
)
config.prepend_extra_operation_tree_builder_classes(
Dry::Monads::SuperDiff::OperationTreeBuilders::WrappedArray
)
end
20 changes: 9 additions & 11 deletions spec/extensions/super_diff_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def run_spec(code)
include Dry::Monads::Try::Mixin
before(:all) do
Dry::Monads.load_extensions(:super_diff)
Dry::Monads.load_extensions(:super_diff, :rspec)
end
it "fails with a diff" do
Expand Down Expand Up @@ -237,22 +237,20 @@ def extract_diff(output, path)

example "with arrays" do
output = run_spec(<<~RUBY)
expect(Failure([:error, :a])).to eql(Failure([:error, :b]))
expect(Failure[:error, :a]).to eql(Failure[:error, :b])
RUBY

expect(output).to eql(<<~DIFF)
expected: Failure([:error, :b])
got: Failure([:error, :a])
expected: Failure[:error, :b]
got: Failure[:error, :a]
(compared using eql?)
Failure(
[
:error,
- :b
+ :a
]
)
Failure[
:error,
- :b
+ :a
]
DIFF
end
end
Expand Down

0 comments on commit 337a403

Please sign in to comment.