Skip to content

Commit e25d3e3

Browse files
committed
Add default_keyword_init option
1 parent 97e84df commit e25d3e3

File tree

2 files changed

+56
-11
lines changed

2 files changed

+56
-11
lines changed

lib/typed_struct.rb

+16-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,23 @@ class TypedStruct < Struct
1818
alias_method :__class__, :class
1919

2020
class << self
21+
@@default_keyword_init = nil
22+
23+
def default_keyword_init
24+
@@default_keyword_init
25+
end
26+
27+
def default_keyword_init=(default)
28+
@@default_keyword_init = default
29+
end
30+
2131
def new(opts = Options.new, **properties)
22-
if const_defined?("RSpec") && RUBY_VERSION < "3.2" && opts[:keyword_init].nil?
23-
opts[:keyword_init] = false
32+
if opts[:keyword_init].nil?
33+
opts[:keyword_init] = if RUBY_VERSION < "3.2"
34+
default_keyword_init || false
35+
else
36+
default_keyword_init
37+
end
2438
end
2539

2640
properties.each_key do |prop|

spec/typed_struct_spec.rb

+40-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# frozen_string_literal: true
22

33
RSpec.describe TypedStruct do
4+
before do
5+
expect(TypedStruct.class_variables).to contain_exactly :@@default_keyword_init
6+
TypedStruct.default_keyword_init = nil
7+
end
8+
49
it "helps avoid primitive obsession" do
510
Price = TypedStruct.new(price: Rational) do
611
%i[- + / *].each do |op|
@@ -28,6 +33,31 @@
2833
expect { y.int = "abc" }.to raise_error TypeError
2934
end
3035

36+
it "has default_keyword_init option" do
37+
attrs = RUBY_VERSION < "3.2" ? {int: {int: 5}} : {int: 5}
38+
39+
TypedStruct.default_keyword_init = nil
40+
expect(TypedStruct.default_keyword_init).to be nil
41+
x = Struct.new(:int)
42+
y = TypedStruct.new(int: Rbs("untyped"))
43+
expect(x.new(int: 5)).to have_attributes attrs
44+
expect(y.new(int: 5)).to have_attributes attrs
45+
46+
TypedStruct.default_keyword_init = true
47+
expect(TypedStruct.default_keyword_init).to be true
48+
x = Struct.new(:int, keyword_init: true)
49+
y = TypedStruct.new(int: Rbs("untyped"))
50+
expect(x.new(int: 5)).to have_attributes int: 5
51+
expect(y.new(int: 5)).to have_attributes int: 5
52+
53+
TypedStruct.default_keyword_init = false
54+
expect(TypedStruct.default_keyword_init).to be false
55+
x = Struct.new(:int, keyword_init: false)
56+
y = TypedStruct.new(int: Rbs("untyped"))
57+
expect(x.new(int: 5)).to have_attributes int: {int: 5}
58+
expect(y.new(int: 5)).to have_attributes int: {int: 5}
59+
end
60+
3161
it "has an options attribute" do
3262
x = TypedStruct.new(int: Integer, str: String)
3363
expect(x.instance_variables).to contain_exactly :@options
@@ -45,13 +75,11 @@
4575
y = TypedStruct.new(int: Rbs("untyped"))
4676
attrs = RUBY_VERSION < "3.2" ? {int: {int: 5}} : {int: 5}
4777
expect(x.new(5)).to have_attributes int: 5
48-
expect(x.new(int: 5)).to have_attributes attrs
4978
expect(y.new(5)).to have_attributes int: 5
79+
expect(x.new(int: 5)).to have_attributes attrs
5080
expect(y.new(int: 5)).to have_attributes attrs
5181
x = Struct.new(:int, keyword_init: true)
5282
y = TypedStruct.new({ keyword_init: true }, int: Rbs("untyped"))
53-
expect { x.new(5) }.to raise_error ArgumentError, "wrong number of arguments (given 1, expected 0)"
54-
expect { y.new(5) }.to raise_error ArgumentError, "wrong number of arguments (given 1, expected 0)"
5583
expect(x.new(int: 5)).to have_attributes int: 5
5684
expect(y.new(int: 5)).to have_attributes int: 5
5785
end
@@ -71,14 +99,16 @@
7199

72100
it "has identical error messages for presence checks" do
73101
x = Struct.new(:int, keyword_init: true)
74-
expect { x.new(str: 5, abc: "xyz") }.to raise_error ArgumentError, "unknown keywords: str, abc"
75102
y = TypedStruct.new({ keyword_init: true }, int: Integer)
103+
expect { x.new(5) }.to raise_error ArgumentError, "wrong number of arguments (given 1, expected 0)"
104+
expect { y.new(5) }.to raise_error ArgumentError, "wrong number of arguments (given 1, expected 0)"
105+
expect { x.new(str: 5, abc: "xyz") }.to raise_error ArgumentError, "unknown keywords: str, abc"
76106
expect { y.new(str: 5, abc: "xyz") }.to raise_error ArgumentError, "unknown keywords: str, abc"
77107
a = x.new(int: 5)
78-
expect { a.str = 5 }.to raise_error NoMethodError, "undefined method `str=' for #<struct int=5>"
79-
expect { a[:str] = 5 }.to raise_error NameError, "no member 'str' in struct"
80108
b = y.new(int: 5)
109+
expect { a.str = 5 }.to raise_error NoMethodError, "undefined method `str=' for #<struct int=5>"
81110
expect { b.str = 5 }.to raise_error NoMethodError, "undefined method `str=' for #<struct int=5>"
111+
expect { a[:str] = 5 }.to raise_error NameError, "no member 'str' in struct"
82112
expect { b[:str] = 5 }.to raise_error NameError, "no member 'str' in struct"
83113
x = Struct.new(:int)
84114
y = TypedStruct.new(int: Integer)
@@ -89,7 +119,7 @@
89119
it "supports the same methods" do
90120
a = Struct.new(:str, :int)
91121
b = TypedStruct.new(str: String, int: Integer)
92-
expect(a.public_methods).to contain_exactly *b.public_methods
122+
expect(a.public_methods).to contain_exactly *b.public_methods.grep_v(:default_keyword_init).grep_v(:default_keyword_init=)
93123
expect(a.public_instance_methods).to contain_exactly *b.public_instance_methods.grep_v(:__class__)
94124
expect(a.public_instance_methods(false)).to contain_exactly *b.new("abc", 5).public_methods(false).grep_v(:[]=)
95125
end
@@ -224,6 +254,7 @@ def c
224254
end
225255
end
226256
end
257+
227258
context "when overriding native methods" do
228259
before { $stderr = StringIO.new }
229260

@@ -297,9 +328,9 @@ def c
297328
context "when keyword_init is false" do
298329
it "treats keyword arguments as if they were positional arguments" do
299330
x = Struct.new(:int, :str, keyword_init: false)
331+
y = TypedStruct.new({ keyword_init: false }, int: Rbs("untyped"), str: Rbs("untyped"))
300332
expect(x.new(int: 5, str: "abc")).to have_attributes int: {int: 5, str: "abc"}, str: nil
301-
x = TypedStruct.new({ keyword_init: false }, int: Rbs("untyped"), str: Rbs("untyped"))
302-
expect(x.new(int: 5, str: "abc")).to have_attributes int: {int: 5, str: "abc"}, str: nil
333+
expect(y.new(int: 5, str: "abc")).to have_attributes int: {int: 5, str: "abc"}, str: nil
303334
end
304335

305336
it "can be used to avoid unnecessary repetition" do

0 commit comments

Comments
 (0)