fear 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +27 -0
  3. data/.github/workflows/rubocop.yml +39 -0
  4. data/.github/workflows/spec.yml +42 -0
  5. data/.rubocop.yml +4 -60
  6. data/.simplecov +17 -0
  7. data/CHANGELOG.md +29 -1
  8. data/Gemfile +5 -5
  9. data/Gemfile.lock +86 -50
  10. data/README.md +240 -209
  11. data/Rakefile +72 -65
  12. data/examples/pattern_extracting.rb +10 -8
  13. data/examples/pattern_matching_binary_tree_set.rb +7 -2
  14. data/examples/pattern_matching_number_in_words.rb +48 -42
  15. data/fear.gemspec +33 -34
  16. data/lib/dry/types/fear/option.rb +125 -0
  17. data/lib/dry/types/fear.rb +8 -0
  18. data/lib/fear/await.rb +33 -0
  19. data/lib/fear/awaitable.rb +28 -0
  20. data/lib/fear/either.rb +15 -4
  21. data/lib/fear/either_api.rb +4 -0
  22. data/lib/fear/either_pattern_match.rb +9 -5
  23. data/lib/fear/empty_partial_function.rb +3 -1
  24. data/lib/fear/failure.rb +7 -7
  25. data/lib/fear/failure_pattern_match.rb +4 -0
  26. data/lib/fear/for.rb +4 -2
  27. data/lib/fear/for_api.rb +5 -1
  28. data/lib/fear/future.rb +157 -82
  29. data/lib/fear/future_api.rb +17 -4
  30. data/lib/fear/left.rb +3 -9
  31. data/lib/fear/left_pattern_match.rb +2 -0
  32. data/lib/fear/none.rb +28 -10
  33. data/lib/fear/none_pattern_match.rb +2 -0
  34. data/lib/fear/option.rb +30 -2
  35. data/lib/fear/option_api.rb +4 -0
  36. data/lib/fear/option_pattern_match.rb +8 -3
  37. data/lib/fear/partial_function/and_then.rb +4 -2
  38. data/lib/fear/partial_function/any.rb +2 -0
  39. data/lib/fear/partial_function/combined.rb +3 -1
  40. data/lib/fear/partial_function/empty.rb +6 -0
  41. data/lib/fear/partial_function/guard/and.rb +2 -0
  42. data/lib/fear/partial_function/guard/and3.rb +2 -0
  43. data/lib/fear/partial_function/guard/or.rb +2 -0
  44. data/lib/fear/partial_function/guard.rb +8 -6
  45. data/lib/fear/partial_function/lifted.rb +2 -0
  46. data/lib/fear/partial_function/or_else.rb +5 -1
  47. data/lib/fear/partial_function.rb +18 -9
  48. data/lib/fear/partial_function_class.rb +3 -1
  49. data/lib/fear/pattern_match.rb +3 -11
  50. data/lib/fear/pattern_matching_api.rb +6 -28
  51. data/lib/fear/promise.rb +7 -5
  52. data/lib/fear/right.rb +3 -9
  53. data/lib/fear/right_biased.rb +5 -3
  54. data/lib/fear/right_pattern_match.rb +4 -0
  55. data/lib/fear/some.rb +35 -8
  56. data/lib/fear/some_pattern_match.rb +2 -0
  57. data/lib/fear/struct.rb +237 -0
  58. data/lib/fear/success.rb +7 -8
  59. data/lib/fear/success_pattern_match.rb +4 -0
  60. data/lib/fear/try.rb +8 -2
  61. data/lib/fear/try_api.rb +4 -0
  62. data/lib/fear/try_pattern_match.rb +9 -5
  63. data/lib/fear/unit.rb +6 -2
  64. data/lib/fear/utils.rb +14 -2
  65. data/lib/fear/version.rb +4 -1
  66. data/lib/fear.rb +26 -44
  67. data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
  68. data/spec/dry/types/fear/option/core_spec.rb +77 -0
  69. data/spec/dry/types/fear/option/default_spec.rb +21 -0
  70. data/spec/dry/types/fear/option/hash_spec.rb +58 -0
  71. data/spec/dry/types/fear/option/option_spec.rb +97 -0
  72. data/spec/fear/awaitable_spec.rb +19 -0
  73. data/spec/fear/done_spec.rb +7 -5
  74. data/spec/fear/either/mixin_spec.rb +4 -2
  75. data/spec/fear/either_pattern_match_spec.rb +10 -8
  76. data/spec/fear/either_pattern_matching_spec.rb +28 -0
  77. data/spec/fear/either_spec.rb +26 -0
  78. data/spec/fear/failure_spec.rb +57 -70
  79. data/spec/fear/for/mixin_spec.rb +15 -0
  80. data/spec/fear/for_spec.rb +19 -17
  81. data/spec/fear/future_spec.rb +477 -237
  82. data/spec/fear/guard_spec.rb +136 -24
  83. data/spec/fear/left_spec.rb +57 -70
  84. data/spec/fear/none_spec.rb +39 -43
  85. data/spec/fear/option/mixin_spec.rb +9 -7
  86. data/spec/fear/option_pattern_match_spec.rb +10 -8
  87. data/spec/fear/option_pattern_matching_spec.rb +34 -0
  88. data/spec/fear/option_spec.rb +142 -0
  89. data/spec/fear/partial_function/any_spec.rb +25 -0
  90. data/spec/fear/partial_function/empty_spec.rb +12 -10
  91. data/spec/fear/partial_function_and_then_spec.rb +39 -37
  92. data/spec/fear/partial_function_composition_spec.rb +46 -44
  93. data/spec/fear/partial_function_or_else_spec.rb +92 -90
  94. data/spec/fear/partial_function_spec.rb +91 -61
  95. data/spec/fear/pattern_match_spec.rb +19 -51
  96. data/spec/fear/pattern_matching_api_spec.rb +31 -0
  97. data/spec/fear/promise_spec.rb +23 -23
  98. data/spec/fear/right_biased/left.rb +28 -26
  99. data/spec/fear/right_biased/right.rb +51 -49
  100. data/spec/fear/right_spec.rb +48 -68
  101. data/spec/fear/some_spec.rb +30 -40
  102. data/spec/fear/success_spec.rb +40 -60
  103. data/spec/fear/try/mixin_spec.rb +19 -3
  104. data/spec/fear/try_api_spec.rb +23 -0
  105. data/spec/fear/try_pattern_match_spec.rb +10 -8
  106. data/spec/fear/try_pattern_matching_spec.rb +34 -0
  107. data/spec/fear/utils_spec.rb +16 -14
  108. data/spec/spec_helper.rb +13 -7
  109. data/spec/struct_pattern_matching_spec.rb +36 -0
  110. data/spec/struct_spec.rb +194 -0
  111. data/spec/support/dry_types.rb +6 -0
  112. metadata +128 -87
  113. data/.travis.yml +0 -13
  114. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +0 -8
  115. data/lib/fear/extractor/any_matcher.rb +0 -15
  116. data/lib/fear/extractor/array_head_matcher.rb +0 -34
  117. data/lib/fear/extractor/array_matcher.rb +0 -38
  118. data/lib/fear/extractor/array_splat_matcher.rb +0 -14
  119. data/lib/fear/extractor/empty_list_matcher.rb +0 -18
  120. data/lib/fear/extractor/extractor_matcher.rb +0 -42
  121. data/lib/fear/extractor/grammar.rb +0 -201
  122. data/lib/fear/extractor/grammar.treetop +0 -129
  123. data/lib/fear/extractor/identifier_matcher.rb +0 -16
  124. data/lib/fear/extractor/matcher/and.rb +0 -36
  125. data/lib/fear/extractor/matcher.rb +0 -54
  126. data/lib/fear/extractor/named_array_splat_matcher.rb +0 -15
  127. data/lib/fear/extractor/pattern.rb +0 -55
  128. data/lib/fear/extractor/typed_identifier_matcher.rb +0 -24
  129. data/lib/fear/extractor/value_matcher.rb +0 -17
  130. data/lib/fear/extractor.rb +0 -108
  131. data/lib/fear/extractor_api.rb +0 -33
  132. data/spec/fear/extractor/array_matcher_spec.rb +0 -228
  133. data/spec/fear/extractor/extractor_matcher_spec.rb +0 -151
  134. data/spec/fear/extractor/grammar_array_spec.rb +0 -23
  135. data/spec/fear/extractor/identified_matcher_spec.rb +0 -47
  136. data/spec/fear/extractor/identifier_matcher_spec.rb +0 -66
  137. data/spec/fear/extractor/pattern_spec.rb +0 -32
  138. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +0 -62
  139. data/spec/fear/extractor/value_matcher_number_spec.rb +0 -77
  140. data/spec/fear/extractor/value_matcher_string_spec.rb +0 -86
  141. data/spec/fear/extractor/value_matcher_symbol_spec.rb +0 -69
  142. data/spec/fear/extractor_api_spec.rb +0 -113
  143. data/spec/fear/extractor_spec.rb +0 -59
