Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Spike.

  • Loading branch information...
commit fab64a28a8a4c4409e49e38488859e207718c74a 0 parents
@jbarnette authored
3  .gitignore
@@ -0,0 +1,3 @@
+/.rbenv-version
+/pkg
+Gemfile.lock
15 .kick
@@ -0,0 +1,15 @@
+require "kicker/utils"
+
+process do |files|
+ if files.any? { |f| /\.rb$/ =~ f }
+ execute "rake test"
+ end
+
+ files.clear
+end
+
+module Kicker::Utils
+ def log(message)
+ nil
+ end
+end
12 .travis.yml
@@ -0,0 +1,12 @@
+language: ruby
+rvm:
+ - 1.8.7
+ - 1.9.2
+ - 1.9.3
+ - jruby-18mode
+ - jruby-19mode
+ - jruby-head
+ - rbx-18mode
+ - rbx-19mode
+ - ree
+ - ruby-head
4 Gemfile
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in watchable.gemspec
+gemspec
125 README.markdown
@@ -0,0 +1,125 @@
+[![Build Status](https://secure.travis-ci.org/jbarnette/watchable.png)](http://travis-ci.org/jbarnette/watchable)
+
+# Watchable
+
+A simple event/notification mixin, reluctantly extracted to a gem.
+This is code I've had floating around for a few years now, but I've
+also incorporated a few extras from node.js' [EventEmitter][ee],
+[jQuery][jq], and [Backbone.Events][be].
+
+[ee]: http://nodejs.org/api/events.html#events_class_events_eventemitter
+[jq]: http://api.jquery.com/on
+[be]: http://documentcloud.github.com/backbone/#Events
+
+## Example
+
+### Fixtures
+
+```ruby
+require "watchable"
+
+class Frob
+ include Watchable
+end
+
+class Callable
+ def call *args
+ p :called! => args
+ end
+end
+
+```
+
+### Watching and Firing
+
+Events can have any number of watchers. Each watcher will be called
+in order, and any args provided when the event is fired will be passed
+along. Watchers will most commonly be blocks, but any object that
+responds to `call` can be used instead.
+
+```ruby
+frob = Frob.new
+
+frob.on :twiddle do |name|
+ puts "#{name} twiddled the frob!"
+end
+
+frob.on :twiddle do |name|
+ puts "(not that there's anything wrong with that)"
+end
+
+frob.on :twiddle, Callable.new
+frob.fire :twiddle, "John"
+```
+
+#### Result
+
+ John twiddled the frob!
+ (not that there's anything wrong with that)
+ { :called! => ["John"] }
+
+### Watching Once
+
+Only want to be notified the first time something happens? `once` is
+like `on`, but fickle.
+
+```ruby
+frob = Frob.new
+
+frob.on :twiddle do
+ p :twiddled!
+end
+
+frob.fire :twiddle
+frob.fire :twiddle
+```
+
+#### Result
+
+ :twiddled!
+
+### Unwatching
+
+Specific blocks or callable objects can be removed from an event's
+watchers, or all the event's watchers can be removed.
+
+```ruby
+b = lambda {}
+frob = Frob.new
+
+frob.on :twiddle, &b
+
+frob.off :twiddle, b # removes the 'b' watcher, same as frob.off :twiddle, &b
+frob.off :twiddle # removes all watchers for the 'twiddle' event
+```
+
+## Compatibility
+
+Watchable is actively developed against MRI Ruby 1.8.7 as a least common
+denominator, but is widely tested against other Ruby versions and
+implementations. Check the [travis-ci][] page for details.
+
+[travis-ci]: http://travis-ci.org/jbarnette/watchable
+
+## License (MIT)
+
+Copyright 2012 John Barnette (john@jbarnette.com)
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 Rakefile
@@ -0,0 +1,9 @@
+require "bundler/gem_tasks"
+
+desc "Run the tests."
+task :test do
+ $: << "lib" << "test"
+ Dir["test/*_test.rb"].each { |f| require f[5..-4] }
+end
+
+task :default => :test
33 lib/watchable.rb
@@ -0,0 +1,33 @@
+module Watchable
+ def watchers
+ @watchers ||= Hash.new { |h, k| h[k] = [] }
+ end
+
+ def fire event, *args
+ watchers[event].each { |w| w && w.call(*args) }
+
+ self
+ end
+
+ def on event, callable = nil, &block
+ watchers[event] << (callable || block)
+
+ self
+ end
+
+ def once event, callable = nil, &block
+ wrapper = lambda do |*args|
+ off event, wrapper
+ (callable || block).call *args
+ end
+
+ on event, wrapper
+ end
+
+ def off event, callable = nil, &block
+ watcher = callable || block
+ watcher ? watchers[event].delete(watcher) : watchers[event].clear
+
+ self
+ end
+end
110 test/watchable_test.rb
@@ -0,0 +1,110 @@
+require "minitest/autorun"
+require "mocha"
+require "watchable"
+
+describe Watchable do
+ before do
+ @obj = Object.new
+ @obj.extend Watchable
+ end
+
+ it "has an empty list of watchers by default" do
+ assert @obj.watchers.empty?
+ end
+
+ it "returns an empty array of watchers for any event" do
+ assert_equal [], @obj.watchers[:foo]
+ end
+
+ describe :fire do
+ it "calls each watcher with optional args" do
+ @obj.on :foo, mock { expects(:call).with :bar, :baz }
+ @obj.fire :foo, :bar, :baz
+ end
+
+ it "calls multiple watchers in order" do
+ fires = sequence "fires"
+
+ @obj.on :foo, mock { expects(:call).in_sequence fires }
+ @obj.on :foo, mock { expects(:call).in_sequence fires }
+
+ @obj.fire :foo
+ end
+
+ it "ignores nil watchers" do
+ @obj.on :foo, nil
+ @obj.fire :foo
+ end
+
+ it "returns the watchable" do
+ assert_same @obj, @obj.fire(:foo)
+ end
+ end
+
+ describe :off do
+ it "can unregister a block" do
+ b = lambda {}
+
+ @obj.on :foo, &b
+ @obj.off :foo, &b
+
+ assert @obj.watchers[:foo].empty?
+ end
+
+ it "can unregister an object" do
+ b = lambda {}
+
+ @obj.on :foo, &b
+ @obj.off :foo, &b
+
+ assert @obj.watchers[:foo].empty?
+ end
+
+ it "can unregister all watchers for an event" do
+ @obj.on(:foo) {}
+ @obj.on(:foo) {}
+
+ assert_equal 2, @obj.watchers[:foo].size
+
+ @obj.off :foo
+ assert @obj.watchers[:foo].empty?
+ end
+
+ it "returns the watchable" do
+ assert_same @obj, @obj.off(:foo)
+ end
+ end
+
+ describe :on do
+ it "can register a block" do
+ b = lambda {}
+
+ @obj.on :foo, &b
+ assert_equal [b], @obj.watchers[:foo]
+ end
+
+ it "can register an object" do
+ b = lambda {}
+
+ @obj.on :foo, b
+ assert_equal [b], @obj.watchers[:foo]
+ end
+
+ it "returns the watchable" do
+ assert_same @obj, @obj.on(:foo) {}
+ end
+ end
+
+ describe :once do
+ it "registers a watcher that's only called on the first fire" do
+ @obj.once :foo, mock { expects :call }
+
+ @obj.fire :foo
+ @obj.fire :foo
+ end
+
+ it "returns the watchable" do
+ assert_same @obj, @obj.once(:foo) {}
+ end
+ end
+end
16 watchable.gemspec
@@ -0,0 +1,16 @@
+Gem::Specification.new do |gem|
+ gem.authors = ["John Barnette"]
+ gem.email = ["john@jbarnette.com"]
+ gem.description = "A simple event mixin, reluctantly extracted to a gem."
+ gem.summary = "Watch an object for events."
+ gem.homepage = "https://github.com/jbarnette/watchable"
+
+ gem.files = `git ls-files`.split "\n"
+ gem.test_files = `git ls-files -- test/*`.split "\n"
+ gem.name = "watchable"
+ gem.require_paths = ["lib"]
+ gem.version = "0.0.0"
+
+ gem.add_development_dependency "minitest"
+ gem.add_development_dependency "mocha"
+end
Please sign in to comment.
Something went wrong with that request. Please try again.