rumonade 0.2.0 → 0.2.1
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.
- 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
|