data/lib/fear/success.rb CHANGED
@@ -1,17 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  class Success
3
5
  include Try
4
6
  include RightBiased::Right
5
7
  include SuccessPatternMatch.mixin
6
8
 
7
- EXTRACTOR = proc do |try|
8
- if Fear::Success === try
9
- Fear.some([try.get])
10
- else
11
- Fear.none
12
- end
13
- end
14
-
15
9
  attr_reader :value
16
10
  protected :value
17
11
 
@@ -104,5 +98,10 @@ module Fear
104
98
 
105
99
  # @return [String]
106
100
  alias to_s inspect
101
+
102
+ # @return [<any>]
103
+ def deconstruct
104
+ [value]
105
+ end
107
106
  end
108
107
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # @api private
3
5
  class SuccessPatternMatch < Fear::TryPatternMatch
@@ -7,4 +9,6 @@ module Fear
7
9
  self
8
10
  end
9
11
  end
12
+
13
+ private_constant :SuccessPatternMatch
10
14
  end
data/lib/fear/try.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # The +Try+ represents a computation that may either result
3
5
  # in an exception, or return a successfully computed value. Instances of +Try+,
@@ -112,7 +114,7 @@ module Fear
112
114
  # this is a +Failure+.
113
115
  # @return [Option]
114
116
  # @example
115
- # Fear.success(42).to_option #=> Fear.some(21)
117
+ # Fear.success(42).to_option #=> Fear.some(42)
116
118
  # Fear.failure(ArgumentError.new).to_option #=> Fear.none()
117
119
  #
118
120
  # @!method any?(&predicate)
@@ -170,7 +172,7 @@ module Fear
170
172
  # @return [Try]
171
173
  # @example
172
174
  # Fear.success(42).select { |v| v > 40 }
173
- # #=> Fear.success(21)
175
+ # #=> Fear.success(42)
174
176
  # Fear.success(42).select { |v| v < 40 }
175
177
  # #=> Fear.failure(Fear::NoSuchElementError.new("Predicate does not hold for 42"))
176
178
  # Fear.failure(ArgumentError.new).select { |v| v < 40 }
@@ -315,3 +317,7 @@ module Fear
315
317
  end
316
318
  end
317
319
  end
320
+
321
+ require "fear/try_pattern_match"
322
+ require "fear/success"
323
+ require "fear/failure"
data/lib/fear/try_api.rb CHANGED
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fear/try"
4
+
1
5
  module Fear
2
6
  module TryApi
3
7
  # Constructs a +Try+ using the block. This
@@ -1,18 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fear/pattern_match"
4
+
1
5
  module Fear
2
6
  # Try pattern matcher
3
7
  #
4
8
  # @note it has two optimized subclasses +Fear::SuccessPatternMatch+ and +Fear::FailurePatternMatch+
5
9
  # @api private
6
10
  class TryPatternMatch < Fear::PatternMatch
7
- SUCCESS_EXTRACTOR = :get.to_proc
8
- FAILURE_EXTRACTOR = :exception.to_proc
9
-
10
11
  # Match against +Fear::Success+
11
12
  #
12
13
  # @param conditions [<#==>]
13
14
  # @return [Fear::TryPatternMatch]
14
15
  def success(*conditions, &effect)
15
- branch = Fear.case(Fear::Success, &SUCCESS_EXTRACTOR).and_then(Fear.case(*conditions, &effect))
16
+ branch = Fear.case(Fear::Success, &:get).and_then(Fear.case(*conditions, &effect))
16
17
  or_else(branch)
17
18
  end
18
19
 
@@ -21,8 +22,11 @@ module Fear
21
22
  # @param conditions [<#==>]
22
23
  # @return [Fear::TryPatternMatch]
23
24
  def failure(*conditions, &effect)
24
- branch = Fear.case(Fear::Failure, &FAILURE_EXTRACTOR).and_then(Fear.case(*conditions, &effect))
25
+ branch = Fear.case(Fear::Failure, &:exception).and_then(Fear.case(*conditions, &effect))
25
26
  or_else(branch)
26
27
  end
27
28
  end
28
29
  end
30
+
31
+ require "fear/success_pattern_match"
32
+ require "fear/failure_pattern_match"
data/lib/fear/unit.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # Represents lack of value. It's typically returned when function completed without a value.
3
5
  #
@@ -17,12 +19,14 @@ module Fear
17
19
  Unit = Object.new.tap do |unit|
18
20
  # @return [String]
19
21
  def unit.to_s
20
- '#<Fear::Unit>'
22
+ "#<Fear::Unit>"
21
23
  end
22
24
 
23
25
  # @return [String]
24
26
  def unit.inspect
