Skip to content
This repository has been archived by the owner on Jan 2, 2018. It is now read-only.

Commit

Permalink
Initial commit of BW::Dispatch.
Browse files Browse the repository at this point in the history
  • Loading branch information
jimsynz committed Jun 12, 2012
0 parents commit 87587cf
Show file tree
Hide file tree
Showing 17 changed files with 417 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
@@ -0,0 +1,8 @@
.rake_tasks~
pkg/*
build/
.DS_Store
0
.repl_history
vendor/
.dat*
5 changes: 5 additions & 0 deletions Gemfile
@@ -0,0 +1,5 @@
source 'https://rubygems.org'
gem 'bubble-wrap', '1.0.0.pre.2'

# Specify your gem's dependencies in bw-dispatch.gemspec
gemspec
19 changes: 19 additions & 0 deletions Gemfile.lock
@@ -0,0 +1,19 @@
PATH
remote: .
specs:
bw-dispatch (0.0.1)
bubble-wrap

GEM
remote: https://rubygems.org/
specs:
bubble-wrap (1.0.0.pre.2)
rake (0.9.2.2)

PLATFORMS
ruby

DEPENDENCIES
bubble-wrap (= 1.0.0.pre.2)
bw-dispatch!
rake
22 changes: 22 additions & 0 deletions LICENSE
@@ -0,0 +1,22 @@
Copyright (c) 2012 James Harton

MIT License

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.
29 changes: 29 additions & 0 deletions README.md
@@ -0,0 +1,29 @@
# BW::Dispatch

TODO: Write a gem description

## Installation

Add this line to your application's Gemfile:

gem 'bw-dispatch'

And then execute:

$ bundle

Or install it yourself as:

$ gem install bw-dispatch

## Usage

TODO: Write usage instructions here

## Contributing

1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Added some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
16 changes: 16 additions & 0 deletions Rakefile
@@ -0,0 +1,16 @@
#!/usr/bin/env rake
$:.unshift("/Library/RubyMotion/lib")
require 'motion/project'
require 'bundler'
require "bundler/gem_tasks"
Bundler.setup
#Bundler.require
require 'bubble-wrap/loader'
require 'bubble-wrap/test'

BW.require 'motion/**/*.rb'

Motion::Project::App.setup do |app|
app.name = 'deferrableTestSuite'
app.identifier = 'io.bubblewrap.deferrableTestSuite'
end
20 changes: 20 additions & 0 deletions bw-dispatch.gemspec
@@ -0,0 +1,20 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/bw-dispatch/version', __FILE__)

Gem::Specification.new do |gem|
gem.authors = ["James Harton"]
gem.email = ["james@sociable.co.nz"]
gem.description = %q{Event loop bubblewrapper}
gem.summary = %q{A bubblewrapper for GCD using common Ruby idioms}
gem.homepage = "https://github.com/jamesotron/bw-dispatch"

gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "bw-dispatch"
gem.require_paths = ["lib"]
gem.version = BW::Dispatch::VERSION

gem.add_dependency 'bubble-wrap'
gem.add_development_dependency 'rake'
end
11 changes: 11 additions & 0 deletions lib/bw-dispatch.rb
@@ -0,0 +1,11 @@
require 'bubble-wrap/loader'
require "bw-dispatch/version"

BW.require 'motion/**/*.rb' do
file('motion/dispatch.rb').uses_framework 'GCD'
file('motion/dispatch.rb').depends_on Dir.glob('motion/dispatch/**/*.rb')
file('motion/dispatch/timer.rb').depends_on 'motion/dispatch/eventable.rb'
file('motion/dispatch/periodic_timer.rb').depends_on 'motion/dispatch/eventable.rb'
file('motion/dispatch/deferrable.rb').depends_on ['motion/dispatch/timer.rb', 'motion/dispatch/future.rb']
file('motion/dispatch/default_deferrable.rb').depends_on 'motion/dispatch/deferrable.rb'
end
5 changes: 5 additions & 0 deletions lib/bw-dispatch/version.rb
@@ -0,0 +1,5 @@
module BW
module Dispatch
VERSION = "0.0.1"
end
end
29 changes: 29 additions & 0 deletions motion/dispatch.rb
@@ -0,0 +1,29 @@
module BubbleWrap
module Dispatch
module_function

def add_timer(interval, callback=nil, &blk)
@timers ||= []
timer = Timer.new(interval,callback,&blk)
timer.on(:fired) do
@timers.delete(timer)
end
@timers.unshift(timer)
timer
end

def add_periodic_timer(interval, callback=nil, &blk)
@periodic_timers ||= []
timer = PeriodicTimer.new(internval,callback,blk)
timer.on(:cancelled) do
@periodic_timers.delete(timer)
end
@periodic_timers.unshift(timer)
timer
end

def defer(op=nil,cb=nil,&blk)
end

end
end
9 changes: 9 additions & 0 deletions motion/dispatch/default_deferrable.rb
@@ -0,0 +1,9 @@
module BubbleWrap
module Dispatch
# A basic class which includes Deferrable when all
# you need is a deferrable without any added behaviour.
class DefaultDeferrable
include ::BubbleWrap::Dispatch::Deferrable
end
end
end
126 changes: 126 additions & 0 deletions motion/dispatch/deferrable.rb
@@ -0,0 +1,126 @@
module BubbleWrap
module Dispatch
# Provides a mixin for deferrable jobs.
module Deferrable

