fear 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +27 -0
- data/.github/workflows/rubocop.yml +39 -0
- data/.github/workflows/spec.yml +42 -0
- data/.rubocop.yml +4 -60
- data/.simplecov +17 -0
- data/CHANGELOG.md +29 -1
- data/Gemfile +5 -5
- data/Gemfile.lock +86 -50
- data/README.md +240 -209
- data/Rakefile +72 -65
- data/examples/pattern_extracting.rb +10 -8
- data/examples/pattern_matching_binary_tree_set.rb +7 -2
- data/examples/pattern_matching_number_in_words.rb +48 -42
- data/fear.gemspec +33 -34
- data/lib/dry/types/fear/option.rb +125 -0
- data/lib/dry/types/fear.rb +8 -0
- data/lib/fear/await.rb +33 -0
- data/lib/fear/awaitable.rb +28 -0
- data/lib/fear/either.rb +15 -4
- data/lib/fear/either_api.rb +4 -0
- data/lib/fear/either_pattern_match.rb +9 -5
- data/lib/fear/empty_partial_function.rb +3 -1
- data/lib/fear/failure.rb +7 -7
- data/lib/fear/failure_pattern_match.rb +4 -0
- data/lib/fear/for.rb +4 -2
- data/lib/fear/for_api.rb +5 -1
- data/lib/fear/future.rb +157 -82
- data/lib/fear/future_api.rb +17 -4
- data/lib/fear/left.rb +3 -9
- data/lib/fear/left_pattern_match.rb +2 -0
- data/lib/fear/none.rb +28 -10
- data/lib/fear/none_pattern_match.rb +2 -0
- data/lib/fear/option.rb +30 -2
- data/lib/fear/option_api.rb +4 -0
- data/lib/fear/option_pattern_match.rb +8 -3
- data/lib/fear/partial_function/and_then.rb +4 -2
- data/lib/fear/partial_function/any.rb +2 -0
- data/lib/fear/partial_function/combined.rb +3 -1
- data/lib/fear/partial_function/empty.rb +6 -0
- data/lib/fear/partial_function/guard/and.rb +2 -0
- data/lib/fear/partial_function/guard/and3.rb +2 -0
- data/lib/fear/partial_function/guard/or.rb +2 -0
- data/lib/fear/partial_function/guard.rb +8 -6
- data/lib/fear/partial_function/lifted.rb +2 -0
- data/lib/fear/partial_function/or_else.rb +5 -1
- data/lib/fear/partial_function.rb +18 -9
- data/lib/fear/partial_function_class.rb +3 -1
- data/lib/fear/pattern_match.rb +3 -11
- data/lib/fear/pattern_matching_api.rb +6 -28
- data/lib/fear/promise.rb +7 -5
- data/lib/fear/right.rb +3 -9
- data/lib/fear/right_biased.rb +5 -3
- data/lib/fear/right_pattern_match.rb +4 -0
- data/lib/fear/some.rb +35 -8
- data/lib/fear/some_pattern_match.rb +2 -0
- data/lib/fear/struct.rb +237 -0
- data/lib/fear/success.rb +7 -8
- data/lib/fear/success_pattern_match.rb +4 -0
- data/lib/fear/try.rb +8 -2
- data/lib/fear/try_api.rb +4 -0
- data/lib/fear/try_pattern_match.rb +9 -5
- data/lib/fear/unit.rb +6 -2
- data/lib/fear/utils.rb +14 -2
- data/lib/fear/version.rb +4 -1
- data/lib/fear.rb +26 -44
- data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
- data/spec/dry/types/fear/option/core_spec.rb +77 -0
- data/spec/dry/types/fear/option/default_spec.rb +21 -0
- data/spec/dry/types/fear/option/hash_spec.rb +58 -0
- data/spec/dry/types/fear/option/option_spec.rb +97 -0
- data/spec/fear/awaitable_spec.rb +19 -0
- data/spec/fear/done_spec.rb +7 -5
- data/spec/fear/either/mixin_spec.rb +4 -2
- data/spec/fear/either_pattern_match_spec.rb +10 -8
- data/spec/fear/either_pattern_matching_spec.rb +28 -0
- data/spec/fear/either_spec.rb +26 -0
- data/spec/fear/failure_spec.rb +57 -70
- data/spec/fear/for/mixin_spec.rb +15 -0
- data/spec/fear/for_spec.rb +19 -17
- data/spec/fear/future_spec.rb +477 -237
- data/spec/fear/guard_spec.rb +136 -24
- data/spec/fear/left_spec.rb +57 -70
- data/spec/fear/none_spec.rb +39 -43
- data/spec/fear/option/mixin_spec.rb +9 -7
- data/spec/fear/option_pattern_match_spec.rb +10 -8
- data/spec/fear/option_pattern_matching_spec.rb +34 -0
- data/spec/fear/option_spec.rb +142 -0
- data/spec/fear/partial_function/any_spec.rb +25 -0
- data/spec/fear/partial_function/empty_spec.rb +12 -10
- data/spec/fear/partial_function_and_then_spec.rb +39 -37
- data/spec/fear/partial_function_composition_spec.rb +46 -44
- data/spec/fear/partial_function_or_else_spec.rb +92 -90
- data/spec/fear/partial_function_spec.rb +91 -61
- data/spec/fear/pattern_match_spec.rb +19 -51
- data/spec/fear/pattern_matching_api_spec.rb +31 -0
- data/spec/fear/promise_spec.rb +23 -23
- data/spec/fear/right_biased/left.rb +28 -26
- data/spec/fear/right_biased/right.rb +51 -49
- data/spec/fear/right_spec.rb +48 -68
- data/spec/fear/some_spec.rb +30 -40
- data/spec/fear/success_spec.rb +40 -60
- data/spec/fear/try/mixin_spec.rb +19 -3
- data/spec/fear/try_api_spec.rb +23 -0
- data/spec/fear/try_pattern_match_spec.rb +10 -8
- data/spec/fear/try_pattern_matching_spec.rb +34 -0
- data/spec/fear/utils_spec.rb +16 -14
- data/spec/spec_helper.rb +13 -7
- data/spec/struct_pattern_matching_spec.rb +36 -0
- data/spec/struct_spec.rb +194 -0
- data/spec/support/dry_types.rb +6 -0
- metadata +128 -87
- data/.travis.yml +0 -13
- data/lib/fear/extractor/anonymous_array_splat_matcher.rb +0 -8
- data/lib/fear/extractor/any_matcher.rb +0 -15
- data/lib/fear/extractor/array_head_matcher.rb +0 -34
- data/lib/fear/extractor/array_matcher.rb +0 -38
- data/lib/fear/extractor/array_splat_matcher.rb +0 -14
- data/lib/fear/extractor/empty_list_matcher.rb +0 -18
- data/lib/fear/extractor/extractor_matcher.rb +0 -42
- data/lib/fear/extractor/grammar.rb +0 -201
- data/lib/fear/extractor/grammar.treetop +0 -129
- data/lib/fear/extractor/identifier_matcher.rb +0 -16
- data/lib/fear/extractor/matcher/and.rb +0 -36
- data/lib/fear/extractor/matcher.rb +0 -54
- data/lib/fear/extractor/named_array_splat_matcher.rb +0 -15
- data/lib/fear/extractor/pattern.rb +0 -55
- data/lib/fear/extractor/typed_identifier_matcher.rb +0 -24
- data/lib/fear/extractor/value_matcher.rb +0 -17
- data/lib/fear/extractor.rb +0 -108
- data/lib/fear/extractor_api.rb +0 -33
- data/spec/fear/extractor/array_matcher_spec.rb +0 -228
- data/spec/fear/extractor/extractor_matcher_spec.rb +0 -151
- data/spec/fear/extractor/grammar_array_spec.rb +0 -23
- data/spec/fear/extractor/identified_matcher_spec.rb +0 -47
- data/spec/fear/extractor/identifier_matcher_spec.rb +0 -66
- data/spec/fear/extractor/pattern_spec.rb +0 -32
- data/spec/fear/extractor/typed_identifier_matcher_spec.rb +0 -62
- data/spec/fear/extractor/value_matcher_number_spec.rb +0 -77
- data/spec/fear/extractor/value_matcher_string_spec.rb +0 -86
- data/spec/fear/extractor/value_matcher_symbol_spec.rb +0 -69
- data/spec/fear/extractor_api_spec.rb +0 -113
- data/spec/fear/extractor_spec.rb +0 -59
data/Rakefile
CHANGED
@@ -1,28 +1,30 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "benchmark/ips"
|
5
|
+
require_relative "lib/fear"
|
4
6
|
|
5
7
|
namespace :perf do
|
6
8
|
# Contains benchmarking against Dry-rb
|
7
9
|
namespace :dry do
|
8
10
|
task :some_fmap_vs_fear_some_map do
|
9
|
-
require
|
11
|
+
require "dry/monads/maybe"
|
10
12
|
|
11
13
|
dry = Dry::Monads::Some.new(42)
|
12
14
|
fear = Fear.some(42)
|
13
15
|
|
14
16
|
Benchmark.ips do |x|
|
15
|
-
x.report(
|
17
|
+
x.report("Dry") { dry.fmap(&:itself) }
|
16
18
|
|
17
|
-
x.report(
|
19
|
+
x.report("Fear") { fear.map(&:itself) }
|
18
20
|
|
19
21
|
x.compare!
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
25
|
task :do_vs_fear_for do
|
24
|
-
require
|
25
|
-
require
|
26
|
+
require "dry/monads/maybe"
|
27
|
+
require "dry/monads/do"
|
26
28
|
|
27
29
|
class Operation
|
28
30
|
include Dry::Monads::Maybe::Mixin
|
@@ -42,9 +44,9 @@ namespace :perf do
|
|
42
44
|
op = Operation.new
|
43
45
|
|
44
46
|
Benchmark.ips do |x|
|
45
|
-
x.report(
|
47
|
+
x.report("Dry") { op.() }
|
46
48
|
|
47
|
-
x.report(
|
49
|
+
x.report("Fear") do |_n|
|
48
50
|
Fear.for(Fear.some(1), Fear.some(2)) do |one, two|
|
49
51
|
one + two
|
50
52
|
end
|
@@ -58,21 +60,21 @@ namespace :perf do
|
|
58
60
|
# Contains internal benchmarking to if optimization works
|
59
61
|
namespace :fear do
|
60
62
|
task :fear_pattern_extracting_with_vs_without_cache do
|
61
|
-
some = Fear.some([:err,
|
63
|
+
some = Fear.some([:err, "not found"])
|
62
64
|
|
63
65
|
class WOCache < Fear::Extractor::Pattern
|
64
66
|
def initialize(pattern)
|
65
67
|
@matcher = compile_pattern_without_cache(pattern)
|
66
68
|
end
|
67
69
|
end
|
68
|
-
pattern =
|
70
|
+
pattern = "Fear::Some([:err, code])"
|
69
71
|
|
70
72
|
Benchmark.ips do |x|
|
71
|
-
x.report(
|
73
|
+
x.report("With cache") do |_n|
|
72
74
|
Fear::Extractor::Pattern.new(pattern).extracted_arguments(some)
|
73
75
|
end
|
74
76
|
|
75
|
-
x.report(
|
77
|
+
x.report("Without cache") do |_n|
|
76
78
|
WOCache.new(pattern).extracted_arguments(some)
|
77
79
|
end
|
78
80
|
|
@@ -85,11 +87,11 @@ namespace :perf do
|
|
85
87
|
condition = Integer
|
86
88
|
|
87
89
|
Benchmark.ips do |x|
|
88
|
-
x.report(
|
90
|
+
x.report("Guard.new") do |n|
|
89
91
|
Fear::PartialFunction::Guard.new(condition) === n
|
90
92
|
end
|
91
93
|
|
92
|
-
x.report(
|
94
|
+
x.report("Guard.and1") do |n|
|
93
95
|
Fear::PartialFunction::Guard.and1(condition) === n
|
94
96
|
end
|
95
97
|
|
@@ -105,11 +107,11 @@ namespace :perf do
|
|
105
107
|
and_and = Fear::PartialFunction::Guard.new(first).and(Fear::PartialFunction::Guard.new(second))
|
106
108
|
|
107
109
|
Benchmark.ips do |x|
|
108
|
-
x.report(
|
110
|
+
x.report("and2") do |n|
|
109
111
|
and2 === n
|
110
112
|
end
|
111
113
|
|
112
|
-
x.report(
|
114
|
+
x.report("Guard#and") do |n|
|
113
115
|
and_and === n
|
114
116
|
end
|
115
117
|
|
@@ -120,7 +122,7 @@ namespace :perf do
|
|
120
122
|
task :and3_vs_and_and do
|
121
123
|
first = Integer
|
122
124
|
second = ->(x) { x > 2 }
|
123
|
-
third = ->(x) {
|
125
|
+
third = ->(x) { x < 10 }
|
124
126
|
|
125
127
|
and3 = Fear::PartialFunction::Guard.and3(first, second, third)
|
126
128
|
|
@@ -129,11 +131,11 @@ namespace :perf do
|
|
129
131
|
.and(Fear::PartialFunction::Guard.new(third))
|
130
132
|
|
131
133
|
Benchmark.ips do |x|
|
132
|
-
x.report(
|
134
|
+
x.report("Guard.and3") do |n|
|
133
135
|
and3 === n
|
134
136
|
end
|
135
137
|
|
136
|
-
x.report(
|
138
|
+
x.report("Guard#and") do |n|
|
137
139
|
and_and_and === n
|
138
140
|
end
|
139
141
|
|
@@ -149,15 +151,15 @@ namespace :perf do
|
|
149
151
|
end
|
150
152
|
|
151
153
|
Benchmark.ips do |x|
|
152
|
-
x.report(
|
154
|
+
x.report("construction") do
|
153
155
|
Fear::PatternMatch.new do |m|
|
154
156
|
m.case(Integer) { |y| y * 2 }
|
155
157
|
m.case(String) { |y| y.to_i(10) * 2 }
|
156
158
|
end
|
157
159
|
end
|
158
160
|
|
159
|
-
x.report(
|
160
|
-
matcher.
|
161
|
+
x.report("execution") do
|
162
|
+
matcher.(42)
|
161
163
|
end
|
162
164
|
|
163
165
|
x.compare!
|
@@ -166,23 +168,29 @@ namespace :perf do
|
|
166
168
|
end
|
167
169
|
|
168
170
|
namespace :pattern_matching do
|
169
|
-
require
|
170
|
-
require
|
171
|
+
require "qo"
|
172
|
+
require "dry/matcher"
|
171
173
|
|
172
|
-
task :
|
173
|
-
|
174
|
-
user = User.new(42, 'Jane')
|
174
|
+
task :option_match_vs_native_pattern_match do
|
175
|
+
some = Fear.some(42)
|
175
176
|
|
176
177
|
Benchmark.ips do |x|
|
177
|
-
x.report(
|
178
|
-
|
179
|
-
|
178
|
+
x.report("case ... in ...") do
|
179
|
+
case some
|
180
|
+
in Fear::Some(41 => x)
|
181
|
+
x
|
182
|
+
in Fear::Some(42 => x)
|
183
|
+
x
|
184
|
+
in Fear::Some(43 => x)
|
185
|
+
x
|
180
186
|
end
|
181
187
|
end
|
182
188
|
|
183
|
-
x.report(
|
184
|
-
|
185
|
-
m.
|
189
|
+
x.report("Option.match") do
|
190
|
+
some.match do |m|
|
191
|
+
m.some(41, &:itself)
|
192
|
+
m.some(42, &:itself)
|
193
|
+
m.some(45, &:itself)
|
186
194
|
end
|
187
195
|
end
|
188
196
|
|
@@ -198,25 +206,24 @@ namespace :perf do
|
|
198
206
|
end
|
199
207
|
end
|
200
208
|
|
201
|
-
SuccessBranch = Qo.create_branch(name:
|
202
|
-
FailureBranch = Qo.create_branch(name:
|
209
|
+
SuccessBranch = Qo.create_branch(name: "success", precondition: Fear::Success, extractor: :get)
|
210
|
+
FailureBranch = Qo.create_branch(name: "failure", precondition: Fear::Failure, extractor: :exception)
|
203
211
|
|
204
212
|
PatternMatch = Qo.create_pattern_match(
|
205
|
-
branches: [SuccessBranch,
|
206
|
-
FailureBranch],
|
213
|
+
branches: [SuccessBranch, FailureBranch],
|
207
214
|
).prepend(ExhaustivePatternMatch)
|
208
215
|
|
209
216
|
Fear::Success.include(PatternMatch.mixin(as: :qo_match))
|
210
217
|
|
211
218
|
success_case = Dry::Matcher::Case.new(
|
212
|
-
match:
|
219
|
+
match: ->(try, *pattern) {
|
213
220
|
try.is_a?(Fear::Success) && pattern.all? { |p| p === try.get }
|
214
221
|
},
|
215
222
|
resolve: ->(try) { try.get },
|
216
223
|
)
|
217
224
|
|
218
225
|
failure_case = Dry::Matcher::Case.new(
|
219
|
-
match:
|
226
|
+
match: ->(try, *pattern) {
|
220
227
|
try.is_a?(Fear::Failure) && pattern.all? { |p| p === try.exception }
|
221
228
|
},
|
222
229
|
resolve: ->(value) { value.exception },
|
@@ -228,27 +235,27 @@ namespace :perf do
|
|
228
235
|
success = Fear::Success.new(4)
|
229
236
|
|
230
237
|
Benchmark.ips do |x|
|
231
|
-
x.report(
|
238
|
+
x.report("Qo") do
|
232
239
|
success.qo_match do |m|
|
233
240
|
m.failure(&:itself)
|
234
241
|
m.success(Integer, ->(y) { y % 5 == 0 }, &:itself)
|
235
|
-
m.success {
|
242
|
+
m.success { "else" }
|
236
243
|
end
|
237
244
|
end
|
238
245
|
|
239
|
-
x.report(
|
246
|
+
x.report("Fear") do
|
240
247
|
success.match do |m|
|
241
248
|
m.failure(&:itself)
|
242
249
|
m.success(Integer, ->(y) { y % 5 == 0 }, &:itself)
|
243
|
-
m.success {
|
250
|
+
m.success { "else" }
|
244
251
|
end
|
245
252
|
end
|
246
253
|
|
247
|
-
x.report(
|
248
|
-
matcher.
|
254
|
+
x.report("Dr::Matcher") do
|
255
|
+
matcher.(success) do |m|
|
249
256
|
m.failure(&:itself)
|
250
257
|
m.success(Integer, ->(y) { y % 5 == 0 }, &:itself)
|
251
|
-
m.success {
|
258
|
+
m.success { "else" }
|
252
259
|
end
|
253
260
|
end
|
254
261
|
|
@@ -264,8 +271,8 @@ namespace :perf do
|
|
264
271
|
end
|
265
272
|
end
|
266
273
|
|
267
|
-
SuccessBranch = Qo.create_branch(name:
|
268
|
-
FailureBranch = Qo.create_branch(name:
|
274
|
+
SuccessBranch = Qo.create_branch(name: "success", precondition: Fear::Success, extractor: :get)
|
275
|
+
FailureBranch = Qo.create_branch(name: "failure", precondition: Fear::Failure, extractor: :exception)
|
269
276
|
|
270
277
|
QoPatternMatch = Qo.create_pattern_match(
|
271
278
|
branches: [SuccessBranch, FailureBranch],
|
@@ -278,22 +285,22 @@ namespace :perf do
|
|
278
285
|
qo_matcher = QoPatternMatch.new do |m|
|
279
286
|
m.success(1, &:itself)
|
280
287
|
m.success(4, &:itself)
|
281
|
-
m.failure {
|
288
|
+
m.failure { "failure" }
|
282
289
|
end
|
283
290
|
|
284
291
|
fear_matcher = Fear::TryPatternMatch.new do |m|
|
285
292
|
m.success(1, &:itself)
|
286
293
|
m.success(4, &:itself)
|
287
|
-
m.failure {
|
294
|
+
m.failure { "failure" }
|
288
295
|
end
|
289
296
|
|
290
297
|
Benchmark.ips do |x|
|
291
|
-
x.report(
|
292
|
-
qo_matcher.
|
298
|
+
x.report("Qo") do
|
299
|
+
qo_matcher.(success)
|
293
300
|
end
|
294
301
|
|
295
|
-
x.report(
|
296
|
-
fear_matcher.
|
302
|
+
x.report("Fear") do
|
303
|
+
fear_matcher.(success)
|
297
304
|
end
|
298
305
|
|
299
306
|
x.compare!
|
@@ -305,33 +312,33 @@ namespace :perf do
|
|
305
312
|
if n <= 1
|
306
313
|
1
|
307
314
|
else
|
308
|
-
n * factorial_proc.
|
315
|
+
n * factorial_proc.(n - 1)
|
309
316
|
end
|
310
317
|
end
|
311
318
|
|
312
319
|
factorial_pm = Fear.matcher do |m|
|
313
320
|
m.case(1, &:itself)
|
314
321
|
m.case(0, &:itself)
|
315
|
-
m.else { |n| n * factorial_pm.
|
322
|
+
m.else { |n| n * factorial_pm.(n - 1) }
|
316
323
|
end
|
317
324
|
|
318
325
|
factorial_qo = Qo.match do |m|
|
319
326
|
m.when(1, &:itself)
|
320
327
|
m.when(0, &:itself)
|
321
|
-
m.else { |n| n * factorial_qo.
|
328
|
+
m.else { |n| n * factorial_qo.(n - 1) }
|
322
329
|
end
|
323
330
|
|
324
331
|
Benchmark.ips do |x|
|
325
|
-
x.report(
|
326
|
-
factorial_proc.
|
332
|
+
x.report("Proc") do
|
333
|
+
factorial_proc.(100)
|
327
334
|
end
|
328
335
|
|
329
|
-
x.report(
|
330
|
-
factorial_pm.
|
336
|
+
x.report("Fear") do
|
337
|
+
factorial_pm.(100)
|
331
338
|
end
|
332
339
|
|
333
|
-
x.report(
|
334
|
-
factorial_qo.
|
340
|
+
x.report("Qo") do
|
341
|
+
factorial_qo.(100)
|
335
342
|
end
|
336
343
|
|
337
344
|
x.compare!
|
@@ -1,15 +1,17 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fear"
|
2
4
|
|
3
5
|
User = Struct.new(:id, :name, :admin)
|
4
6
|
|
5
|
-
matcher =
|
6
|
-
|
7
|
+
matcher = proc do |value|
|
8
|
+
case value
|
9
|
+
in User(admin: true, name:)
|
7
10
|
puts "Hi #{name}, you are welcome"
|
8
|
-
|
9
|
-
|
10
|
-
puts 'Only admins allowed here'
|
11
|
+
in User(admin: false)
|
12
|
+
puts "Only admins are allowed here"
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|
14
|
-
matcher.
|
15
|
-
matcher.
|
16
|
+
matcher.(User.new(1, "Jane", true))
|
17
|
+
matcher.(User.new(1, "John", false))
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fear"
|
2
4
|
|
3
5
|
# @example Usage
|
4
6
|
# set = BinaryTreeSet.new
|
@@ -10,8 +12,11 @@ require 'fear'
|
|
10
12
|
#
|
11
13
|
class BinaryTreeSet
|
12
14
|
Position = Module.new
|
15
|
+
private_constant(:Position)
|
13
16
|
Right = Module.new.include(Position)
|
17
|
+
private_constant(:Right)
|
14
18
|
Left = Module.new.include(Position)
|
19
|
+
private_constant(:Left)
|
15
20
|
|
16
21
|
def initialize(elem = 0, removed: true)
|
17
22
|
@elem = elem
|
@@ -87,7 +92,7 @@ class BinaryTreeSet
|
|
87
92
|
# @param position [Position]
|
88
93
|
# @return [Fear::Option<BinaryTreeSet>]
|
89
94
|
private def leaf(position)
|
90
|
-
if subtrees.
|
95
|
+
if subtrees.has_key?(position)
|
91
96
|
Fear.some(subtrees[position])
|
92
97
|
else
|
93
98
|
Fear.none
|
@@ -1,54 +1,60 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fear"
|
2
4
|
|
3
5
|
class ToWords
|
6
|
+
NUMBERS = {
|
7
|
+
0 => "zero",
|
8
|
+
1 => "one",
|
9
|
+
2 => "two",
|
10
|
+
3 => "three",
|
11
|
+
4 => "four",
|
12
|
+
5 => "five",
|
13
|
+
6 => "six",
|
14
|
+
7 => "seven",
|
15
|
+
8 => "eight",
|
16
|
+
9 => "nine",
|
17
|
+
10 => "ten",
|
18
|
+
11 => "eleven",
|
19
|
+
12 => "twelve",
|
20
|
+
13 => "thirteen",
|
21
|
+
14 => "fourteen",
|
22
|
+
15 => "fifteen",
|
23
|
+
16 => "sixteen",
|
24
|
+
17 => "seventeen",
|
25
|
+
18 => "eighteen",
|
26
|
+
19 => "nineteen",
|
27
|
+
20 => "twenty",
|
28
|
+
30 => "thirty",
|
29
|
+
40 => "forty",
|
30
|
+
50 => "fifty",
|
31
|
+
60 => "sixty",
|
32
|
+
70 => "seventy",
|
33
|
+
80 => "eighty",
|
34
|
+
90 => "ninety",
|
35
|
+
}.freeze
|
36
|
+
private_constant :NUMBERS
|
37
|
+
|
4
38
|
CONVERTER = Fear.matcher do |m|
|
5
|
-
|
6
|
-
0 => 'zero',
|
7
|
-
1 => 'one',
|
8
|
-
2 => 'two',
|
9
|
-
3 => 'three',
|
10
|
-
4 => 'four',
|
11
|
-
5 => 'five',
|
12
|
-
6 => 'six',
|
13
|
-
7 => 'seven',
|
14
|
-
8 => 'eight',
|
15
|
-
9 => 'nine',
|
16
|
-
10 => 'ten',
|
17
|
-
11 => 'eleven',
|
18
|
-
12 => 'twelve',
|
19
|
-
13 => 'thirteen',
|
20
|
-
14 => 'fourteen',
|
21
|
-
15 => 'fifteen',
|
22
|
-
16 => 'sixteen',
|
23
|
-
17 => 'seventeen',
|
24
|
-
18 => 'eighteen',
|
25
|
-
19 => 'nineteen',
|
26
|
-
20 => 'twenty',
|
27
|
-
30 => 'thirty',
|
28
|
-
40 => 'forty',
|
29
|
-
50 => 'fifty',
|
30
|
-
60 => 'sixty',
|
31
|
-
70 => 'seventy',
|
32
|
-
80 => 'eighty',
|
33
|
-
90 => 'ninety',
|
34
|
-
}.each_pair do |number, in_words|
|
39
|
+
NUMBERS.each_pair do |number, in_words|
|
35
40
|
m.case(number) { in_words }
|
36
41
|
end
|
37
|
-
m.case(->(n) { n < 0 }) { |n| "minus #{CONVERTER.
|
38
|
-
m.case(->(n) { n < 100 }) { |n| "#{CONVERTER.
|
39
|
-
m.case(->(n) { n < 200 }) { |n| "one hundred #{CONVERTER.
|
40
|
-
m.case(->(n) { n < 1_000 }) { |n| "#{CONVERTER.
|
41
|
-
m.case(->(n) { n < 2_000 }) { |n| "one thousand #{CONVERTER.
|
42
|
-
m.case(->(n) { n < 1_000_000 }) { |n| "#{CONVERTER.
|
42
|
+
m.case(->(n) { n < 0 }) { |n| "minus #{CONVERTER.(-n)}" }
|
43
|
+
m.case(->(n) { n < 100 }) { |n| "#{CONVERTER.((n / 10) * 10)}-#{CONVERTER.(n % 10)}" }
|
44
|
+
m.case(->(n) { n < 200 }) { |n| "one hundred #{CONVERTER.(n % 100)}" }
|
45
|
+
m.case(->(n) { n < 1_000 }) { |n| "#{CONVERTER.(n / 100)} hundreds #{CONVERTER.(n % 100)}" }
|
46
|
+
m.case(->(n) { n < 2_000 }) { |n| "one thousand #{CONVERTER.(n % 1000)}" }
|
47
|
+
m.case(->(n) { n < 1_000_000 }) { |n| "#{CONVERTER.(n / 1_000)} thousands #{CONVERTER.(n % 1_000)}" }
|
43
48
|
m.else { |n| raise "#{n} too big " }
|
44
49
|
end
|
50
|
+
private_constant :CONVERTER
|
45
51
|
|
46
52
|
def self.call(number)
|
47
|
-
Fear.case(Integer, &:itself).and_then(CONVERTER).
|
53
|
+
Fear.case(Integer, &:itself).and_then(CONVERTER).(number)
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
51
|
-
ToWords.
|
52
|
-
ToWords.
|
53
|
-
ToWords.
|
54
|
-
ToWords.
|
57
|
+
ToWords.(99) #=> 'ninety-nine'
|
58
|
+
ToWords.(133) #=> 'one hundred thirty-three
|
59
|
+
ToWords.(777) #=> 'seven hundreds seventy-seven'
|
60
|
+
ToWords.(254_555) #=> 'two hundreds fifty-four thousands five hundreds fifty-five'
|
data/fear.gemspec
CHANGED
@@ -1,41 +1,40 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
2
4
|
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
6
|
|
5
|
-
require
|
7
|
+
require "fear/version"
|
6
8
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name
|
8
|
-
spec.version
|
9
|
-
spec.authors
|
10
|
-
spec.email
|
11
|
-
spec.summary
|
12
|
-
spec.description
|
13
|
-
spec.homepage
|
14
|
-
spec.license
|
15
|
-
|
16
|
-
spec.files = `git ls-files -z`.split("\x0")
|
17
|
-
spec.executables = spec.files.grep(%r{^bin\/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^spec\/})
|
19
|
-
spec.require_paths = ['lib']
|
20
|
-
|
21
|
-
spec.post_install_message = <<-MSG
|
22
|
-
Fear v0.11.0 introduces backwards-incompatible changes.
|
23
|
-
Please see https://github.com/bolshakov/fear/blob/master/CHANGELOG.md#0110 for details.
|
24
|
-
Successfully installed fear-#{Fear::VERSION}
|
25
|
-
MSG
|
9
|
+
spec.name = "fear"
|
10
|
+
spec.version = Fear::VERSION
|
11
|
+
spec.authors = ["Tema Bolshakov"]
|
12
|
+
spec.email = ["abolshakov@spbtv.com"]
|
13
|
+
spec.summary = "%q{Ruby port of some Scala's monads.}"
|
14
|
+
spec.description = "Ruby port of some Scala's monads."
|
15
|
+
spec.homepage = "https://github.com/bolshakov/fear"
|
16
|
+
spec.license = "MIT"
|
26
17
|
|
27
|
-
spec.
|
28
|
-
spec.
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.executables = spec.files.grep(%r{^bin\/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^spec\/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
|
29
23
|
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
32
|
-
spec.add_development_dependency
|
33
|
-
spec.add_development_dependency
|
34
|
-
spec.add_development_dependency
|
35
|
-
spec.add_development_dependency
|
36
|
-
spec.add_development_dependency
|
37
|
-
spec.add_development_dependency
|
38
|
-
spec.add_development_dependency
|
39
|
-
spec.add_development_dependency
|
40
|
-
spec.add_development_dependency
|
24
|
+
spec.add_development_dependency "benchmark-ips"
|
25
|
+
spec.add_development_dependency "bundler"
|
26
|
+
spec.add_development_dependency "concurrent-ruby"
|
27
|
+
spec.add_development_dependency "dry-matcher"
|
28
|
+
spec.add_development_dependency "dry-monads"
|
29
|
+
spec.add_development_dependency "qo"
|
30
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
31
|
+
spec.add_development_dependency "rspec", "~> 3.1"
|
32
|
+
spec.add_development_dependency "rubocop-rspec", "1.34.0"
|
33
|
+
spec.add_development_dependency "rubocop", "1.32.0"
|
34
|
+
spec.add_development_dependency "ruby_coding_standard"
|
35
|
+
spec.add_development_dependency "yard"
|
36
|
+
spec.add_development_dependency "dry-types"
|
37
|
+
spec.add_development_dependency "fear-rspec"
|
38
|
+
spec.add_development_dependency "simplecov"
|
39
|
+
spec.add_development_dependency "simplecov-lcov"
|
41
40
|
end
|