fear 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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