camertron-option 1.0.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.
@@ -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