Created
December 5, 2011 22:17
-
-
Save avdi/1435647 to your computer and use it in GitHub Desktop.
Experimenting with Object#when in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def maybe_a_sandwich | |
nil | |
end | |
# Methods that might return nil are annoying. We want to write a nice | |
# confident chain: | |
result = nil | |
result = maybe_a_sandwich.add_horseradish.get_into_my_belly! rescue $! | |
result # => #<NoMethodError: undefined method `add_horseradish' for nil:NilClass> | |
# But then we find out it might return nil and have to rewrite it with | |
# special-case handling: | |
class NoSandwich | |
def method_missing(*) | |
self | |
end | |
def to_s | |
"I'm so hungry!" | |
end | |
end | |
sandwich = (maybe_a_sandwich || NoSandwich.new) | |
result = sandwich.add_horseradish.get_into_my_belly! | |
result # => I'm so hungry! | |
# It would be cool if we could just insert the special case into the | |
# chain. | |
class Object | |
def when(matcher) | |
if matcher === self then yield(self) else self end | |
end | |
end | |
result = maybe_a_sandwich.when(nil){NoSandwich.new}.add_horseradish.get_into_my_belly! | |
result # => I'm so hungry! | |
# I can think of lots of other uses for this. Like a special case for | |
# an empty collection: | |
def sandwiches | |
[] | |
end | |
result = begin | |
sandwiches.when(->(c){c.empty?}){raise "Out of sandwiches!"}.each do |s| | |
s.get_into_my_belly! | |
end | |
rescue | |
$! | |
end | |
result # => #<RuntimeError: Out of sandwiches!> | |
# Or a modifying filter: | |
class Sandwich | |
attr_accessor :contents | |
def get_into_my_belly! | |
"yum" | |
end | |
def gross? | |
contents.include?(:liver) | |
end | |
end | |
sandwich = Sandwich.new | |
sandwich.contents = [:roast_beef, :swiss_chees, :liver] | |
result = sandwich.when(->(s){s.gross?}){|s| | |
s.contents.delete(:liver); s | |
}.get_into_my_belly! | |
result # => "yum" | |
# Man for some reason I'm hungry for a sandwich now. | |
# P.S. The number of keywords Ruby will let you also use as method | |
# names is frankly a little shocking. Here's Object#if: | |
class Object | |
def if(predicate) | |
if predicate.to_proc.(self) | |
yield(self) | |
else | |
self | |
end | |
end | |
end | |
result = sandwich.if(:gross?){|s| | |
s.contents.delete(:liver); s | |
}.get_into_my_belly! | |
result # => "yum" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment