Skip to content

Instantly share code, notes, and snippets.

@Narnach
Last active August 16, 2017 10:26
Show Gist options
  • Save Narnach/33661 to your computer and use it in GitHub Desktop.
Save Narnach/33661 to your computer and use it in GitHub Desktop.
First naive implementation of futures in Ruby
# Simple Future implementation, also known as a Promise in Javascript land.
# Based off an old gist ( https://gist.github.com/Narnach/33661 ) and modernized for Ruby 2.
#
# Wrap a block transparently in a Thread, and redirect any method calls to the `value` of that Thread, aka the return value of a Thread.
# This allows you to create code that runs async but becomes blocking when you try to interact with the result of it.
#
# future = Future.wrap { 1 + 2 }
# future + 3 # => 6
#
# Inherit from BasicObject to not get all the usual cruft and allow _all_ method calls to be proxy'd to the value.
class Future < BasicObject
def initialize(thread)
@thread = thread
end
def self.wrap(&block)
new(::Thread.new(&block))
end
# Allow comparison with the thread value
def ==(other)
@thread.value == other
end
# Allows `respond_to?` to work for method_missing implemented methods. Because all methods are implemented via method_missing, this is rather useful.
def respond_to_missing?(method, include_all=false)
@thread.value.respond_to?(method, include_all)
end
# Dispatch all method calls to the value of the thread.
def method_missing(*args, &block)
@thread.value.public_send(*args, &block)
end
end
describe Future do
it 'should return what the block returns' do
val = Future.wrap { 1 + 2 }
val.should == 3
end
it 'should allow transparent interaction with the return value' do
val = Future.wrap { sleep 0.01; 1 + 2 }
(val + 3).should == 6
end
end
# The snippet that started futures in Ruby
# Written by Wes Oldenbeuving, Erik Terpstra and Remco van 't Veer
# Created at the first meeting of ACK (for lack of a better name),
# the Amsterdam Coders Kollective.
def future(&block)
t = Thread.new(&block)
class << t
(instance_methods - %w[value __send__ __id__]).each do |meth|
eval("undef #{meth}")
end
def method_missing(*args, &block)
value.send(*args,&block)
end
end
t
end
f = future do
sleep 1
123
end
puts "present"
puts f * 5
puts "future"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment