monadic 0.0.2 → 0.0.3

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/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## v0.0.3
4
+
5
+ `Some#map` and `Some#select` accept proc and block, you can now use:
6
+
7
+ Option("FOO").map(&:downcase) # NEW
8
+ Option("FOO").map { |e| e.downcase } # old
9
+ Option("FOO").downcase # old
10
+
11
+ Removed `#none?`, please use `#empty?` instead.
data/README.md CHANGED
@@ -1,32 +1,32 @@
1
1
  # Monadic
2
2
 
3
- is a gem to help dealing with exceptional situations, it comes from the sphere of functional programming and bringing the goodies I have come to love in Scala to my ruby projects (hence I will be using more Scala like constructs than Haskell).
3
+ helps dealing with exceptional situations, it comes from the sphere of functional programming and bringing the goodies I have come to love in Scala to my ruby projects (hence I will be using more Scala like constructs than Haskell).
4
4
 
5
5
  See also http://en.wikipedia.org/wiki/Monad
6
6
 
7
- Planned are the following monadics:
7
+ We have the following monadics:
8
8
 
9
9
  - Option (Maybe in Haskell)
10
10
  - Either *planned
11
11
 
12
- ## Installation
13
-
14
- Add this line to your application's Gemfile:
15
-
16
- gem 'monadic'
12
+ What's the point of using monads in ruby? To me it started with having a safe way to deal with nil objects and other exceptions.
13
+ Thus you contain the erroneous behaviour within a monad - an indivisible, impenetrable unit.
17
14
 
18
- And then execute:
15
+ ## Usage
19
16
 
20
- $ bundle
17
+ ### Option
18
+ Is an optional type, which helps to handle error conditions gracefully. The one thing to remember about option is: 'What goes into the Option, stays in the Option'.
21
19
 
22
- Or install it yourself as:
23
20
 
24
- $ gem install monadic
21
+ Option(User.find(123)).name._ # ._ is a shortcut for .value
25
22
 
26
- ## Usage
23
+ # if you prefer the alias Maybe instead of option
24
+ Maybe(User.find(123)).name._
27
25
 
28
- ### Option
29
- Is an optional type, which helps to handle error conditions gracefully. The one thing to remember about option is: 'What goes into the Option, stays in the Option'.
26
+ # confidently diving into nested hashes
27
+ Maybe({})[:a][:b][:c] == None
28
+ Maybe({})[:a][:b][:c].value('unknown') == None
29
+ Maybe(a: 1)[:a]._ == 1
30
30
 
31
31
  Basic usage examples:
32
32
 
@@ -38,7 +38,6 @@ Basic usage examples:
38
38
  Option(nil)._ == "None"
39
39
  "#{Option(nil)}" == "None"
40
40
  Option(nil)._("unknown") == "unknown"
41
- Option(nil).none? == true
42
41
  Option(nil).empty? == true
43
42
  Option(nil).truly? == false
44
43
 
@@ -46,7 +45,6 @@ Basic usage examples:
46
45
  Option('FOO').downcase == Some('foo')
47
46
  Option('FOO').downcase.value == "foo"
48
47
  Option('FOO').downcase._ == "foo"
49
- Option('foo').none? == false
50
48
  Option('foo').empty? == false
51
49
  Option('foo').truly? == true
52
50
 
@@ -68,6 +66,8 @@ Treat it like an array:
68
66
  Falsey values (kind-of) examples:
69
67
 
70
68
  user = Option(User.find(123))
69
+ user.name._
70
+
71
71
  user.value('You are not logged in') { |user| "You are logged in as #{user.name}" }.should == 'You are logged in as foo'
72
72
 
73
73
  if user != nil
@@ -80,7 +80,7 @@ Falsey values (kind-of) examples:
80
80
  user.subscribed?.value(false) # same as above
81
81
  user.subscribed?.or(false) # same as above
82
82
 
83
- Remember! an Option is never false, if you want to know if it is false, call `#none?` of `#truly?`
83
+ Remember! an Option is never false (in Ruby terms), if you want to know if it is false, call `#empty?` of `#truly?`
84
84
 
85
85
  `#truly?` will return true or false, always.
86
86
 
@@ -105,8 +105,34 @@ Slug example
105
105
  end
106
106
 
107
107
 