def self.included(base)
base.extend ::BubbleWrap::Dispatch::Future
end

# Specify a block to be executed if and when the Deferrable object
# receives a status of :succeeded. See set_deferred_status for more
# information.
# Calling this method on a Deferrable object whose status is not yet
# known will cause the callback block to be stored on an internal
# list. If you call this method on a Deferrable whose status is
# :succeeded, the block will be executed immediately, receiving
# the parameters given to the prior set_deferred_status call.
def callback(&blk)
return unless blk
@deferred_status ||= :unknown
if @deferred_status == :succeeded
blk.call(*@deferred_args)
elsif @deferred_status != :failed
@callbacks ||= []
@callbacks.unshift blk
end
end

# Cancels an outstanding timeout if any. Undoes the action of timeout.
def cancel_timeout
@deferred_timeout ||= nil
if @deferred_timeout
@deferred_timeout.cancal
@deferred_timeout = nil
end
end

# Specify a block to be executed if and when the Deferrable object
# receives a status of :failed. See set_deferred_status for more
# information.
def errback(&blk)
return unless blk
@deferred_status ||= :unknown
if @deferred_status == :failed
blk.call(*@deferred_args)
elsif @deferred_status != :succeeded
@errbacks ||= []
@errbacks.unshift blk
end
end

# Sugar for set_deferred_status(:failed, …)
def fail(*args)
set_deferred_status :fail, *args
end
alias set_deferred_failure fail

# Sets the “disposition” (status) of the Deferrable object. See also
# the large set of sugarings for this method. Note that if you call
# this method without arguments, no arguments will be passed to the
# callback/errback. If the user has coded these with arguments,
# then the user code will throw an argument exception. Implementors
# of deferrable classes must document the arguments they will supply
# to user callbacks.
# OBSERVE SOMETHING VERY SPECIAL here: you may call this method even
# on the INSIDE of a callback. This is very useful when a
# previously-registered callback wants to change the parameters that
# will be passed to subsequently-registered ones.
# You may give either :succeeded or :failed as the status argument.
# If you pass :succeeded, then all of the blocks passed to the object
# using the callback method (if any) will be executed BEFORE the
# set_deferred_status method returns. All of the blocks passed to the
# object using errback will be discarded.
# If you pass :failed, then all of the blocks passed to the object
# using the errback method (if any) will be executed BEFORE the
# set_deferred_status method returns. All of the blocks passed to the
# object using # callback will be discarded.
# If you pass any arguments to set_deferred_status in addition to the
# status argument, they will be passed as arguments to any callbacks
# or errbacks that are executed. It’s your responsibility to ensure
# that the argument lists specified in your callbacks and errbacks match
# the arguments given in calls to set_deferred_status, otherwise Ruby
# will raise an ArgumentError.
def set_deferred_status(status, *args)
cancel_timeout
@errbacks ||= nil
@callbacks ||= nil
@deferred_status = status
@deferred_args = args
case @deferred_status
when :succeeded
if @callbacks
while cb = @callbacks.pop
cb.call(*@deferred_args)
end
end
@errbacks.clear if @errbacks
when :failed
if @errbacks
while eb = @errbacks.pop
eb.call(*@deferred_args)
end
end
@callbacks.clear if @callbacks
end
end

# Sugar for set_deferred_status(:succeeded, …)
def succeed(*args)
set_deferred_status :succeeded, *args
end
alias set_deferred_success succeed

# Setting a timeout on a Deferrable causes it to go into the failed
# state after the Timeout expires (passing no arguments to the object’s
# errbacks). Setting the status at any time prior to a call to the
# expiration of the timeout will cause the timer to be cancelled.
def timeout(seconds)
cancel_timeout
me = self
@deferred_timeout = Timer.new(seconds) {me.fail}
end

end
end
end
17 changes: 17 additions & 0 deletions motion/dispatch/eventable.rb
@@ -0,0 +1,17 @@
module BubbleWrap
module Dispatch
module Eventable

def on(event, &blk)
@events ||= Hash.new { [] }
@events[event].unshift blk
end

def trigger(event, *args)
@events ||= Hash.new { [] }
@events[event].map(&call)
end

end
end
end
22 changes: 22 additions & 0 deletions motion/dispatch/future.rb
@@ -0,0 +1,22 @@
module BubbleWrap
module Dispatch
module Future

# A future is a sugaring of a typical deferrable usage.
def future arg, cb=nil, eb=nil, &blk
arg = arg.call if arg.respond_to?(:call)

if arg.respond_to?(:set_deferred_status)
if cb || eb
arg.callback(&cb) if cb
arg.errback(&eb) if eb
else
arg.callback(&blk) if blk
end
end

arg
end
end
end
end
27 changes: 27 additions & 0 deletions motion/dispatch/periodic_timer.rb
@@ -0,0 +1,27 @@
module BubbleWrap
module Dispatch
# Creates a repeating timer.
class PeriodicTimer
include Eventable

attr_accessor :interval

# Create a new timer that fires after a given number of seconds
def initialize(interval, callback=nil, &blk)
self.interval = interval
fire = proc {
(callback || blk).call
trigger(:fired)
}
@timer = NSTimer.scheduledTimerWithTimeInterval(interval,target: fire, selector: 'call:', userInfo: nil, repeats: true)
end

# Cancel the timer
def cancel
@timer.invalidate
trigger(:cancelled)
end

end
end
end

0 comments on commit 87587cf

Please sign in to comment.