fear 0.9.0 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f87d9ddc3fd6c7bdd259ed164ca27668c3421f1
4
- data.tar.gz: f1cd2982080e8b8f302be524f55ef18f9406b3e6
3
+ metadata.gz: 9f6f57b15284f2ed3034bc697608d9c44e7401b1
4
+ data.tar.gz: 439fb988ae4fab755815100e092a65db332e960e
5
5
  SHA512:
6
- metadata.gz: 18926b68929c6b68b8e7e3fe1f5352a2998dade76e1860097b470a93e7d236142a84b1cf98a2413b6b093dd412461bc5fa764f10a3753cf932ab0558d8d59d9e
7
- data.tar.gz: 95c5eecb1b37444c63fdf713bcee58306053ba5708b10b4c7f6d4fa4dd53804143bc321cb5b10927297f136951d83573d9924086b6faa0794a239513603b12c3
6
+ metadata.gz: 8409d00dd720c5dfbd623e73d72abd86ae5c90235e08dd646ad2e9c19b17c502d1a3d0f9a372ae23bb1af1e6cd9571cd425a6c7305e95256746031a6a634c71b
7
+ data.tar.gz: 051a50aa71c6fad65b52798ee303f59037c988365a449afa4653afab5f62d53443c31d0d731d07f09a39c564d330cd927243b701e7925aefa913f78ba7fd52fb
data/.rubocop.yml CHANGED
@@ -1,6 +1,10 @@
1
1
  inherit_gem:
2
2
  spbtv_code_style: .strict_rubocop.yml
3
3
 
4
+ AllCops:
5
+ Exclude:
6
+ - 'gemfiles/**/*'
7
+
4
8
  Style/MethodName:
5
9
  Enabled: false
6
10
 
data/.travis.yml CHANGED
@@ -1,9 +1,17 @@
1
+ ---
1
2
  language: ruby
2
3
  rvm:
3
- - 2.3.7
4
- - 2.4.4
5
- - 2.5.1
4
+ - 2.4.5
5
+ - 2.5.3
6
+ - 2.6.1
7
+ before_script:
8
+ - bundle install
6
9
  script:
7
- - rubocop -D
8
- - bundle exec rspec spec
9
- env: CODECLIMATE_REPO_TOKEN=548312a77df2507c49494d3c6e78cc62631517c39440644c38217d9be1f47e26
10
+ - bundle exec rspec
11
+ - bundle exec rubocop --fail-level C
12
+ gemfile:
13
+ - gemfiles/dry_equalizer_0.2.1.gemfile
14
+ - gemfiles/dry_equalizer_0.1.0.gemfile
15
+ addons:
16
+ code_climate:
17
+ repo_token: c326cca5984d0e32d2c6b5d9b985756ee9312f63fc6a9480fc9cfa55c454d68a
data/Appraisals ADDED
@@ -0,0 +1,36 @@
1
+ require 'yaml'
2
+
3
+ ruby_versions = %w(2.4.5 2.5.3 2.6.1)
4
+ dry_equalizer_versions = %w(0.1.0 0.2.1)
5
+
6
+ dry_equalizer_versions.each do |version|
7
+ appraise "dry-equalizer-#{version}" do
8
+ gem 'dry-equalizer', version
9
+ end
10
+ end
11
+
12
+ Dir.glob('gemfiles/*.gemfile').tap do |gemfiles|
13
+ travis = ::YAML.dump(
14
+ 'language' => 'ruby',
15
+ 'rvm' => ruby_versions,
16
+ 'before_script' => [
17
+ 'bundle install',
18
+ ],
19
+ 'script' => [
20
+ 'bundle exec rspec',
21
+ 'bundle exec rubocop --fail-level C',
22
+ ],
23
+ 'gemfile' => gemfiles,
24
+ 'addons' => {
25
+ 'code_climate' => {
26
+ 'repo_token' => 'c326cca5984d0e32d2c6b5d9b985756ee9312f63fc6a9480fc9cfa55c454d68a'
27
+ }
28
+ }
29
+
30
+
31
+ )
32
+
33
+ ::File.open('.travis.yml', 'w+') do |file|
34
+ file.write(travis)
35
+ end
36
+ end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.10.0
2
+
3
+ * Test against last supported ruby versions: 2.4.5, 2.5.3, 2.6.1 ([@bolshakov][])
4
+ * You can use `fear` with any `dry-equalizer` version (up to 0.2.1) ([@bolshakov][])
5
+
1
6
  ## 0.9.0
2
7
 
3
8
  * Test against last supported ruby versions: 2.3.7, 2.4.4, 2.5.1 ([@bolshakov][])
data/README.md CHANGED
@@ -25,60 +25,371 @@ Or install it yourself as:
25
25
 
26
26
  ## Usage
27
27
 