25
- '#<Fear::Unit>'
27
+ "#<Fear::Unit>"
26
28
  end
27
29
  end.freeze
30
+
31
+ public_constant :Unit
28
32
  end
data/lib/fear/utils.rb CHANGED
@@ -1,7 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # @private
3
5
  module Utils
6
+ EMPTY_STRING = ""
7
+ public_constant :EMPTY_STRING
8
+
4
9
  UNDEFINED = Object.new.freeze
10
+ public_constant :UNDEFINED
11
+
12
+ EMPTY_HASH = {}.freeze
13
+ public_constant :EMPTY_HASH
14
+
15
+ EMPTY_ARRAY = [].freeze
16
+ public_constant :EMPTY_ARRAY
5
17
 
6
18
  class << self
7
19
  def assert_arg_or_block!(method_name, *args)
@@ -20,13 +32,13 @@ module Fear
20
32
 
21
33
  def assert_type!(value, *types)
22
34
  if types.none? { |type| value.is_a?(type) }
23
- raise TypeError, "expected `#{value.inspect}` to be of #{types.join(', ')} class"
35
+ raise TypeError, "expected `#{value.inspect}` to be of #{types.join(", ")} class"
24
36
  end
25
37
  end
26
38
 
27
39
  def return_or_call_proc(value)
28
40
  if value.respond_to?(:call)
29
- value.call
41
+ value.()
30
42
  else
31
43
  value
32
44
  end
data/lib/fear/version.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
- VERSION = '1.0.0'.freeze
4
+ VERSION = "2.0.0"
5
+ public_constant :VERSION
3
6
  end
data/lib/fear.rb CHANGED
@@ -1,66 +1,48 @@
1
- require 'fear/either_api'
2
- require 'fear/extractor_api'
3
- require 'fear/for_api'
4
- require 'fear/future_api'
5
- require 'fear/option_api'
6
- require 'fear/pattern_matching_api'
7
- require 'fear/try_api'
8
- require 'fear/version'
1
+ # frozen_string_literal: true
2
+
3
+ require "fear/utils"
4
+ require "fear/right_biased"
5
+ require "fear/struct"
6
+ require "fear/unit"
7
+ require "fear/either_api"
8
+ require "fear/for_api"
9
+ require "fear/future_api"
10
+ require "fear/option_api"
11
+ require "fear/pattern_matching_api"
12
+ require "fear/try_api"
13
+ require "fear/version"
9
14
 
10
15
  module Fear
11
16
  Error = Class.new(StandardError)
17
+ public_constant :Error
18
+
12
19
  IllegalStateException = Class.new(Error)
20
+ public_constant :IllegalStateException
21
+
13
22
  MatchError = Class.new(Error)
23
+ public_constant :MatchError
24
+
14
25
  NoSuchElementError = Class.new(Error)
26
+ public_constant :NoSuchElementError
27
+
15
28
  PatternSyntaxError = Class.new(Error)
29
+ public_constant :PatternSyntaxError
16
30
 
17
31
  extend EitherApi
18
- extend ExtractorApi
19
32
  extend ForApi
20
33
  extend FutureApi
21
34
  extend OptionApi
22
35
  extend PatternMatchingApi
23
36
  extend TryApi
24
37
 
25
- autoload :EmptyPartialFunction, 'fear/empty_partial_function'
26
- autoload :PartialFunction, 'fear/partial_function'
27
- autoload :PartialFunctionClass, 'fear/partial_function_class'
28
- autoload :PatternMatch, 'fear/pattern_match'
29
- autoload :Extractor, 'fear/extractor'
30
-
31
- autoload :Unit, 'fear/unit'
32
- autoload :For, 'fear/for'
33
- autoload :RightBiased, 'fear/right_biased'
34
- autoload :Utils, 'fear/utils'
35
-
36
- autoload :None, 'fear/none'
37
- autoload :NoneClass, 'fear/none'
38
- autoload :NonePatternMatch, 'fear/none_pattern_match'
39
- autoload :Option, 'fear/option'
40
- autoload :OptionPatternMatch, 'fear/option_pattern_match'
41
- autoload :Some, 'fear/some'
42
- autoload :SomePatternMatch, 'fear/some_pattern_match'
43
-
44
- autoload :Failure, 'fear/failure'
45
- autoload :FailurePatternMatch, 'fear/failure_pattern_match'
46
- autoload :Success, 'fear/success'
47
- autoload :SuccessPatternMatch, 'fear/success_pattern_match'
48
- autoload :Try, 'fear/try'
49
- autoload :TryPatternMatch, 'fear/try_pattern_match'
50
-
51
- autoload :Either, 'fear/either'
52
- autoload :EitherPatternMatch, 'fear/either_pattern_match'
53
- autoload :Left, 'fear/left'
54
- autoload :LeftPatternMatch, 'fear/left_pattern_match'
55
- autoload :Right, 'fear/right'
56
- autoload :RightPatternMatch, 'fear/right_pattern_match'
57
-
58
- autoload :Future, 'fear/future'
59
-
60
38
  module Mixin
