multi_case Build Status Dependency Status Code Climate

An improved case operator for ruby which allows you to make a choice based on how variable changes (a state machine of ifs).

Installation

gem install multi_case

Usage

Interfaces

There are two interfaces available, one is an unobtrusive module, which can be included into your class like so:

require 'multi_case'

class Cookie
  include MultiCase::API

  def eat_it!
    mutli_case weight_was, weight do
      multi(150 => 0..50) { puts "That was a good bite!" }
      multi(150 => 150..(+1.0/0.0)) { puts "Are you puked?!" }
    end
  end
end

Another one is to extend your Kernel making multi_case function global:

require 'multi_case/core_ext'

mutli_case ENV['petals_was'], ENV['petal'] do
  multi([] => (0..100).step(2)) { puts "Любит" }
  multi([] => (1..100).step(2)) { puts "Не любит" }
end

Features

Most common use-case is to make a decision based on how your variable changes, it also might be used as an unbotrusive state-machine implementation. For example, if you have a post and its state went from :draft to :published you might want to notify all your subscribed users about that, which can be implemented like so:

class Post
  def notify
    multi_case status_was, status do
      multi([] => :draft)         { Notification.needs_input(post).to_editors }
      multi(:draft => :published) { Notification.new_post(post).to_subscribers }
      multi([] => :destroyed)     { Notification.destoyed_post(post).to_admins }
    end
  end
end

It also returns values so it can be used to replace nested case statements:

beer_status = multi_case bottles_was, bottles do
  multi(0 => 1..10) { :party_time }
  multi(1..10 => 0) { :needs_refil }
end

Since it uses case-matching (===) internally you can use all its goodiness:

multi_case x, y do
  multi(String => /a+/i) { 0 }
  multi(1..100 => [:x, :y, :z]) { 0 }
end

Syntax

multi_case expect two values and block which contains matchers, first value is matched against key of one-elemnt hash which is provided to multi, second value is matched against value of said hash.

multi expect you to provide one-elemnt hash and block, if hash has array in it then all array elements are matched against value sequentially and empty array means "match-all". The block provided to multi is executed in case of correct match and its result is what is returned by multi_case.

Implementation

The mutli_case statement is complete equivalent of nested case statements, so these two snippets are intedent to work exactly the same:

result = multi_case old_value, new_value do
  multi [:a, :b] => [:c, :d] { :e }
  multi [:f, :g] => [:h, :i] { :j }
  multi [] => [:k, :l] { :m }
  multi [:n, :o] => [] { :p }
  multi [:f, :g] => [:q, :r] { :s }
end
result = case old_value
when :a, :b
  case new_value
  when :c, :d then :e
  end
when :f, :g
  case new_value
  when :h, :i then :j
  when :q, :r then :s
  end
when :n, :o then :p
else
  case new_value
  when :k, :l then :m
  end
end

Copyright

Copytright © 2013 Andrey Korzhuev. See LICENSE for details.