Skip to content

Commit 6c98bc0

Browse files
committed
impl
1 parent 1f6070b commit 6c98bc0

File tree

7 files changed

+151
-5
lines changed

7 files changed

+151
-5
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-
smart_types (0.3.0)
4+
smart_types (0.4.0)
55
smart_engine (~> 0.10)
66

77
GEM

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,20 @@ SmartCore::Types::Protocol::InstanceOf(::Time, ::DateTime, ::Date) # time or dat
117117

118118
---
119119

120+
#### Struct:
121+
122+
```ruby
123+
SmartCore::Types::Protocol::Struct
124+
```
125+
126+
```ruby
127+
# examples (SmartCore::Types::Struct::StrictHash):
128+
SmartCore::Types::Struct::StrictHash(int: SmartCore::Types::Value::Integer, str: SmartCore::Types::Value::String) # hash with the following schema: {int: <integer>, str: <string>}
129+
SmartCore::Types::Struct::StrictHash(klass: SmartCore::Types::Protocol::InstanceOf(::Class)) # hash with the following schema: {klass: <class>}
130+
```
131+
132+
---
133+
120134
#### Variadic:
121135

122136
```ruby

lib/smart_core/types.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44

55
# @api public
66
# @since 0.1.0
7+
# @version 0.4.0
78
module SmartCore::Types
8-
require_relative 'types/version'
99
require_relative 'types/errors'
10-
require_relative 'types/system'
1110
require_relative 'types/primitive'
12-
require_relative 'types/value'
1311
require_relative 'types/protocol'
12+
require_relative 'types/struct'
13+
require_relative 'types/system'
14+
require_relative 'types/value'
1415
require_relative 'types/variadic'
16+
require_relative 'types/version'
1517
end

lib/smart_core/types/struct.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22

33
# @api public
44
# @since 0.1.0
5+
# @version 0.4.0
56
class SmartCore::Types::Struct < SmartCore::Types::Primitive
7+
require_relative 'struct/strict_hash'
68
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
using SmartCore::Ext::BasicObjectAsObject
4+
5+
# @api public
6+
# @since 0.4.0
7+
SmartCore::Types::Struct.define_type(:StrictHash) do |type|
8+
type.runtime_attributes_checker do |runtime_attrs|
9+
schema = runtime_attrs.first
10+
11+
next false unless runtime_attrs.size == 1
12+
next false unless schema.is_a?(::Hash)
13+
14+
schema.all? { |_key, value| value.is_a?(SmartCore::Types::Primitive) }
15+
end
16+
17+
type.define_checker do |value, schema_values|
18+
schema = schema_values.first
19+
20+
next false unless SmartCore::Types::Value::Hash.valid?(value)
21+
next false unless value.keys == schema.keys
22+
23+
value.all? { |k, v| schema[k].valid?(v) }
24+
end
25+
end

lib/smart_core/types/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ module Types
77
# @api public
88
# @since 0.1.0
99
# @version 0.3.0
10-
VERSION = '0.3.0'
10+
VERSION = '0.4.0'
1111
end
1212
end
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe 'SmartCore::Types::Struct::StrictHash' do
4+
describe 'runtime-based behavior' do
5+
specify 'fails on extra attributes' do
6+
expect do
7+
SmartCore::Types::Struct::StrictHash({ a: SmartCore::Types::Value::String }, :excessive)
8+
end.to raise_error(SmartCore::Types::IncorrectRuntimeAttributesError)
9+
end
10+
11+
specify 'fails when schema attribute is not hash object' do
12+
expect do
13+
SmartCore::Types::Struct::StrictHash(%i[bad master])
14+
end.to raise_error(SmartCore::Types::IncorrectRuntimeAttributesError)
15+
end
16+
17+
specify 'fails when schema values are not types' do
18+
expect do
19+
SmartCore::Types::Struct::StrictHash({ a: String })
20+
end.to raise_error(SmartCore::Types::IncorrectRuntimeAttributesError)
21+
end
22+
23+
specify 'requires schema (runtime attribute)' do
24+
expect do
25+
SmartCore::Types::Struct::StrictHash()
26+
end.to raise_error(SmartCore::Types::IncorrectRuntimeAttributesError)
27+
end
28+
end
29+
30+
describe 'logic' do
31+
let(:correct_value) { Hash[int: 10, str: '20', bool: false] }
32+
33+
let(:type) do
34+
SmartCore::Types::Struct::StrictHash(
35+
int: SmartCore::Types::Value::Integer,
36+
str: SmartCore::Types::Value::String,
37+
bool: SmartCore::Types::Value::Boolean
38+
)
39+
end
40+
41+
specify 'type-checking' do
42+
expect(type.valid?(correct_value)).to eq(true)
43+
44+
expect(type.valid?(nil)).to eq(false)
45+
expect(type.valid?({})).to eq(false)
46+
expect(type.valid?(correct_value.merge(int: nil))).to eq(false)
47+
expect(type.valid?(correct_value.merge(int: :test))).to eq(false)
48+
expect(type.valid?(correct_value.merge(str: []))).to eq(false)
49+
expect(type.valid?(correct_value.merge(bool: {}))).to eq(false)
50+
end
51+
52+
specify 'type-validation' do
53+
expect { type.validate!(correct_value) }.not_to raise_error
54+
55+
expect { type.validate!(nil) }.to raise_error(SmartCore::Types::TypeError)
56+
expect { type.validate!({}) }.to raise_error(SmartCore::Types::TypeError)
57+
expect { type.validate!(correct_value.merge(int: nil)) }
58+
.to raise_error(SmartCore::Types::TypeError)
59+
expect { type.validate!(correct_value.merge(int: :test)) }
60+
.to raise_error(SmartCore::Types::TypeError)
61+
expect { type.validate!(correct_value.merge(str: [])) }
62+
.to raise_error(SmartCore::Types::TypeError)
63+
expect { type.validate!(correct_value.merge(bool: {})) }
64+
.to raise_error(SmartCore::Types::TypeError)
65+
end
66+
67+
context 'nilable type' do
68+
let(:type) do
69+
SmartCore::Types::Struct::StrictHash(
70+
int: SmartCore::Types::Value::Integer,
71+
str: SmartCore::Types::Value::String,
72+
bool: SmartCore::Types::Value::Boolean
73+
).nilable
74+
end
75+
76+
specify 'type-checking' do
77+
expect(type.valid?(correct_value)).to eq(true)
78+
expect(type.valid?(nil)).to eq(true)
79+
80+
expect(type.valid?({})).to eq(false)
81+
expect(type.valid?(correct_value.merge(int: nil))).to eq(false)
82+
expect(type.valid?(correct_value.merge(int: :test))).to eq(false)
83+
expect(type.valid?(correct_value.merge(str: []))).to eq(false)
84+
expect(type.valid?(correct_value.merge(bool: {}))).to eq(false)
85+
end
86+
87+
specify 'type-validation' do
88+
expect { type.validate!(correct_value) }.not_to raise_error
89+
expect { type.validate!(nil) }.not_to raise_error
90+
91+
expect { type.validate!({}) }.to raise_error(SmartCore::Types::TypeError)
92+
expect { type.validate!(correct_value.merge(int: nil)) }
93+
.to raise_error(SmartCore::Types::TypeError)
94+
expect { type.validate!(correct_value.merge(int: :test)) }
95+
.to raise_error(SmartCore::Types::TypeError)
96+
expect { type.validate!(correct_value.merge(str: [])) }
97+
.to raise_error(SmartCore::Types::TypeError)
98+
expect { type.validate!(correct_value.merge(bool: {})) }
99+
.to raise_error(SmartCore::Types::TypeError)
100+
end
101+
end
102+
end
103+
end

0 commit comments

Comments
 (0)