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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +105 -0
- data/.simplecov +2 -2
- data/.standard.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +14 -2
- data/Gemfile.lock +84 -78
- data/LICENSE.txt +1 -1
- data/README.md +18 -70
- data/Rakefile +55 -142
- data/benchmarks/dry_do_vs_fear_for.txt +7 -6
- data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +7 -6
- data/benchmarks/factorial.txt +7 -9
- data/benchmarks/fear_gaurd_and1_vs_new.txt +7 -6
- data/benchmarks/fear_gaurd_and2_vs_and.txt +8 -7
- data/benchmarks/fear_gaurd_and3_vs_and_and.txt +7 -6
- data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +7 -6
- data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +8 -10
- data/examples/pattern_extracting.rb +2 -2
- data/examples/pattern_matching_number_in_words.rb +12 -12
- data/fear.gemspec +5 -21
- data/lib/fear/either/left_projection.rb +237 -0
- data/lib/fear/either/pattern_match.rb +49 -0
- data/lib/fear/either.rb +21 -12
- data/lib/fear/either_api.rb +2 -4
- data/lib/fear/empty_partial_function.rb +3 -3
- data/lib/fear/failure/pattern_match.rb +14 -0
- data/lib/fear/failure.rb +5 -5
- data/lib/fear/for.rb +1 -1
- data/lib/fear/for_api.rb +1 -3
- data/lib/fear/future.rb +6 -6
- data/lib/fear/future_api.rb +0 -5
- data/lib/fear/left/pattern_match.rb +15 -0
- data/lib/fear/left.rb +4 -4
- data/lib/fear/none.rb +0 -87
- data/lib/fear/none_class/pattern_match.rb +16 -0
- data/lib/fear/none_class.rb +85 -0
- data/lib/fear/option/pattern_match.rb +47 -0
- data/lib/fear/option.rb +2 -6
- data/lib/fear/option_api.rb +1 -3
- data/lib/fear/partial_function/and_then.rb +2 -2
- data/lib/fear/partial_function/combined.rb +3 -3
- data/lib/fear/partial_function/empty.rb +3 -5
- data/lib/fear/partial_function/guard.rb +0 -4
- data/lib/fear/partial_function/or_else.rb +2 -4
- data/lib/fear/partial_function.rb +0 -9
- data/lib/fear/partial_function_class.rb +2 -2
- data/lib/fear/pattern_match.rb +5 -4
- data/lib/fear/pattern_matching_api.rb +1 -4
- data/lib/fear/right/pattern_match.rb +15 -0
- data/lib/fear/right.rb +4 -4
- data/lib/fear/right_biased.rb +2 -0
- data/lib/fear/some/pattern_match.rb +15 -0
- data/lib/fear/some.rb +3 -3
- data/lib/fear/success/pattern_match.rb +16 -0
- data/lib/fear/success.rb +5 -5
- data/lib/fear/try/pattern_match.rb +29 -0
- data/lib/fear/try.rb +3 -7
- data/lib/fear/try_api.rb +1 -3
- data/lib/fear/utils.rb +1 -1
- data/lib/fear/version.rb +1 -1
- data/lib/fear.rb +3 -14
- data/spec/fear/awaitable_spec.rb +0 -2
- data/spec/fear/either/left_projection_spec.rb +289 -0
- data/spec/fear/{either_pattern_match_spec.rb → either/pattern_match_spec.rb} +7 -7
- data/spec/fear/either_spec.rb +1 -1
- data/spec/fear/left_spec.rb +1 -1
- data/spec/fear/{option_pattern_match_spec.rb → option/pattern_match_spec.rb} +6 -6
- data/spec/fear/option_spec.rb +2 -2
- data/spec/fear/partial_function/any_spec.rb +3 -3
- data/spec/fear/partial_function/empty_spec.rb +2 -2
- data/spec/fear/partial_function_and_then_spec.rb +5 -5
- data/spec/fear/partial_function_composition_spec.rb +6 -6
- data/spec/fear/partial_function_or_else_spec.rb +13 -13
- data/spec/fear/partial_function_spec.rb +7 -7
- data/spec/fear/pattern_match_spec.rb +5 -5
- data/spec/fear/right_spec.rb +1 -1
- data/spec/fear/{try_pattern_match_spec.rb → try/try_pattern_match_spec.rb} +7 -7
- data/spec/fear/try_api_spec.rb +2 -2
- data/spec/fear/utils_spec.rb +3 -3
- data/spec/support/.keep +0 -0
- metadata +27 -296
- data/.github/workflows/rubocop.yml +0 -39
- data/.github/workflows/spec.yml +0 -43
- data/.rubocop.yml +0 -7
- data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +0 -11
- data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +0 -11
- data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +0 -11
- data/lib/dry/types/fear/option.rb +0 -125
- data/lib/dry/types/fear.rb +0 -8
- data/lib/fear/either_pattern_match.rb +0 -52
- data/lib/fear/failure_pattern_match.rb +0 -12
- data/lib/fear/left_pattern_match.rb +0 -11
- data/lib/fear/none_pattern_match.rb +0 -14
- data/lib/fear/option_pattern_match.rb +0 -50
- data/lib/fear/right_pattern_match.rb +0 -13
- data/lib/fear/some_pattern_match.rb +0 -13
- data/lib/fear/struct.rb +0 -237
- data/lib/fear/success_pattern_match.rb +0 -14
- data/lib/fear/try_pattern_match.rb +0 -32
- data/spec/dry/types/fear/option/constrained_spec.rb +0 -22
- data/spec/dry/types/fear/option/core_spec.rb +0 -77
- data/spec/dry/types/fear/option/default_spec.rb +0 -21
- data/spec/dry/types/fear/option/hash_spec.rb +0 -58
- data/spec/dry/types/fear/option/option_spec.rb +0 -97
- data/spec/struct_pattern_matching_spec.rb +0 -36
- data/spec/struct_spec.rb +0 -194
- data/spec/support/dry_types.rb +0 -6
@@ -1,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
|
data/lib/dry/types/fear.rb
DELETED
@@ -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,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"
|
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
|