optional 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ /.bundle
2
+ /log/*.log
3
+ /tmp
4
+ /tags
5
+ /coverage
6
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ optional (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.1)
10
+ multi_json (1.7.2)
11
+ rspec (2.13.0)
12
+ rspec-core (~> 2.13.0)
13
+ rspec-expectations (~> 2.13.0)
14
+ rspec-mocks (~> 2.13.0)
15
+ rspec-core (2.13.1)
16
+ rspec-expectations (2.13.0)
17
+ diff-lcs (>= 1.1.3, < 2.0)
18
+ rspec-mocks (2.13.0)
19
+ simplecov (0.7.1)
20
+ multi_json (~> 1.0)
21
+ simplecov-html (~> 0.7.1)
22
+ simplecov-html (0.7.1)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ optional!
29
+ rspec
30
+ simplecov
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 On The Beach Ltd
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.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # id
2
+ ### simple models based on hashes
3
+
4
+ JSON is a great way to transfer data between systems, and it's easy to parse into a Ruby hash. But sometimes it's nice to have actual methods to call when you want to get attributes from your data, rather than coupling your entire codebase to the hash representation by littering it with calls to `fetch` or `[]`. The same goes for BSON documents stored in Mongo.
5
+
6
+ That's where `id` (as in Freud) comes in. You define your model classes using syntax that should look pretty familiar if you've used any popular Ruby ORMs - but `id` is not an ORM. Model objects defined with `id` have a constructor that accepts a hash, and you define the values of this hash that are made readable as fields - but that hash can come from any source.
7
+
8
+ #### Defining a model
9
+
10
+ Defining a model looks like this:
11
+
12
+ class MyModel
13
+ include Id::Model
14
+
15
+ field :foo
16
+ field :bar, default: 42
17
+ field :baz, key: 'barry'
18
+
19
+ end
20
+
21
+ my_model = MyModel.new(foo: 7, barry: 'hello')
22
+ my_model.foo # => 7
23
+ my_model.bar # => 42
24
+ my_model.baz # => 'hello'
25
+
26
+ As you can see, you can specify default values as well as key aliases.
27
+
28
+ #### Associations
29
+
30
+ You can also specify has_one or has_many "associations" - what would be nested subdocuments in MongoDB for example - like this:
31
+
32
+ class Zoo
33
+ include Id::Model
34
+
35
+ has_many :lions
36
+ has_many :zebras
37
+ has_one :zookeeper, type: Person
38
+ end
39
+
40
+ zoo = Zoo.new(lions: [{name: 'Hetty'}],
41
+ zebras: [{name: 'Lisa'}],
42
+ zookeeper: {name: 'Russell' d})
43
+
44
+ zoo.lions.first.class # => Lion
45
+ zoo.lions.first.name # => "Hetty"
46
+ zoo.zookeeper.class # => Person
47
+ zoo.zookeeper.name # => "Russell"
48
+
49
+ Types are inferred from the association name unless one is specified.
50
+
51
+ #### Designed for immutability
52
+
53
+ `id` models provide accessor methods, but no mutator methods, because they are designed for immutability. How do immutable models work? When you need to change some field of a model object, a new copy of the object is created with the field changed as required. This is handled for you by `id`'s `set` method:
54
+
55
+ person = Person.new(name: 'Russell', job: 'programmer')
56
+ person.set(name: 'Radek') # => returns a new Person whose name is Radek and whose job is 'programmer'
57
+
58
+ You can even set fields on nested models in this way:
59
+
60
+ person.hat.set(color: 'red') # => returns a new person object with a new hat object with its color set to red
@@ -0,0 +1,4 @@
1
+ require_relative 'option/all'
2
+ require_relative 'option'
3
+ require_relative 'some'
4
+ require_relative 'none'
@@ -0,0 +1,35 @@
1
+ module None
2
+ include Option
3
+ extend self
4
+
5
+ def each
6
+ end
7
+
8
+ def none?
9
+ true
10
+ end
11
+
12
+ def some?(type=nil)
13
+ false
14
+ end
15
+
16
+ def value
17
+ raise Option::ValueOfNoneError
18
+ end
19
+
20
+ def value_or
21
+ yield
22
+ end
23
+
24
+ def & other
25
+ self
26
+ end
27
+
28
+ def | other
29
+ other
30
+ end
31
+
32
+ def to_s
33
+ "None"
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'enumerable'
2
+ require_relative 'errors'
3
+ require_relative 'match'
@@ -0,0 +1,38 @@
1
+ module Option
2
+ module Enumerable
3
+ include ::Enumerable
4
+
5
+ def map
6
+ from_array super
7
+ end
8
+ alias_method :collect, :map
9
+ alias_method :flat_map, :map
10
+ alias_method :collect_concat, :map
11
+
12
+ def detect
13
+ from_value super
14
+ end
15
+ alias_method :find, :detect
16
+
17
+ def select
18
+ from_array super
19
+ end
20
+ alias_method :find_all, :select
21
+
22
+ def grep(value)
23
+ from_array super
24
+ end
25
+
26
+ def reject
27
+ from_array super
28
+ end
29
+
30
+ def reduce(*args, &block)
31
+ if none? && (args.size < 1 || args.size < 2 && block.nil?)
32
+ raise ValueOfNoneError
33
+ end
34
+ super
35
+ end
36
+ alias_method :inject, :reduce
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ module Option
2
+ class ValueOfNoneError < StandardError
3
+ end
4
+ class BadMatchError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,84 @@
1
+ module Option
2
+ class Match
3
+
4
+ def some(guard=always, &block)
5
+ some_clauses << SomeClause.new(guard, block)
6
+ end
7
+
8
+ def none(&block)
9
+ self.none_clause = NoneClause.new(block)
10
+ end
11
+
12
+ def evaluate(option)
13
+ case option
14
+ when Some
15
+ matched(option).evaluate(option.value)
16
+ when None
17
+ none_clause.evaluate
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def some_clauses
24
+ @some_clauses ||= []
25
+ end
26
+
27
+ def none_clause
28
+ @none_clause ||= NoneClause.new(lambda {})
29
+ end
30
+
31
+ def matched(option)
32
+ some_clauses.find { |clause| clause.matches? option.value }.tap do |match|
33
+ raise Option::BadMatchError if match.nil?
34
+ end
35
+ end
36
+
37
+ def always
38
+ lambda { |x| true }
39
+ end
40
+
41
+ attr_writer :none_clause
42
+
43
+ class SomeClause
44
+
45
+ def initialize(guard=always, block)
46
+ @guard = guard
47
+ @block = block
48
+ end
49
+
50
+ def matches?(value)
51
+ guard.call(value)
52
+ end
53
+
54
+ def evaluate(value)
55
+ block.call(value)
56
+ end
57
+
58
+ private
59
+
60
+ attr_reader :block
61
+
62
+ def guard
63
+ @guard.is_a?(Proc) ? @guard : ->(x) { x == @guard }
64
+ end
65
+
66
+ end
67
+
68
+ class NoneClause
69
+
70
+ def initialize(block)
71
+ @block = block
72
+ end
73
+
74
+ def evaluate
75
+ block.call
76
+ end
77
+
78
+ private
79
+
80
+ attr_reader :block
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,24 @@
1
+ module Option
2
+ include Option::Enumerable
3
+
4
+ def match(&block)
5
+ Match.new.tap { |m| block.call(m) }.evaluate(self)
6
+ end
7
+
8
+ protected
9
+
10
+ def and_option(option)
11
+ none? ? self : Some[*[option.value, value].flatten]
12
+ end
13
+
14
+ private
15
+
16
+ def from_array(values)
17
+ values.empty? ? None : Some[values.first]
18
+ end
19
+
20
+ def from_value(value)
21
+ value.nil? ? None : Some[value]
22
+ end
23
+
24
+ end
@@ -0,0 +1,46 @@
1
+ class Some
2
+ include Option
3
+
4
+ attr_reader :value
5
+
6
+ def initialize(value)
7
+ @value = value
8
+ end
9
+
10
+ def each
11
+ yield value
12
+ end
13
+
14
+ def none?(&block)
15
+ block.nil? ? false : super
16
+ end
17
+
18
+ def value_or
19
+ value
20
+ end
21
+
22
+ def some?(type=value.class)
23
+ value.class == type
24
+ end
25
+
26
+ def & other
27
+ other.and_option(self)
28
+ end
29
+
30
+ def == other
31
+ other.some? && value == other.value
32
+ end
33
+
34
+ def | other
35
+ self
36
+ end
37
+
38
+ def to_s
39
+ "Some[#{value}]"
40
+ end
41
+
42
+ def self.[](*values)
43
+ new(values.size == 1 ? values.first : values)
44
+ end
45
+
46
+ end
data/lib/optional.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative 'optional/all'
data/optional.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'optional'
3
+ s.version = '0.0.1'
4
+ s.date = '2013-04-19'
5
+ s.summary = "Optional values with pattern matching"
6
+ s.description = "Make nils go bye bye with Options!"
7
+ s.authors = ["Russell Dunphy", "Radek Molenda"]
8
+ s.email = ['russell@russelldunphy.com', 'radek.molenda@gmail.com']
9
+ s.files = `git ls-files`.split($\)
10
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
11
+ s.require_paths = ["lib"]
12
+ s.homepage = 'http://github.com/onthebeach/optional'
13
+
14
+ s.add_development_dependency "rspec"
15
+ s.add_development_dependency "simplecov"
16
+
17
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe None do
4
+
5
+ let (:cat) { Cat.new("MOGGIE!") }
6
+
7
+ subject { None }
8
+
9
+ it { should be_none }
10
+ it { should_not be_some }
11
+ it { should_not be_some Cat }
12
+
13
+ it { should eq None }
14
+ it { should_not eq Some[cat] }
15
+
16
+ it "does not have a value" do
17
+ expect { subject.value }.to raise_error Option::ValueOfNoneError
18
+ end
19
+
20
+ it "does, however, allow you to supply a default in place of a value" do
21
+ subject.value_or { cat }.should eq cat
22
+ end
23
+
24
+ it "can be anded with another none, yielding none" do
25
+ (None & None).should be_none
26
+ end
27
+
28
+ it "can be anded with a some, yielding none" do
29
+ (None & Some[cat]).should be_none
30
+ end
31
+
32
+ it "can be ored with another none, yielding none" do
33
+ (None | None).should be_none
34
+ end
35
+
36
+ it "can be ored with a some, yielding the some" do
37
+ (None | Some[cat]).should eq Some[cat]
38
+ end
39
+
40
+ it "prints as None" do
41
+ None.to_s.should eq "None"
42
+ end
43
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ describe Option::Enumerable do
4
+
5
+ let (:cat) { Cat.new("MOGGIE!") }
6
+
7
+ describe "#map" do
8
+
9
+ it "maps a some to a some" do
10
+ Some[cat].map(&:name).should eq Some["MOGGIE!"]
11
+ end
12
+
13
+ it "also works for collect" do
14
+ Some[cat].collect(&:name).should eq Some["MOGGIE!"]
15
+ end
16
+
17
+ it "also works for flat_map" do
18
+ Some[cat].flat_map(&:name).should eq Some["MOGGIE!"]
19
+ end
20
+
21
+ it "maps none to none" do
22
+ None.map(&:name).should be_none
23
+ end
24
+
25
+ it "also works for collect" do
26
+ None.collect(&:name).should be_none
27
+ end
28
+
29
+ it "also works for flat_map" do
30
+ None.flat_map(&:name).should be_none
31
+ end
32
+
33
+ it "also works for collect_concat" do
34
+ None.collect_concat(&:name).should be_none
35
+ end
36
+ end
37
+
38
+ describe "#detect" do
39
+ it "returns none if called on a none" do
40
+ None.detect{ |pet| pet.name == "MOGGIE!" }.should be_none
41
+ end
42
+
43
+ it "returns a some if called on a some that matches" do
44
+ Some[cat].detect{ |pet| pet.name == "MOGGIE!" }.should eq Some[cat]
45
+ end
46
+
47
+ it "returns a none if called on a some that doesn't match" do
48
+ Some[cat].detect{ |pet| pet.name == "DOGGIE!" }.should be_none
49
+ end
50
+
51
+ it "also works for find" do
52
+ None.find{ |pet| pet.name == "MOGGIE!" }.should be_none
53
+ Some[cat].find{ |pet| pet.name == "MOGGIE!" }.should eq Some[cat]
54
+ Some[cat].find{ |pet| pet.name == "DOGGIE!" }.should be_none
55
+ end
56
+
57
+ end
58
+
59
+ describe "#select" do
60
+
61
+ it "returns a none if called on a none" do
62
+ None.select(&:even?).should be_none
63
+ end
64
+
65
+ it "returns a none if called on a some that doesn't match" do
66
+ Some[3].select(&:even?).should be_none
67
+ end
68
+
69
+ it "returns a some if called on a some that matches" do
70
+ Some[3].select(&:odd?).should eq Some[3]
71
+ end
72
+
73
+ it "also works for find_all" do
74
+ None.find_all(&:even?).should be_none
75
+ Some[3].find_all(&:even?).should be_none
76
+ Some[3].find_all(&:odd?).should eq Some[3]
77
+ end
78
+
79
+ end
80
+
81
+ describe "#grep" do
82
+ it "returns a none if no matching elements" do
83
+ None.grep(/ing/).should be_none
84
+ end
85
+
86
+ it "returns a some otherwise" do
87
+ Some["finger"].grep(/ing/).should eq Some["finger"]
88
+ end
89
+ end
90
+
91
+ describe "#reduce" do
92
+ it "raises an error if it is called on None" do
93
+ expect { None.reduce(:+) }.to raise_error Option::ValueOfNoneError
94
+ expect { None.reduce{ |acc, x| acc + x } }.to raise_error Option::ValueOfNoneError
95
+ end
96
+
97
+ it "works with a none as expected otherwise" do
98
+ None.reduce(5, :+).should eq 5
99
+ None.reduce(5) { |acc, x| acc + x }.should eq 5
100
+ end
101
+
102
+ it "works with a some as expected" do
103
+ Some[4].reduce(:+).should eq 4
104
+ Some[4].reduce(5, :+).should eq 9
105
+ Some[4].reduce(5) { |acc, x| acc + x }.should eq 9
106
+ end
107
+
108
+ it "also works as inject" do
109
+ Some[4].inject(:+).should eq 4
110
+ end
111
+ end
112
+
113
+ describe "#none?" do
114
+ it "returns true if given a block the block evaluates to false for the value" do
115
+ Some[6].none?(&:odd?).should be_true
116
+ end
117
+ end
118
+
119
+ describe "#reject" do
120
+ it "returns either some or none" do
121
+ None.reject(&:odd?).should be_none
122
+ Some[3].reject(&:odd?).should be_none
123
+ Some[4].reject(&:odd?).should eq Some[4]
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Option do
4
+
5
+ let (:cat) { Cat.new("MOGGIE!") }
6
+ let (:dog) { Dog.new("DOGGIE!") }
7
+
8
+ context "== pattern matching ==" do
9
+
10
+ subject do
11
+ option.match do |m|
12
+ m.some { |cat| cat.name }
13
+ m.none { "no cat :-(" }
14
+ end
15
+ end
16
+
17
+ context "a match can have one None branch, which matches only None" do
18
+ let (:option) { None }
19
+ it { should eq "no cat :-(" }
20
+ end
21
+
22
+ context "a match can have a catchall Some branch which matches any Some" do
23
+ let (:option) { Some[cat] }
24
+ it { should eq "MOGGIE!" }
25
+ end
26
+
27
+ it "can have multiple Some branches, each one matching a value" do
28
+ Some[cat].match do |m|
29
+ m.some(dog) { :canteloupe }
30
+ m.some(cat) { :fishsticks }
31
+ end.should eq :fishsticks
32
+ end
33
+
34
+ it "can take a lambda as a guard to match against" do
35
+ Some[cat].match do |m|
36
+ m.some ->(pet) { pet.name == "DOGGIE!" } { :fishsticks }
37
+ m.some ->(pet) { pet.name == "MOGGIE!" } { :canteloupe }
38
+ end.should eq :canteloupe
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe Some do
4
+
5
+ let (:cat) { Cat.new("MOGGIE!") }
6
+ let (:dog) { Dog.new("DOGGIE!") }
7
+
8
+ subject { Some[cat] }
9
+
10
+ it { should be_some }
11
+ it { should be_some Cat }
12
+ it { should_not be_some Dog }
13
+
14
+ it { should_not be_none }
15
+
16
+ it { should eq Some[cat] }
17
+ it { should_not eq Some[dog] }
18
+ it { should_not eq None }
19
+
20
+ it "has a value" do
21
+ Some[cat].value.should eq cat
22
+ end
23
+
24
+ it "can be passed a default value but won't use it" do
25
+ Some[cat].value_or { dog }.should eq cat
26
+ end
27
+
28
+ it "can be anded with another some" do
29
+ (Some[cat] & Some[dog]).should eq Some[cat, dog]
30
+ end
31
+
32
+ it "can be anded with a none, resulting in none" do
33
+ (Some[cat] & None).should eq None
34
+ end
35
+
36
+ it "can be ored with another some" do
37
+ (Some[cat] | Some[dog]).should eq Some[cat]
38
+ end
39
+
40
+ it "can be ored with a none resulting in itself" do
41
+ (Some[cat] | None).should eq Some[cat]
42
+ end
43
+
44
+ it "prints as Some[value]" do
45
+ Some[4].to_s.should eq "Some[4]"
46
+ end
47
+ end
@@ -0,0 +1,14 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter '/spec'
4
+ end
5
+
6
+ require 'rspec'
7
+ require_relative 'support/cat'
8
+ require_relative '../lib/optional'
9
+
10
+
11
+ RSpec.configure do |config|
12
+ config.order = :rand
13
+ config.color_enabled = true
14
+ end
@@ -0,0 +1,21 @@
1
+ class Cat
2
+ attr_reader :name
3
+ def initialize(name)
4
+ @name = name
5
+ end
6
+
7
+ def == other
8
+ other.is_a?(Cat) && other.name == name
9
+ end
10
+ end
11
+
12
+ class Dog
13
+ attr_reader :name
14
+ def initialize(name)
15
+ @name = name
16
+ end
17
+
18
+ def == other
19
+ other.is_a?(Dog) && other.name == name
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: optional
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Russell Dunphy
9
+ - Radek Molenda
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-04-19 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: simplecov
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ description: Make nils go bye bye with Options!
48
+ email:
49
+ - russell@russelldunphy.com
50
+ - radek.molenda@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - Gemfile
57
+ - Gemfile.lock
58
+ - LICENSE.md
59
+ - README.md
60
+ - lib/optional.rb
61
+ - lib/optional/all.rb
62
+ - lib/optional/none.rb
63
+ - lib/optional/option.rb
64
+ - lib/optional/option/all.rb
65
+ - lib/optional/option/enumerable.rb
66
+ - lib/optional/option/errors.rb
67
+ - lib/optional/option/match.rb
68
+ - lib/optional/some.rb
69
+ - optional.gemspec
70
+ - spec/lib/optional/none_spec.rb
71
+ - spec/lib/optional/option/enumerable_spec.rb
72
+ - spec/lib/optional/option_spec.rb
73
+ - spec/lib/optional/some_spec.rb
74
+ - spec/spec_helper.rb
75
+ - spec/support/cat.rb
76
+ homepage: http://github.com/onthebeach/optional
77
+ licenses: []
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 1.8.25
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: Optional values with pattern matching
100
+ test_files:
101
+ - spec/lib/optional/none_spec.rb
102
+ - spec/lib/optional/option/enumerable_spec.rb
103
+ - spec/lib/optional/option_spec.rb
104
+ - spec/lib/optional/some_spec.rb
105
+ - spec/spec_helper.rb
106
+ - spec/support/cat.rb
107
+ has_rdoc: