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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +1 -5
  3. data/CHANGELOG.md +8 -0
  4. data/Gemfile +14 -2
  5. data/Gemfile.lock +48 -61
  6. data/LICENSE.txt +1 -1
  7. data/README.md +18 -70
  8. data/Rakefile +32 -119
  9. data/benchmarks/dry_do_vs_fear_for.txt +7 -6
  10. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +7 -6
  11. data/benchmarks/factorial.txt +7 -9
  12. data/benchmarks/fear_gaurd_and1_vs_new.txt +7 -6
  13. data/benchmarks/fear_gaurd_and2_vs_and.txt +8 -7
  14. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +7 -6
  15. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +7 -6
  16. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +8 -10
  17. data/fear.gemspec +4 -19
  18. data/lib/fear/either/left_projection.rb +237 -0
  19. data/lib/fear/either/pattern_match.rb +49 -0
  20. data/lib/fear/either.rb +20 -11
  21. data/lib/fear/either_api.rb +2 -4
  22. data/lib/fear/empty_partial_function.rb +1 -1
  23. data/lib/fear/failure/pattern_match.rb +14 -0
  24. data/lib/fear/failure.rb +1 -1
  25. data/lib/fear/for_api.rb +0 -2
  26. data/lib/fear/future.rb +1 -1
  27. data/lib/fear/future_api.rb +0 -5
  28. data/lib/fear/left/pattern_match.rb +15 -0
  29. data/lib/fear/left.rb +1 -1
  30. data/lib/fear/none.rb +0 -87
  31. data/lib/fear/none_class/pattern_match.rb +16 -0
  32. data/lib/fear/none_class.rb +85 -0
  33. data/lib/fear/option/pattern_match.rb +47 -0
  34. data/lib/fear/option.rb +1 -5
  35. data/lib/fear/option_api.rb +1 -3
  36. data/lib/fear/partial_function/empty.rb +3 -5
  37. data/lib/fear/partial_function/guard.rb +0 -4
  38. data/lib/fear/partial_function/or_else.rb +0 -2
  39. data/lib/fear/partial_function.rb +0 -9
  40. data/lib/fear/partial_function_class.rb +1 -1
  41. data/lib/fear/pattern_match.rb +3 -2
  42. data/lib/fear/pattern_matching_api.rb +0 -3
  43. data/lib/fear/right/pattern_match.rb +15 -0
  44. data/lib/fear/right.rb +1 -1
  45. data/lib/fear/right_biased.rb +2 -0
  46. data/lib/fear/some/pattern_match.rb +15 -0
  47. data/lib/fear/some.rb +1 -1
  48. data/lib/fear/success/pattern_match.rb +16 -0
  49. data/lib/fear/success.rb +1 -1
  50. data/lib/fear/try/pattern_match.rb +29 -0
  51. data/lib/fear/try.rb +3 -7
  52. data/lib/fear/try_api.rb +0 -2
  53. data/lib/fear/version.rb +1 -1
  54. data/lib/fear.rb +3 -14
  55. data/spec/fear/awaitable_spec.rb +0 -2
  56. data/spec/fear/either/left_projection_spec.rb +289 -0
  57. data/spec/fear/{either_pattern_match_spec.rb → either/pattern_match_spec.rb} +1 -1
  58. data/spec/fear/{option_pattern_match_spec.rb → option/pattern_match_spec.rb} +1 -1
  59. data/spec/fear/partial_function/empty_spec.rb +1 -1
  60. data/spec/fear/{try_pattern_match_spec.rb → try/try_pattern_match_spec.rb} +1 -1
  61. data/spec/support/.keep +0 -0
  62. metadata +29 -255
  63. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +0 -11
  64. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +0 -11
  65. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +0 -11
  66. data/lib/dry/types/fear/option.rb +0 -125
  67. data/lib/dry/types/fear.rb +0 -8
  68. data/lib/fear/either_pattern_match.rb +0 -52
  69. data/lib/fear/failure_pattern_match.rb +0 -12
  70. data/lib/fear/left_pattern_match.rb +0 -11
  71. data/lib/fear/none_pattern_match.rb +0 -14
  72. data/lib/fear/option_pattern_match.rb +0 -50
  73. data/lib/fear/right_pattern_match.rb +0 -13
  74. data/lib/fear/some_pattern_match.rb +0 -13
  75. data/lib/fear/struct.rb +0 -237
  76. data/lib/fear/success_pattern_match.rb +0 -14
  77. data/lib/fear/try_pattern_match.rb +0 -32
  78. data/spec/dry/types/fear/option/constrained_spec.rb +0 -22
  79. data/spec/dry/types/fear/option/core_spec.rb +0 -77
  80. data/spec/dry/types/fear/option/default_spec.rb +0 -21
  81. data/spec/dry/types/fear/option/hash_spec.rb +0 -58
  82. data/spec/dry/types/fear/option/option_spec.rb +0 -97
  83. data/spec/struct_pattern_matching_spec.rb +0 -36
  84. data/spec/struct_spec.rb +0 -194
  85. 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"
@@ -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
@@ -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