Skip to content

Commit 3e16a06

Browse files
authored
Allow default values to be specified upon creation (#3)
1 parent ec33df5 commit 3e16a06

File tree

4 files changed

+64
-13
lines changed

4 files changed

+64
-13
lines changed

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
typed_struct (0.1.2)
4+
typed_struct (0.1.3)
55
rbs (~> 1.0)
66

77
GEM

lib/typed_struct.rb

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
require_relative "typed_struct/type_checking"
55
require "rbs"
66

7+
def Rbs(type_str)
8+
RBS::Parser.parse_type(type_str)
9+
end
10+
711
class TypedStruct < Struct
812
include TypeChecking
913

@@ -13,8 +17,10 @@ class TypedStruct < Struct
1317
# any methods which are able to be overridden
1418
alias_method :__class__, :class
1519

20+
Options = nil
21+
1622
class << self
17-
def new(**properties)
23+
def new(opts = Options.new, **properties)
1824
properties.each_key do |prop|
1925
if method_defined?(prop)
2026
$stdout.puts OVERRIDING_NATIVE_METHOD_MSG % [prop.inspect, caller(3).first]
@@ -28,7 +34,7 @@ def new(**properties)
2834
end
2935

3036
klass.instance_eval do
31-
@options = { types: properties }
37+
@options = { types: properties, options: opts }
3238

3339
define_method :[]= do |key, val|
3440
prop = properties[key]
@@ -51,17 +57,16 @@ def new(**properties)
5157

5258
def initialize(**attrs)
5359
opts = self.__class__.options
54-
opts[:types].each do |prop, expected_type|
55-
passed_value = attrs[prop]
56-
next if val_is_type? passed_value, expected_type
57-
58-
raise "Unexpected type #{passed_value.class} for #{prop.inspect} (expected #{expected_type})"
60+
vals = opts[:types].to_h do |prop, expected_type|
61+
value = attrs.fetch(prop, opts[:options][:default])
62+
unless val_is_type? value, expected_type
63+
raise "Unexpected type #{value.class} for #{prop.inspect} (expected #{expected_type})"
64+
end
65+
[prop, value]
5966
end
6067

61-
super
68+
super **vals
6269
end
63-
end
6470

65-
def Rbs(type_str)
66-
RBS::Parser.parse_type(type_str)
71+
Options = TypedStruct.new({ default: nil }, default: Rbs("untyped"))
6772
end

lib/typed_struct/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
class TypedStruct < Struct
4-
VERSION = "0.1.2"
4+
VERSION = "0.1.3"
55
end

spec/typed_struct_spec.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,50 @@
2929
expect(y.__class__).to be_an_instance_of Class
3030
end
3131
end
32+
33+
context "when passing options" do
34+
it "allows for default values" do
35+
x = TypedStruct.new(
36+
TypedStruct::Options.new(default: 5),
37+
int: Integer,
38+
str: String,
39+
)
40+
y = x.new str: "abc"
41+
expect(y.str).to eq "abc"
42+
expect(y.int).to eq 5
43+
end
44+
45+
it "allows anything responding to [] to be passed as options" do
46+
x = TypedStruct.new(
47+
{ default: 3 },
48+
xyz: /foobar/,
49+
abc: :abc,
50+
)
51+
y = x.new abc: :abc, xyz: "foobarbaz"
52+
expect(y.abc).to eq :abc
53+
expect(y.xyz).to eq "foobarbaz"
54+
end
55+
56+
it "breaks if a missing type and the type of the default don't match" do
57+
x = TypedStruct.new(
58+
TypedStruct::Options.new(default: 1),
59+
int: Integer,
60+
str: String,
61+
)
62+
63+
expect { x.new(int: 4) }.to raise_error "Unexpected type Integer for :str (expected String)"
64+
end
65+
end
66+
67+
context "when no options passed" do
68+
it "defaults to nil for missing properties" do
69+
x = TypedStruct.new abc: Rbs("String?")
70+
expect(x.new.abc).to be_nil
71+
end
72+
73+
it "errors if the missing property cannot be nil" do
74+
x = TypedStruct.new abc: Rbs("String")
75+
expect { x.new }.to raise_error "Unexpected type NilClass for :abc (expected String)"
76+
end
77+
end
3278
end

0 commit comments

Comments
 (0)