strict 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.agents/resume +4 -0
- data/.agents/setup +105 -0
- data/.rspec +2 -0
- data/.rubocop.yml +14 -4
- data/.tool-versions +1 -1
- data/API.md +335 -0
- data/CHANGELOG.md +110 -1
- data/Gemfile +13 -0
- data/Gemfile.lock +117 -41
- data/README.md +232 -6
- data/Rakefile +13 -7
- data/benchmark/baseline.rb +204 -0
- data/lib/strict/assignment_error.rb +5 -2
- data/lib/strict/attribute.rb +8 -49
- data/lib/strict/attributes/class.rb +0 -6
- data/lib/strict/attributes/coercer.rb +4 -3
- data/lib/strict/attributes/configuration.rb +15 -0
- data/lib/strict/attributes/dsl.rb +18 -10
- data/lib/strict/attributes/generated_methods.rb +113 -0
- data/lib/strict/attributes/instance.rb +32 -15
- data/lib/strict/declaration.rb +109 -0
- data/lib/strict/detailed_validator.rb +9 -0
- data/lib/strict/error.rb +8 -1
- data/lib/strict/initialization_error.rb +11 -2
- data/lib/strict/interface.rb +39 -21
- data/lib/strict/interfaces/conformance.rb +125 -0
- data/lib/strict/interfaces/instance.rb +1 -49
- data/lib/strict/method.rb +72 -53
- data/lib/strict/method_call_error.rb +12 -2
- data/lib/strict/method_return_error.rb +2 -2
- data/lib/strict/methods/dsl.rb +16 -8
- data/lib/strict/methods/module.rb +27 -8
- data/lib/strict/methods/verifiable_method.rb +234 -70
- data/lib/strict/object.rb +1 -1
- data/lib/strict/parameter.rb +3 -54
- data/lib/strict/return.rb +14 -12
- data/lib/strict/rspec.rb +159 -0
- data/lib/strict/union.rb +180 -0
- data/lib/strict/unions/coercer.rb +55 -0
- data/lib/strict/validation.rb +40 -0
- data/lib/strict/validators/all_of.rb +11 -3
- data/lib/strict/validators/array_of.rb +18 -3
- data/lib/strict/validators/hash_of.rb +23 -3
- data/lib/strict/value.rb +18 -4
- data/lib/strict/version.rb +1 -1
- data/lib/strict/violation.rb +9 -0
- data/lib/strict.rb +10 -9
- data/sig/strict/rspec.rbs +10 -0
- data/sig/strict.rbs +213 -1
- data/strict.gemspec +1 -9
- metadata +19 -120
- data/lib/strict/accessor/attributes.rb +0 -15
- data/lib/strict/accessor/module.rb +0 -45
- data/lib/strict/reader/attributes.rb +0 -15
- data/lib/strict/reader/module.rb +0 -27
data/Gemfile.lock
CHANGED
|
@@ -1,66 +1,142 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
strict (
|
|
4
|
+
strict (2.0.0)
|
|
5
5
|
zeitwerk (~> 2.6)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
activesupport (8.1.3.1)
|
|
11
|
+
base64
|
|
12
|
+
bigdecimal
|
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
14
|
+
connection_pool (>= 2.2.5)
|
|
15
|
+
drb
|
|
16
|
+
i18n (>= 1.6, < 2)
|
|
17
|
+
json
|
|
18
|
+
logger (>= 1.4.2)
|
|
19
|
+
minitest (>= 5.1)
|
|
20
|
+
securerandom (>= 0.3)
|
|
21
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
22
|
+
uri (>= 0.13.1)
|
|
23
|
+
ast (2.4.3)
|
|
24
|
+
base64 (0.3.0)
|
|
25
|
+
bigdecimal (4.1.2)
|
|
26
|
+
concurrent-ruby (1.3.8)
|
|
27
|
+
connection_pool (3.0.2)
|
|
28
|
+
debug (1.11.1)
|
|
29
|
+
irb (~> 1.10)
|
|
30
|
+
reline (>= 0.3.8)
|
|
31
|
+
diff-lcs (1.6.2)
|
|
32
|
+
drb (2.2.3)
|
|
33
|
+
erb (6.0.7)
|
|
34
|
+
factory_bot (6.6.0)
|
|
35
|
+
activesupport (>= 6.1.0)
|
|
36
|
+
gem-release (2.2.4)
|
|
37
|
+
i18n (1.15.2)
|
|
38
|
+
concurrent-ruby (~> 1.0)
|
|
39
|
+
io-console (0.9.2)
|
|
40
|
+
irb (1.18.0)
|
|
41
|
+
pp (>= 0.6.0)
|
|
42
|
+
prism (>= 1.3.0)
|
|
43
|
+
rdoc (>= 4.0.0)
|
|
44
|
+
reline (>= 0.4.2)
|
|
45
|
+
json (2.21.2)
|
|
46
|
+
language_server-protocol (3.17.0.6)
|
|
47
|
+
lint_roller (1.1.0)
|
|
48
|
+
logger (1.7.0)
|
|
49
|
+
minitest (6.0.6)
|
|
50
|
+
drb (~> 2.0)
|
|
51
|
+
prism (~> 1.5)
|
|
52
|
+
parallel (2.1.0)
|
|
53
|
+
parser (3.3.12.0)
|
|
23
54
|
ast (~> 2.4.1)
|
|
55
|
+
racc
|
|
56
|
+
pp (0.6.4)
|
|
57
|
+
prettyprint
|
|
58
|
+
prettyprint (0.2.0)
|
|
59
|
+
prism (1.9.0)
|
|
60
|
+
racc (1.8.1)
|
|
24
61
|
rainbow (3.1.1)
|
|
25
|
-
rake (13.
|
|
26
|
-
|
|
27
|
-
|
|
62
|
+
rake (13.4.2)
|
|
63
|
+
rbs (4.1.3)
|
|
64
|
+
logger
|
|
65
|
+
prism (>= 1.6.0)
|
|
66
|
+
tsort
|
|
67
|
+
rdoc (8.0.0)
|
|
68
|
+
erb
|
|
69
|
+
prism (>= 1.6.0)
|
|
70
|
+
rbs (>= 4.0.0)
|
|
71
|
+
tsort
|
|
72
|
+
regexp_parser (2.12.0)
|
|
73
|
+
reline (0.7.0)
|
|
28
74
|
io-console (~> 0.5)
|
|
29
|
-
|
|
30
|
-
|
|
75
|
+
rspec (3.13.2)
|
|
76
|
+
rspec-core (~> 3.13.0)
|
|
77
|
+
rspec-expectations (~> 3.13.0)
|
|
78
|
+
rspec-mocks (~> 3.13.0)
|
|
79
|
+
rspec-core (3.13.6)
|
|
80
|
+
rspec-support (~> 3.13.0)
|
|
81
|
+
rspec-expectations (3.13.5)
|
|
82
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
83
|
+
rspec-support (~> 3.13.0)
|
|
84
|
+
rspec-mocks (3.13.8)
|
|
85
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
86
|
+
rspec-support (~> 3.13.0)
|
|
87
|
+
rspec-support (3.13.7)
|
|
88
|
+
rubocop (1.89.0)
|
|
31
89
|
json (~> 2.3)
|
|
32
|
-
|
|
33
|
-
|
|
90
|
+
language_server-protocol (~> 3.17.0.2)
|
|
91
|
+
lint_roller (~> 1.1.0)
|
|
92
|
+
parallel (>= 1.10)
|
|
93
|
+
parser (>= 3.3.0.2)
|
|
34
94
|
rainbow (>= 2.2.2, < 4.0)
|
|
35
|
-
regexp_parser (>=
|
|
36
|
-
|
|
37
|
-
rubocop-ast (>= 1.28.0, < 2.0)
|
|
95
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
96
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
38
97
|
ruby-progressbar (~> 1.7)
|
|
39
|
-
unicode-display_width (>= 2.4.0, <
|
|
40
|
-
rubocop-ast (1.
|
|
41
|
-
parser (>= 3.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
rubocop (~> 1.
|
|
98
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
99
|
+
rubocop-ast (1.50.0)
|
|
100
|
+
parser (>= 3.3.7.2)
|
|
101
|
+
prism (~> 1.7)
|
|
102
|
+
rubocop-factory_bot (2.28.0)
|
|
103
|
+
lint_roller (~> 1.1)
|
|
104
|
+
rubocop (~> 1.72, >= 1.72.1)
|
|
105
|
+
rubocop-rake (0.7.1)
|
|
106
|
+
lint_roller (~> 1.1)
|
|
107
|
+
rubocop (>= 1.72.1)
|
|
108
|
+
rubocop-rspec (3.10.2)
|
|
109
|
+
lint_roller (~> 1.1)
|
|
110
|
+
regexp_parser (>= 2.0)
|
|
111
|
+
rubocop (~> 1.86, >= 1.86.2)
|
|
46
112
|
ruby-progressbar (1.13.0)
|
|
47
|
-
|
|
48
|
-
|
|
113
|
+
securerandom (0.4.1)
|
|
114
|
+
tsort (0.2.0)
|
|
115
|
+
tzinfo (2.0.6)
|
|
116
|
+
concurrent-ruby (~> 1.0)
|
|
117
|
+
unicode-display_width (3.2.0)
|
|
118
|
+
unicode-emoji (~> 4.1)
|
|
119
|
+
unicode-emoji (4.2.0)
|
|
120
|
+
uri (1.1.1)
|
|
121
|
+
zeitwerk (2.8.3)
|
|
49
122
|
|
|
50
123
|
PLATFORMS
|
|
51
124
|
arm64-darwin-21
|
|
125
|
+
arm64-darwin-23
|
|
52
126
|
x86_64-linux
|
|
53
127
|
|
|
54
128
|
DEPENDENCIES
|
|
55
|
-
debug (
|
|
129
|
+
debug (~> 1.11)
|
|
130
|
+
factory_bot (~> 6.5)
|
|
56
131
|
gem-release (~> 2.2)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
rubocop (~> 1.
|
|
61
|
-
rubocop-
|
|
62
|
-
rubocop-rake (~> 0.
|
|
132
|
+
rake (~> 13.4)
|
|
133
|
+
rbs (~> 4.1)
|
|
134
|
+
rspec (~> 3.13)
|
|
135
|
+
rubocop (~> 1.89)
|
|
136
|
+
rubocop-factory_bot (~> 2.28)
|
|
137
|
+
rubocop-rake (~> 0.7)
|
|
138
|
+
rubocop-rspec (~> 3.10)
|
|
63
139
|
strict!
|
|
64
140
|
|
|
65
141
|
BUNDLED WITH
|
|
66
|
-
|
|
142
|
+
4.0.19
|
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Strict provides a means to strictly validate instantiation of values, instantiat
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
+
Strict requires Ruby 3.3 or newer.
|
|
8
|
+
|
|
7
9
|
Install the gem and add to the application's Gemfile by executing:
|
|
8
10
|
|
|
9
11
|
```sh
|
|
@@ -18,6 +20,8 @@ $ gem install strict
|
|
|
18
20
|
|
|
19
21
|
## Usage
|
|
20
22
|
|
|
23
|
+
See [Supported API](API.md) for the 2.0 compatibility boundary.
|
|
24
|
+
|
|
21
25
|
### `Strict::Value`
|
|
22
26
|
|
|
23
27
|
```rb
|
|
@@ -49,6 +53,83 @@ Money.new(amount_in_cents: 100_00) == Money.new(amount_in_cents: 100_00)
|
|
|
49
53
|
# => true
|
|
50
54
|
```
|
|
51
55
|
|
|
56
|
+
Subclasses can add attributes while retaining their inherited attributes:
|
|
57
|
+
|
|
58
|
+
```rb
|
|
59
|
+
class Person
|
|
60
|
+
include Strict::Value
|
|
61
|
+
|
|
62
|
+
attributes do
|
|
63
|
+
name String
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class Employee < Person
|
|
68
|
+
attributes do
|
|
69
|
+
employee_id String
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
A subclass can also redeclare an inherited attribute by its exact name. The subclass declaration replaces the inherited validator, coercer, and default without changing the parent or the attribute order.
|
|
75
|
+
|
|
76
|
+
### `Strict::Union`
|
|
77
|
+
|
|
78
|
+
```rb
|
|
79
|
+
class PaymentResult
|
|
80
|
+
include Strict::Union
|
|
81
|
+
|
|
82
|
+
discriminator :status
|
|
83
|
+
|
|
84
|
+
attributes do
|
|
85
|
+
request_id String
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
variant :authorized, tag: "payment.authorized" do
|
|
89
|
+
attributes do
|
|
90
|
+
authorization_id String
|
|
91
|
+
amount_in_cents Integer
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def successful? = true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
variant :declined do
|
|
98
|
+
attributes do
|
|
99
|
+
reason String
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def successful? = false
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
authorized = PaymentResult::Authorized.new(
|
|
107
|
+
request_id: "request_123",
|
|
108
|
+
authorization_id: "auth_123",
|
|
109
|
+
amount_in_cents: 1_000
|
|
110
|
+
)
|
|
111
|
+
authorized.to_h
|
|
112
|
+
# => { status: "payment.authorized", request_id: "request_123", authorization_id: "auth_123", amount_in_cents: 1_000 }
|
|
113
|
+
|
|
114
|
+
authorized.successful?
|
|
115
|
+
# => true
|
|
116
|
+
|
|
117
|
+
result = PaymentResult.coercer.call(
|
|
118
|
+
"status" => "declined",
|
|
119
|
+
"request_id" => "request_456",
|
|
120
|
+
"reason" => "insufficient_funds"
|
|
121
|
+
)
|
|
122
|
+
# => #<PaymentResult::Declined status=:declined request_id="request_456" reason="insufficient_funds">
|
|
123
|
+
|
|
124
|
+
case result
|
|
125
|
+
in PaymentResult::Authorized(authorization_id:)
|
|
126
|
+
authorization_id
|
|
127
|
+
in PaymentResult::Declined(reason:)
|
|
128
|
+
reason
|
|
129
|
+
end
|
|
130
|
+
# => "insufficient_funds"
|
|
131
|
+
```
|
|
132
|
+
|
|
52
133
|
### `Strict::Object`
|
|
53
134
|
|
|
54
135
|
```rb
|
|
@@ -78,11 +159,59 @@ Stateful.new(some_state: "123") == Stateful.new(some_state: "123")
|
|
|
78
159
|
# => false
|
|
79
160
|
```
|
|
80
161
|
|
|
162
|
+
Validation errors provide structured violations with paths into nested values:
|
|
163
|
+
|
|
164
|
+
```rb
|
|
165
|
+
class Batch
|
|
166
|
+
include Strict::Value
|
|
167
|
+
|
|
168
|
+
attributes do
|
|
169
|
+
labels ArrayOf(String)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
begin
|
|
174
|
+
Batch.new(labels: ["ready", 404], extra: true)
|
|
175
|
+
rescue Strict::InitializationError => error
|
|
176
|
+
error.violations.map do |violation|
|
|
177
|
+
[violation.path, violation.code, violation.value, violation.validator]
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
# => [
|
|
181
|
+
# [[:labels, 1], :invalid, 404, String],
|
|
182
|
+
# [[:extra], :unexpected, true, nil]
|
|
183
|
+
# ]
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The codes are `:invalid`, `:missing`, and `:unexpected`. Custom validators only need to implement `===`; Strict reports a rejected value against that validator at the current path. A custom validator can include `Strict::DetailedValidator` and implement `violations(value)` when it needs to report relative nested paths:
|
|
187
|
+
|
|
188
|
+
```rb
|
|
189
|
+
class Emails
|
|
190
|
+
include Strict::DetailedValidator
|
|
191
|
+
|
|
192
|
+
def violations(value)
|
|
193
|
+
unless Array === value
|
|
194
|
+
return [Strict::Violation.new(path: [], code: :invalid, value: value, validator: Array)]
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
value.each_with_index.filter_map do |email, index|
|
|
198
|
+
next if String === email
|
|
199
|
+
|
|
200
|
+
Strict::Violation.new(path: [index], code: :invalid, value: email, validator: String)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
The module provides `===` from `violations`, and Strict prefixes each relative path with its enclosing attribute, parameter, or collection path.
|
|
207
|
+
|
|
208
|
+
When an attribute or parameter validator provides a `.coercer`, Strict uses it automatically before validation. An explicit `coerce:` value overrides it, and `coerce: false` disables it. This lets nested Strict values, unions, and interfaces accept the input handled by their class coercers without repeating `coerce:` in each declaration. `ArrayOf` and `HashOf` propagate coercers from their element, key, and value validators, so compositions such as `ArrayOf(ValueClass)` also coerce automatically.
|
|
209
|
+
|
|
81
210
|
### `Strict::Method`
|
|
82
211
|
|
|
83
212
|
```rb
|
|
84
213
|
class UpdateEmail
|
|
85
|
-
|
|
214
|
+
include Strict::Method
|
|
86
215
|
|
|
87
216
|
sig do
|
|
88
217
|
user_id String, coerce: ->(value) { value.to_s }
|
|
@@ -108,11 +237,13 @@ UpdateEmail.new.call(user_id: "123", email: "456")
|
|
|
108
237
|
# => Strict::MethodReturnError
|
|
109
238
|
```
|
|
110
239
|
|
|
240
|
+
`returns` validates the exact value produced by the method. It never coerces or replaces that value, and a successful call returns the same object to the caller.
|
|
241
|
+
|
|
111
242
|
### `Strict::Interface`
|
|
112
243
|
|
|
113
244
|
```rb
|
|
114
245
|
class Storage
|
|
115
|
-
|
|
246
|
+
include Strict::Interface
|
|
116
247
|
|
|
117
248
|
expose(:write) do
|
|
118
249
|
key String
|
|
@@ -147,7 +278,15 @@ module Storages
|
|
|
147
278
|
end
|
|
148
279
|
end
|
|
149
280
|
|
|
150
|
-
|
|
281
|
+
adapter = Storages::Memory.new
|
|
282
|
+
|
|
283
|
+
Storage.implemented_by?(adapter)
|
|
284
|
+
# => true
|
|
285
|
+
|
|
286
|
+
Storage.verify_implementation!(adapter)
|
|
287
|
+
# => nil
|
|
288
|
+
|
|
289
|
+
storage = Storage.new(adapter)
|
|
151
290
|
# => #<Storage implementation=#<Storages::Memory>>
|
|
152
291
|
|
|
153
292
|
storage.write(key: "some/path/to/file.rb", contents: "Hello")
|
|
@@ -173,6 +312,88 @@ storage = Storage.new(Storages::Wat.new)
|
|
|
173
312
|
# => Strict::ImplementationDoesNotConformError
|
|
174
313
|
```
|
|
175
314
|
|
|
315
|
+
### RSpec extensions
|
|
316
|
+
|
|
317
|
+
Strict provides supported, opt-in integration with RSpec 3.13. RSpec remains an optional dependency and is not loaded by
|
|
318
|
+
`require "strict"`. Add RSpec to the test bundle:
|
|
319
|
+
|
|
320
|
+
```rb
|
|
321
|
+
group :test do
|
|
322
|
+
gem "rspec", "~> 3.13"
|
|
323
|
+
end
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Then require the adapter from the spec helper:
|
|
327
|
+
|
|
328
|
+
```rb
|
|
329
|
+
require "strict/rspec"
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The adapter provides matchers for validators and interfaces:
|
|
333
|
+
|
|
334
|
+
```rb
|
|
335
|
+
expect(String).to validate("value")
|
|
336
|
+
expect(String).not_to validate(1)
|
|
337
|
+
|
|
338
|
+
expect(Storages::Memory.new).to conform_to(Storage)
|
|
339
|
+
expect(Object.new).not_to conform_to(Storage)
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
When validation fails, `validate` uses `Strict::Violation` records to report root and nested
|
|
343
|
+
`Strict::DetailedValidator` failure paths.
|
|
344
|
+
|
|
345
|
+
`strict_double` builds an RSpec verifying double. For an interface, it stubs every exposed method to `nil` unless a
|
|
346
|
+
different result is provided, so the double conforms without extra setup:
|
|
347
|
+
|
|
348
|
+
```rb
|
|
349
|
+
storage = strict_double(Storage, write: true, read: "contents")
|
|
350
|
+
|
|
351
|
+
expect(storage).to conform_to(Storage)
|
|
352
|
+
Storage.new(storage).read(key: "some/path")
|
|
353
|
+
# => "contents"
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
RSpec instance doubles also satisfy Strict class validators for attributes, signed parameters, and return values. Plain
|
|
357
|
+
doubles remain invalid:
|
|
358
|
+
|
|
359
|
+
```rb
|
|
360
|
+
class Item
|
|
361
|
+
include Strict::Value
|
|
362
|
+
|
|
363
|
+
attributes do
|
|
364
|
+
sku String
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
class Shipment
|
|
369
|
+
include Strict::Value
|
|
370
|
+
|
|
371
|
+
attributes do
|
|
372
|
+
item Item
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
item = instance_double(Item, sku: "item_123")
|
|
377
|
+
Shipment.new(item: item)
|
|
378
|
+
# => #<Shipment item=#<InstanceDouble(Item)>>
|
|
379
|
+
|
|
380
|
+
Shipment.new(item: double("item"))
|
|
381
|
+
# => Strict::InitializationError
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Matcher objects can also stand in for validated fields or elements of built-in collection validators when constructing
|
|
385
|
+
expected Strict values. RSpec recursively applies the nested matchers in argument expectations:
|
|
386
|
+
|
|
387
|
+
```rb
|
|
388
|
+
expect(dispatcher).to have_received(:ship).with(
|
|
389
|
+
shipment: Shipment.new(
|
|
390
|
+
item: have_attributes(sku: "item_123")
|
|
391
|
+
)
|
|
392
|
+
)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
This composition does not change normal Strict value equality or hashing.
|
|
396
|
+
|
|
176
397
|
### Configuration
|
|
177
398
|
|
|
178
399
|
Strict exposes some configuration options which can be configured globally via `Strict.configure { ... }` or overridden
|
|
@@ -192,10 +413,10 @@ Strict.configure do |c|
|
|
|
192
413
|
end
|
|
193
414
|
|
|
194
415
|
Strict.configure do |c|
|
|
195
|
-
c.sample_rate =
|
|
416
|
+
c.sample_rate = 1 # always run validation
|
|
196
417
|
end
|
|
197
418
|
|
|
198
|
-
# Locally within the block (only applies to the current
|
|
419
|
+
# Locally within the block (only applies to the current execution context)
|
|
199
420
|
|
|
200
421
|
Strict.with_overrides(sample_rate: 0) do
|
|
201
422
|
# Use Strict as you normally would
|
|
@@ -206,6 +427,9 @@ Strict.with_overrides(sample_rate: 0) do
|
|
|
206
427
|
end
|
|
207
428
|
```
|
|
208
429
|
|
|
430
|
+
Overrides are local to the current execution context (fiber). They can be nested and are restored when a block returns
|
|
431
|
+
or raises. Neither a newly created fiber nor a new thread inherits an active override.
|
|
432
|
+
|
|
209
433
|
#### `Strict.configuration.random`
|
|
210
434
|
|
|
211
435
|
The instance of a `Random::Formatter` that Strict uses in tandom with the `sample_rate` to determine when validation
|
|
@@ -223,7 +447,9 @@ run validations. The `sample_rate` is used in tandem with `random` to determine
|
|
|
223
447
|
|
|
224
448
|
## Development
|
|
225
449
|
|
|
226
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake
|
|
450
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the specs. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
451
|
+
|
|
452
|
+
Run `bundle exec rake benchmark` to measure baseline timing and allocations for value initialization, mutable assignment, verified method calls, interface construction, interface calls, `to_h`, equality, and hashing. Set `ITERATIONS`, `WARMUP_ITERATIONS`, or `SAMPLES` to change the workload, and set `FORMAT=markdown` to produce a Markdown table. Pull requests publish this table in a non-blocking job summary and upload it as an artifact. These benchmarks report measurements only and do not enforce thresholds.
|
|
227
453
|
|
|
228
454
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
229
455
|
|
data/Rakefile
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
|
-
require "
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
t.libs << "test"
|
|
8
|
-
t.libs << "lib"
|
|
9
|
-
t.test_files = FileList["test/**/*_test.rb"]
|
|
10
|
-
end
|
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
11
7
|
|
|
12
8
|
require "rubocop/rake_task"
|
|
13
9
|
|
|
14
10
|
RuboCop::RakeTask.new
|
|
15
11
|
|
|
16
|
-
|
|
12
|
+
desc "Validate RBS signatures"
|
|
13
|
+
task :rbs do
|
|
14
|
+
sh "bundle exec rbs -I sig validate"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "Run timing and allocation baselines"
|
|
18
|
+
task :benchmark do
|
|
19
|
+
sh "bundle exec ruby benchmark/baseline.rb", verbose: false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
task default: %i[spec rubocop rbs]
|