fear 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +3 -6
  3. data/CHANGELOG.md +8 -0
  4. data/Gemfile +14 -2
  5. data/Gemfile.lock +52 -67
  6. data/LICENSE.txt +1 -1
  7. data/README.md +19 -71
  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 +2 -4
  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/spec_helper.rb +0 -4
  62. data/spec/support/.keep +0 -0
  63. metadata +29 -255
  64. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +0 -11
  65. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +0 -11
  66. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +0 -11
  67. data/lib/dry/types/fear/option.rb +0 -125
  68. data/lib/dry/types/fear.rb +0 -8
  69. data/lib/fear/either_pattern_match.rb +0 -52
  70. data/lib/fear/failure_pattern_match.rb +0 -12
  71. data/lib/fear/left_pattern_match.rb +0 -11
  72. data/lib/fear/none_pattern_match.rb +0 -14
  73. data/lib/fear/option_pattern_match.rb +0 -50
  74. data/lib/fear/right_pattern_match.rb +0 -13
  75. data/lib/fear/some_pattern_match.rb +0 -13
  76. data/lib/fear/struct.rb +0 -237
  77. data/lib/fear/success_pattern_match.rb +0 -14
  78. data/lib/fear/try_pattern_match.rb +0 -32
  79. data/spec/dry/types/fear/option/constrained_spec.rb +0 -22
  80. data/spec/dry/types/fear/option/core_spec.rb +0 -77
  81. data/spec/dry/types/fear/option/default_spec.rb +0 -21
  82. data/spec/dry/types/fear/option/hash_spec.rb +0 -58
  83. data/spec/dry/types/fear/option/option_spec.rb +0 -97
  84. data/spec/struct_pattern_matching_spec.rb +0 -36
  85. data/spec/struct_spec.rb +0 -194
  86. 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