|
1 | 1 | # frozen_string_literal: true
|
2 | 2 |
|
3 | 3 | 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 | + |
4 | 9 | it "helps avoid primitive obsession" do
|
5 | 10 | Price = TypedStruct.new(price: Rational) do
|
6 | 11 | %i[- + / *].each do |op|
|
|
28 | 33 | expect { y.int = "abc" }.to raise_error TypeError
|
29 | 34 | end
|
30 | 35 |
|
| 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 | + |
31 | 61 | it "has an options attribute" do
|
32 | 62 | x = TypedStruct.new(int: Integer, str: String)
|
33 | 63 | expect(x.instance_variables).to contain_exactly :@options
|
|
45 | 75 | y = TypedStruct.new(int: Rbs("untyped"))
|
46 | 76 | attrs = RUBY_VERSION < "3.2" ? {int: {int: 5}} : {int: 5}
|
47 | 77 | expect(x.new(5)).to have_attributes int: 5
|
48 |
| - expect(x.new(int: 5)).to have_attributes attrs |
49 | 78 | expect(y.new(5)).to have_attributes int: 5
|
| 79 | + expect(x.new(int: 5)).to have_attributes attrs |
50 | 80 | expect(y.new(int: 5)).to have_attributes attrs
|
51 | 81 | x = Struct.new(:int, keyword_init: true)
|
52 | 82 | 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)" |
55 | 83 | expect(x.new(int: 5)).to have_attributes int: 5
|
56 | 84 | expect(y.new(int: 5)).to have_attributes int: 5
|
57 | 85 | end
|
|
71 | 99 |
|
72 | 100 | it "has identical error messages for presence checks" do
|
73 | 101 | x = Struct.new(:int, keyword_init: true)
|
74 |
| - expect { x.new(str: 5, abc: "xyz") }.to raise_error ArgumentError, "unknown keywords: str, abc" |
75 | 102 | 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" |
76 | 106 | expect { y.new(str: 5, abc: "xyz") }.to raise_error ArgumentError, "unknown keywords: str, abc"
|
77 | 107 | 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" |
80 | 108 | b = y.new(int: 5)
|
| 109 | + expect { a.str = 5 }.to raise_error NoMethodError, "undefined method `str=' for #<struct int=5>" |
81 | 110 | 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" |
82 | 112 | expect { b[:str] = 5 }.to raise_error NameError, "no member 'str' in struct"
|
83 | 113 | x = Struct.new(:int)
|
84 | 114 | y = TypedStruct.new(int: Integer)
|
|
89 | 119 | it "supports the same methods" do
|
90 | 120 | a = Struct.new(:str, :int)
|
91 | 121 | 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=) |
93 | 123 | expect(a.public_instance_methods).to contain_exactly *b.public_instance_methods.grep_v(:__class__)
|
94 | 124 | expect(a.public_instance_methods(false)).to contain_exactly *b.new("abc", 5).public_methods(false).grep_v(:[]=)
|
95 | 125 | end
|
@@ -224,6 +254,7 @@ def c
|
224 | 254 | end
|
225 | 255 | end
|
226 | 256 | end
|
| 257 | + |
227 | 258 | context "when overriding native methods" do
|
228 | 259 | before { $stderr = StringIO.new }
|
229 | 260 |
|
@@ -297,9 +328,9 @@ def c
|
297 | 328 | context "when keyword_init is false" do
|
298 | 329 | it "treats keyword arguments as if they were positional arguments" do
|
299 | 330 | x = Struct.new(:int, :str, keyword_init: false)
|
| 331 | + y = TypedStruct.new({ keyword_init: false }, int: Rbs("untyped"), str: Rbs("untyped")) |
300 | 332 | 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 |
303 | 334 | end
|
304 | 335 |
|
305 | 336 | it "can be used to avoid unnecessary repetition" do
|
|
0 commit comments