108
- see also [Option Type ](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/) and
109
- [NullObject and Falsiness by @avdi](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
108
+ see also
109
+
110
+ * [Monad equivalend in Ruby](http://stackoverflow.com/questions/2709361/monad-equivalent-in-ruby)
111
+ * [Option Type ](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
112
+ * [NullObject and Falsiness by @avdi](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
113
+ * [andand](https://github.com/raganwald/andand/blob/master/README.textile)
114
+ * [ick](http://ick.rubyforge.org/)
115
+ * [Monads in Ruby](http://moonbase.rydia.net/mental/writings/programming/monads-in-ruby/00introduction.html)
116
+ * [The Maybe Monad in Ruby](http://pretheory.wordpress.com/2008/02/14/the-maybe-monad-in-ruby/)
117
+ * [Monads in Ruby with nice syntax](http://www.valuedlessons.com/2008/01/monads-in-ruby-with-nice-syntax.html)
118
+ * [Maybe in Ruby](https://github.com/bhb/maybe)
119
+ * [Monads on the Cheap](http://osteele.com/archives/2007/12/cheap-monads)
120
+ * [Rumonade](https://github.com/ms-ati/rumonade)
121
+ * [Monads for functional programming](http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf)
122
+
123
+ ## Installation
124
+
125
+ Add this line to your application's Gemfile:
126
+
127
+ gem 'monadic'
128
+
129
+ And then execute:
130
+
131
+ $ bundle
132
+
133
+ Or install it yourself as:
134
+
135
+ $ gem install monadic
110
136
 
111
137
  ## Contributing
112
138
 
@@ -1,89 +1,93 @@
1
1
  require 'singleton'
2
+ # Represents optional values. Instances of Option are either an instance of Some or the object None.
3
+ #
2
4
 
5
+ # Helper function which returns Some or None respectively, depending on their value
6
+ # I find this moar simplistic in ruby than the traditional #bind and #unit
3
7
  def Option(value)
4
8
  return None if value.nil? || (value.respond_to?(:empty?) && value.empty?)
5
9
  return Some.new(value)
6
10
  end
7
- alias :Some :Option
11
+ alias :Some :Option
12
+ alias :Maybe :Option
8
13
 
9
- class Some
14
+ # Represents the Option if there is some value available
15
+ class Some
10
16
  def initialize(value)
11
17
  @value = value
12
18
  end
13
19
 
14
- def none?
15
- false
16
- end
17
- alias :empty? :none?
18
-
19
- def truly?
20
- @value == true
21
- end
22
-
23
- def else(default)
24
- return default if none?
25
- return self
26
- end
27
-
28
20
  def to_ary
29
21
  return [@value].flatten if @value.respond_to? :flatten
30
22
  return [@value]
31
23
  end
32
24
  alias :to_a :to_ary
33
25
 
34
- def map(&block)
35
- return Option(@value.map(&block)) if @value.is_a?(Enumerable)
36
- return Option(block.call)
26
+ def empty?
27
+ false
37
28
  end
38
29
 
39
- def select(&block)
40
- return Option(@value.select(&block)) if @value.is_a?(Enumerable)
41
- return None unless block.call(@value)
42
- return self
30
+ def truly?
31
+ @value == true
43
32
  end
44
33
 
45
34
  def value(default=None, &block)
46
- return default if none?
35
+ return default if empty?
47
36
  return block.call(@value) if block_given?
48
37
  return @value
49
- end
38
+ end
50
39
  alias :or :value
51
40
  alias :_ :value
52
41
 
42
+ def map(func = nil, &block)
43
+ return Option(@value.map(&block)) if @value.is_a?(Enumerable)
44
+ return Option((func || block).call(@value))
45
+ end
46
+
53
47
  def method_missing(m, *args)
54
48
  Option(@value.__send__(m, *args))
55
- end
49
+ end
50
+
51
+ def select(func = nil, &block)
52
+ return Option(@value.select(&block)) if @value.is_a?(Enumerable)
53
+ return None unless (func || block).call(@value)
54
+ return self
55
+ end
56
56
 
57
57
  def ==(other)
58
58
  return false unless other.is_a? Some
59
59
  @value == other.instance_variable_get(:@value)
60
- end
60
+ end
61
+
62
+ def to_s
63
+ "Some(#{@value.to_s})"
64
+ end
61
65
  end
62
66
 
67
+ # Represents the Option if there is no value available
63
68
  class None
64
69
  class << self
65
- def method_missing(m, *args)
66
- self
67
- end
68
-
69
- def value(default=self)
70
- default
71
- end
72
- alias :or :value
73
- alias :_ :value
74
-
75
70
  def to_ary
76
71
  []
77
72
  end
78
73
  alias :to_a :to_ary
79
74
 
80
- def none?
75
+ def empty?
81
76
  true
82
77
  end
83
- alias :empty? :none?
78
+
79
+ def method_missing(m, *args)
80
+ self
81
+ end
84
82
 
85
83
  def truly?
86
84
  false
87
85
  end
86
+
87
+ def value(default=self)
88
+ default
89
+ end
90
+ alias :or :value
91
+ alias :_ :value
88
92
  end
89
93
  end
@@ -1,3 +1,3 @@
1
1
  module Monadic
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/monadic.gemspec CHANGED
@@ -20,4 +20,5 @@ Gem::Specification.new do |gem|
20
20
  gem.add_development_dependency 'guard-rspec'
21
21
  gem.add_development_dependency 'guard-bundler'
22
22
  gem.add_development_dependency 'growl'
23
+ gem.add_development_dependency 'activesupport'
23
24
  end
data/spec/option_spec.rb CHANGED
@@ -7,22 +7,29 @@ describe 'Option' do
7
7
 
8
8
  it 'None stays None' do
9
9
  Option(nil)._.should == None
10
- Option(nil).none?.should be_true
11
10
  Option(nil).empty?.should be_true
12
11
  end
13
12
 
14
13
  it 'Some stays Some' do
15
14
  Option('foo').should be_kind_of(Some)
16
- Option('foo').none?.should be_false
17
15
  Option('foo').empty?.should be_false
18
16
  end
19
17
 
20
- it 'None to_s is "None"' do
18
+ it 'Some#to_s is "Some(value)"' do
19
+ Some(123).to_s.should == "Some(123)"
20
+ end
21
+
22
+ it 'None#to_s is "None"' do
21
23
  option = Option(nil)
22
24
  "#{option}".should == "None"
23
25
  "#{option._}".should == "None"
24
26
  end
25
27
 
28
+ it 'None is always empty' do
29
+ None.empty?.should be_true
30
+ Maybe(nil).empty?.should be_true
31
+ end
32
+
26
33
  it '[] as value always returns None()' do
27
34
  Option([]).a.should == None
28
35
  end
@@ -56,11 +63,6 @@ describe 'Option' do
56
63
  Option(nil).should_not be_false
57
64
  end
58
65
 
59
- it 'tells you if it is none when calling #none?' do
60
- Option('foo').none?.should be_false
61
- Option(nil).none?.should be_true
62
- end
63
-
64
66
  class User
65
67
  attr_reader :name
66
68
  def initialize(name)
@@ -105,4 +107,25 @@ describe 'Option' do
105
107
  Option(nil).to_a.should == []
106
108
  end
107
109
 
110
+ it 'diving into hashes' do
111
+ Maybe({})['a']['b']['c'].should == None
112
+ Maybe({a: 1})[:a]._.should == 1
113
+ end
114
+
115
+ it 'should support Rumonades example' do
116
+ require 'active_support/time'
117
+ def format_date_in_march(time_or_date_or_nil)
118
+ Option(time_or_date_or_nil). # wraps possibly-nil value in an Option monad (Some or None)
119
+ map(&:to_date). # transforms a contained Time value into a Date value
120
+ select {|d| d.month == 3}. # filters out non-matching Date values (Some becomes None)
121
+ map(&:to_s). # transforms a contained Date value into a String value
122
+ map {|s| s.gsub('-', '')}. # transforms a contained String value by removing '-'
123
+ value("not in march!") # returns the contained value, or the alternative if None
124
+ end
125
+
126
+ format_date_in_march(nil).should == "not in march!"
127
+ format_date_in_march(Time.parse('2009-01-01 01:02')).should == "not in march!"
128
+ format_date_in_march(Time.parse('2011-03-21 12:34')).should == "20110321"
129
+ end
130
+
108
131
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monadic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-29 00:00:00.000000000 Z
12
+ date: 2012-04-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -91,6 +91,22 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: activesupport
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
94
110
  description: brings some functional goodness to ruby
95
111
  email:
96
112
  - pz@anixe.pl
@@ -100,6 +116,7 @@ extra_rdoc_files: []
100
116
  files:
101
117
  - .gitignore
102
118
  - .rspec
119
+ - CHANGELOG.md
103
120
  - Gemfile
104
121
  - Guardfile
105
122
  - LICENSE