Class: Maybe

Inherits:
Object
  • Object
show all
Defined in:
lib/maybe.rb,
lib/maybe/version.rb

Overview

Wrap a value into a Maybe monad (or Optional type). Since monads are chainable and every value is wrapped in one, things don’t blow up with NoMethodError on nils.

Useful for retrieving something in deeply nested structures where nils could be produced along the way.

Examples:

Simple usage.

maybe = Maybe.new('Hello world').upcase.reverse
maybe.class  # => Maybe
maybe.unwrap # => 'Hello world'

maybe = Maybe.new(nil).upcase.reverse
maybe.class  # => Maybe
maybe.unwrap # => nil

maybe = Maybe.new('Hello world').make_nil.upcase.reverse
maybe.class  # => Maybe
maybe.unwrap # => nil

JSON example evaluation to a string.

response = {
  data: {
    item: {
      name: 'Hello!'
    }
  }
}

Maybe.new(good_response)[:data][:item][:name].upcase.unwrap # => 'HELLO!'

JSON example evaluating to nil.

response = {
  data: {}
}

Maybe.new(good_response)[:data][:item][:name].upcase.unwrap # => nil

Constant Summary collapse

VERSION =

Version number, happy now?

'0.0.1'

Instance Method Summary collapse

Constructor Details

#initialize(value) ⇒ Maybe

Wrap a value into a new Maybe.

Parameters:

  • value (Object)

    Anything.



44
45
46
# File 'lib/maybe.rb', line 44

def initialize(value)
  @value = value
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(*args, &block) ⇒ Maybe

Chain another action and return a new value wrapped in a new Maybe or the same Maybe if the old value is a nil. Calls chain under the hood.

Examples:

Maybe.new('Hello world').upcase.reverse

Returns:

See Also:



69
70
71
72
73
# File 'lib/maybe.rb', line 69

def method_missing(*args, &block)
  chain do |value|
    value.public_send(*args, &block)
  end
end

Instance Method Details

#chain {|value| ... } ⇒ Maybe

Chain another action and return a new value wrapped in a new Maybe or the same Maybe if the old value is a nil.

Examples:

value = Maybe.new('Hello world')
        .chain(&:upcase)
        .chain(&:reverse)
        .unwrap # => 'DLROW OLLEH'

value = Maybe.new('Hello world')
        .chain { |_string| nil }
        .chain(&:reverse)
        .unwrap # => nil

Yield Parameters:

  • value (Object)

    Old value goes in the block.

Yield Returns:

  • (Object)

    New value comes out of the block.

Returns:



104
105
106
107
108
109
110
111
112
# File 'lib/maybe.rb', line 104

def chain
  # Don't call the block if we are wrapping a nil (hint: that would cause
  # a +NoMethodError+).
  return self if @value.nil?

  # Call the passed block to get the new value and wrap it again.
  new_value = yield @value
  self.class.new(new_value)
end

#respond_to_missing?(method_name, include_private = false) ⇒ Bool

Also support respond_to?.

Examples:

Maybe.new('Hello world').upcase.reverse.respond_to?(:size) # => true
Maybe.new(nil).upcase.reverse.respond_to?(:size)           # => false

Returns:

  • (Bool)


82
83
84
# File 'lib/maybe.rb', line 82

def respond_to_missing?(method_name, include_private = false)
  super || @value.respond_to?(method_name, include_private)
end

#unwrapObject

Return the wrapped value.

Examples:

Maybe.new('Hello').unwrap        # => 'Hello'
Maybe.new('Hello').upcase.unwrap # => 'HELLO'
Maybe.new(nil).upcase.unwrap     # => nil

Returns:

  • (Object)


56
57
58
# File 'lib/maybe.rb', line 56

def unwrap
  @value
end