fear 2.0.1 → 3.1.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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +105 -0
  3. data/.simplecov +2 -2
  4. data/.standard.yml +1 -0
  5. data/CHANGELOG.md +8 -0
  6. data/Gemfile +14 -2
  7. data/Gemfile.lock +84 -78
  8. data/LICENSE.txt +1 -1
  9. data/README.md +18 -70
  10. data/Rakefile +55 -142
  11. data/benchmarks/dry_do_vs_fear_for.txt +7 -6
  12. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +7 -6
  13. data/benchmarks/factorial.txt +7 -9
  14. data/benchmarks/fear_gaurd_and1_vs_new.txt +7 -6
  15. data/benchmarks/fear_gaurd_and2_vs_and.txt +8 -7
  16. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +7 -6
  17. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +7 -6
  18. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +8 -10
  19. data/examples/pattern_extracting.rb +2 -2
  20. data/examples/pattern_matching_number_in_words.rb +12 -12
  21. data/fear.gemspec +5 -21
  22. data/lib/fear/either/left_projection.rb +237 -0
  23. data/lib/fear/either/pattern_match.rb +49 -0
  24. data/lib/fear/either.rb +21 -12
  25. data/lib/fear/either_api.rb +2 -4
  26. data/lib/fear/empty_partial_function.rb +3 -3
  27. data/lib/fear/failure/pattern_match.rb +14 -0
  28. data/lib/fear/failure.rb +5 -5
  29. data/lib/fear/for.rb +1 -1
  30. data/lib/fear/for_api.rb +1 -3
  31. data/lib/fear/future.rb +6 -6
  32. data/lib/fear/future_api.rb +0 -5
  33. data/lib/fear/left/pattern_match.rb +15 -0
  34. data/lib/fear/left.rb +4 -4
  35. data/lib/fear/none.rb +0 -87
  36. data/lib/fear/none_class/pattern_match.rb +16 -0
  37. data/lib/fear/none_class.rb +85 -0
  38. data/lib/fear/option/pattern_match.rb +47 -0
  39. data/lib/fear/option.rb +2 -6
  40. data/lib/fear/option_api.rb +1 -3
  41. data/lib/fear/partial_function/and_then.rb +2 -2
  42. data/lib/fear/partial_function/combined.rb +3 -3
  43. data/lib/fear/partial_function/empty.rb +3 -5
  44. data/lib/fear/partial_function/guard.rb +0 -4
  45. data/lib/fear/partial_function/or_else.rb +2 -4
  46. data/lib/fear/partial_function.rb +0 -9
  47. data/lib/fear/partial_function_class.rb +2 -2
  48. data/lib/fear/pattern_match.rb +5 -4
  49. data/lib/fear/pattern_matching_api.rb +1 -4
  50. data/lib/fear/right/pattern_match.rb +15 -0
  51. data/lib/fear/right.rb +4 -4
  52. data/lib/fear/right_biased.rb +2 -0
  53. data/lib/fear/some/pattern_match.rb +15 -0
  54. data/lib/fear/some.rb +3 -3
  55. data/lib/fear/success/pattern_match.rb +16 -0
  56. data/lib/fear/success.rb +5 -5
  57. data/lib/fear/try/pattern_match.rb +29 -0
  58. data/lib/fear/try.rb +3 -7
  59. data/lib/fear/try_api.rb +1 -3
  60. data/lib/fear/utils.rb +1 -1
  61. data/lib/fear/version.rb +1 -1
  62. data/lib/fear.rb +3 -14
  63. data/spec/fear/awaitable_spec.rb +0 -2
  64. data/spec/fear/either/left_projection_spec.rb +289 -0
  65. data/spec/fear/{either_pattern_match_spec.rb → either/pattern_match_spec.rb} +7 -7
  66. data/spec/fear/either_spec.rb +1 -1
  67. data/spec/fear/left_spec.rb +1 -1
  68. data/spec/fear/{option_pattern_match_spec.rb → option/pattern_match_spec.rb} +6 -6
  69. data/spec/fear/option_spec.rb +2 -2
  70. data/spec/fear/partial_function/any_spec.rb +3 -3
  71. data/spec/fear/partial_function/empty_spec.rb +2 -2
  72. data/spec/fear/partial_function_and_then_spec.rb +5 -5
  73. data/spec/fear/partial_function_composition_spec.rb +6 -6
  74. data/spec/fear/partial_function_or_else_spec.rb +13 -13
  75. data/spec/fear/partial_function_spec.rb +7 -7
  76. data/spec/fear/pattern_match_spec.rb +5 -5
  77. data/spec/fear/right_spec.rb +1 -1
  78. data/spec/fear/{try_pattern_match_spec.rb → try/try_pattern_match_spec.rb} +7 -7
  79. data/spec/fear/try_api_spec.rb +2 -2
  80. data/spec/fear/utils_spec.rb +3 -3
  81. data/spec/support/.keep +0 -0
  82. metadata +27 -296
  83. data/.github/workflows/rubocop.yml +0 -39
  84. data/.github/workflows/spec.yml +0 -43
  85. data/.rubocop.yml +0 -7
  86. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +0 -11
  87. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +0 -11
  88. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +0 -11
  89. data/lib/dry/types/fear/option.rb +0 -125
  90. data/lib/dry/types/fear.rb +0 -8
  91. data/lib/fear/either_pattern_match.rb +0 -52
  92. data/lib/fear/failure_pattern_match.rb +0 -12
  93. data/lib/fear/left_pattern_match.rb +0 -11
  94. data/lib/fear/none_pattern_match.rb +0 -14
  95. data/lib/fear/option_pattern_match.rb +0 -50
  96. data/lib/fear/right_pattern_match.rb +0 -13
  97. data/lib/fear/some_pattern_match.rb +0 -13
  98. data/lib/fear/struct.rb +0 -237
  99. data/lib/fear/success_pattern_match.rb +0 -14
  100. data/lib/fear/try_pattern_match.rb +0 -32
  101. data/spec/dry/types/fear/option/constrained_spec.rb +0 -22
  102. data/spec/dry/types/fear/option/core_spec.rb +0 -77
  103. data/spec/dry/types/fear/option/default_spec.rb +0 -21
  104. data/spec/dry/types/fear/option/hash_spec.rb +0 -58
  105. data/spec/dry/types/fear/option/option_spec.rb +0 -97
  106. data/spec/struct_pattern_matching_spec.rb +0 -36
  107. data/spec/struct_spec.rb +0 -194
  108. data/spec/support/dry_types.rb +0 -6
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Dry
4
- module Types
5
- class Option
6
- include Type
7
- include ::Dry::Equalizer(:type, :options, inspect: false, immutable: true)
8
- include Decorator
9
- include Builder
10
- include Printable
11
-
12
- # @param [Fear::Option, Object] input
13
- #
14
- # @return [Fear::Option]
15
- #
16
- # @api private
17
- def call_unsafe(input = Undefined)
18
- case input
19
- when ::Fear::Option
20
- input
21
- when Undefined
22
- Fear.none
23
- else
24
- Fear.option(type.call_unsafe(input))
25
- end
26
- end
27
-
28
- # @param [Fear::Option, Object] input
29
- #
30
- # @return [Fear::Option]
31
- #
32
- # @api private
33
- def call_safe(input = Undefined)
34
- case input
35
- when ::Fear::Option
36
- input
37
- when Undefined
38
- Fear.none
39
- else
40
- Fear.option(type.call_safe(input) { |output = input| return yield(output) })
41
- end
42
- end
43
-
44
- # @param [Object] input
45
- #
46
- # @return [Result::Success]
47
- #
48
- # @api public
49
- def try(input = Undefined)
50
- result = type.try(input)
51
-
52
- if result.success?
53
- Result::Success.new(Fear.option(result.input))
54
- else
55
- result
56
- end
57
- end
58
-
59
- # @return [true]
60
- #
61
- # @api public
62
- def default?
63
- true
64
- end
65
-
66
- # @param [Object] value
67
- #
68
- # @see Dry::Types::Builder#default
69
- #
70
- # @raise [ArgumentError] if nil provided as default value
71
- #
72
- # @api public
73
- def default(value)
74
- if value.nil?
75
- raise ArgumentError, "nil cannot be used as a default of a maybe type"
76
- else
77
- super
78
- end
79
- end
80
- end
81
-
82
- module Builder
83
- # Turn a type into a maybe type
84
- #
85
- # @return [Option]
86
- #
87
- # @api public
88
- def option
89
- Option.new(Types["nil"] | self)
90
- end
91
- end
92
-
93
- # @api private
94
- class Schema
95
- class Key
96
- # @api private
97
- def option
98
- __new__(type.option)
99
- end
100
- end
101
- end
102
-
103
- # @api private
104
- class Printer
105
- MAPPING[Option] = :visit_option
106
-
107
- # @api private
108
- def visit_option(maybe)
109
- visit(maybe.type) do |type|
110
- yield "Fear::Option<#{type}>"
111
- end
112
- end
113
- end
114
-
115
- # Register non-coercible maybe types
116
- NON_NIL.each_key do |name|
117
- register("option.strict.#{name}", self[name.to_s].option)
118
- end
119
-
120
- # Register coercible maybe types
121
- COERCIBLE.each_key do |name|
122
- register("option.coercible.#{name}", self["coercible.#{name}"].option)
123
- end
124
- end
125
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dry/types"
4
- require "fear"
5
-
6
- Dry::Types.register_extension(:fear_option) do
7
- require "dry/types/fear/option"
8
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fear/pattern_match"
4
-
5
- module Fear
6
- # Either pattern matcher
7
- #
8
- # @example
9
- # pattern_match =
10
- # EitherPatternMatch.new
11
- # .right(Integer, ->(x) { x > 2 }) { |x| x * 2 }
12
- # .right(String) { |x| x.to_i * 2 }
13
- # .left(String) { :err }
14
- # .else { 'error '}
15
- #
16
- # pattern_match.call(42) => 'NaN'
17
- #
18
- # @example the same matcher may be defined using block syntax
19
- # EitherPatternMatch.new do |m|
20
- # m.right(Integer, ->(x) { x > 2 }) { |x| x * 2 }
21
- # m.right(String) { |x| x.to_i * 2 }
22
- # m.left(String) { :err }
23
- # m.else { 'error '}
24
- # end
25
- #
26
- # @note it has two optimized subclasses +Fear::LeftPatternMatch+ and +Fear::RightPatternMatch+
27
- # @api private
28
- class EitherPatternMatch < Fear::PatternMatch
29
- # Match against +Fear::Right+
30
- #
31
- # @param conditions [<#==>]
32
- # @return [Fear::EitherPatternMatch]
33
- def right(*conditions, &effect)
34
- branch = Fear.case(Fear::Right, &:right_value).and_then(Fear.case(*conditions, &effect))
35
- or_else(branch)
36
- end
37
- alias success right
38
-
39
- # Match against +Fear::Left+
40
- #
41
- # @param conditions [<#==>]
42
- # @return [Fear::EitherPatternMatch]
43
- def left(*conditions, &effect)
44
- branch = Fear.case(Fear::Left, &:left_value).and_then(Fear.case(*conditions, &effect))
45
- or_else(branch)
46
- end
47
- alias failure left
48
- end
49
- end
50
-
51
- require "fear/left_pattern_match"
52
- require "fear/right_pattern_match"
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fear
4
- # @api private
5
- class FailurePatternMatch < Fear::TryPatternMatch
6
- def success(*_conditions)
7
- self
8
- end
9
- end
10
-
11
- private_constant :FailurePatternMatch
12
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fear
4
- # @api private
5
- class LeftPatternMatch < Fear::EitherPatternMatch
6
- def right(*)
7
- self
8
- end
9
- alias success right
10
- end
11
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fear
4
- # @api private
5
- class NonePatternMatch < OptionPatternMatch
6
- # @param conditions [<#==>]
7
- # @return [Fear::OptionPatternMatch]
8
- def some(*_conditions)
9
- self
10
- end
11
- end
12
-
13
- private_constant :NonePatternMatch
14
- end
@@ -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"
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fear/either_pattern_match"
4
-
5
- module Fear
6
- # @api private
7
- class RightPatternMatch < EitherPatternMatch
8
- def left(*)
9
- self
10
- end
11
- alias failure left
12
- end
13
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Fear
4
- # @api private
5
- class SomePatternMatch < OptionPatternMatch
6
- # @return [Fear::OptionPatternMatch]
7
- def none
8
- self
9
- end
10
- end
11
-
12
- private_constant :SomePatternMatch
13
- end
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