Skip to content

Instantly share code, notes, and snippets.

@avdi
Created December 5, 2011 22:17
Show Gist options
  • Select an option

  • Save avdi/1435647 to your computer and use it in GitHub Desktop.

Select an option

Save avdi/1435647 to your computer and use it in GitHub Desktop.

Revisions

  1. Avdi Grimm revised this gist Dec 5, 2011. 1 changed file with 18 additions and 0 deletions.
    18 changes: 18 additions & 0 deletions when.rb
    Original file line number Diff line number Diff line change
    @@ -76,3 +76,21 @@ def gross?
    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"
  2. Avdi Grimm created this gist Dec 5, 2011.
    78 changes: 78 additions & 0 deletions when.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    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.