Skip to content

Instantly share code, notes, and snippets.

@seanlilmateus
Last active January 12, 2018 16:11

Revisions

  1. seanlilmateus revised this gist Feb 23, 2013. 1 changed file with 162 additions and 52 deletions.
    214 changes: 162 additions & 52 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -1,123 +1,233 @@
    #!/usr/bin/env macruby -wKU
    framework 'Foundation'

    module Dispatch
    module Futuristic
    def proxy
    Class.new(BasicObject) do
    def initialize(obj)
    @obj = obj
    end

    def method_missing(meth, *args, &blk)
    Future.new { @obj.send(meth, *args, &blk) }
    end

    def respond_to_missing?(meth, include_private = false)
    @obj.respond_to?(meth)
    end
    end
    end

    def future
    proxy.new(self)
    end
    end

    class Promise < BasicObject

    # MacRuby and Rubymotion BasicObject#initialize doesn't like blocks, so we have to do this
    def self.new(&block)
    ::Kernel.raise(::ArgumentError, "You cannot initalize a Dispatch::Promise without a block") unless block_given?
    ::Kernel.raise(::ArgumentError, "Dispatch::Promise, wrong number of arguments (#{block.arity} for 0)") if block.arity > 0
    self.alloc.initialization(block)
    end

    def when_done(call_back)
    @group.notify(@futures_queue) { call_back[@value] }
    end


    # setup Grand Central Dispatch concurrent Queue and Group
    def initialization(block)
    init
    @computation = block
    @arg = nil
    # Groups are just simple layers on top of semaphores.
    @group = ::Dispatch::Group.new
    # Each thread gets its own FIFO queue upon which we will dispatch
    # the delayed computation passed in the &block variable.
    @futures_queue ||= ::Dispatch::Queue.concurrent("org.macruby.promise-#{::Thread.current.object_id}") #
    #Dispatch.once do
    @group = ::Dispatch::Group.new
    # Each thread gets its own FIFO queue upon which we will dispatch
    # the delayed computation passed in the &block variable.
    @promise_queue = ::Dispatch::Queue.concurrent("org.macruby.dispatch.promise-0x#{hash.to_s(16)}") #
    #end
    @value = @success = false
    self
    end

    def inspect
    value.inspect
    __value__.inspect
    end

    def done?
    !!@value
    end
    alias_method :ready?, :done?
    private

    def __force__
    # Asynchronously dispatch the future to the thread-local queue.
    @futures_queue.async(@group) { @value = @computation.call }
    @promise_queue.async(@group) { @value = @computation.call(*@arg) }
    end

    def value
    def __value__
    # Wait fo the computation to finish. If it has already finished, then
    # just return the value in question.
    self.__force__ unless self.done?
    __force__ unless done?
    @group.wait
    @value
    end



    # like method_missing for objc
    # without this 'promise = Dispatch::Promise.new { NSData.dataWithContentsOfFile(file_name) }' will not work
    # NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)
    # since promise will not respond to NSData#bytes and return a NSInvalidArgumentException
    alias_method :description, :inspect
    private
    def method_missing(method_name, *args, &block)
    self.value.send(method_name, *args, &block) if value.respond_to?(method_name)
    end
    def method_missing(meth, *args, &block)
    __value__.send(meth, *args, &block)
    end

    def respond_to_missing?(method_name, include_private = false)
    value.respond_to?(method_name, include_private) || super
    end
    def respond_to_missing?(method_name, include_private = false)
    __value__.respond_to?(method_name, include_private) || super
    end

    def forwardingTargetForSelector(sel)
    value if value.respond_to?(sel)
    end
    def forwardingTargetForSelector(sel)
    __value__ if __value__.respond_to?(sel)
    end
    end

    class Future < BasicObject
    class Future < Promise
    # MacRuby and Rubymotion BasicObject#initialize doesn't like blocks, so we have to do this
    def self.new(&block)
    ::Kernel.raise(::ArgumentError, "You cannot initalize a Dispatch::Future without a block") unless block_given?
    ::Kernel.raise(::ArgumentError, "Dispatch::Future, wrong number of arguments (#{block.arity} for 0)") if block.arity > 0
    self.alloc.initialization(block)
    class << self
    def sequence(*args, &block)
    array = Array(args).flatten
    if block_given?
    result = []
    Queue.concurrent(:high).apply(array.count) do |idx|
    result << Future.new { block.call(array[idx]) }
    end
    NSArray.arrayWithArray(result)
    else
    array.extend(Futuristic)
    array.future
    end
    end

    def new(arg=nil, &block)
    ::Kernel.raise(::ArgumentError, "You cannot initalize a Dispatch::Future without a block") unless block_given?
    self.alloc.initialization(arg, block)
    end

    end

    def initialization(block)
    init
    @promise = Promise.new(&block)
    @promise.__force__
    def when_done(&call_back)
    @group.notify(@promise_queue) { call_back[__value__] }
    self
    end

    def when_done(&block); @promise.when_done(block);end
    def initialization(arg, block)
    super(block)
    @arg = arg
    __force__
    self
    end

    def description
    if @promise.done?
    value.inspect
    else
    NSString.stringWithString(super.gsub(/>/, ' running...>'))
    end
    state = done? ? :finished : :running
    NSString.stringWithString("<#{self.class} 0x#{hash.to_s(16)} STATE:#{state} on #@promise_queue...>")
    end

    def value; __value__; end

    private
    def method_missing(meth, *args, &block)
    self.send(meth, *args, &block)
    end

    def respond_to_missing?(method_name, include_private = false)
    self.respond_to?(method_name, include_private)
    end

    def value
    @promise.value
    end
    def forwardingTargetForSelector(sel)
    self if self.respond_to?(sel)
    end

    alias_method :inspect, :description
    end
    end

    class Person
    include Dispatch::Futuristic

    attr_accessor :id, :name, :email

    def initialize(dictionary = {})
    setValuesForKeysWithDictionary(dictionary) if dictionary.is_a?(Hash)
    end

    def dance(&block)
    sleep 4
    block.call(Dispatch::Queue.current)
    end

    def setValue(value, forUndefinedKey:key); end
    end

    person = Person.new(name: "Mateus", id:10, email:"[email protected]")

    # running on background queue
    person.future.dance do |queue|
    puts "running queue: #{queue}"
    end

    # running on main queue
    person.dance do |queue|
    puts "running queue: #{queue}"
    end


    urls = ["http://www.facebook.com/", "http://www.google.com/"].map { |url| NSURL.URLWithString(url) }
    result = Dispatch::Future.sequence(urls) do |url|
    NSString.stringWithContentsOfURL(url, encoding:NSUTF8StringEncoding, error:nil)
    end

    NSLog("Result: %@", result.last.value)
    p result.first


    future = Dispatch::Future.new { sleep 0.4; 10}

    p future.ready?
    p future.value
    p future.ready?

    future = Dispatch::Future.new(40) { |value| value + 2 }

    p future.ready?
    p future.value
    sleep 5

    # Dispatch Future
    the_future = Dispatch::Future.new { sleep 1; 6 / 2 }
    the_future.when_done { |value| value = 10 }
    p the_future # <Future: 0x400cf57c0 running...>
    p the_future # <Dispatch::Future 0x400d4bae0 STATE:running on org.macruby.dispatch.promise-0x400d4bae0...>

    calculation = Dispatch::Future.new { sleep 2; 4 * 4 }
    p calculation # <Future: 0x400cf6ee0 running...>
    p calculation # <Dispatch::Future 0x400d4bae0 STATE:running on org.macruby.dispatch.promise-0x400d4bae0...>
    calculation.when_done { |value| puts value + 10 } # 26
    p calculation.value + 2 # 18

    # Dispatch Promise
    promise = Dispatch::Promise.new { 12 }
    promise2 = Dispatch::Promise.new { sleep 2; 10 }
    result = promise2 + promise
    p result

    a = Dispatch::Promise.new { 10 / 2} # 10 / 2 = 5
    b = Dispatch::Promise.new { a + 1 } # 5 + 1 = 6
    c = Dispatch::Promise.new { a - 1 } # 5 - 1 = 4
    puts b * c # 24



    file_name = "/usr/share/dict/words"
    promise = Dispatch::Promise.new { NSData.dataWithContentsOfFile(file_name) }
    string = NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)
    string = NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)

    summe = Dispatch::Future.sequence([*1..100]).inject { |sum, x| sum += x*2-1 }
    p summe.value

    p string
    futures = [*1..1000].map { |i| Dispatch::Future.new { sleep 1; i * 2 } }
    p futures.inject(0) { |sum, x| sum += x.value }
  2. seanlilmateus revised this gist Jan 13, 2013. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@ def self.new(&block)
    end

    def when_done(call_back)
    @group.notify(::Thread.current[:futures]) { call_back[@value] }
    @group.notify(@futures_queue) { call_back[@value] }
    end

    # setup Grand Central Dispatch concurrent Queue and Group
    @@ -22,7 +22,7 @@ def initialization(block)
    @group = ::Dispatch::Group.new
    # Each thread gets its own FIFO queue upon which we will dispatch
    # the delayed computation passed in the &block variable.
    ::Thread.current[:futures] ||= ::Dispatch::Queue.concurrent("org.macruby.promise-#{::Thread.current.object_id}") #
    @futures_queue ||= ::Dispatch::Queue.concurrent("org.macruby.promise-#{::Thread.current.object_id}") #
    @value = @success = false
    self
    end
    @@ -37,7 +37,7 @@ def done?

    def __force__
    # Asynchronously dispatch the future to the thread-local queue.
    ::Thread.current[:futures].async(@group) { @value = @computation.call }
    @futures_queue.async(@group) { @value = @computation.call }
    end

    def value
  3. seanlilmateus revised this gist Jan 2, 2013. 1 changed file with 17 additions and 17 deletions.
    34 changes: 17 additions & 17 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -35,36 +35,36 @@ def done?
    !!@value
    end

    def resolve
    def __force__
    # Asynchronously dispatch the future to the thread-local queue.
    ::Thread.current[:futures].async(@group) { @value = @computation.call }
    end

    def value
    # Wait fo the computation to finish. If it has already finished, then
    # just return the value in question.
    self.resolve unless self.done?
    self.__force__ unless self.done?
    @group.wait
    @value
    end

    def method_missing(method_name, *args, &block)
    self.value.send(method_name, *args, &block) if value.respond_to?(method_name)
    end

    def respond_to_missing?(method_name, include_private = false)
    value.respond_to?(method_name, include_private) || super
    end


    # like method_missing for objc
    # without this 'promise = Dispatch::Promise.new { NSData.dataWithContentsOfFile(file_name) }' will not work
    # NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)
    # since promise will not respond to NSData#bytes and return a NSInvalidArgumentException
    def forwardingTargetForSelector(sel)
    value if value.respond_to?(sel)
    end

    # since promise will not respond to NSData#bytes and return a NSInvalidArgumentException
    alias_method :description, :inspect
    private
    def method_missing(method_name, *args, &block)
    self.value.send(method_name, *args, &block) if value.respond_to?(method_name)
    end

    def respond_to_missing?(method_name, include_private = false)
    value.respond_to?(method_name, include_private) || super
    end

    def forwardingTargetForSelector(sel)
    value if value.respond_to?(sel)
    end
    end

    class Future < BasicObject
    @@ -78,7 +78,7 @@ def self.new(&block)
    def initialization(block)
    init
    @promise = Promise.new(&block)
    @promise.resolve
    @promise.__force__
    self
    end

  4. seanlilmateus revised this gist Jan 2, 2013. 1 changed file with 11 additions and 8 deletions.
    19 changes: 11 additions & 8 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,5 @@
    #!/usr/bin/env macruby -wKU
    framework 'Foundation'

    module Dispatch
    class Promise < BasicObject

    @@ -19,6 +18,11 @@ def when_done(call_back)
    def initialization(block)
    init
    @computation = block
    # Groups are just simple layers on top of semaphores.
    @group = ::Dispatch::Group.new
    # Each thread gets its own FIFO queue upon which we will dispatch
    # the delayed computation passed in the &block variable.
    ::Thread.current[:futures] ||= ::Dispatch::Queue.concurrent("org.macruby.promise-#{::Thread.current.object_id}") #
    @value = @success = false
    self
    end
    @@ -31,16 +35,15 @@ def done?
    !!@value
    end

    def value
    # Each thread gets its own FIFO queue upon which we will dispatch
    # the delayed computation passed in the &block variable.
    ::Thread.current[:futures] ||= ::Dispatch::Queue.concurrent("org.macruby.promise-#{::Thread.current.object_id}") #
    # Groups are just simple layers on top of semaphores.
    @group = ::Dispatch::Group.new
    def resolve
    # Asynchronously dispatch the future to the thread-local queue.
    ::Thread.current[:futures].async(@group) { @value = @computation.call }
    end

    def value
    # Wait fo the computation to finish. If it has already finished, then
    # just return the value in question.
    self.resolve unless self.done?
    @group.wait
    @value
    end
    @@ -75,7 +78,7 @@ def self.new(&block)
    def initialization(block)
    init
    @promise = Promise.new(&block)
    @promise.value
    @promise.resolve
    self
    end

  5. seanlilmateus revised this gist Jan 2, 2013. 1 changed file with 21 additions and 22 deletions.
    43 changes: 21 additions & 22 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    #!/usr/bin/env macruby -wKU

    framework 'Foundation'

    module Dispatch
    class Promise < BasicObject

    @@ -18,34 +18,27 @@ def when_done(call_back)
    # setup Grand Central Dispatch concurrent Queue and Group
    def initialization(block)
    init
    @computation = block
    @value = @success = false
    # Each thread gets its own FIFO queue upon which we will dispatch
    # the delayed computation passed in the &block variable.
    ::Thread.current[:futures] ||= ::Dispatch::Queue.concurrent("org.macruby.promise-#{::Thread.current.object_id}")
    # Groups are just simple layers on top of semaphores.
    @group = ::Dispatch::Group.new
    # Asynchronously dispatch the future to the thread-local queue.
    ::Thread.current[:futures].async(@group) { @value = block.call }
    self
    end

    def inspect
    value.inspect
    end

    # like method_missing for objc
    # without this 'promise = Dispatch::Promise.new { NSData.dataWithContentsOfFile(file_name) }' will not work
    # NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)
    # since promise will not respond to NSData#bytes and return a NSInvalidArgumentException
    def forwardingTargetForSelector(sel)
    value if value.respond_to?(sel)
    end


    def done?
    !!@value
    end

    def value
    # Each thread gets its own FIFO queue upon which we will dispatch
    # the delayed computation passed in the &block variable.
    ::Thread.current[:futures] ||= ::Dispatch::Queue.concurrent("org.macruby.promise-#{::Thread.current.object_id}") #
    # Groups are just simple layers on top of semaphores.
    @group = ::Dispatch::Group.new
    # Asynchronously dispatch the future to the thread-local queue.
    ::Thread.current[:futures].async(@group) { @value = @computation.call }
    # Wait fo the computation to finish. If it has already finished, then
    # just return the value in question.
    @group.wait
    @@ -59,6 +52,15 @@ def method_missing(method_name, *args, &block)
    def respond_to_missing?(method_name, include_private = false)
    value.respond_to?(method_name, include_private) || super
    end

    # like method_missing for objc
    # without this 'promise = Dispatch::Promise.new { NSData.dataWithContentsOfFile(file_name) }' will not work
    # NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)
    # since promise will not respond to NSData#bytes and return a NSInvalidArgumentException
    def forwardingTargetForSelector(sel)
    value if value.respond_to?(sel)
    end

    alias_method :description, :inspect
    end

    @@ -73,6 +75,7 @@ def self.new(&block)
    def initialization(block)
    init
    @promise = Promise.new(&block)
    @promise.value
    self
    end

    @@ -88,11 +91,7 @@ def description

    def value
    @promise.value
    end

    def method_missing(method, *args, &block)
    @promise.send(method, *args, &block)
    end
    end
    alias_method :inspect, :description
    end
    end
  6. seanlilmateus revised this gist Dec 29, 2012. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -109,8 +109,8 @@ def method_missing(method, *args, &block)

    # Dispatch Promise
    promise = Dispatch::Promise.new { 12 }
    promise2 = Dispatch::Promise.new { sleep 2; 10.to_i }
    result = promise2.to_int + promise.to_int
    promise2 = Dispatch::Promise.new { sleep 2; 10 }
    result = promise2 + promise
    p result


  7. seanlilmateus revised this gist Dec 29, 2012. 1 changed file with 1 addition and 5 deletions.
    6 changes: 1 addition & 5 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -10,11 +10,7 @@ def self.new(&block)
    ::Kernel.raise(::ArgumentError, "Dispatch::Promise, wrong number of arguments (#{block.arity} for 0)") if block.arity > 0
    self.alloc.initialization(block)
    end

    def class
    value.class
    end


    def when_done(call_back)
    @group.notify(::Thread.current[:futures]) { call_back[@value] }
    end
  8. seanlilmateus revised this gist Dec 29, 2012. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -112,10 +112,10 @@ def method_missing(method, *args, &block)
    p calculation.value + 2 # 18

    # Dispatch Promise
    promise = Dispatch::Promise.new { 10 }
    promise2 = Dispatch::Promise.new { sleep 2 ; 10 }
    result = promise2 + promise
    p result # 20
    promise = Dispatch::Promise.new { 12 }
    promise2 = Dispatch::Promise.new { sleep 2; 10.to_i }
    result = promise2.to_int + promise.to_int
    p result


    file_name = "/usr/share/dict/words"
  9. seanlilmateus revised this gist Dec 29, 2012. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -108,8 +108,8 @@ def method_missing(method, *args, &block)

    calculation = Dispatch::Future.new { sleep 2; 4 * 4 }
    p calculation # <Future: 0x400cf6ee0 running...>
    calculation.when_done { |value| puts value + 10 } # 26
    p calculation + 2 # 18
    calculation.when_done { |value| puts value + 10 } # 26
    p calculation.value + 2 # 18

    # Dispatch Promise
    promise = Dispatch::Promise.new { 10 }
  10. seanlilmateus revised this gist Dec 29, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -108,7 +108,7 @@ def method_missing(method, *args, &block)

    calculation = Dispatch::Future.new { sleep 2; 4 * 4 }
    p calculation # <Future: 0x400cf6ee0 running...>
    calculation.when_done { puts calculation + 10 } # 26
    calculation.when_done { |value| puts value + 10 } # 26
    p calculation + 2 # 18

    # Dispatch Promise
  11. seanlilmateus revised this gist Dec 29, 2012. 1 changed file with 33 additions and 18 deletions.
    51 changes: 33 additions & 18 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,20 @@
    #!/usr/bin/env macruby -wKU

    framework 'Foundation'
    module Dispatch
    class Promise < BasicObject

    # MacRuby and Rubymotion BasicObject#initialize doesn't like blocks, so we have to do this
    def self.new(&block)
    raise ArgumentError, "You can initalize a Future without a block" unless block_given?
    ::Kernel.raise(::ArgumentError, "Cannot store a Dispatch::Promise that requires an argument") if block.arity > 0
    ::Kernel.raise(::ArgumentError, "You cannot initalize a Dispatch::Promise without a block") unless block_given?
    ::Kernel.raise(::ArgumentError, "Dispatch::Promise, wrong number of arguments (#{block.arity} for 0)") if block.arity > 0
    self.alloc.initialization(block)
    end

    def class
    value.class
    end

    def when_done(call_back)
    @group.notify(::Thread.current[:futures]) { call_back[@value] }
    end
    @@ -17,11 +25,11 @@ def initialization(block)
    @value = @success = false
    # Each thread gets its own FIFO queue upon which we will dispatch
    # the delayed computation passed in the &block variable.
    ::Thread.current[:futures] ||= ::Dispatch::Queue.concurrent("org.macruby.futures-#{::Thread.current.object_id}")
    ::Thread.current[:futures] ||= ::Dispatch::Queue.concurrent("org.macruby.promise-#{::Thread.current.object_id}")
    # Groups are just simple layers on top of semaphores.
    @group = ::Dispatch::Group.new
    # Asynchronously dispatch the future to the thread-local queue.
    ::Thread.current[:futures].async(@group) { @value = block.call }
    ::Thread.current[:futures].async(@group) { @value = block.call }
    self
    end

    @@ -48,23 +56,24 @@ def value
    @value
    end

    def method_missing(method, *args, &block)
    value.send(method, *args, &block)
    def method_missing(method_name, *args, &block)
    self.value.send(method_name, *args, &block) if value.respond_to?(method_name)
    end

    def respond_to_missing?(method, include_private = false)
    value.respond_to?(method, include_private)
    def respond_to_missing?(method_name, include_private = false)
    value.respond_to?(method_name, include_private) || super
    end
    alias_method :description, :inspect
    end

    class Future < BasicObject

    class Future < BasicObject
    # MacRuby and Rubymotion BasicObject#initialize doesn't like blocks, so we have to do this
    def self.new(&block)
    raise ArgumentError, "You can initalize a Future without a block" unless block_given?
    ::Kernel.raise(::ArgumentError, "Cannot store a Dispatch::Future that requires an argument") if block.arity > 0
    ::Kernel.raise(::ArgumentError, "You cannot initalize a Dispatch::Future without a block") unless block_given?
    ::Kernel.raise(::ArgumentError, "Dispatch::Future, wrong number of arguments (#{block.arity} for 0)") if block.arity > 0
    self.alloc.initialization(block)
    end

    def initialization(block)
    init
    @promise = Promise.new(&block)
    @@ -74,10 +83,10 @@ def initialization(block)
    def when_done(&block); @promise.when_done(block);end

    def description
    if !@promise.done?
    NSString.stringWithString(super.gsub(/>/, ' running...>'))
    else
    if @promise.done?
    value.inspect
    else
    NSString.stringWithString(super.gsub(/>/, ' running...>'))
    end
    end

    @@ -88,7 +97,6 @@ def value
    def method_missing(method, *args, &block)
    @promise.send(method, *args, &block)
    end

    alias_method :inspect, :description
    end
    end
    @@ -107,4 +115,11 @@ def method_missing(method, *args, &block)
    promise = Dispatch::Promise.new { 10 }
    promise2 = Dispatch::Promise.new { sleep 2 ; 10 }
    result = promise2 + promise
    p result # 20
    p result # 20


    file_name = "/usr/share/dict/words"
    promise = Dispatch::Promise.new { NSData.dataWithContentsOfFile(file_name) }
    string = NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)

    p string
  12. seanlilmateus revised this gist Dec 29, 2012. 1 changed file with 7 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -93,6 +93,7 @@ def method_missing(method, *args, &block)
    end
    end

    # Dispatch Future
    the_future = Dispatch::Future.new { sleep 1; 6 / 2 }
    the_future.when_done { |value| value = 10 }
    p the_future # <Future: 0x400cf57c0 running...>
    @@ -101,3 +102,9 @@ def method_missing(method, *args, &block)
    p calculation # <Future: 0x400cf6ee0 running...>
    calculation.when_done { puts calculation + 10 } # 26
    p calculation + 2 # 18

    # Dispatch Promise
    promise = Dispatch::Promise.new { 10 }
    promise2 = Dispatch::Promise.new { sleep 2 ; 10 }
    result = promise2 + promise
    p result # 20
  13. seanlilmateus revised this gist Dec 29, 2012. 1 changed file with 85 additions and 42 deletions.
    127 changes: 85 additions & 42 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -1,16 +1,18 @@
    class Future < BasicObject

    # completation callback, I don't like it either :-P
    def completed(&block); @success = block; end

    # MacRuby and Rubymotion BasicObject#initialize doesn't like blocks, so we have to do this
    def self.new(&block)
    module Dispatch
    class Promise < BasicObject
    # MacRuby and Rubymotion BasicObject#initialize doesn't like blocks, so we have to do this
    def self.new(&block)
    raise ArgumentError, "You can initalize a Future without a block" unless block_given?
    ::Kernel.raise(::ArgumentError, "Cannot store a Dispatch::Promise that requires an argument") if block.arity > 0
    self.alloc.initialization(block)
    end

    # setup Grand Central Dispatch concurrent Queue and Group
    def initialization(block)
    end

    def when_done(call_back)
    @group.notify(::Thread.current[:futures]) { call_back[@value] }
    end

    # setup Grand Central Dispatch concurrent Queue and Group
    def initialization(block)
    init
    @value = @success = false
    # Each thread gets its own FIFO queue upon which we will dispatch
    @@ -19,42 +21,83 @@ def initialization(block)
    # Groups are just simple layers on top of semaphores.
    @group = ::Dispatch::Group.new
    # Asynchronously dispatch the future to the thread-local queue.
    ::Thread.current[:futures].async(@group) { @value = block.call }
    @group.notify(::Thread.current[:futures]) { @success[@value] if @success }
    ::Thread.current[:futures].async(@group) { @value = block.call }
    self
    end
    end

    def inspect
    value.inspect
    end

    # like method_missing for objc
    # without this 'promise = Dispatch::Promise.new { NSData.dataWithContentsOfFile(file_name) }' will not work
    # NSString.alloc.initWithData(promise, encoding:NSUTF8StringEncoding)
    # since promise will not respond to NSData#bytes and return a NSInvalidArgumentException
    def forwardingTargetForSelector(sel)
    value if value.respond_to?(sel)
    end

    def done?
    !!@value
    end

    def value
    # Wait fo the computation to finish. If it has already finished, then
    # just return the value in question.
    @group.wait
    @value
    end

    def method_missing(method, *args, &block)
    value.send(method, *args, &block)
    end

    def inspect
    if !@value
    "#{description} still running..."
    else
    @value.inspect
    end
    end

    def value
    # Wait fo the computation to finish. If it has already finished, then
    # just return the value in question.
    @group.wait
    @value
    end

    def method_missing(method, *args, &block)
    value.send(method, *args, &block)
    end

    def respond_to_missing?(method, include_private = false)
    def respond_to_missing?(method, include_private = false)
    value.respond_to?(method, include_private)
    end
    end
    end
    alias_method :description, :inspect
    end

    class Future < BasicObject
    def self.new(&block)
    raise ArgumentError, "You can initalize a Future without a block" unless block_given?
    ::Kernel.raise(::ArgumentError, "Cannot store a Dispatch::Future that requires an argument") if block.arity > 0
    self.alloc.initialization(block)
    end

    def initialization(block)
    init
    @promise = Promise.new(&block)
    self
    end

    def when_done(&block); @promise.when_done(block);end

    def description
    if !@promise.done?
    NSString.stringWithString(super.gsub(/>/, ' running...>'))
    else
    value.inspect
    end
    end

    def value
    @promise.value
    end

    def method_missing(method, *args, &block)
    @promise.send(method, *args, &block)
    end

    alias_method :inspect, :description
    end
    end

    the_future = Future.new { sleep 1; 6 / 2 }
    the_future.completed { |value| value = 10 }
    the_future = Dispatch::Future.new { sleep 1; 6 / 2 }
    the_future.when_done { |value| value = 10 }
    p the_future # <Future: 0x400cf57c0 running...>

    calculation = Future.new { sleep 2; 4 * 4 }
    calculation = Dispatch::Future.new { sleep 2; 4 * 4 }
    p calculation # <Future: 0x400cf6ee0 running...>
    calculation.completed { puts calculation + 10 } # 26
    p calculation + 2 # 18
    calculation.when_done { puts calculation + 10 } # 26
    p calculation + 2 # 18
  14. seanlilmateus revised this gist Nov 23, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -27,7 +27,7 @@ def initialization(block)

    def inspect
    if !@value
    description.gsub(">", " running...>")
    "#{description} still running..."
    else
    @value.inspect
    end
  15. seanlilmateus renamed this gist Nov 23, 2012. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  16. seanlilmateus created this gist Nov 23, 2012.
    60 changes: 60 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,60 @@
    class Future < BasicObject

    # completation callback, I don't like it either :-P
    def completed(&block); @success = block; end

    # MacRuby and Rubymotion BasicObject#initialize doesn't like blocks, so we have to do this
    def self.new(&block)
    raise ArgumentError, "You can initalize a Future without a block" unless block_given?
    self.alloc.initialization(block)
    end

    # setup Grand Central Dispatch concurrent Queue and Group
    def initialization(block)
    init
    @value = @success = false
    # Each thread gets its own FIFO queue upon which we will dispatch
    # the delayed computation passed in the &block variable.
    ::Thread.current[:futures] ||= ::Dispatch::Queue.concurrent("org.macruby.futures-#{::Thread.current.object_id}")
    # Groups are just simple layers on top of semaphores.
    @group = ::Dispatch::Group.new
    # Asynchronously dispatch the future to the thread-local queue.
    ::Thread.current[:futures].async(@group) { @value = block.call }
    @group.notify(::Thread.current[:futures]) { @success[@value] if @success }
    self
    end


    def inspect
    if !@value
    description.gsub(">", " running...>")
    else
    @value.inspect
    end
    end

    def value
    # Wait fo the computation to finish. If it has already finished, then
    # just return the value in question.
    @group.wait
    @value
    end

    def method_missing(method, *args, &block)
    value.send(method, *args, &block)
    end

    def respond_to_missing?(method, include_private = false)
    value.respond_to?(method, include_private)
    end
    end


    the_future = Future.new { sleep 1; 6 / 2 }
    the_future.completed { |value| value = 10 }
    p the_future # <Future: 0x400cf57c0 running...>

    calculation = Future.new { sleep 2; 4 * 4 }
    p calculation # <Future: 0x400cf6ee0 running...>
    calculation.completed { puts calculation + 10 } # 26
    p calculation + 2 # 18