61
39
  include Either::Mixin
62
40
  include For::Mixin
63
41
  include Option::Mixin
64
42
  include Try::Mixin
65
43
  end
44
+
45
+ class << self
46
+ include Mixin
47
+ end
66
48
  end
@@ -0,0 +1,22 @@
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
@@ -0,0 +1,77 @@
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
@@ -0,0 +1,21 @@
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
@@ -0,0 +1,58 @@
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
@@ -0,0 +1,97 @@
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
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fear/awaitable"
4
+
5
+ RSpec.describe Fear::Awaitable do
6
+ subject(:awaitable) { Object.new.extend(Fear::Awaitable) }
7
+
8
+ describe "#__ready__" do
9
+ it "must implement the method" do
10
+ expect { awaitable.__ready__(1) }.to raise_error(NotImplementedError)
11
+ end
12
+ end
13
+
14
+ describe "#__result__" do
15
+ it "must implement the method" do
16
+ expect { awaitable.__result__(1) }.to raise_error(NotImplementedError)
17
+ end
18
+ end
19
+ end
@@ -1,17 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Fear::Unit do
2
- describe '#to_s' do
4
+ describe "#to_s" do
3
5
  subject { described_class.to_s }
4
6
 
5
- it { is_expected.to eq('#<Fear::Unit>') }
7
+ it { is_expected.to eq("#<Fear::Unit>") }
6
8
  end
7
9
 
8
- describe '#inspect' do
10
+ describe "#inspect" do
9
11
  subject { described_class.inspect }
10
12
 
11
- it { is_expected.to eq('#<Fear::Unit>') }
13
+ it { is_expected.to eq("#<Fear::Unit>") }
12
14
  end
13
15
 
14
- describe '#==' do
16
+ describe "#==" do
15
17
  it { is_expected.to eq(described_class) }
16
18
  end
17
19
  end
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Fear::Either::Mixin do
2
4
  include Fear::Either::Mixin
3
5
 
4
- describe 'Left()' do
6
+ describe "Left()" do
5
7
  subject { Left(42) }
6
8
 
7
9
  it { is_expected.to eq(Fear::Left.new(42)) }
8
10
  end
9
11
 
10
- describe 'Right()' do
12
+ describe "Right()" do
11
13
  subject { Right(42) }
12
14
 
13
15
  it { is_expected.to eq(Fear::Right.new(42)) }
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Fear::EitherPatternMatch do
2
- context 'Right' do
4
+ context "Right" do
3
5
  let(:matcher) do
4
6
  described_class.new do |m|
5
7
  m.right(:even?.to_proc) { |x| "#{x} is even" }
@@ -8,15 +10,15 @@ RSpec.describe Fear::EitherPatternMatch do
8
10
  end
9
11
 
10
12
  it do
11
- expect(matcher.call(Fear.right(4))).to eq('4 is even')
12
- expect(matcher.call(Fear.right(3))).to eq('3 is odd')
13
+ expect(matcher.(Fear.right(4))).to eq("4 is even")
14
+ expect(matcher.(Fear.right(3))).to eq("3 is odd")
13
15
  expect do
14
- matcher.call(Fear.left(44))
16
+ matcher.(Fear.left(44))
15
17
  end.to raise_error(Fear::MatchError)
16
18
  end
17
19
  end
18
20
 
19
- context 'Left' do
21
+ context "Left" do
20
22
  let(:matcher) do
21
23
  described_class.new do |m|
22
24
  m.left(:even?.to_proc) { |x| "#{x} is even" }
@@ -25,10 +27,10 @@ RSpec.describe Fear::EitherPatternMatch do
25
27
  end
26
28
 
27
29
  it do
28
- expect(matcher.call(Fear.left(4))).to eq('4 is even')
29
- expect(matcher.call(Fear.left(3))).to eq('3 is odd')
30
+ expect(matcher.(Fear.left(4))).to eq("4 is even")
31
+ expect(matcher.(Fear.left(3))).to eq("3 is odd")
30
32
  expect do
31
- matcher.call(Fear.right(44))
33
+ matcher.(Fear.right(44))
32
34
  end.to raise_error(Fear::MatchError)
33
35
  end
34
36
  end