Skip to content

Commit bb367ea

Browse files
Fix monkey patch of ActiveRecord::Base for records without primary key (#282)
This fixes inspection and diffing for ActiveRecord objects without a primary key.
1 parent aceceec commit bb367ea

File tree

8 files changed

+140
-2
lines changed

8 files changed

+140
-2
lines changed

Diff for: CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
- Add official Rails 7.2 support. [#279](https://github.com./splitwise/super_diff/pull/279)
1313
- Add official Rails 8.0 support. [#281](https://github.com./splitwise/super_diff/pull/281)
1414

15+
### Bug fixes
16+
17+
- Fix ActiveRecord's `attributes_for_super_diff` and tree builders related to Active Records to handle models that do not have a primary key.
18+
([#282](https://github.com./splitwise/super_diff/pull/282))
19+
1520
### Other changes
1621

1722
- Fix `logger` dependency issues in CI. [#277](https://github.com./splitwise/super_diff/pull/277)

Diff for: lib/super_diff/active_record/inspection_tree_builders/active_record_model.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ def call
2727

2828
t1.nested do |t2|
2929
t2.insert_separated_list(
30-
[id] + (object.attributes.keys.sort - [id])
30+
if id.nil?
31+
object.attributes.keys.sort
32+
else
33+
[id] + (object.attributes.keys.sort - [id])
34+
end
3135
) do |t3, name|
3236
t3.as_prefix_when_rendering_to_lines do |t4|
3337
t4.add_text "#{name}: "

Diff for: lib/super_diff/active_record/monkey_patches.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ def attributes_for_super_diff
77
id_attr = self.class.primary_key
88

99
(attributes.keys.sort - [id_attr]).reduce(
10-
{ id_attr.to_sym => id }
10+
id_attr.nil? ? {} : { id_attr.to_sym => id }
1111
) { |hash, key| hash.merge(key.to_sym => attributes[key]) }
1212
end
1313
end

Diff for: lib/super_diff/active_record/operation_tree_builders/active_record_model.rb

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ def id
1616
end
1717

1818
def attribute_names
19+
return expected.attributes.keys.sort if id.nil?
20+
1921
[id] + (expected.attributes.keys.sort - [id])
2022
end
2123
end

Diff for: spec/support/models/active_record.rb

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module ActiveRecord
77
def self.define_tables
88
SuperDiff::Test::Models::ActiveRecord::Person.define_table
99
SuperDiff::Test::Models::ActiveRecord::ShippingAddress.define_table
10+
SuperDiff::Test::Models::ActiveRecord::TimeSeriesData.define_table
1011
end
1112
end
1213
end

Diff for: spec/support/models/active_record/timeseries_data.rb

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
module SuperDiff
4+
module Test
5+
module Models
6+
module ActiveRecord
7+
class TimeSeriesData < ::ActiveRecord::Base
8+
def self.define_table
9+
::ActiveRecord::Base
10+
.connection
11+
.create_table(
12+
:time_series_data,
13+
force: true,
14+
id: false
15+
) do |t|
16+
t.integer :value, null: false
17+
t.datetime :at, null: false
18+
end
19+
end
20+
end
21+
end
22+
end
23+
end
24+
end

Diff for: spec/support/shared_examples/active_record.rb

+84
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,48 @@
6161
end
6262
end
6363

64+
context 'when comparing two instances of an ActiveRecord model that does not have a primary key' do
65+
it 'produces the correct output' do
66+
as_both_colored_and_uncolored do |color_enabled|
67+
snippet = <<~TEST.strip
68+
actual = SuperDiff::Test::Models::ActiveRecord::TimeSeriesData.new(
69+
value: 456,
70+
at: Time.parse('2015-07-04T17:05:37Z'),
71+
)
72+
expected = SuperDiff::Test::Models::ActiveRecord::TimeSeriesData.new(
73+
value: 123,
74+
at: Time.parse('2015-07-04T17:05:37Z'),
75+
)
76+
expect(actual).to eq(expected)
77+
TEST
78+
program = make_program(snippet, color_enabled: color_enabled)
79+
80+
expected_output =
81+
build_expected_output(
82+
color_enabled: color_enabled,
83+
snippet: 'expect(actual).to eq(expected)',
84+
newline_before_expectation: true,
85+
expectation:
86+
proc do
87+
line do
88+
plain 'Expected '
89+
actual %(#<SuperDiff::Test::Models::ActiveRecord::TimeSeriesData at: #<Time 2015-07-04 17:05:37 +00:00 (UTC)>, value: 456>)
90+
end
91+
92+
line do
93+
plain ' to eq '
94+
expected %(#<SuperDiff::Test::Models::ActiveRecord::TimeSeriesData at: #<Time 2015-07-04 17:05:37 +00:00 (UTC)>, value: 123>)
95+
end
96+
end
97+
)
98+
99+
expect(program).to produce_output_when_run(expected_output).in_color(
100+
color_enabled
101+
)
102+
end
103+
end
104+
end
105+
64106
context 'when comparing instances of two different ActiveRecord models' do
65107
it 'produces the correct output' do
66108
as_both_colored_and_uncolored do |color_enabled|
@@ -105,6 +147,48 @@
105147
end
106148
end
107149

150+
context 'when comparing instances of two different ActiveRecord models with one not having a primary key' do
151+
it 'produces the correct output' do
152+
as_both_colored_and_uncolored do |color_enabled|
153+
snippet = <<~TEST.strip
154+
actual = SuperDiff::Test::Models::ActiveRecord::Person.new(
155+
name: "Elliot",
156+
age: 31,
157+
)
158+
expected = SuperDiff::Test::Models::ActiveRecord::TimeSeriesData.new(
159+
value: 123,
160+
at: Time.parse('2015-07-04T17:05:37Z'),
161+
)
162+
expect(actual).to eq(expected)
163+
TEST
164+
program = make_program(snippet, color_enabled: color_enabled)
165+
166+
expected_output =
167+
build_expected_output(
168+
color_enabled: color_enabled,
169+
snippet: 'expect(actual).to eq(expected)',
170+
newline_before_expectation: true,
171+
expectation:
172+
proc do
173+
line do
174+
plain 'Expected '
175+
actual %(#<SuperDiff::Test::Models::ActiveRecord::Person person_id: nil, age: 31, name: "Elliot">)
176+
end
177+
178+
line do
179+
plain ' to eq '
180+
expected %(#<SuperDiff::Test::Models::ActiveRecord::TimeSeriesData at: #<Time 2015-07-04 17:05:37 +00:00 (UTC)>, value: 123>)
181+
end
182+
end
183+
)
184+
185+
expect(program).to produce_output_when_run(expected_output).in_color(
186+
color_enabled
187+
)
188+
end
189+
end
190+
end
191+
108192
context 'when comparing an ActiveRecord object with nothing' do
109193
it 'produces the correct output' do
110194
as_both_colored_and_uncolored do |color_enabled|

Diff for: spec/unit/active_record/object_inspection_spec.rb

+18
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,24 @@
7979
end
8080
end
8181

82+
context 'given an ActiveRecord object without a primary key' do
83+
context 'given as_lines: false' do
84+
it 'returns an inspected version of the object' do
85+
string =
86+
described_class.inspect_object(
87+
SuperDiff::Test::Models::ActiveRecord::TimeSeriesData.new(
88+
value: 123,
89+
at: Time.parse('2015-07-04T17:05:37Z')
90+
),
91+
as_lines: false
92+
)
93+
expect(string).to eq(
94+
%(#<SuperDiff::Test::Models::ActiveRecord::TimeSeriesData at: #<Time 2015-07-04 17:05:37 +00:00 (UTC)>, value: 123>)
95+
)
96+
end
97+
end
98+
end
99+
82100
context 'given an ActiveRecord::Relation object' do
83101
context 'given as_lines: false' do
84102
it 'returns an inspected version of the Relation' do

0 commit comments

Comments
 (0)