Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
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
Ruby

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
lib
spec
.gitignore
.rspec
CHANGELOG.md
Gemfile
Guardfile
LICENSE
README.md
Rakefile
monadic.gemspec

README.md

Monadic

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).

See also http://en.wikipedia.org/wiki/Monad

We have the following monadics:

  • Option (Maybe in Haskell)
  • Either *planned

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. Thus you contain the erroneous behaviour within a monad - an indivisible, impenetrable unit.

Usage

Option

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'.

Option(User.find(123)).name._         # ._ is a shortcut for .value 

# if you prefer the alias Maybe instead of option
Maybe(User.find(123)).name._

# confidently diving into nested hashes
Maybe({})[:a][:b][:c]                   == None
Maybe({})[:a][:b][:c].value('unknown')  == None
Maybe(a: 1)[:a]._                       == 1

Basic usage examples:

# handling nil (None serves as NullObject)
obj = nil
Option(obj).a.b.c            == None

# None stays None
Option(nil)._                == "None"
"#{Option(nil)}"             == "None"
Option(nil)._("unknown")     == "unknown"
Option(nil).empty?           == true
Option(nil).truly?           == false

# Some stays Some, unless you unbox it
Option('FOO').downcase       == Some('foo') 
Option('FOO').downcase.value == "foo"
Option('FOO').downcase._     == "foo"
Option('foo').empty?         == false
Option('foo').truly?         == true

Map, select:

Option(123).map   { |value| User.find(value) } == Option(someUser)    # if user found
Option(0).map     { |value| User.find(value) } == None                # if user not found
Option([1,2]).map { |value| value.to_s }       == Option(["1", "2"])  # for all Enumerables

Option('foo').select { |value| value.start_with?('f') } == Some('foo')
Option('bar').select { |value| value.start_with?('f') } == None

Treat it like an array:

Option(123).to_a         == [123]
Option([123, 456]).to_a  == [123, 456]
Option(nil)              == []

Falsey values (kind-of) examples:

user = Option(User.find(123))
user.name._

user.value('You are not logged in') { |user| "You are logged in as #{user.name}" }.should == 'You are logged in as foo'

if user != nil
  "You are logged in as foo"
else
  "You are not logged in"

user.subscribed?              # always true
user.subscribed?.truly?       # true if subscribed is true
user.subscribed?.value(false) # same as above
user.subscribed?.or(false)    # same as above

Remember! an Option is never false (in Ruby terms), if you want to know if it is false, call #empty? of #truly?

#truly? will return true or false, always.

Slug example

# instead of 
def slug(title)
  if title
    title.strip.downcase.tr_s('^[a-z0-9]', '-')
  end
end

# or 

def slug(title)
  title && title.strip.downcase.tr_s('^[a-z0-9]', '-')
end

# do it with a default
def slug(title)
  Option(title).strip.downcase.tr_s('^[a-z0-9]', '-')._('unknown-title')
end

see also

Installation

Add this line to your application's Gemfile:

gem 'monadic'

And then execute:

$ bundle

Or install it yourself as:

$ gem install monadic

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Added some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request
Something went wrong with that request. Please try again.