Skip to content

Instantly share code, notes, and snippets.

@jbarnette
Created March 6, 2012 22:43
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jbarnette/1989514 to your computer and use it in GitHub Desktop.
Save jbarnette/1989514 to your computer and use it in GitHub Desktop.
module Watchable
def events
@events ||= Hash.new { |h,k| h[k] = [] }
end
def fire event, *args
events[event].each { |e| e[*args] }
end
def on event, &block
events[event] << block
end
end
@brianmario
Copy link

❤️

@krainboltgreene
Copy link

Why not Hash.new [] for #3?

I'm probably going to nab this for Ripped.

@drbrain
Copy link

drbrain commented Mar 6, 2012

@krainboltgreene because:

ruby -e 'h = Hash.new []; p h[1].object_id, h[2].object_id'

@jbarnette
Copy link
Author

@krainboltgreene Hash.new [] will reuse the same Array instance for every key, and down that path lies madness and confusion.

@krainboltgreene
Copy link

-Fair enough, but you can still do Hash.new { [] } :D (No same object problem either!)-

Nevermind I just realized what happens. Durrh.

@jbarnette
Copy link
Author

Nope, it won't memoize the key without explicit assignment.

@krainboltgreene
Copy link

Yep, I realized (literally 1 second after I hit submit) what would happen. Keys == Faster than brain.

@jimmycuadra
Copy link

This module should be called EventEmitter. :trollface:

@jamiehodge
Copy link

module Watchable
  extend self 

  def events
    @event ||= Hash.new { |h,k| h[k] = [] }
  end

  def on event, &block
    events[event] << block
  end

  def fire event, *args
    events[event].each { |e| e[*args] }
  end

  def reset!
    @event = nil
  end
end

require 'minitest/autorun'

describe Watchable do

  before do
    Watchable.reset!
  end

  describe '#events' do

    it 'must be a hash' do
      Watchable.events.must_equal Hash.new
    end

    it 'must initialize a value to an empty array' do
      Watchable.events[:foo].must_equal []
    end
  end

  describe '#on' do

    it 'must add a given block to a given key' do
      bang = proc { 'bang!' }
      Watchable.on :foo, &bang
      Watchable.events[:foo].first === bang
    end
  end

  describe '#fire' do

    it 'must call all blocks for a given key' do
      bang = proc { p 'bang!' }
      pow = proc { p 'pow!' }

      Watchable.on :foo, &bang
      Watchable.on :foo, &pow

      out, err = capture_io do
        Watchable.fire :foo
      end

      out.must_match 'bang!'
      out.must_match 'pow!'
    end

    it 'must pass args to block for a given key' do
      p_args = proc {|*args| p args }

      Watchable.on :foo, &p_args

      out, err = capture_io do
        Watchable.fire :foo, 'bang!', 'pow!'
      end

      out.must_match 'bang!'
      out.must_match 'pow!'
    end
  end
end

@jbarnette
Copy link
Author

@krainboltgreene
Copy link

You could update the gist and use it as a microgem :)

@jbarnette
Copy link
Author

I absolutely could. ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment