Skip to content

Commit

Permalink
feat: date like objects support (#198)
Browse files Browse the repository at this point in the history
Tbh, I just went through appearances of `/(TimeLike|time_like)/` and
created similar logic for date like objects :D

I'm really hesitant about whether `DateTime` objects should be
considered `date_like` (although ActiveSupport defines
`DateTime#acts_like_date?`). Since `Date` and `DateTime` are never
equal, `expect(Date.new(2020, 1, 1)).to eq(DateTime.new(2020, 1, 1))`
will result in a confusing diff (see the
[spec/support/shared_examples/active_support.rb](https://github.com/mcmire/super_diff/pull/198/files#diff-4076c9b6c197bc5a4b90327f7efc27f9a0bc5f40ec98dfe94adf9c08f103a269R103-R143))
  • Loading branch information
fizvlad authored Jan 30, 2024
1 parent 34e9609 commit 4965ac0
Show file tree
Hide file tree
Showing 14 changed files with 412 additions and 1 deletion.
9 changes: 9 additions & 0 deletions lib/super_diff.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "attr_extras/explicit"
require "diff-lcs"
require "patience_diff"
require "date"

module SuperDiff
autoload(
Expand Down Expand Up @@ -57,6 +58,14 @@ def self.time_like?(value)
value.is_a?(Time)
end

def self.date_like?(value)
# Check for ActiveSupport's #acts_like_date? for their date-like objects
# In case class is both time-like and date-like, we should treat it as
# time-like. This is governed by the order of `Differs::DEFAULTS` entries
(value.respond_to?(:acts_like_date?) && value.acts_like_date?) ||
value.is_a?(Date)
end

def self.primitive?(value)
case value
when true, false, nil, Symbol, Numeric, Regexp, Class
Expand Down
1 change: 1 addition & 0 deletions lib/super_diff/differs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Differs
autoload :Main, "super_diff/differs/main"
autoload :MultilineString, "super_diff/differs/multiline_string"
autoload :TimeLike, "super_diff/differs/time_like"
autoload :DateLike, "super_diff/differs/date_like"
end
end

Expand Down
15 changes: 15 additions & 0 deletions lib/super_diff/differs/date_like.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module SuperDiff
module Differs
class DateLike < Base
def self.applies_to?(expected, actual)
SuperDiff.date_like?(expected) && SuperDiff.date_like?(actual)
end

protected

def operation_tree_builder_class
OperationTreeBuilders::DateLike
end
end
end
end
1 change: 1 addition & 0 deletions lib/super_diff/differs/defaults.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Differs
Array,
Hash,
TimeLike,
DateLike,
MultilineString,
CustomObject,
DefaultObject
Expand Down
4 changes: 4 additions & 0 deletions lib/super_diff/object_inspection/inspection_tree_builders.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ module InspectionTreeBuilders
:TimeLike,
"super_diff/object_inspection/inspection_tree_builders/time_like"
)
autoload(
:DateLike,
"super_diff/object_inspection/inspection_tree_builders/date_like"
)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module SuperDiff
module ObjectInspection
module InspectionTreeBuilders
class DateLike < Base
def self.applies_to?(value)
SuperDiff.date_like?(value)
end

def call
InspectionTree.new do
as_lines_when_rendering_to_lines(collection_bookend: :open) do
add_text { |date| "#<#{date.class} " }

when_rendering_to_lines { add_text "{" }
end

when_rendering_to_string do
add_text { |date| date.strftime("%Y-%m-%d") }
end

when_rendering_to_lines do
nested do |date|
insert_separated_list(%i[year month day]) do |name|
add_text name.to_s
add_text ": "
add_inspection_of date.public_send(name)
end
end
end

as_lines_when_rendering_to_lines(collection_bookend: :close) do
when_rendering_to_lines { add_text "}" }

add_text ">"
end
end
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module InspectionTreeBuilders
Hash,
Primitive,
TimeLike,
DateLike,
DefaultObject
].freeze
end
Expand Down
1 change: 1 addition & 0 deletions lib/super_diff/operation_tree_builders.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module OperationTreeBuilders
"super_diff/operation_tree_builders/multiline_string"
)
autoload :TimeLike, "super_diff/operation_tree_builders/time_like"
autoload :DateLike, "super_diff/operation_tree_builders/date_like"
end
end

Expand Down
15 changes: 15 additions & 0 deletions lib/super_diff/operation_tree_builders/date_like.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module SuperDiff
module OperationTreeBuilders
class DateLike < CustomObject
def self.applies_to?(expected, actual)
SuperDiff.date_like?(expected) && SuperDiff.date_like?(actual)
end

protected

def attribute_names
%w[year month day]
end
end
end
end
2 changes: 1 addition & 1 deletion lib/super_diff/operation_tree_builders/defaults.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module SuperDiff
module OperationTreeBuilders
DEFAULTS = [Array, Hash, TimeLike, CustomObject].freeze
DEFAULTS = [Array, Hash, TimeLike, DateLike, CustomObject].freeze
end
end
72 changes: 72 additions & 0 deletions spec/integration/rspec/eq_matcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,78 @@
end
end

context "when comparing two different Date instances" do
it "produces the correct failure message when used in the positive" do
as_both_colored_and_uncolored do |color_enabled|
snippet = <<~RUBY
expected = Date.new(2023, 10, 14)
actual = Date.new(2023, 10, 31)
expect(expected).to eq(actual)
RUBY
program = make_plain_test_program(snippet, color_enabled: color_enabled)

expected_output =
build_expected_output(
color_enabled: color_enabled,
snippet: "expect(expected).to eq(actual)",
expectation:
proc do
line do
plain "Expected "
actual "#<Date 2023-10-14>"
plain " to eq "
expected "#<Date 2023-10-31>"
plain "."
end
end,
diff:
proc do
plain_line " #<Date {"
plain_line " year: 2023,"
plain_line " month: 10,"
expected_line "- day: 31"
actual_line "+ day: 14"
plain_line " }>"
end
)

expect(program).to produce_output_when_run(expected_output).in_color(
color_enabled
)
end
end

it "produces the correct failure message when used in the negative" do
as_both_colored_and_uncolored do |color_enabled|
snippet = <<~RUBY
date = Date.new(2023, 10, 14)
expect(date).not_to eq(date)
RUBY
program = make_plain_test_program(snippet, color_enabled: color_enabled)

expected_output =
build_expected_output(
color_enabled: color_enabled,
snippet: "expect(date).not_to eq(date)",
expectation:
proc do
line do
plain "Expected "
actual "#<Date 2023-10-14>"
plain " not to eq "
expected "#<Date 2023-10-14>"
plain "."
end
end
)

expect(program).to produce_output_when_run(expected_output).in_color(
color_enabled
)
end
end
end

context "when comparing a single-line string with a multi-line string" do
it "produces the correct failure message" do
as_both_colored_and_uncolored do |color_enabled|
Expand Down
79 changes: 79 additions & 0 deletions spec/support/shared_examples/active_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,83 @@
end
end
end

context "when comparing Date instance and date-like DateTime instance for same day",
active_record: true do
it "produces the correct failure message when used in the positive" do
as_both_colored_and_uncolored do |color_enabled|
snippet = <<~RUBY
expected = Date.new(2023, 10, 14)
actual = DateTime.new(2023, 10, 14, 18, 22, 26)
expect(expected).to eq(actual)
RUBY
program =
make_rspec_rails_test_program(snippet, color_enabled: color_enabled)

expected_output =
build_expected_output(
color_enabled: color_enabled,
snippet: "expect(expected).to eq(actual)",
expectation:
proc do
line do
plain "Expected "
actual "#<Date 2023-10-14>"
plain " to eq "
expected "#<DateTime 2023-10-14 18:22:26 +00:00 (+00:00)>"
plain "."
end
end
)

expect(program).to produce_output_when_run(expected_output).in_color(
color_enabled
)
end
end
end

context "when comparing Date instance and date-like DateTime instance for another day",
active_record: true do
it "produces the diff for date like objects comparison" do
as_both_colored_and_uncolored do |color_enabled|
snippet = <<~RUBY
expected = Date.new(2023, 10, 14)
actual = DateTime.new(2023, 10, 31, 18, 22, 26)
expect(expected).to eq(actual)
RUBY
program =
make_rspec_rails_test_program(snippet, color_enabled: color_enabled)

expected_output =
build_expected_output(
color_enabled: color_enabled,
snippet: "expect(expected).to eq(actual)",
expectation:
proc do
line do
plain "Expected "
actual "#<Date 2023-10-14>"
plain " to eq "
expected "#<DateTime 2023-10-31 18:22:26 +00:00 (+00:00)>"
plain "."
end
end,
diff:
proc do
plain_line " #<Date {"
plain_line " year: 2023,"
plain_line " month: 10,"
expected_line "- day: 31"
actual_line "+ day: 14"
plain_line " }>"
end
)

expect(program).to produce_output_when_run(expected_output).in_color(
color_enabled
)
end
end
end
end
Loading

0 comments on commit 4965ac0

Please sign in to comment.