Skip to content

Commit 768abb9

Browse files
committed
[GR-18163] Reimplement Data#with to not call Data.new
PullRequest: truffleruby/4555
2 parents d583795 + e899c1e commit 768abb9

File tree

5 files changed

+76
-1
lines changed

5 files changed

+76
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Compatibility:
3333
* Implement `rb_error_frozen_object` for the google-protobuf gem (@nirvdrum).
3434
* Adjust a `FrozenError`'s message and add a receiver when a frozen module or class is modified (e.g. by defining or undefining an instance method or by defining a nested module (@andrykonchin).
3535
* Fix `Kernel#sprintf` and `%p` format specification to produce `"nil"` for `nil` argument (#3846, @andrykonchin).
36+
* Reimplement `Data#with` to not call `Data.new` that can be removed or redefined (#3890, @andrykonchin).
3637

3738
Performance:
3839

spec/ruby/core/data/fixtures/classes.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,12 @@ def initialize(amount:, unit:)
1717
end
1818

1919
Empty = Data.define()
20+
21+
DataWithOverriddenInitialize = Data.define(:amount, :unit) do
22+
def initialize(*rest, **kw)
23+
super
24+
ScratchPad.record [:initialize, rest, kw]
25+
end
26+
end
2027
end
2128
end

spec/ruby/core/data/initialize_spec.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,44 @@
7171
it "supports Data with no fields" do
7272
-> { DataSpecs::Empty.new }.should_not raise_error
7373
end
74+
75+
it "can be overridden" do
76+
ScratchPad.record []
77+
78+
measure_class = Data.define(:amount, :unit) do
79+
def initialize(*, **)
80+
super
81+
ScratchPad << :initialize
82+
end
83+
end
84+
85+
measure_class.new(42, "m")
86+
ScratchPad.recorded.should == [:initialize]
87+
end
88+
89+
context "when it is overridden" do
90+
it "is called with keyword arguments when given positional arguments" do
91+
ScratchPad.clear
92+
DataSpecs::DataWithOverriddenInitialize.new(42, "m")
93+
ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}]
94+
end
95+
96+
it "is called with keyword arguments when given keyword arguments" do
97+
ScratchPad.clear
98+
DataSpecs::DataWithOverriddenInitialize.new(amount: 42, unit: "m")
99+
ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}]
100+
end
101+
102+
it "is called with keyword arguments when given alternative positional arguments" do
103+
ScratchPad.clear
104+
DataSpecs::DataWithOverriddenInitialize[42, "m"]
105+
ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}]
106+
end
107+
108+
it "is called with keyword arguments when given alternative keyword arguments" do
109+
ScratchPad.clear
110+
DataSpecs::DataWithOverriddenInitialize[amount: 42, unit: "m"]
111+
ScratchPad.recorded.should == [:initialize, [], {amount: 42, unit: "m"}]
112+
end
113+
end
74114
end

spec/ruby/core/data/with_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,28 @@
3030
data.with(4, "m")
3131
}.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)")
3232
end
33+
34+
it "does not depend on the Data.new method" do
35+
subclass = Class.new(DataSpecs::Measure)
36+
data = subclass.new(amount: 42, unit: "km")
37+
38+
def subclass.new(*)
39+
raise "Data.new is called"
40+
end
41+
42+
data_copy = data.with(unit: "m")
43+
data_copy.amount.should == 42
44+
data_copy.unit.should == "m"
45+
end
46+
47+
ruby_version_is "3.3" do
48+
it "calls #initialize" do
49+
data = DataSpecs::DataWithOverriddenInitialize.new(42, "m")
50+
ScratchPad.clear
51+
52+
data.with(amount: 0)
53+
54+
ScratchPad.recorded.should == [:initialize, [], {amount: 0, unit: "m"}]
55+
end
56+
end
3357
end

src/main/ruby/truffleruby/core/data.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ def self.new(*args, **kwargs)
9595
instance_methods_module.module_eval "#{<<~'RUBY'}", __FILE__, __LINE__+1
9696
# truffleruby_primitives: true
9797
98+
ORIGINAL_NEW = klass.method(:new)
99+
private_constant :ORIGINAL_NEW
100+
98101
def initialize(**kwargs)
99102
members_hash = Primitive.class(self)::CLASS_MEMBERS_HASH
100103
kwargs.each do |member, value|
@@ -135,7 +138,7 @@ def to_h(&block)
135138
def with(**changes)
136139
return self if changes.empty?
137140
138-
Primitive.class(self).new(**to_h.merge(changes))
141+
ORIGINAL_NEW.call(**to_h.merge(changes))
139142
end
140143
141144
def inspect

0 commit comments

Comments
 (0)