rumonade 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.md +29 -0
- data/README.rdoc +1 -1
- data/lib/rumonade.rb +1 -0
- data/lib/rumonade/either.rb +0 -1
- data/lib/rumonade/error_handling.rb +67 -0
- data/lib/rumonade/option.rb +2 -2
- data/lib/rumonade/version.rb +1 -1
- data/test/error_handling_test.rb +40 -0
- data/test/option_test.rb +10 -2
- metadata +6 -3
- data/CHANGELOG.rdoc +0 -22
data/HISTORY.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# HISTORY
|
2
|
+
|
3
|
+
## v0.2.1 (Oct 22, 2011)
|
4
|
+
|
5
|
+
- fixed Option#map to disallow becoming None on a nil result
|
6
|
+
- other fixes and documentation updates
|
7
|
+
- See full list @ https://github.com/ms-ati/rumonade/compare/v0.2.0...v0.2.1
|
8
|
+
|
9
|
+
## v0.2.0 (Oct 18, 2011)
|
10
|
+
|
11
|
+
- added a scala-like Either class w/ LeftProjection and RightProjection monads
|
12
|
+
|
13
|
+
## v0.1.2 (Oct 12, 2011)
|
14
|
+
|
15
|
+
- progress towards Either class
|
16
|
+
- changed documentation to yard from rdoc
|
17
|
+
|
18
|
+
## v0.1.1 (Sep 19, 2011)
|
19
|
+
|
20
|
+
- added a first stab at documentation for Option
|
21
|
+
- fixed certain errors with #map
|
22
|
+
- added #select
|
23
|
+
|
24
|
+
## v0.1.0 (Sep 17, 2011)
|
25
|
+
|
26
|
+
- Initial Feature Set
|
27
|
+
- general implementation and testing of monadic laws based on `unit` and `bind`
|
28
|
+
- scala-like Option class w/ Some & None
|
29
|
+
- scala-like extensions to Array
|
data/README.rdoc
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Project: github[http://github.com/ms-ati/rumonade]
|
4
4
|
|
5
|
-
Documentation: rubydoc.info[http://rubydoc.info/gems/rumonade/0.2.
|
5
|
+
Documentation: rubydoc.info[http://rubydoc.info/gems/rumonade/0.2.1/file/README.rdoc]
|
6
6
|
|
7
7
|
== A Ruby[http://www.ruby-lang.org] Monad[http://en.wikipedia.org/wiki/Monad_(functional_programming)] Library, Inspired by Scala[http://www.scala-lang.org]
|
8
8
|
|
data/lib/rumonade.rb
CHANGED
data/lib/rumonade/either.rb
CHANGED
@@ -0,0 +1,67 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
module Rumonade
|
4
|
+
|
5
|
+
# A partial function is a unary function where the domain does not necessarily include all values.
|
6
|
+
# The function {#defined_at?} allows to test dynamically if a value is in the domain of the function.
|
7
|
+
#
|
8
|
+
# NOTE: This is only here to mimic the Scala library just enough to allow a close translation of the exception
|
9
|
+
# handling functionality. It's not because I'm the sort that just loves pure functional idioms so damn much for
|
10
|
+
# their own sake. Just FYI.
|
11
|
+
class PartialFunction < DelegateClass(Proc)
|
12
|
+
def initialize(defined_at_proc, call_proc)
|
13
|
+
super(call_proc)
|
14
|
+
@defined_at_proc = defined_at_proc
|
15
|
+
end
|
16
|
+
|
17
|
+
# Checks if a value is contained in the function's domain.
|
18
|
+
# @param x the value to test
|
19
|
+
# @return [Boolean] Returns +true+, iff +x+ is in the domain of this function, +false+ otherwise.
|
20
|
+
def defined_at?(x)
|
21
|
+
@defined_at_proc.call(x)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Composes this partial function with a fallback partial function which
|
25
|
+
# gets applied where this partial function is not defined.
|
26
|
+
# @param [PartialFunction] other the fallback function
|
27
|
+
# @return [PartialFunction] a partial function which has as domain the union of the domains
|
28
|
+
# of this partial function and +other+. The resulting partial function takes +x+ to +self.call(x)+
|
29
|
+
# where +self+ is defined, and to +other.call(x)+ where it is not.
|
30
|
+
def or_else(other)
|
31
|
+
PartialFunction.new(lambda { |x| self.defined_at?(x) || other.defined_at?(x) },
|
32
|
+
lambda { |x| if self.defined_at?(x) then self.call(x) else other.call(x) end })
|
33
|
+
end
|
34
|
+
|
35
|
+
# Composes this partial function with a transformation function that
|
36
|
+
# gets applied to results of this partial function.
|
37
|
+
# @param [Proc] func the transformation function
|
38
|
+
# @return [PartialFunction] a partial function with the same domain as this partial function, which maps
|
39
|
+
# arguments +x+ to +func.call(self.call(x))+.
|
40
|
+
def and_then(func)
|
41
|
+
PartialFunction.new(@defined_at_proc, lambda { |x| func.call(self.call(x)) })
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Classes representing the components of exception handling.
|
46
|
+
# Each class is independently composable. Some example usages:
|
47
|
+
#
|
48
|
+
# require "rumonade"
|
49
|
+
# require "uri"
|
50
|
+
#
|
51
|
+
# s = "http://"
|
52
|
+
# x1 = catching(URI::InvalidURIError).opt { URI.parse(s) }
|
53
|
+
# x2 = catching(URI::InvalidURIError, NoMethodError).either { URI.parse(s) }
|
54
|
+
#
|
55
|
+
module ErrorHandling
|
56
|
+
|
57
|
+
# Should re-raise exceptions like +Interrupt+ and +NoMemoryError+ by default.
|
58
|
+
# @param [Exception] ex the exception to consider re-raising
|
59
|
+
# @return [Boolean] Returns +true+ if a subclass of +StandardError+, otherwise +false+.
|
60
|
+
def should_reraise?(ex)
|
61
|
+
case ex
|
62
|
+
when StandardError; false
|
63
|
+
else true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/rumonade/option.rb
CHANGED
@@ -40,7 +40,7 @@ module Rumonade # :nodoc:
|
|
40
40
|
class << self
|
41
41
|
# @return [Option] Returns an +Option+ containing the given value
|
42
42
|
def unit(value)
|
43
|
-
Rumonade.
|
43
|
+
Rumonade.Some(value)
|
44
44
|
end
|
45
45
|
|
46
46
|
# @return [Option] Returns the empty +Option+
|
@@ -101,7 +101,7 @@ module Rumonade # :nodoc:
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def to_s
|
104
|
-
"Some(#{value.to_s})"
|
104
|
+
"Some(#{value.nil? ? 'nil' : value.to_s})"
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
data/lib/rumonade/version.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
|
3
|
+
class ErrorHandlingTest < Test::Unit::TestCase
|
4
|
+
include Rumonade
|
5
|
+
include Rumonade::ErrorHandling
|
6
|
+
|
7
|
+
def test_partial_function_constructor_delegates_call_and_defined_at_to_given_procs
|
8
|
+
pf = PartialFunction.new(lambda { |x| x < 1000 }, lambda { |x| x * 10 })
|
9
|
+
assert pf.defined_at?(999)
|
10
|
+
assert !pf.defined_at?(1000)
|
11
|
+
assert_equal 420, pf.call(42)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_partial_function_or_else_returns_a_partial_function_with_union_of_defined_at_predicates
|
15
|
+
pf = PartialFunction.new(lambda { |x| x < 1000 }, lambda { |x| x * 10 })
|
16
|
+
.or_else(PartialFunction.new(lambda { |x| x > 5000 }, lambda { |x| x / 5 }))
|
17
|
+
assert pf.defined_at?(999)
|
18
|
+
assert !pf.defined_at?(1000)
|
19
|
+
assert !pf.defined_at?(5000)
|
20
|
+
assert pf.defined_at?(5001)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_partial_function_or_else_returns_a_partial_function_with_fallback_when_outside_defined_at
|
24
|
+
pf = PartialFunction.new(lambda { |x| x < 1000 }, lambda { |x| x * 10 })
|
25
|
+
.or_else(PartialFunction.new(lambda { |x| x > 5000 }, lambda { |x| x / 5 }))
|
26
|
+
assert_equal 9990, pf.call(999)
|
27
|
+
assert_equal 1001, pf.call(5005)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_partial_function_and_then_returns_a_partial_function_with_func_called_on_result_of_pf_call
|
31
|
+
pf = PartialFunction.new(lambda { |x| x < 1000 }, lambda { |x| x * 10 })
|
32
|
+
.and_then(lambda { |x| x / 5 })
|
33
|
+
assert_equal 1800, pf.call(900)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_should_reraise_returns_true_if_not_subclass_of_standard_error
|
37
|
+
assert should_reraise?(NoMemoryError.new)
|
38
|
+
assert !should_reraise?(ZeroDivisionError.new)
|
39
|
+
end
|
40
|
+
end
|
data/test/option_test.rb
CHANGED
@@ -4,10 +4,10 @@ class OptionTest < Test::Unit::TestCase
|
|
4
4
|
include Rumonade
|
5
5
|
include MonadAxiomTestHelpers
|
6
6
|
|
7
|
-
def
|
8
|
-
assert_same None, Option.unit(nil)
|
7
|
+
def test_when_option_with_nil_returns_none_singleton_except_unit
|
9
8
|
assert_same None, Option(nil)
|
10
9
|
assert_same NoneClass.instance, None
|
10
|
+
assert_not_equal None, Option.unit(nil)
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_when_option_with_value_returns_some
|
@@ -15,6 +15,7 @@ class OptionTest < Test::Unit::TestCase
|
|
15
15
|
assert_equal Some(42), Option(42)
|
16
16
|
assert_equal Some(42), Some.new(42)
|
17
17
|
assert_not_equal None, Some(nil)
|
18
|
+
assert_equal Some(nil), Option.unit(nil)
|
18
19
|
end
|
19
20
|
|
20
21
|
def test_when_option_constructor_raises
|
@@ -85,6 +86,7 @@ class OptionTest < Test::Unit::TestCase
|
|
85
86
|
assert_equal "Some(1)", Some(1).to_s
|
86
87
|
assert_equal "None", None.to_s
|
87
88
|
assert_equal "Some(Some(None))", Some(Some(None)).to_s
|
89
|
+
assert_equal "Some(nil)", Some(nil).to_s
|
88
90
|
end
|
89
91
|
|
90
92
|
def test_each_behaves_correctly
|
@@ -108,4 +110,10 @@ class OptionTest < Test::Unit::TestCase
|
|
108
110
|
assert_equal None, Some(1).select { |n| n < 0 }
|
109
111
|
assert_equal None, None.select { |n| n < 0 }
|
110
112
|
end
|
113
|
+
|
114
|
+
def test_some_map_to_nil_follows_scala_behavior_returning_some_of_nil
|
115
|
+
# scala> Option(1).map { x => null }
|
116
|
+
# res0: Option[Null] = Some(null)
|
117
|
+
assert_equal Some(nil), Option(1).map { |n| nil }
|
118
|
+
end
|
111
119
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: rumonade
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.2.
|
5
|
+
version: 0.2.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Marc Siegel
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-10-
|
13
|
+
date: 2011-10-22 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: test-unit
|
@@ -34,14 +34,15 @@ extra_rdoc_files: []
|
|
34
34
|
|
35
35
|
files:
|
36
36
|
- .gitignore
|
37
|
-
- CHANGELOG.rdoc
|
38
37
|
- Gemfile
|
38
|
+
- HISTORY.md
|
39
39
|
- MIT-LICENSE.txt
|
40
40
|
- README.rdoc
|
41
41
|
- Rakefile
|
42
42
|
- lib/rumonade.rb
|
43
43
|
- lib/rumonade/array.rb
|
44
44
|
- lib/rumonade/either.rb
|
45
|
+
- lib/rumonade/error_handling.rb
|
45
46
|
- lib/rumonade/errors.rb
|
46
47
|
- lib/rumonade/lazy_identity.rb
|
47
48
|
- lib/rumonade/monad.rb
|
@@ -50,6 +51,7 @@ files:
|
|
50
51
|
- rumonade.gemspec
|
51
52
|
- test/array_test.rb
|
52
53
|
- test/either_test.rb
|
54
|
+
- test/error_handling_test.rb
|
53
55
|
- test/lazy_identity_test.rb
|
54
56
|
- test/option_test.rb
|
55
57
|
- test/test_helper.rb
|
@@ -83,6 +85,7 @@ summary: A Scala-inspired Monad library for Ruby
|
|
83
85
|
test_files:
|
84
86
|
- test/array_test.rb
|
85
87
|
- test/either_test.rb
|
88
|
+
- test/error_handling_test.rb
|
86
89
|
- test/lazy_identity_test.rb
|
87
90
|
- test/option_test.rb
|
88
91
|
- test/test_helper.rb
|
data/CHANGELOG.rdoc
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
=== 0.2.0 / 2011-10-18
|
2
|
-
|
3
|
-
* scala-like Either class w/ LeftProjection and RightProjection monads
|
4
|
-
|
5
|
-
=== 0.1.2 / 2011-10-12
|
6
|
-
|
7
|
-
* Progress towards Either
|
8
|
-
* changed documentation to yard from rdoc
|
9
|
-
|
10
|
-
=== 0.1.1 / 2011-09-19
|
11
|
-
|
12
|
-
* Maintenance
|
13
|
-
* added a first stab at documentation for Option
|
14
|
-
* fixed certain errors with #map
|
15
|
-
* added #select
|
16
|
-
|
17
|
-
=== 0.1.0 / 2011-09-17
|
18
|
-
|
19
|
-
* Initial Feature Set
|
20
|
-
* general implementation and testing of monadic laws based on `unit` and `bind`
|
21
|
-
* scala-like Option class w/ Some & None
|
22
|
-
* scala-like extensions to Array
|