28
+ * [Option](#option-documentation)
29
+ * [Try](#try-documentation)
30
+ * [Either](#either-documentation)
31
+ * [For composition](#for-composition)
32
+ * [Pattern Matching](#pattern-matching)
33
+
28
34
  ### Option ([Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Option))
29
35
 
30
36
  Represents optional (nullable) values. Instances of `Option` are either an instance of
31
37
  `Some` or the object `None`.
32
38
 
33
- The most idiomatic way to use an `Option` instance is to treat it
34
- as a collection and use `map`, `flat_map`, `select`, or `each`:
39
+ The most idiomatic way to use an `Option` instance is to treat it as a collection
35
40
 
36
41
  ```ruby
37
- include Fear::Option::Mixin
42
+ name = Option(params[:name])
43
+ upper = name.map(&:strip).select { |n| n.length != 0 }.map(&:upcase)
44
+ puts upper.get_or_else('')
45
+ ```
46
+
47
+ This allows for sophisticated chaining of `Option` values without
48
+ having to check for the existence of a value.
38
49
 
39
- def normalize_name(name)
40
- Option(name)
41
- .map(&:strip)
42
- .select { |n| n.length != 0 }
43
- .map(&:upcase)
44
- .get_or_else('NONAME')
50
+ A less-idiomatic way to use `Option` values is via pattern matching
51
+
52
+ ```ruby
53
+ name = Option(params[:name])
54
+ case name
55
+ when Some
56
+ puts name.strip.upcase
57
+ when None
58
+ puts 'No name value'
45
59
  end
60
+ ```
61
+
62
+ or manually checking for non emptiness
46
63
 
47
- normalize_name('robert paulson ') #=> 'ROBERT PAULSON'
48
- normalize_name(nil) #=> 'NONAME'
64
+ ```ruby
65
+ name = Option(params[:name])
66
+ if name.empty?
67
+ puts 'No name value'
68
+ else
69
+ puts name.strip.upcase
70
+ end
49
71
  ```
50
72
 
51
- This allows for sophisticated chaining of `Option` values without
52
- having to check for the existence of a value.
73
+ #### Option#get_or_else
74
+
75
+ Returns the value from this `Some` or evaluates the given default argument if this is a `None`.
76
+
77
+ ```ruby
78
+ Some(42).get_or_else { 24/2 } #=> 42
79
+ None().get_or_else { 24/2 } #=> 12
80
+
81
+ Some(42).get_or_else(12) #=> 42
82
+ None().get_or_else(12) #=> 12
83
+ ```
84
+
85
+ #### Option#or_else
86
+
87
+ returns self `Some` or the given alternative if this is a `None`.
88
+
89
+ ```ruby
90
+ Some(42).or_else { Some(21) } #=> Some(42)
91
+ None().or_else { Some(21) } #=> Some(21)
92
+ None().or_else { None() } #=> None()
93
+ ```
94
+
95
+ #### Option#inlude?
96
+
97
+ Checks if `Option` has an element that is equal (as determined by `==`) to given values.
98
+
99
+ ```ruby
100
+ Some(17).include?(17) #=> true
101
+ Some(17).include?(7) #=> false
102
+ None().include?(17) #=> false
103
+ ```
104
+
105
+ #### Option#each
106
+
107
+ Performs the given block if this is a `Some`.
108
+
109
+ ```ruby
110
+ Some(17).each { |value| puts value } #=> prints 17
111
+ None().each { |value| puts value } #=> does nothing
112
+ ```
113
+
114
+ #### Option#map
115
+
116
+ Maps the given block to the value from this `Some` or returns self if this is a `None`
117
+
118
+ ```ruby
119
+ Some(42).map { |v| v/2 } #=> Some(21)
120
+ None().map { |v| v/2 } #=> None()
121
+ ```
122
+
123
+ #### Option#flat_map
124
+
125
+ Returns the given block applied to the value from this `Some` or returns self if this is a `None`
126
+
127
+ ```ruby
128
+ Some(42).flat_map { |v| Some(v/2) } #=> Some(21)
129
+ None().flat_map { |v| Some(v/2) } #=> None()
130
+ ```
131
+
132
+ #### Option#to_a
133
+
134
+ Returns an `Array` containing the `Some` value or an empty `Array` if this is a `None`
135
+
136
+ ```ruby
137
+ Some(42).to_a #=> [21]
138
+ None().to_a #=> []
139
+ ```
140
+
141
+ #### Option#any?
142
+
143
+ Returns `false` if `None` or returns the result of the application of the given predicate to the `Some` value.
144
+
145
+ ```ruby
146
+ Some(12).any?( |v| v > 10) #=> true
147
+ Some(7).any?( |v| v > 10) #=> false
148
+ None().any?( |v| v > 10) #=> false
149
+ ```
150
+
151
+ #### Option#select
152
+
153
+ Returns self if it is nonempty and applying the predicate to this `Option`'s value returns `true`. Otherwise,
154
+ return `None`.
155
+
156
+ ```ruby
157
+ Some(42).select { |v| v > 40 } #=> Success(21)
158
+ Some(42).select { |v| v < 40 } #=> None()
159
+ None().select { |v| v < 40 } #=> None()
160
+ ```
161
+
162
+ #### Option#reject
163
+
164
+ Returns `Some` if applying the predicate to this `Option`'s value returns `false`. Otherwise, return `None`.
165
+
166
+ ```ruby
167
+ Some(42).reject { |v| v > 40 } #=> None
168
+ Some(42).reject { |v| v < 40 } #=> Some(42)
169
+ None().reject { |v| v < 40 } #=> None
170
+ ```
171
+
172
+ #### Option#get
173
+
174
+ Not an idiomatic way of using Option at all. Returns values of raise `NoSuchElementError` error if option is empty.
175
+
176
+ #### Option#empty?
177
+
178
+ Returns `true` if the `Option` is `None`, `false` otherwise.
179
+
180
+ ```ruby
181
+ Some(42).empty? #=> false
182
+ None().empty? #=> true
183
+ ```
184
+
185
+ @see https://github.com/scala/scala/blob/2.11.x/src/library/scala/Option.scala
186
+
53
187
 
54
188
  ### Try ([Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Try))
55
189
 
56
- The `Try` represents a computation that may either result in an exception,
57
- or return a successfully computed value. Instances of `Try`, are either
58
- an instance of `Success` or `Failure`.
190
+ The `Try` represents a computation that may either result
191
+ in an exception, or return a successfully computed value. Instances of `Try`,
192
+ are either an instance of `Success` or `Failure`.
59
193
 
60
- For example, `Try` can be used to perform division on a user-defined input,
61
- without the need to do explicit exception-handling in all of the places
62
- that an exception might occur.
194
+ For example, `Try` can be used to perform division on a
195
+ user-defined input, without the need to do explicit
196
+ exception-handling in all of the places that an exception
197
+ might occur.
63
198
 
64
199
  ```ruby
65
200
  include Fear::Try::Mixin
66
201
 
67
202
  dividend = Try { Integer(params[:dividend]) }
68
203
  divisor = Try { Integer(params[:divisor]) }
204
+ problem = dividend.flat_map { |x| divisor.map { |y| x / y } }
69
205
 
70
- result = dividend.flat_map { |x| divisor.map { |y| x / y } }
71
-
72
- if result.success?
73
- puts "Result of #{dividend.get} / #{divisor.get} is: #{result.get}"
206
+ if problem.success?
207
+ puts "Result of #{dividend.get} / #{divisor.get} is: #{problem.get}"
74
208
  else
75
209
  puts "You must've divided by zero or entered something wrong. Try again"
76
- puts "Info from the exception: #{result.exception.message}"
210
+ puts "Info from the exception: #{problem.exception.message}"
77
211
  end
78
212
  ```
79
213
 
80
- ### Either ([Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Either))
214
+ An important property of `Try` shown in the above example is its
215
+ ability to _pipeline_, or chain, operations, catching exceptions
216
+ along the way. The `flat_map` and `map` combinators in the above
217
+ example each essentially pass off either their successfully completed
218
+ value, wrapped in the `Success` type for it to be further operated
219
+ upon by the next combinator in the chain, or the exception wrapped
220
+ in the `Failure` type usually to be simply passed on down the chain.
221
+ Combinators such as `recover_with` and `recover` are designed to provide some
222
+ type of default behavior in the case of failure.
223
+
224
+ *NOTE*: Only non-fatal exceptions are caught by the combinators on `Try`.
225
+ Serious system errors, on the other hand, will be thrown.
226
+
227
+ #### Try#get_or_else
228
+
229
+ Returns the value from this `Success` or evaluates the given default argument if this is a `Failure`.
230
+
231
+ ```ruby
232
+ Success(42).get_or_else { 24/2 } #=> 42
233
+ Failure(ArgumentError.new).get_or_else { 24/2 } #=> 12
234
+ ```
235
+
236
+ #### Try#include?
237
+
238
+ Returns `true` if it has an element that is equal given values, `false` otherwise.
239
+
240
+ ```ruby
241
+ Success(17).include?(17) #=> true
242
+ Success(17).include?(7) #=> false
243
+ Failure(ArgumentError.new).include?(17) #=> false
244
+ ```
245
+
246
+ #### Try#each
247
+
248
+ Performs the given block if this is a `Success`. If block raise an error,
249
+ then this method may raise an exception.
250
+
251
+ ```ruby
252
+ Success(17).each { |value| puts value } #=> prints 17
253
+ Failure(ArgumentError.new).each { |value| puts value } #=> does nothing
254
+ ```
255
+
256
+ #### Try#map
257
+
258
+ Maps the given block to the value from this `Success` or returns self if this is a `Failure`.
259
+
260
+ ```ruby
261
+ Success(42).map { |v| v/2 } #=> Success(21)
262
+ Failure(ArgumentError.new).map { |v| v/2 } #=> Failure(ArgumentError.new)
263
+ ```
264
+
265
+ #### Try#flat_map
266
+
267
+ Returns the given block applied to the value from this `Success`or returns self if this is a `Failure`.
268
+
269
+ ```ruby
270
+ Success(42).flat_map { |v| Success(v/2) } #=> Success(21)
271
+ Failure(ArgumentError.new).flat_map { |v| Success(v/2) } #=> Failure(ArgumentError.new)
272
+ ```
273
+
274
+ #### Try#to_a
275
+
276
+ Returns an `Array` containing the `Success` value or an empty `Array` if this is a `Failure`.
277
+
278
+ ```ruby
279
+ Success(42).to_a #=> [21]
280
+ Failure(ArgumentError.new).to_a #=> []
281
+ ```
282
+
283
+ #### Try#to_option
284
+
285
+ Returns an `Some` containing the `Success` value or a `None` if this is a `Failure`.
286
+
287
+ ```ruby
288
+ Success(42).to_option #=> Some(21)
289
+ Failure(ArgumentError.new).to_option #=> None()
290
+ ```
291
+
292
+ #### Try#any?
293
+
294
+ Returns `false` if `Failure` or returns the result of the application of the given predicate to the `Success` value.
295
+
296
+ ```ruby
297
+ Success(12).any?( |v| v > 10) #=> true
298
+ Success(7).any?( |v| v > 10) #=> false
299
+ Failure(ArgumentError.new).any?( |v| v > 10) #=> false
300
+ ```
301
+
302
+ #### Try#success? and Try#failure?
303
+
304
+
305
+ ```ruby
306
+ Success(12).success? #=> true
307
+ Success(12).failure? #=> true
308
+
309
+ Failure(ArgumentError.new).success? #=> false
310
+ Failure(ArgumentError.new).failure? #=> true
311
+ ```
312
+
313
+ #### Try#get
314
+
315
+ Returns the value from this `Success` or raise the exception if this is a `Failure`.
316
+
317
+ ```ruby
318
+ Success(42).get #=> 42
319
+ Failure(ArgumentError.new).get #=> ArgumentError: ArgumentError
320
+ ```
321
+
322
+ #### Try#or_else
323
+
324
+ Returns self `Try` if it's a `Success` or the given alternative if this is a `Failure`.
325
+
326
+ ```ruby
327
+ Success(42).or_else { Success(-1) } #=> Success(42)
328
+ Failure(ArgumentError.new).or_else { Success(-1) } #=> Success(-1)
329
+ Failure(ArgumentError.new).or_else { Try { 1/0 } } #=> Failure(ZeroDivisionError.new('divided by 0'))
330
+ ```
331
+
332
+ #### Try#flatten
333
+
334
+ Transforms a nested `Try`, ie, a `Success` of `Success`, into an un-nested `Try`, ie, a `Success`.
335
+
336
+ ```ruby
337
+ Success(42).flatten #=> Success(42)
338
+ Success(Success(42)).flatten #=> Success(42)
339
+ Success(Failure(ArgumentError.new)).flatten #=> Failure(ArgumentError.new)
340
+ Failure(ArgumentError.new).flatten { -1 } #=> Failure(ArgumentError.new)
341
+ ```
342
+
343
+ #### Try#select
344
+
345
+ Converts this to a `Failure` if the predicate is not satisfied.
346
+
347
+ ```ruby
348
+ Success(42).select { |v| v > 40 }
349
+ #=> Success(21)
350
+ Success(42).select { |v| v < 40 }
351
+ #=> Failure(Fear::NoSuchElementError.new("Predicate does not hold for 42"))
352
+ Failure(ArgumentError.new).select { |v| v < 40 }
353
+ #=> Failure(ArgumentError.new)
354
+ ```
81
355
 
356
+ #### Try#recover_with
357
+
358
+ Applies the given block to exception. This is like `flat_map` for the exception.
359
+
360
+ ```ruby
361
+ Success(42).recover_with { |e| Success(e.massage) }
362
+ #=> Success(42)
363
+ Failure(ArgumentError.new).recover_with { |e| Success(e.massage) }
364
+ #=> Success('ArgumentError')
365
+ Failure(ArgumentError.new).recover_with { |e| fail }
366
+ #=> Failure(RuntimeError)
367
+ ```
368
+
369
+ #### Try#recover
370
+
371
+ Applies the given block to exception. This is like `map` for the exception.
372
+
373
+ ```ruby
374
+ Success(42).recover { |e| e.massage }
375
+ #=> Success(42)
376
+ Failure(ArgumentError.new).recover { |e| e.massage }
377
+ #=> Success('ArgumentError')
378
+ Failure(ArgumentError.new).recover { |e| fail }
379
+ #=> Failure(RuntimeError)
380
+ ```
381
+
382
+ #### Try#to_either
383
+
384
+ Returns `Left` with exception if this is a `Failure`, otherwise returns `Right` with `Success` value.
385
+
386
+ ```ruby
387
+ Success(42).to_either #=> Right(42)
388
+ Failure(ArgumentError.new).to_either #=> Left(ArgumentError.new)
389
+ ```
390
+
391
+ ### Either ([Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Either))
392
+
82
393
  Represents a value of one of two possible types (a disjoint union.)
83
394
  An instance of `Either` is either an instance of `Left` or `Right`.
84
395
 
@@ -86,19 +397,17 @@ A common use of `Either` is as an alternative to `Option` for dealing
86
397
  with possible missing values. In this usage, `None` is replaced
87
398
  with a `Left` which can contain useful information.
88
399
  `Right` takes the place of `Some`. Convention dictates
89
- that `Left` is used for failure and `Right` is used for success.
400
+ that `Left` is used for failure and `Right` is used for Right.
90
401
 
91
- For example, you could use `Either<String, Fixnum>` to select whether a
92
- received input is a `String` or an `Fixnum`.
402
+ For example, you could use `Either<String, Fixnum>` to `#select_or_else` whether a
403
+ received input is a +String+ or an +Fixnum+.
93
404
 
94
405
  ```ruby
95
- include Fear::Either::Mixin
96
-
97
- input = Readline.readline('Type Either a string or an Int: ', true)
406
+ in = Readline.readline('Type Either a string or an Int: ', true)
98
407
  result = begin
99
- Right(Integer(input))
408
+ Right(Integer(in))
100
409
  rescue ArgumentError
101
- Left(input)
410
+ Left(in)
102
411
  end
103
412
 
104
413
  puts(
@@ -107,7 +416,197 @@ puts(
107
416
  -> (x) { "You passed me the Int: #{x}, which I will increment. #{x} + 1 = #{x+1}" }
108
417
  )
109
418
  )
110
- ```
419
+ ```
420
+
421
+ Either is right-biased, which means that `Right` is assumed to be the default case to
422
+ operate on. If it is `Left`, operations like `#map`, `#flat_map`, ... return the `Left` value
423
+ unchanged.
424
+
425
+ #### Either#get_or_else
426
+
427
+ Returns the value from this `Right` or evaluates the given default argument if this is a `Left`.
428
+
429
+ ```ruby
430
+ Right(42).get_or_else { 24/2 } #=> 42
431
+ Left('undefined').get_or_else { 24/2 } #=> 12
432
+
433
+ Right(42).get_or_else(12) #=> 42
434
+ Left('undefined').get_or_else(12) #=> 12
435
+ ```
436
+
437
+ #### Either#or_else
438
+
439
+ Returns self `Right` or the given alternative if this is a `Left`.
440
+
441
+ ```ruby
442
+ Right(42).or_else { Right(21) } #=> Right(42)
443
+ Left('unknown').or_else { Right(21) } #=> Right(21)
444
+ Left('unknown').or_else { Left('empty') } #=> Left('empty')
445
+ ```
446
+
447
+ #### Either#include?
448
+
449
+ Returns `true` if `Right` has an element that is equal to given value, `false` otherwise.
450
+
451
+ ```ruby
452
+ Right(17).include?(17) #=> true
453
+ Right(17).include?(7) #=> false
454
+ Left('undefined').include?(17) #=> false
455
+ ```
456
+
457
+ #### Either#each
458
+
459
+ Performs the given block if this is a `Right`.
460
+
461
+ ```ruby
462
+ Right(17).each { |value| puts value } #=> prints 17
463
+ Left('undefined').each { |value| puts value } #=> does nothing
464
+ ```
465
+
466
+ #### Either#map
467
+
468
+ Maps the given block to the value from this `Right` or returns self if this is a `Left`.
469
+
470
+ ```ruby
471
+ Right(42).map { |v| v/2 } #=> Right(21)
472
+ Left('undefined').map { |v| v/2 } #=> Left('undefined')
473
+ ```
474
+
475
+ #### Either#flat_map
476
+
477
+ Returns the given block applied to the value from this `Right` or returns self if this is a `Left`.
478
+
479
+ ```ruby
480
+ Right(42).flat_map { |v| Right(v/2) } #=> Right(21)
481
+ Left('undefined').flat_map { |v| Right(v/2) } #=> Left('undefined')
482
+ ```
483
+
484
+ #### Either#to_a
485
+
486
+ Returns an `Array` containing the `Right` value or an empty `Array` if this is a `Left`.
487
+
488
+ ```ruby
489
+ Right(42).to_a #=> [21]
490
+ Left('undefined').to_a #=> []
491
+ ```
492
+
493
+ #### Either#to_option
494
+
495
+ Returns an `Some` containing the `Right` value or a `None` if this is a `Left`.
496
+
497
+ ```ruby
498
+ Right(42).to_option #=> Some(21)
499
+ Left('undefined').to_option #=> None()
500
+ ```
501
+
502
+ #### Either#any?
503
+
504
+ Returns `false` if `Left` or returns the result of the application of the given predicate to the `Right` value.
505
+
506
+ ```ruby
507
+ Right(12).any?( |v| v > 10) #=> true
508
+ Right(7).any?( |v| v > 10) #=> false
509
+ Left('undefined').any?( |v| v > 10) #=> false
510
+ ```
511
+
512
+ #### Either#right?, Either#success?
513
+
514
+ Returns `true` if this is a `Right`, `false` otherwise.
515
+
516
+ ```ruby
517
+ Right(42).right? #=> true
518
+ Left('err').right? #=> false
519
+ ```
520
+
521
+ #### Either#left?, Either#failure?
522
+
523
+ Returns `true` if this is a `Left`, `false` otherwise.
524
+
525
+ ```ruby
526
+ Right(42).left? #=> false
527
+ Left('err').left? #=> true
528
+ ```
529
+
530
+ #### Either#select_or_else
531
+
532
+ Returns `Left` of the default if the given predicate does not hold for the right value, otherwise,
533
+ returns `Right`.
534
+
535
+ ```ruby
536
+ Right(12).select_or_else(-1, &:even?) #=> Right(12)
537
+ Right(7).select_or_else(-1, &:even?) #=> Left(-1)
538
+ Left(12).select_or_else(-1, &:even?) #=> Left(12)
539
+ Left(12).select_or_else(-> { -1 }, &:even?) #=> Left(12)
540
+ ```
541
+
542
+ #### Either#select
543
+
544
+ Returns `Left` of value if the given predicate does not hold for the right value, otherwise, returns `Right`.
545
+
546
+ ```ruby
547
+ Right(12).select(&:even?) #=> Right(12)
548
+ Right(7).select(&:even?) #=> Left(7)
549
+ Left(12).select(&:even?) #=> Left(12)
550
+ Left(7).select(&:even?) #=> Left(7)
551
+ ```
552
+
553
+ #### Either#reject
554
+
555
+ Returns `Left` of value if the given predicate holds for the right value, otherwise, returns `Right`.
556
+
557
+ ```ruby
558
+ Right(12).reject(&:even?) #=> Left(12)
559
+ Right(7).reject(&:even?) #=> Right(7)
560
+ Left(12).reject(&:even?) #=> Left(12)
561
+ Left(7).reject(&:even?) #=> Left(7)
562
+ ```
563
+
564
+ #### Either#swap
565
+
566
+ If this is a `Left`, then return the left value in `Right` or vice versa.
567
+
568
+ ```ruby
569
+ Left('left').swap #=> Right('left')
570
+ Right('right').swap #=> Light('left')
571
+ ```
572
+
573
+ #### Either#reduce
574
+
575
+ Applies `reduce_left` if this is a `Left` or `reduce_right` if this is a `Right`.
576
+
577
+ ```ruby
578
+ result = possibly_failing_operation()
579
+ log(
580
+ result.reduce(
581
+ ->(ex) { "Operation failed with #{ex}" },
582
+ ->(v) { "Operation produced value: #{v}" },
583
+ )
584
+ )
585
+ ```
586
+
587
+ #### Either#join_right
588
+
589
+ Joins an `Either` through `Right`. This method requires that the right side of this `Either` is itself an
590
+ `Either` type. This method, and `join_left`, are analogous to `Option#flatten`
591
+
592
+ ```ruby
593
+ Right(Right(12)).join_right #=> Right(12)
594
+ Right(Left("flower")).join_right #=> Left("flower")
595
+ Left("flower").join_right #=> Left("flower")
596
+ Left(Right("flower")).join_right #=> Left(Right("flower"))
597
+ ```
598
+
599
+ #### Either#join_right
600
+
601
+ Joins an `Either` through `Left`. This method requires that the left side of this `Either` is itself an
602
+ `Either` type. This method, and `join_right`, are analogous to `Option#flatten`
603
+
604
+ ```ruby
605
+ Left(Right("flower")).join_left #=> Right("flower")
606
+ Left(Left(12)).join_left #=> Left(12)
607
+ Right("daisy").join_left #=> Right("daisy")
608
+ Right(Left("daisy")).join_left #=> Right(Left("daisy"))
609
+ ```
111
610
 
112
611
  ### For composition
113
612
 
@@ -116,49 +615,41 @@ It supports two such operations - `flat_map` and `map`. Any class providing them
116
615
  is supported by `For`.
117
616
 
118
617
  ```ruby
119
- include Fear::For::Mixin
618
+ For(a: Some(2), b: Some(3)) { a * b } #=> Some(6)
619
+ ```
120
620
 
121
- def divide(dividend, divisor)
122
- For(x: dividend, y: divisor) do
123
- x / y
124
- end
125
- end
621
+ If one of operands is None, the result is None
622
+
623
+ ```ruby
624
+ For(a: Some(2), b: None()) { a * b } #=> None()
625
+ For(a: None(), b: Some(2)) { a * b } #=> None()
626
+ ```
126
627
 
127
- dividend = Try { Integer(params[:dividend]) } #=> Try(4)
128
- divisor = Try { Integer(params[:divisor]) } #=> Try(2)
628
+ Lets look at first example:
129
629
 
130
- divide(dividend, divisor) #=> Try(2)
630
+ ```ruby
631
+ For(a: Some(2), b: Some(3)) { a * b }
131
632
  ```
132
633
 
133
- It would be translated to
634
+ would be translated to:
134
635
 
135
636
  ```ruby
136
- Success(4).flat_map do |x|
137
- Success(2).map do |y|
138
- x / y
637
+ Some(2).flat_map do |a|
638
+ Some(3).map do |b|
639
+ a * b
139
640
  end
140
641
  end
141
642
  ```
142
643
 
143
- If one of operands is Failure, the result is Failure
644
+ It works with arrays as well
144
645
 
145
646
  ```ruby
647
+ For(a: [1, 2], b: [2, 3], c: [3, 4]) { a * b * c }
648
+ #=> [6, 8, 9, 12, 12, 16, 18, 24]
146
649
 
147
- dividend = Try { 42 }
148
- divisor = Try { Integer('ehuton') }
149
-
150
- divide(dividend, divisor) #=> Failure(<ArgumentError: invalid value for Integer(): "ehuton">)
151
650
  ```
152
651
 
153
- `For` works with arrays as well
154
-
155
- ```ruby
156
- For(a: [1, 2], b: [2, 3], c: [3, 4]) do
157
- a * b * c
158
- end #=> [6, 8, 9, 12, 12, 16, 18, 24]
159
- ```
160
-
161
- would be translated to:
652
+ it is translated to:
162
653
 
163
654
  ```ruby
164
655
  [1, 2].flat_map do |a|
@@ -170,6 +661,25 @@ would be translated to:
170
661
  end
171
662
  ```
172
663
 
664
+ If you pass lambda as a variable value, it would be evaluated
665
+ only on demand.
666
+
667
+ ```ruby
668
+ For(a: -> { None() }, b: -> { fail 'kaboom' } ) { a * b }
669
+ #=> None()
670
+ ```
671
+
672
+ It does not fail since `b` is not evaluated.
673
+ You can refer to previously defined variables from within lambdas.
674
+
675
+ ```ruby
676
+ maybe_user = find_user('Paul') #=> <#Option value=<#User ...>>
677
+
678
+ For(user: maybe_user, birthday: -> { user.birthday }) do
679
+ "#{user.name} was born on #{birthday}"
680
+ end #=> Some('Paul was born on 1987-06-17')
681
+ ```
682
+
173
683
  ### Pattern Matching
174
684
 
175
685
  `Option`, `Either`, and `Try` contains enhanced version of `#===` method. It performs matching not
data/fear.gemspec CHANGED
@@ -19,13 +19,14 @@ Gem::Specification.new do |spec|
19
19
  spec.test_files = spec.files.grep(%r{^spec\/})
20
20
  spec.require_paths = ['lib']
21
21
 
22
- spec.add_runtime_dependency 'dry-equalizer', '0.2.0'
22
+ spec.add_runtime_dependency 'dry-equalizer', '<= 0.2.1'
23
23
 
24
- spec.add_development_dependency 'bundler', '~> 1.7'
24
+ spec.add_development_dependency 'appraisal'
25
+ spec.add_development_dependency 'bundler'
25
26
  spec.add_development_dependency 'rake', '~> 10.0'
26
27
  spec.add_development_dependency 'rspec', '~> 3.1'
27
- spec.add_development_dependency 'spbtv_code_style'
28
- spec.add_development_dependency 'rubocop-rspec', '1.13.0'
29
28
  spec.add_development_dependency 'rubocop', '0.47.1'
29
+ spec.add_development_dependency 'rubocop-rspec', '1.13.0'
30
30
  spec.add_development_dependency 'simplecov'
31
+ spec.add_development_dependency 'spbtv_code_style'
31
32
  end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "dry-equalizer", "0.1.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,78 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ fear (0.10.0)
5
+ dry-equalizer (<= 0.2.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ appraisal (2.2.0)
11
+ bundler
12
+ rake
13
+ thor (>= 0.14.0)
14
+ ast (2.4.0)
15
+ diff-lcs (1.3)
16
+ docile (1.3.1)
17
+ dry-equalizer (0.1.0)
18
+ json (2.1.0)
19
+ parser (2.6.0.0)
20
+ ast (~> 2.4.0)
21
+ powerpack (0.1.2)
22
+ rainbow (2.2.2)
23
+ rake
24
+ rake (10.5.0)
25
+ rspec (3.8.0)
26
+ rspec-core (~> 3.8.0)
27
+ rspec-expectations (~> 3.8.0)
28
+ rspec-mocks (~> 3.8.0)
29
+ rspec-core (3.8.0)
30
+ rspec-support (~> 3.8.0)
31
+ rspec-expectations (3.8.2)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.8.0)
34
+ rspec-mocks (3.8.0)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.8.0)
37
+ rspec-support (3.8.0)
38
+ rspec_junit_formatter (0.4.1)
39
+ rspec-core (>= 2, < 4, != 2.12.0)
40
+ rubocop (0.47.1)
41
+ parser (>= 2.3.3.1, < 3.0)
42
+ powerpack (~> 0.1)
43
+ rainbow (>= 1.99.1, < 3.0)
44
+ ruby-progressbar (~> 1.7)
45
+ unicode-display_width (~> 1.0, >= 1.0.1)
46
+ rubocop-checkstyle_formatter (0.4.0)
47
+ rubocop (>= 0.35.1)
48
+ rubocop-rspec (1.13.0)
49
+ rubocop (>= 0.42.0)
50
+ ruby-progressbar (1.10.0)
51
+ simplecov (0.16.1)
52
+ docile (~> 1.1)
53
+ json (>= 1.8, < 3)
54
+ simplecov-html (~> 0.10.0)
55
+ simplecov-html (0.10.2)
56
+ spbtv_code_style (1.7.0)
57
+ rspec_junit_formatter
58
+ rubocop-checkstyle_formatter
59
+ thor (0.20.3)
60
+ unicode-display_width (1.4.1)
61
+
62
+ PLATFORMS
63
+ ruby
64
+
65
+ DEPENDENCIES
66
+ appraisal
67
+ bundler
68
+ dry-equalizer (= 0.1.0)
69
+ fear!
70
+ rake (~> 10.0)
71
+ rspec (~> 3.1)
72
+ rubocop (= 0.47.1)
73
+ rubocop-rspec (= 1.13.0)
74
+ simplecov
75
+ spbtv_code_style
76
+
77
+ BUNDLED WITH
78
+ 1.16.2
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "dry-equalizer", "0.2.1"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,78 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ fear (0.10.0)
5
+ dry-equalizer (<= 0.2.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ appraisal (2.2.0)
11
+ bundler
12
+ rake
13
+ thor (>= 0.14.0)
14
+ ast (2.4.0)
15
+ diff-lcs (1.3)
16
+ docile (1.3.1)
17
+ dry-equalizer (0.2.1)
18
+ json (2.1.0)
19
+ parser (2.6.0.0)
20
+ ast (~> 2.4.0)
21
+ powerpack (0.1.2)
22
+ rainbow (2.2.2)
23
+ rake
24
+ rake (10.5.0)
25
+ rspec (3.8.0)
26
+ rspec-core (~> 3.8.0)
27
+ rspec-expectations (~> 3.8.0)
28
+ rspec-mocks (~> 3.8.0)
29
+ rspec-core (3.8.0)
30
+ rspec-support (~> 3.8.0)
31
+ rspec-expectations (3.8.2)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.8.0)
34
+ rspec-mocks (3.8.0)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.8.0)
37
+ rspec-support (3.8.0)
38
+ rspec_junit_formatter (0.4.1)
39
+ rspec-core (>= 2, < 4, != 2.12.0)
40
+ rubocop (0.47.1)
41
+ parser (>= 2.3.3.1, < 3.0)
42
+ powerpack (~> 0.1)
43
+ rainbow (>= 1.99.1, < 3.0)
44
+ ruby-progressbar (~> 1.7)
45
+ unicode-display_width (~> 1.0, >= 1.0.1)
46
+ rubocop-checkstyle_formatter (0.4.0)
47
+ rubocop (>= 0.35.1)
48
+ rubocop-rspec (1.13.0)
49
+ rubocop (>= 0.42.0)
50
+ ruby-progressbar (1.10.0)
51
+ simplecov (0.16.1)
52
+ docile (~> 1.1)
53
+ json (>= 1.8, < 3)
54
+ simplecov-html (~> 0.10.0)
55
+ simplecov-html (0.10.2)
56
+ spbtv_code_style (1.7.0)
57
+ rspec_junit_formatter
58
+ rubocop-checkstyle_formatter
59
+ thor (0.20.3)
60
+ unicode-display_width (1.4.1)
61
+
62
+ PLATFORMS
63
+ ruby
64
+
65
+ DEPENDENCIES
66
+ appraisal
67
+ bundler
68
+ dry-equalizer (= 0.2.1)
69
+ fear!
70
+ rake (~> 10.0)
71
+ rspec (~> 3.1)
72
+ rubocop (= 0.47.1)
73
+ rubocop-rspec (= 1.13.0)
74
+ simplecov
75
+ spbtv_code_style
76
+
77
+ BUNDLED WITH
78
+ 1.16.2
data/lib/fear/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Fear
2
- VERSION = '0.9.0'.freeze
2
+ VERSION = '0.10.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fear
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tema Bolshakov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-21 00:00:00.000000000 Z
11
+ date: 2019-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-equalizer
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '='
17
+ - - "<="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.2.0
19
+ version: 0.2.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '='
24
+ - - "<="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: appraisal
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
25
39
  - !ruby/object:Gem::Version
26
- version: 0.2.0
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - "~>"
45
+ - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: '1.7'
47
+ version: '0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - "~>"
52
+ - - ">="
39
53
  - !ruby/object:Gem::Version
40
- version: '1.7'
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -67,19 +81,19 @@ dependencies:
67
81
  - !ruby/object:Gem::Version
68
82
  version: '3.1'
69
83
  - !ruby/object:Gem::Dependency
70
- name: spbtv_code_style
84
+ name: rubocop
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - ">="
87
+ - - '='
74
88
  - !ruby/object:Gem::Version
75
- version: '0'
89
+ version: 0.47.1
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">="
94
+ - - '='
81
95
  - !ruby/object:Gem::Version
82
- version: '0'
96
+ version: 0.47.1
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rubocop-rspec
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -95,21 +109,21 @@ dependencies:
95
109
  - !ruby/object:Gem::Version
96
110
  version: 1.13.0
97
111
  - !ruby/object:Gem::Dependency
98
- name: rubocop
112
+ name: simplecov
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
- - - '='
115
+ - - ">="
102
116
  - !ruby/object:Gem::Version
103
- version: 0.47.1
117
+ version: '0'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - '='
122
+ - - ">="
109
123
  - !ruby/object:Gem::Version
110
- version: 0.47.1
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
- name: simplecov
126
+ name: spbtv_code_style
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - ">="
@@ -134,12 +148,17 @@ files:
134
148
  - ".rubocop.yml"
135
149
  - ".travis.yml"
136
150
  - ".yardopts"
151
+ - Appraisals
137
152
  - CHANGELOG.md
138
153
  - Gemfile
139
154
  - LICENSE.txt
140
155
  - README.md
141
156
  - Rakefile
142
157
  - fear.gemspec
158
+ - gemfiles/dry_equalizer_0.1.0.gemfile
159
+ - gemfiles/dry_equalizer_0.1.0.gemfile.lock
160
+ - gemfiles/dry_equalizer_0.2.1.gemfile
161
+ - gemfiles/dry_equalizer_0.2.1.gemfile.lock
143
162
  - lib/fear.rb
144
163
  - lib/fear/done.rb
145
164
  - lib/fear/either.rb