camertron-option 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 18e340be2a52ace7173a04dbaab005f4254c1b54
4
+ data.tar.gz: 85d9e7fe331c69594acf0dddca8f86b1a99079e0
5
+ SHA512:
6
+ metadata.gz: 84163abd2e4109372bab89dd9426bcb0aba01ca4a533f1d98aa3279c0952aa1e5660583332adeed20ae2121f3421b9097b818b5449609944cf91c65d906d5b8a
7
+ data.tar.gz: 9dd4ce0407da20c4bff121cc8614deef94bae544b42b6a2110451ebe7533296e606b01bcccb60a151a154fbf84cc4334c83e23351dfe3d8d9513a9cd778bac2e
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem install rake
4
+ - gem install bundler
5
+ rvm:
6
+ - 2.1.0
7
+ - 2.0.0
8
+ - 1.9.3
9
+ - 1.9.2
10
+ - jruby-18mode
11
+ - jruby-19mode
12
+ - 1.8.7
13
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in option.gemspec
4
+ gemspec :path => "ci", :name => "option"
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Rob Ares
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,82 @@
1
+ # Option
2
+
3
+ A Ruby port of Scala's Option monad. Tries to be faithful
4
+ but also pragmatic in RE: to duck typing.
5
+
6
+ Blog post: http://robares.com/2012/09/16/ruby-port-of-scala-option/
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'option'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install option
21
+
22
+ ## Usage
23
+
24
+ Generally, you want to use the Option(A) wrapper method to box
25
+ your value. This will make the right decision as to what your initial
26
+ value should be:
27
+
28
+ ```ruby
29
+ foo = Option("bar")
30
+ ```
31
+
32
+ This will allow you to now manipulate the value in the box via various means:
33
+
34
+ ```ruby
35
+
36
+ # get the value
37
+ foo.get #=> "bar"
38
+
39
+ # return a default if the box is None
40
+ None.get_or_else { "default" } #=> "default"
41
+
42
+ # map the value to another option
43
+ foo.map { |v| v.upcase } #=> Some("BAR")
44
+
45
+ # does the value meet a requirement?
46
+ foo.exists? { |v| v == "bar" } #=> true
47
+
48
+ # return the value or nil depending on the state
49
+ foo.or_nil #=> "bar"
50
+
51
+ # chain values
52
+ foo.map { |v| v * 2 }.map { |v| v.upcase }.get_or_else { "missing" } #=> BARBAR
53
+
54
+ # attempt to extract a value but default if None
55
+ None.fold(-> { "missing" }) { |v| v.upcase } #=> missing
56
+
57
+ # filter values returning an option
58
+ foo.filter { |v| v == "baz" } #=> None
59
+
60
+ # Side-effects
61
+ foo.inside { |v| v.upcase! } #=> Some("BAR")
62
+
63
+ # use in a for loop
64
+ for value in foo
65
+ puts value #=> bar
66
+ end
67
+ ```
68
+
69
+ ## Build Status
70
+ [![Travis CI](https://secure.travis-ci.org/rares/option.png)](http://travis-ci.org/rares/option)
71
+
72
+ ## Code Climate
73
+ [![Code Climate](https://codeclimate.com/github/rares/option.png)](https://codeclimate.com/github/rares/option)
74
+
75
+ ## Contributing
76
+
77
+ 1. Fork it
78
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
79
+ 3. Implement your feature. Patches not considered without accompanying tests.
80
+ 4. Commit your changes (`git commit -am 'Added some feature'`)
81
+ 5. Push to the branch (`git push origin my-new-feature`)
82
+ 6. Create new Pull Request
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:spec) do |test|
7
+ test.libs << "lib" << "spec"
8
+ test.pattern = "spec/**/*_spec.rb"
9
+ end
10
+
11
+ task :default => :spec
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/option/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Rob Ares"]
6
+ gem.email = ["rob.ares@gmail.com"]
7
+ gem.description = %q{Ruby port of Scala's Option Monad}
8
+ gem.summary = %q{Option attempts to be faithful to the useful parts of the scala api. We lose the type safety but still is quite useful when dealing with optional values.}
9
+ gem.homepage = "http://www.github.com/rares/option"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(spec)/})
14
+ gem.name = "camertron-option"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Option::VERSION
17
+
18
+ gem.add_development_dependency "rake", "= 0.9.2.2"
19
+ gem.add_development_dependency "minitest", "= 3.4.0"
20
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../../lib/option/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Rob Ares"]
6
+ gem.email = ["rob.ares@gmail.com"]
7
+ gem.description = %q{Ruby port of Scala's Option Monad}
8
+ gem.summary = %q{Option attempts to be faithful to the useful parts of the scala api. We lose the type safety but still is quite useful when dealing with optional values.}
9
+ gem.homepage = "http://www.github.com/rares/option"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(spec)/})
14
+ gem.name = "option"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Option::VERSION
17
+
18
+ gem.add_development_dependency "rake", "= 0.9.2.2"
19
+ gem.add_development_dependency "minitest", "= 3.4.0"
20
+ end
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MREwDwYDVQQDDAhyb2Iu
3
+ YXJlczEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
4
+ MB4XDTE0MDMwMzE1MzYyMloXDTE1MDMwMzE1MzYyMlowPzERMA8GA1UEAwwIcm9i
5
+ LmFyZXMxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
6
+ bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALbltpKu3b5+nvN46Eew
7
+ IDzCxoi9wbrbt2I9zsuF0i6BxH8It5ce+4dGHpzUTK7Kw6HHesUUL2xePE7aaf4D
8
+ SfYOLqE/qVam/JqnRBHNWvBBZl/qXtcm4TS8HpO1EXaQZ8uubYnaAKF6Drgv3eUq
9
+ Y/GZ9XwyVSeRJDKDtjrWLhwi+SJENbWbUIu2HelNCoscQmza9rGdciVz65IDKWxO
10
+ vA7kzCVhykGxCHb/K2ICslo4+49+zB7owtvrrUWN/tuPX5CkLwNG42B3zGED+dgI
11
+ Jpm12B3DuTrSn2sd1brj+PiqmR9no1fPDKPLZ9IoOZsL+gUSo2zyCciKSAFmIhUz
12
+ agMCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFPRT
13
+ NwXu52FT6zVrl9P0Ils7MbCOMB0GA1UdEQQWMBSBEnJvYi5hcmVzQGdtYWlsLmNv
14
+ bTAdBgNVHRIEFjAUgRJyb2IuYXJlc0BnbWFpbC5jb20wDQYJKoZIhvcNAQEFBQAD
15
+ ggEBAHlZQvEnbCVAM+peq/BDA/l4Kxzo7Pf/k1hm76P5xwThW5DDsnA0TN/tj5zN
16
+ vagQHqC1rjSUyZdpsmirMATm4DpiwyIwxrSxzLHO4kSJNsX3hB9wcSdeiVc7gH1a
17
+ 2rCnN1ZcvSM1lcVkRW3BX4ycyl86MkvHrgP9ytdA1tZgHQM30ZAw6h86si4TOMwJ
18
+ OIuMBzMI/Ssl3C8DJlN6UlkytPjppqD/3qukepuVOm0gbKqAOlGbDEK/FfSzV0Mr
19
+ 77c8ApML1B7wubSs7fykaPuXZX4VL2Gsus1znAJtqiIpuGw3cupy9mApeesCI6kO
20
+ 290ocpWJbAZG98uB33L0VfcVIGs=
21
+ -----END CERTIFICATE-----
@@ -0,0 +1,268 @@
1
+ module OptionHelpers
2
+ class OptionMatcher
3
+ attr_reader :return_value
4
+
5
+ def initialize(option)
6
+ @option = option
7
+ @return_value = nil
8
+ end
9
+
10
+ def case(type)
11
+ if type.is_a?(OptionType)
12
+ if @option.present?
13
+ option_val = @option.get
14
+ if option_val.is_a?(type.type)
15
+ @return_value = yield(option_val)
16
+ end
17
+ end
18
+ elsif type == SomeClass
19
+ if @option.present?
20
+ @return_value = yield(@option.get)
21
+ end
22
+ elsif type.is_a?(NoneClass)
23
+ if !@option.present?
24
+ @return_value = yield(@option)
25
+ end
26
+ else
27
+ raise TypeError, "can't match an Option against a #{type.to_s}"
28
+ end
29
+ end
30
+
31
+ def else
32
+ @return_value = yield(@option)
33
+ end
34
+ end
35
+
36
+ class OptionType
37
+ attr_reader :type
38
+
39
+ def initialize(type)
40
+ @type = type
41
+ end
42
+
43
+ class << self
44
+ def for_class(klass)
45
+ case klass
46
+ when Class
47
+ option_type_cache[klass] ||= OptionType.new(klass)
48
+ else
49
+ raise TypeError, "Must be a Class"
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def option_type_cache
56
+ @option_type_cache ||= {}
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ class OptionClass
63
+
64
+ def or_nil
65
+ end
66
+
67
+ class << self
68
+ def [](type)
69
+ OptionHelpers::OptionType.for_class(type)
70
+ end
71
+ end
72
+
73
+ def ==(that)
74
+ case that
75
+ when OptionClass then or_nil == that.or_nil
76
+ else or_nil == that
77
+ end
78
+ end
79
+
80
+ def match
81
+ matcher = OptionHelpers::OptionMatcher.new(self)
82
+ yield matcher
83
+ matcher.return_value
84
+ end
85
+
86
+ private
87
+
88
+ def assert_option(result)
89
+ case result
90
+ when OptionClass then return result
91
+ else raise TypeError, "Must be an Option"
92
+ end
93
+ end
94
+ end
95
+
96
+ class SomeClass < OptionClass
97
+
98
+ def initialize(value)
99
+ @value = value
100
+ end
101
+
102
+ def to_a
103
+ [get]
104
+ end
105
+
106
+ def get
107
+ @value
108
+ end
109
+
110
+ def get_or_else(&blk)
111
+ get
112
+ end
113
+
114
+ def each(&blk)
115
+ blk.call(get)
116
+
117
+ nil
118
+ end
119
+
120
+ def or_nil
121
+ get
122
+ end
123
+
124
+ def present?
125
+ true
126
+ end
127
+
128
+ def empty?
129
+ false
130
+ end
131
+
132
+ def map(&blk)
133
+ self.class.new(blk.call(get))
134
+ end
135
+
136
+ def flat_map(&blk)
137
+ assert_option(blk.call(get))
138
+ end
139
+
140
+ def fold(if_empty, &blk)
141
+ blk.call(get)
142
+ end
143
+
144
+ def exists?(&blk)
145
+ !! blk.call(get)
146
+ end
147
+
148
+ def include?(value)
149
+ get == value
150
+ end
151
+
152
+ def filter(&blk)
153
+ exists?(&blk) ? self : None
154
+ end
155
+
156
+ def inside(&blk)
157
+ blk.call(get)
158
+ self
159
+ end
160
+
161
+ def or_else(&blk)
162
+ self
163
+ end
164
+
165
+ def flatten
166
+ case get
167
+ when OptionClass then get.flatten
168
+ else self
169
+ end
170
+ end
171
+
172
+ def error(*argv)
173
+ self
174
+ end
175
+ end
176
+
177
+ class NoneClass < OptionClass
178
+ def [](type)
179
+ raise TypeError, "can't specify a type of NoneClass"
180
+ end
181
+
182
+ def dup
183
+ raise TypeError, "can't dup NoneClass"
184
+ end
185
+
186
+ def clone
187
+ raise TypeError, "can't clone NoneClass"
188
+ end
189
+
190
+ def to_a
191
+ []
192
+ end
193
+
194
+ def get
195
+ raise IndexError, "None.get"
196
+ end
197
+
198
+ def get_or_else(&blk)
199
+ blk.call
200
+ end
201
+
202
+ def each(&blk)
203
+ nil
204
+ end
205
+
206
+ def or_nil
207
+ nil
208
+ end
209
+
210
+ def present?
211
+ false
212
+ end
213
+
214
+ def empty?
215
+ true
216
+ end
217
+
218
+ def map(&blk)
219
+ flat_map(&blk)
220
+ end
221
+
222
+ def flat_map(&blk)
223
+ self
224
+ end
225
+
226
+ def fold(if_empty, &blk)
227
+ if_empty.call
228
+ end
229
+
230
+ def exists?(&blk)
231
+ false
232
+ end
233
+
234
+ def include?(value)
235
+ false
236
+ end
237
+
238
+ def filter(&blk)
239
+ self
240
+ end
241
+
242
+ def inside(&blk)
243
+ self
244
+ end
245
+
246
+ def or_else(&blk)
247
+ assert_option(blk.call)
248
+ end
249
+
250
+ def flatten
251
+ self
252
+ end
253
+
254
+ def error(*argv)
255
+ argv.empty? ? raise : raise(*argv)
256
+ end
257
+ end
258
+
259
+ None = NoneClass.new
260
+ Some = SomeClass
261
+
262
+ def Some(value)
263
+ SomeClass.new(value)
264
+ end
265
+
266
+ def Option(value=nil)
267
+ value.nil? ? None : Some(value)
268
+ end
@@ -0,0 +1,3 @@
1
+ module Option
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,33 @@
1
+ require "spec_helper"
2
+
3
+ some = Some("test")
4
+ upcase = lambda { |v| Some(v.upcase) }
5
+ empty = lambda { |v| None }
6
+
7
+ describe "Option" do
8
+
9
+ describe "when applying the 3 monadic axioms" do
10
+
11
+ describe "left identity" do
12
+
13
+ it "obeys (return x) >>= f == f x" do
14
+ some.flat_map(&upcase).must_equal(upcase.call(some.get))
15
+ end
16
+ end
17
+
18
+ describe "right identity" do
19
+
20
+ it "obeys m >>= return == m" do
21
+ some.flat_map { |v| Some(v) }.must_equal(some)
22
+ end
23
+ end
24
+
25
+ describe "associative composition" do
26
+
27
+ it "obeys (m >>= f) >>= g == m >>= (\\x -> f x >>= g)" do
28
+ some.flat_map(&upcase).flat_map(&empty).
29
+ must_equal(upcase.call(some.get).flat_map(&empty))
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,283 @@
1
+ require "spec_helper"
2
+
3
+ describe NoneClass do
4
+
5
+ it "#dup results in a TypeError" do
6
+ lambda { None.dup }.must_raise TypeError
7
+ end
8
+
9
+ it "#clone results in a TypeError" do
10
+ lambda { None.clone }.must_raise TypeError
11
+ end
12
+
13
+ it "#to_a returns an empty array" do
14
+ None.to_a.must_equal([])
15
+ end
16
+
17
+ it "#get raises IndexError" do
18
+ lambda { None.get }.must_raise IndexError
19
+ end
20
+
21
+ it "#get_or_else executes the block" do
22
+ None.get_or_else { "Some" }.must_equal "Some"
23
+ end
24
+
25
+ it "#each does not execute the block" do
26
+ expected = nil
27
+ None.each { |v| expected = v }
28
+
29
+ expected.must_be_nil
30
+ end
31
+
32
+ it "#or_nil should return nil" do
33
+ None.or_nil.must_be_nil
34
+ end
35
+
36
+ it "#present? should be false" do
37
+ None.present?.must_equal(false)
38
+ end
39
+
40
+ it "#empty? should be true" do
41
+ None.empty?.must_equal(true)
42
+ end
43
+
44
+ it "#map should return itself" do
45
+ None.map {}.must_be_none
46
+ end
47
+
48
+ it "#flat_map should return itself" do
49
+ None.flat_map {}.must_be_none
50
+ end
51
+
52
+ it "#exists? should return false" do
53
+ None.exists? {}.must_equal(false)
54
+ end
55
+
56
+ it "#include? should return false" do
57
+ None.include?(value).must_equal(false)
58
+ end
59
+
60
+ it "#fold should invoke the default proc" do
61
+ None.fold(proc { value }) { |v| v.to_f }.must_equal(value)
62
+ end
63
+
64
+ it "#filter with a true predicate returns itself" do
65
+ Option(value).filter { |i| i == 12 }.must_be_some(value)
66
+ end
67
+
68
+ it "#filter with a false predicate returns None" do
69
+ Option(value).filter { |i| i == 1 }.must_be_none
70
+ end
71
+
72
+ it "should be aliased to None" do
73
+ None.must_be_instance_of(NoneClass)
74
+ end
75
+
76
+ it "#inside should return itself without invoking the block" do
77
+ expected = nil
78
+ None.inside { |v| expected = value }
79
+ expected.must_be_nil
80
+ end
81
+
82
+ it "#or_else should invoke the block and return an Option" do
83
+ None.or_else { Some(value) }.must_be_some(value)
84
+ end
85
+
86
+ it "#or_else should raise a TypeError if an Option is not returned" do
87
+ lambda { None.or_else { value } }.must_raise TypeError
88
+ end
89
+
90
+ it "#flatten should return itself" do
91
+ None.flatten.must_be_none
92
+ end
93
+
94
+ it "#error should raise a RuntimeError with the given message" do
95
+ lambda { None.error("error") }.must_raise RuntimeError, "error"
96
+ end
97
+
98
+ it "#error should raise the error passed to it" do
99
+ lambda { None.error(ArgumentError.new("name")) }.must_raise ArgumentError, "name"
100
+ end
101
+
102
+ it "should assemble an Error from the arguments passed in" do
103
+ lambda { None.error(StandardError, "this is a problem") }.must_raise StandardError, "this is a problem"
104
+ end
105
+
106
+ it "cannot be typed" do
107
+ lambda { None[String] }.must_raise TypeError, "can't specify a type of NoneClass"
108
+ end
109
+ end
110
+
111
+ describe SomeClass do
112
+
113
+ it "#to_a returns the value wrapped in an array" do
114
+ Some(value).to_a.must_equal([value])
115
+ end
116
+
117
+ it "#get returns the inner value" do
118
+ Some(value).get.must_equal(value)
119
+ end
120
+
121
+ it "#get_or_else does not execute the block;" do
122
+ expected = nil
123
+ Some(value).get_or_else { expected = true }
124
+
125
+ expected.must_be_nil
126
+ end
127
+
128
+ it "#get_or_else returns the value" do
129
+ Some(value).get_or_else { }.must_equal(value)
130
+ end
131
+
132
+ it "#each executes the block passing the inner value" do
133
+ expected = nil
134
+ Some(value).each { |v| expected = v }
135
+
136
+ expected.must_equal(value)
137
+ end
138
+
139
+ it "#or_nil should return the inner value" do
140
+ Some(value).or_nil.must_equal(value)
141
+ end
142
+
143
+ it "#present? should be true" do
144
+ Some(value).present?.must_equal(true)
145
+ end
146
+
147
+ it "#empty? should be false" do
148
+ Some(value).empty?.must_equal(false)
149
+ end
150
+
151
+ it "#map should return the result of the proc over the value in a Some" do
152
+ Some(value).map { |v| v * 2 }.must_be_some(24)
153
+ end
154
+
155
+ it "#flat_map should raise TypeError if the returned value is not an Option" do
156
+ lambda { Some(value).flat_map { |v| v * 2 } }.must_raise TypeError
157
+ end
158
+
159
+ it "#flat_map should return an Option value from the block" do
160
+ Some(value).flat_map { |v| Option(v * 2) }.must_be_some(24)
161
+ end
162
+
163
+ it "#flat_map can return None from the block" do
164
+ Some(value).flat_map { |_| None }.must_be_none
165
+ end
166
+
167
+ it "#exists? should return true when the block evaluates true" do
168
+ Some(value).exists? { |v| v % 2 == 0 }.must_equal(true)
169
+ end
170
+
171
+ it "#exists? should return false when the block evaluates false" do
172
+ Some(value).exists? { |v| v % 2 != 0 }.must_equal(false)
173
+ end
174
+
175
+ it "#include? should return true when the passed value and the boxed value are the same" do
176
+ Some(value).include?(value).must_equal(true)
177
+ end
178
+
179
+ it "#include? should return false when the passed value and the boxed value are not the same" do
180
+ Some(value).include?(value + 1).must_equal(false)
181
+ end
182
+
183
+ it "#fold should map the proc over the value and return it" do
184
+ Some(value).fold(proc { value * 2 }) { |v| v * 3 }.must_equal(36)
185
+ end
186
+
187
+ it "#filter should return itself" do
188
+ None.filter { |i| i == 0 }.must_be_none
189
+ end
190
+
191
+ it "#inside should invoke the proc and return itself" do
192
+ expected = nil
193
+ Some(value).inside { |v| expected = v }.must_be_some(value)
194
+ expected.must_equal(value)
195
+ end
196
+
197
+ it "#or_else should return itself" do
198
+ Some(value).or_else { None }.must_be_some(value)
199
+ end
200
+
201
+ it "should wrap the creation of a Some" do
202
+ Some(value).must_be_instance_of(SomeClass)
203
+ end
204
+
205
+ it "should be aliased to Some" do
206
+ Some.new(value).must_be_some(value)
207
+ end
208
+
209
+ it "#flatten" do
210
+ inner_value = Some(Some(Some(value))).flatten
211
+ inner_value.must_be_some(value)
212
+ inner_value.or_nil.must_equal(value)
213
+ end
214
+
215
+ it "#error should return the Some" do
216
+ value = !!(Some(value).error("error") rescue false)
217
+ value.must_equal true
218
+ end
219
+
220
+ it "can be typed" do
221
+ opt = Some[String]
222
+ opt.class.must_equal OptionHelpers::OptionType
223
+ opt.type.must_equal String
224
+ end
225
+
226
+ it "must be typed using a class, not a value" do
227
+ lambda { Some[5] }.must_raise TypeError, "Must be a Class"
228
+ end
229
+ end
230
+
231
+ describe OptionClass do
232
+
233
+ it "must return a some if the passed value is not nil" do
234
+ Option(value).must_be_some(value)
235
+ end
236
+
237
+ it "must return a None if the passed value is nil" do
238
+ Option(nil).must_be_none
239
+ end
240
+
241
+ it "should do equality checks against the boxed value" do
242
+ Option(value).must_equal(value)
243
+ end
244
+
245
+ it "allows matching typed options" do
246
+ Option(5).match do |matcher|
247
+ matcher.case(Some[Fixnum]) { |val| val * 2 }
248
+ matcher.case(Some[String]) { |val| "bar" }
249
+ end.must_equal 10
250
+
251
+ Option("foo").match do |matcher|
252
+ matcher.case(Some[Fixnum]) { |val| val * 2 }
253
+ matcher.case(Some[String]) { |val| "bar" }
254
+ end.must_equal "bar"
255
+ end
256
+
257
+ it "allows matching untyped options" do
258
+ Option(5).match do |matcher|
259
+ matcher.case(Some) { |val| val * 2 }
260
+ end.must_equal 10
261
+ end
262
+
263
+ it "allows matching on none" do
264
+ None.match do |matcher|
265
+ matcher.case(None) { "none" }
266
+ end.must_equal "none"
267
+ end
268
+
269
+ it "allows matching with an else block" do
270
+ Option(5).match do |matcher|
271
+ matcher.case(Some[String]) { |val| "bar" }
272
+ matcher.else { |val| val.get * 2}
273
+ end.must_equal 10
274
+ end
275
+
276
+ it "does not allow matching against non-option types" do
277
+ lambda do
278
+ Option(5).match do |matcher|
279
+ matcher.case(Fixnum) { 1 }
280
+ end
281
+ end.must_raise TypeError, "can't match an Option against a Fixnum"
282
+ end
283
+ end
@@ -0,0 +1,23 @@
1
+ require "minitest/autorun"
2
+ require "minitest/pride"
3
+ require "minitest/spec"
4
+
5
+ require "option"
6
+
7
+ module MiniTest::Assertions
8
+
9
+ def assert_some(value, option, msg = nil)
10
+ assert (option.is_a?(Some) && option.or_nil == value), "Expected Some(#{value})"
11
+ end
12
+
13
+ def assert_none(value, option, msg = nil)
14
+ assert option == None, "Expected None"
15
+ end
16
+ end
17
+
18
+ OptionClass.infect_an_assertion :assert_some, :must_be_some
19
+ OptionClass.infect_an_assertion :assert_none, :must_be_none
20
+
21
+ def value
22
+ 12
23
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: camertron-option
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Rob Ares
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.2.2
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.2.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 3.4.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 3.4.0
41
+ description: Ruby port of Scala's Option Monad
42
+ email:
43
+ - rob.ares@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - .travis.yml
50
+ - Gemfile
51
+ - LICENSE
52
+ - README.md
53
+ - Rakefile
54
+ - camertron-option.gemspec
55
+ - ci/option.gemspec
56
+ - gem-public_cert.pem
57
+ - lib/option.rb
58
+ - lib/option/version.rb
59
+ - spec/acceptance_spec.rb
60
+ - spec/option_spec.rb
61
+ - spec/spec_helper.rb
62
+ homepage: http://www.github.com/rares/option
63
+ licenses: []
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.1.11
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Option attempts to be faithful to the useful parts of the scala api. We lose
85
+ the type safety but still is quite useful when dealing with optional values.
86
+ test_files:
87
+ - spec/acceptance_spec.rb
88
+ - spec/option_spec.rb
89
+ - spec/spec_helper.rb