fear 2.0.1 → 3.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/.github/workflows/spec.yml +1 -5
- data/CHANGELOG.md +8 -0
- data/Gemfile +14 -2
- data/Gemfile.lock +48 -61
- data/LICENSE.txt +1 -1
- data/README.md +18 -70
- data/Rakefile +32 -119
- data/benchmarks/dry_do_vs_fear_for.txt +7 -6
- data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +7 -6
- data/benchmarks/factorial.txt +7 -9
- data/benchmarks/fear_gaurd_and1_vs_new.txt +7 -6
- data/benchmarks/fear_gaurd_and2_vs_and.txt +8 -7
- data/benchmarks/fear_gaurd_and3_vs_and_and.txt +7 -6
- data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +7 -6
- data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +8 -10
- data/fear.gemspec +4 -19
- data/lib/fear/either/left_projection.rb +237 -0
- data/lib/fear/either/pattern_match.rb +49 -0
- data/lib/fear/either.rb +20 -11
- data/lib/fear/either_api.rb +2 -4
- data/lib/fear/empty_partial_function.rb +1 -1
- data/lib/fear/failure/pattern_match.rb +14 -0
- data/lib/fear/failure.rb +1 -1
- data/lib/fear/for_api.rb +0 -2
- data/lib/fear/future.rb +1 -1
- data/lib/fear/future_api.rb +0 -5
- data/lib/fear/left/pattern_match.rb +15 -0
- data/lib/fear/left.rb +1 -1
- data/lib/fear/none.rb +0 -87
- data/lib/fear/none_class/pattern_match.rb +16 -0
- data/lib/fear/none_class.rb +85 -0
- data/lib/fear/option/pattern_match.rb +47 -0
- data/lib/fear/option.rb +1 -5
- data/lib/fear/option_api.rb +1 -3
- data/lib/fear/partial_function/empty.rb +3 -5
- data/lib/fear/partial_function/guard.rb +0 -4
- data/lib/fear/partial_function/or_else.rb +0 -2
- data/lib/fear/partial_function.rb +0 -9
- data/lib/fear/partial_function_class.rb +1 -1
- data/lib/fear/pattern_match.rb +3 -2
- data/lib/fear/pattern_matching_api.rb +0 -3
- data/lib/fear/right/pattern_match.rb +15 -0
- data/lib/fear/right.rb +1 -1
- data/lib/fear/right_biased.rb +2 -0
- data/lib/fear/some/pattern_match.rb +15 -0
- data/lib/fear/some.rb +1 -1
- data/lib/fear/success/pattern_match.rb +16 -0
- data/lib/fear/success.rb +1 -1
- data/lib/fear/try/pattern_match.rb +29 -0
- data/lib/fear/try.rb +3 -7
- data/lib/fear/try_api.rb +0 -2
- data/lib/fear/version.rb +1 -1
- data/lib/fear.rb +3 -14
- data/spec/fear/awaitable_spec.rb +0 -2
- data/spec/fear/either/left_projection_spec.rb +289 -0
- data/spec/fear/{either_pattern_match_spec.rb → either/pattern_match_spec.rb} +1 -1
- data/spec/fear/{option_pattern_match_spec.rb → option/pattern_match_spec.rb} +1 -1
- data/spec/fear/partial_function/empty_spec.rb +1 -1
- data/spec/fear/{try_pattern_match_spec.rb → try/try_pattern_match_spec.rb} +1 -1
- data/spec/support/.keep +0 -0
- metadata +29 -255
- data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +0 -11
- data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +0 -11
- data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +0 -11
- data/lib/dry/types/fear/option.rb +0 -125
- data/lib/dry/types/fear.rb +0 -8
- data/lib/fear/either_pattern_match.rb +0 -52
- data/lib/fear/failure_pattern_match.rb +0 -12
- data/lib/fear/left_pattern_match.rb +0 -11
- data/lib/fear/none_pattern_match.rb +0 -14
- data/lib/fear/option_pattern_match.rb +0 -50
- data/lib/fear/right_pattern_match.rb +0 -13
- data/lib/fear/some_pattern_match.rb +0 -13
- data/lib/fear/struct.rb +0 -237
- data/lib/fear/success_pattern_match.rb +0 -14
- data/lib/fear/try_pattern_match.rb +0 -32
- data/spec/dry/types/fear/option/constrained_spec.rb +0 -22
- data/spec/dry/types/fear/option/core_spec.rb +0 -77
- data/spec/dry/types/fear/option/default_spec.rb +0 -21
- data/spec/dry/types/fear/option/hash_spec.rb +0 -58
- data/spec/dry/types/fear/option/option_spec.rb +0 -97
- data/spec/struct_pattern_matching_spec.rb +0 -36
- data/spec/struct_spec.rb +0 -194
- data/spec/support/dry_types.rb +0 -6
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "fear/pattern_match"
|
4
|
-
|
5
|
-
module Fear
|
6
|
-
# Option pattern matcher
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
# pattern_match =
|
10
|
-
# OptionPatternMatch.new
|
11
|
-
# .some(Integer) { |x| x * 2 }
|
12
|
-
# .some(String) { |x| x.to_i * 2 }
|
13
|
-
# .none { 'NaN' }
|
14
|
-
# .else { 'error '}
|
15
|
-
#
|
16
|
-
# pattern_match.call(42) => 'NaN'
|
17
|
-
#
|
18
|
-
# @example the same matcher may be defined using block syntax
|
19
|
-
# OptionPatternMatch.new do |m|
|
20
|
-
# m.some(Integer) { |x| x * 2 }
|
21
|
-
# m.some(String) { |x| x.to_i * 2 }
|
22
|
-
# m.none { 'NaN' }
|
23
|
-
# m.else { 'error '}
|
24
|
-
# end
|
25
|
-
#
|
26
|
-
# @note it has two optimized subclasses +Fear::SomePatternMatch+ and +Fear::NonePatternMatch+
|
27
|
-
# @api private
|
28
|
-
class OptionPatternMatch < Fear::PatternMatch
|
29
|
-
# Match against Some
|
30
|
-
#
|
31
|
-
# @param conditions [<#==>]
|
32
|
-
# @return [Fear::OptionPatternMatch]
|
33
|
-
def some(*conditions, &effect)
|
34
|
-
branch = Fear.case(Fear::Some, &:get).and_then(Fear.case(*conditions, &effect))
|
35
|
-
or_else(branch)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Match against None
|
39
|
-
#
|
40
|
-
# @param effect [Proc]
|
41
|
-
# @return [Fear::OptionPatternMatch]
|
42
|
-
def none(&effect)
|
43
|
-
branch = Fear.case(Fear::None, &effect)
|
44
|
-
or_else(branch)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
require "fear/some_pattern_match"
|
50
|
-
require "fear/none_pattern_match"
|
data/lib/fear/struct.rb
DELETED
@@ -1,237 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "fear/pattern_match"
|
4
|
-
|
5
|
-
module Fear
|
6
|
-
# Structs are like regular classes and good for modeling immutable data.
|
7
|
-
#
|
8
|
-
# A minimal struct requires just a list of attributes:
|
9
|
-
#
|
10
|
-
# User = Fear::Struct.with_attributes(:id, :email, :admin)
|
11
|
-
# john = User.new(id: 2, email: 'john@example.com', admin: false)
|
12
|
-
#
|
13
|
-
# john.email #=> 'john@example.com'
|
14
|
-
#
|
15
|
-
# Instead of `.with_attributes` factory method you can use classic inheritance:
|
16
|
-
#
|
17
|
-
# class User < Fear::Struct
|
18
|
-
# attribute :id
|
19
|
-
# attribute :email
|
20
|
-
# attribute :admin
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# Since structs are immutable, you are not allowed to reassign their attributes
|
24
|
-
#
|
25
|
-
# john.email = ''john.doe@example.com'' #=> raises NoMethodError
|
26
|
-
#
|
27
|
-
# Two structs of the same type with the same attributes are equal
|
28
|
-
#
|
29
|
-
# john1 = User.new(id: 2, email: 'john@example.com', admin: false)
|
30
|
-
# john2 = User.new(id: 2, admin: false, email: 'john@example.com')
|
31
|
-
# john1 == john2 #=> true
|
32
|
-
#
|
33
|
-
# You can create a shallow copy of a +Struct+ by using copy method optionally changing its attributes.
|
34
|
-
#
|
35
|
-
# john = User.new(id: 2, email: 'john@example.com', admin: false)
|
36
|
-
# admin_john = john.copy(admin: true)
|
37
|
-
#
|
38
|
-
# john.admin #=> false
|
39
|
-
# admin_john.admin #=> true
|
40
|
-
#
|
41
|
-
class Struct
|
42
|
-
include PatternMatch.mixin
|
43
|
-
|
44
|
-
@attributes = [].freeze
|
45
|
-
|
46
|
-
class << self
|
47
|
-
# @param base [Fear::Struct]
|
48
|
-
# @api private
|
49
|
-
def inherited(base)
|
50
|
-
base.instance_variable_set(:@attributes, attributes)
|
51
|
-
end
|
52
|
-
|
53
|
-
# Defines attribute
|
54
|
-
#
|
55
|
-
# @param name [Symbol]
|
56
|
-
# @return [Symbol] attribute name
|
57
|
-
#
|
58
|
-
# @example
|
59
|
-
# class User < Fear::Struct
|
60
|
-
# attribute :id
|
61
|
-
# attribute :email
|
62
|
-
# end
|
63
|
-
#
|
64
|
-
def attribute(name)
|
65
|
-
name.to_sym.tap do |symbolized_name|
|
66
|
-
@attributes << symbolized_name
|
67
|
-
attr_reader symbolized_name
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# Members of this struct
|
72
|
-
#
|
73
|
-
# @return [<Symbol>]
|
74
|
-
def attributes
|
75
|
-
@attributes.dup
|
76
|
-
end
|
77
|
-
|
78
|
-
# Creates new struct with given attributes
|
79
|
-
# @param members [<Symbol>]
|
80
|
-
# @return [Fear::Struct]
|
81
|
-
#
|
82
|
-
# @example
|
83
|
-
# User = Fear::Struct.with_attributes(:id, :email, :admin) do
|
84
|
-
# def admin?
|
85
|
-
# @admin
|
86
|
-
# end
|
87
|
-
# end
|
88
|
-
#
|
89
|
-
def with_attributes(*members, &block)
|
90
|
-
members = members
|
91
|
-
block = block
|
92
|
-
|
93
|
-
Class.new(self) do
|
94
|
-
members.each { |member| attribute(member) }
|
95
|
-
class_eval(&block) if block
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# @param attributes [{Symbol => any}]
|
101
|
-
def initialize(**attributes)
|
102
|
-
_check_missing_attributes!(attributes)
|
103
|
-
_check_unknown_attributes!(attributes)
|
104
|
-
|
105
|
-
@values = members.each_with_object([]) do |name, values|
|
106
|
-
attributes.fetch(name).tap do |value|
|
107
|
-
_set_attribute(name, value)
|
108
|
-
values << value
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# Creates a shallow copy of this struct optionally changing the attributes arguments.
|
114
|
-
# @param attributes [{Symbol => any}]
|
115
|
-
#
|
116
|
-
# @example
|
117
|
-
# User = Fear::Struct.new(:id, :email, :admin)
|
118
|
-
# john = User.new(id: 2, email: 'john@example.com', admin: false)
|
119
|
-
# john.admin #=> false
|
120
|
-
# admin_john = john.copy(admin: true)
|
121
|
-
# admin_john.admin #=> true
|
122
|
-
#
|
123
|
-
def copy(**attributes)
|
124
|
-
self.class.new(**to_h.merge(attributes))
|
125
|
-
end
|
126
|
-
|
127
|
-
# Returns the struct attributes as an array of symbols
|
128
|
-
# @return [<Symbol>]
|
129
|
-
#
|
130
|
-
# @example
|
131
|
-
# User = Fear::Struct.new(:id, :email, :admin)
|
132
|
-
# john = User.new(email: 'john@example.com', admin: false, id: 2)
|
133
|
-
# john.attributes #=> [:id, :email, :admin]
|
134
|
-
#
|
135
|
-
def members
|
136
|
-
self.class.attributes
|
137
|
-
end
|
138
|
-
|
139
|
-
# Returns the values for this struct as an Array.
|
140
|
-
# @return [Array]
|
141
|
-
#
|
142
|
-
# @example
|
143
|
-
# User = Fear::Struct.new(:id, :email, :admin)
|
144
|
-
# john = User.new(email: 'john@example.com', admin: false, id: 2)
|
145
|
-
# john.to_a #=> [2, 'john@example.com', false]
|
146
|
-
#
|
147
|
-
def to_a
|
148
|
-
@values.dup
|
149
|
-
end
|
150
|
-
|
151
|
-
# @overload to_h()
|
152
|
-
# Returns a Hash containing the names and values for the struct's attributes
|
153
|
-
# @return [{Symbol => any}]
|
154
|
-
#
|
155
|
-
# @overload to_h(&block)
|
156
|
-
# Applies block to pairs of name name and value and use them to construct hash
|
157
|
-
# @yieldparam pair [<Symbol, any>] yields pair of name name and value
|
158
|
-
# @return [{Symbol => any}]
|
159
|
-
#
|
160
|
-
# @example
|
161
|
-
# User = Fear::Struct.new(:id, :email, :admin)
|
162
|
-
# john = User.new(email: 'john@example.com', admin: false, id: 2)
|
163
|
-
# john.to_h #=> {id: 2, email: 'john@example.com', admin: false}
|
164
|
-
# john.to_h do |key, value|
|
165
|
-
# [key.to_s, value]
|
166
|
-
# end #=> {'id' => 2, 'email' => 'john@example.com', 'admin' => false}
|
167
|
-
#
|
168
|
-
def to_h(&block)
|
169
|
-
pairs = members.zip(@values)
|
170
|
-
if block_given?
|
171
|
-
Hash[pairs.map(&block)]
|
172
|
-
else
|
173
|
-
Hash[pairs]
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
# @param other [any]
|
178
|
-
# @return [Boolean]
|
179
|
-
def ==(other)
|
180
|
-
other.is_a?(other.class) && to_h == other.to_h
|
181
|
-
end
|
182
|
-
|
183
|
-
INSPECT_TEMPLATE = "<#Fear::Struct %{class_name} %{attributes}>"
|
184
|
-
private_constant :INSPECT_TEMPLATE
|
185
|
-
|
186
|
-
# @return [String]
|
187
|
-
#
|
188
|
-
# @example
|
189
|
-
# User = Fear::Struct.with_attributes(:id, :email)
|
190
|
-
# user = User.new(id: 2, email: 'john@exmaple.com')
|
191
|
-
# user.inspect #=> "<#Fear::Struct User id=2, email=>'john@exmaple.com'>"
|
192
|
-
#
|
193
|
-
def inspect
|
194
|
-
attributes = to_h.map { |key, value| "#{key}=#{value.inspect}" }.join(", ")
|
195
|
-
|
196
|
-
format(INSPECT_TEMPLATE, class_name: self.class.name, attributes: attributes)
|
197
|
-
end
|
198
|
-
alias to_s inspect
|
199
|
-
|
200
|
-
MISSING_KEYWORDS_ERROR = "missing keywords: %{keywords}"
|
201
|
-
private_constant :MISSING_KEYWORDS_ERROR
|
202
|
-
|
203
|
-
private def _check_missing_attributes!(provided_attributes)
|
204
|
-
missing_attributes = members - provided_attributes.keys
|
205
|
-
|
206
|
-
unless missing_attributes.empty?
|
207
|
-
raise ArgumentError, format(MISSING_KEYWORDS_ERROR, keywords: missing_attributes.join(", "))
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
UNKNOWN_KEYWORDS_ERROR = "unknown keywords: %{keywords}"
|
212
|
-
private_constant :UNKNOWN_KEYWORDS_ERROR
|
213
|
-
|
214
|
-
private def _check_unknown_attributes!(provided_attributes)
|
215
|
-
unknown_attributes = provided_attributes.keys - members
|
216
|
-
|
217
|
-
unless unknown_attributes.empty?
|
218
|
-
raise ArgumentError, format(UNKNOWN_KEYWORDS_ERROR, keywords: unknown_attributes.join(", "))
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
# @return [void]
|
223
|
-
private def _set_attribute(name, value)
|
224
|
-
instance_variable_set(:"@#{name}", value)
|
225
|
-
end
|
226
|
-
|
227
|
-
# @param keys [Hash, nil]
|
228
|
-
# @return [Hash]
|
229
|
-
def deconstruct_keys(keys)
|
230
|
-
if keys
|
231
|
-
to_h.slice(*(self.class.attributes & keys))
|
232
|
-
else
|
233
|
-
to_h
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Fear
|
4
|
-
# @api private
|
5
|
-
class SuccessPatternMatch < Fear::TryPatternMatch
|
6
|
-
# @param conditions [<#==>]
|
7
|
-
# @return [Fear::TryPatternMatch]
|
8
|
-
def failure(*_conditions)
|
9
|
-
self
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
private_constant :SuccessPatternMatch
|
14
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "fear/pattern_match"
|
4
|
-
|
5
|
-
module Fear
|
6
|
-
# Try pattern matcher
|
7
|
-
#
|
8
|
-
# @note it has two optimized subclasses +Fear::SuccessPatternMatch+ and +Fear::FailurePatternMatch+
|
9
|
-
# @api private
|
10
|
-
class TryPatternMatch < Fear::PatternMatch
|
11
|
-
# Match against +Fear::Success+
|
12
|
-
#
|
13
|
-
# @param conditions [<#==>]
|
14
|
-
# @return [Fear::TryPatternMatch]
|
15
|
-
def success(*conditions, &effect)
|
16
|
-
branch = Fear.case(Fear::Success, &:get).and_then(Fear.case(*conditions, &effect))
|
17
|
-
or_else(branch)
|
18
|
-
end
|
19
|
-
|
20
|
-
# Match against +Fear::Failure+
|
21
|
-
#
|
22
|
-
# @param conditions [<#==>]
|
23
|
-
# @return [Fear::TryPatternMatch]
|
24
|
-
def failure(*conditions, &effect)
|
25
|
-
branch = Fear.case(Fear::Failure, &:exception).and_then(Fear.case(*conditions, &effect))
|
26
|
-
or_else(branch)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
require "fear/success_pattern_match"
|
32
|
-
require "fear/failure_pattern_match"
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "support/dry_types"
|
4
|
-
|
5
|
-
RSpec.describe Dry::Types::Constrained, :option do
|
6
|
-
context "with a option type" do
|
7
|
-
subject(:type) do
|
8
|
-
Dry::Types["nominal.string"].constrained(size: 4).option
|
9
|
-
end
|
10
|
-
|
11
|
-
it_behaves_like "Dry::Types::Nominal without primitive"
|
12
|
-
|
13
|
-
it "passes when constraints are not violated" do
|
14
|
-
expect(type[nil]).to be_none
|
15
|
-
expect(type["hell"]).to be_some_of("hell")
|
16
|
-
end
|
17
|
-
|
18
|
-
it "raises when a given constraint is violated" do
|
19
|
-
expect { type["hel"] }.to raise_error(Dry::Types::ConstraintError, /hel/)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,77 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "support/dry_types"
|
4
|
-
|
5
|
-
RSpec.describe Dry::Types::Nominal, :option do
|
6
|
-
describe "with opt-in option types" do
|
7
|
-
context "with strict string" do
|
8
|
-
let(:string) { Dry::Types["option.strict.string"] }
|
9
|
-
|
10
|
-
it_behaves_like "Dry::Types::Nominal without primitive" do
|
11
|
-
let(:type) { string }
|
12
|
-
end
|
13
|
-
|
14
|
-
it "accepts nil" do
|
15
|
-
expect(string[nil]).to be_none
|
16
|
-
end
|
17
|
-
|
18
|
-
it "accepts a string" do
|
19
|
-
expect(string["something"]).to be_some_of("something")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context "with coercible string" do
|
24
|
-
let(:string) { Dry::Types["option.coercible.string"] }
|
25
|
-
|
26
|
-
it_behaves_like "Dry::Types::Nominal without primitive" do
|
27
|
-
let(:type) { string }
|
28
|
-
end
|
29
|
-
|
30
|
-
it "accepts nil" do
|
31
|
-
expect(string[nil]).to be_none
|
32
|
-
end
|
33
|
-
|
34
|
-
it "accepts a string" do
|
35
|
-
expect(string[:something]).to be_some_of("something")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe "defining coercible Option String" do
|
41
|
-
let(:option_string) { Dry::Types["coercible.string"].option }
|
42
|
-
|
43
|
-
it_behaves_like "Dry::Types::Nominal without primitive" do
|
44
|
-
let(:type) { option_string }
|
45
|
-
end
|
46
|
-
|
47
|
-
it "accepts nil" do
|
48
|
-
expect(option_string[nil]).to be_none
|
49
|
-
end
|
50
|
-
|
51
|
-
it "accepts an object coercible to a string" do
|
52
|
-
expect(option_string[123]).to be_some_of("123")
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
describe "defining Option String" do
|
57
|
-
let(:option_string) { Dry::Types["strict.string"].option }
|
58
|
-
|
59
|
-
it_behaves_like "Dry::Types::Nominal without primitive" do
|
60
|
-
let(:type) { option_string }
|
61
|
-
end
|
62
|
-
|
63
|
-
it "accepts nil and returns None instance" do
|
64
|
-
value = option_string[nil]
|
65
|
-
|
66
|
-
expect(value).to be_none
|
67
|
-
expect(value.map(&:downcase).map(&:upcase)).to be_none
|
68
|
-
end
|
69
|
-
|
70
|
-
it "accepts a string and returns Some instance" do
|
71
|
-
value = option_string["SomeThing"]
|
72
|
-
|
73
|
-
expect(value).to be_some_of("SomeThing")
|
74
|
-
expect(value.map(&:downcase).map(&:upcase)).to be_some_of("SOMETHING")
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "support/dry_types"
|
4
|
-
|
5
|
-
RSpec.describe Dry::Types::Nominal, "#default", :option do
|
6
|
-
context "with a maybe" do
|
7
|
-
subject(:type) { Dry::Types["strict.integer"].option }
|
8
|
-
|
9
|
-
it_behaves_like "Dry::Types::Nominal without primitive" do
|
10
|
-
let(:type) { Dry::Types["strict.integer"].option.default(0) }
|
11
|
-
end
|
12
|
-
|
13
|
-
it "does not allow nil" do
|
14
|
-
expect { type.default(nil) }.to raise_error(ArgumentError, /nil/)
|
15
|
-
end
|
16
|
-
|
17
|
-
it "accepts a non-nil value" do
|
18
|
-
expect(type.default(0)[0]).to be_some_of(0)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "support/dry_types"
|
4
|
-
|
5
|
-
RSpec.describe Dry::Types::Hash, :option do
|
6
|
-
let(:email) { Dry::Types["option.strict.string"] }
|
7
|
-
|
8
|
-
context "Symbolized constructor" do
|
9
|
-
subject(:hash) do
|
10
|
-
Dry::Types["nominal.hash"].schema(
|
11
|
-
name: "string",
|
12
|
-
email: email,
|
13
|
-
).with_key_transform(&:to_sym)
|
14
|
-
end
|
15
|
-
|
16
|
-
describe "#[]" do
|
17
|
-
it "sets None as a default value for option" do
|
18
|
-
result = hash["name" => "Jane"]
|
19
|
-
|
20
|
-
expect(result[:email]).to be_none
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
context "Schema constructor" do
|
26
|
-
subject(:hash) do
|
27
|
-
Dry::Types["nominal.hash"].schema(
|
28
|
-
name: "string",
|
29
|
-
email: email,
|
30
|
-
)
|
31
|
-
end
|
32
|
-
|
33
|
-
describe "#[]" do
|
34
|
-
it "sets None as a default value for option types" do
|
35
|
-
result = hash[name: "Jane"]
|
36
|
-
|
37
|
-
expect(result[:email]).to be_none
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context "Strict with defaults" do
|
43
|
-
subject(:hash) do
|
44
|
-
Dry::Types["nominal.hash"].schema(
|
45
|
-
name: "string",
|
46
|
-
email: email,
|
47
|
-
)
|
48
|
-
end
|
49
|
-
|
50
|
-
describe "#[]" do
|
51
|
-
it "sets None as a default value for option types" do
|
52
|
-
result = hash[name: "Jane"]
|
53
|
-
|
54
|
-
expect(result[:email]).to be_none
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "support/dry_types"
|
4
|
-
|
5
|
-
RSpec.describe Dry::Types::Nominal, "#option", :option do
|
6
|
-
context "with a nominal" do
|
7
|
-
subject(:type) { Dry::Types["nominal.string"].option }
|
8
|
-
|
9
|
-
it_behaves_like "Dry::Types::Nominal without primitive"
|
10
|
-
|
11
|
-
it "returns None when value is nil" do
|
12
|
-
expect(type[nil]).to be_none
|
13
|
-
end
|
14
|
-
|
15
|
-
it "returns Some when value exists" do
|
16
|
-
expect(type["hello"]).to be_some_of("hello")
|
17
|
-
end
|
18
|
-
|
19
|
-
it "returns original if input is already a option" do
|
20
|
-
expect(type[Fear.some("hello")]).to be_some_of("hello")
|
21
|
-
end
|
22
|
-
|
23
|
-
it "aliases #[] as #call" do
|
24
|
-
expect(type.("hello")).to be_some_of("hello")
|
25
|
-
end
|
26
|
-
|
27
|
-
it "does not have primitive" do
|
28
|
-
expect(type).to_not respond_to(:primitive)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context "with a strict type" do
|
33
|
-
subject(:type) { Dry::Types["strict.integer"].option }
|
34
|
-
|
35
|
-
it_behaves_like "Dry::Types::Nominal without primitive"
|
36
|
-
|
37
|
-
it "returns None when value is nil" do
|
38
|
-
expect(type[nil]).to be_none
|
39
|
-
end
|
40
|
-
|
41
|
-
it "returns Some when value exists" do
|
42
|
-
expect(type[231]).to be_some_of(231)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context "with a sum" do
|
47
|
-
subject(:type) { Dry::Types["nominal.bool"].option }
|
48
|
-
|
49
|
-
it_behaves_like "Dry::Types::Nominal without primitive"
|
50
|
-
|
51
|
-
it "returns None when value is nil" do
|
52
|
-
expect(type[nil]).to be_none
|
53
|
-
end
|
54
|
-
|
55
|
-
it "returns Some when value exists" do
|
56
|
-
expect(type[true]).to be_some_of(true)
|
57
|
-
expect(type[false]).to be_some_of(false)
|
58
|
-
end
|
59
|
-
|
60
|
-
it "does not have primitive" do
|
61
|
-
expect(type).to_not respond_to(:primitive)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context "with keys" do
|
66
|
-
subject(:type) do
|
67
|
-
Dry::Types["hash"].schema(foo: Dry::Types["integer"]).key(:foo)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "gets wrapped by key type" do
|
71
|
-
expect(type.option).to be_a(Dry::Types::Schema::Key)
|
72
|
-
expect(type.option[nil]).to be_none
|
73
|
-
expect(type.option[1]).to be_some_of(1)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
describe "#try" do
|
78
|
-
subject(:type) { Dry::Types["coercible.integer"].option }
|
79
|
-
|
80
|
-
it "maps successful result" do
|
81
|
-
expect(type.try("1")).to eq(Dry::Types::Result::Success.new(Fear.some(1)))
|
82
|
-
expect(type.try(nil)).to eq(Dry::Types::Result::Success.new(Fear.none))
|
83
|
-
expect(type.try("a")).to be_a(Dry::Types::Result::Failure)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
describe "#call" do
|
88
|
-
describe "safe calls" do
|
89
|
-
subject(:type) { Dry::Types["coercible.integer"].option }
|
90
|
-
|
91
|
-
specify do
|
92
|
-
expect(type.("a") { :fallback }).to be(:fallback)
|
93
|
-
expect(type.(Fear.some(1)) { :fallback }).to eq(Fear.some(1))
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Fear::Struct do
|
4
|
-
describe "pattern matching" do
|
5
|
-
subject do
|
6
|
-
case struct
|
7
|
-
in Fear::Struct(a: 42)
|
8
|
-
"a = 42"
|
9
|
-
in Fear::Struct(a: 43, **rest)
|
10
|
-
"a = 43, #{rest}"
|
11
|
-
in Fear::Struct(a:)
|
12
|
-
"a = #{a}"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
let(:struct_class) { described_class.with_attributes(:a, :b) }
|
17
|
-
|
18
|
-
context "when match single value" do
|
19
|
-
let(:struct) { struct_class.new(b: 43, a: 42) }
|
20
|
-
|
21
|
-
it { is_expected.to eq("a = 42") }
|
22
|
-
end
|
23
|
-
|
24
|
-
context "when match single value and capture the rest" do
|
25
|
-
let(:struct) { struct_class.new(b: 42, a: 43) }
|
26
|
-
|
27
|
-
it { is_expected.to eq("a = 43, {:b=>42}") }
|
28
|
-
end
|
29
|
-
|
30
|
-
context "when capture a value" do
|
31
|
-
let(:struct) { struct_class.new(b: 45, a: 44) }
|
32
|
-
|
33
|
-
it { is_expected.to eq("a = 44") }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|