Created
October 23, 2014 18:31
-
-
Save reu/52d0cb910d774d226938 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This class implements async evaluation by transparently executing the block on a different thread. | |
class Future < Lazy | |
def initialize(&block) | |
@attribute = ::Lazy.new(&block) | |
@thread = ::Thread.new { @attribute.__send__(:__call__) } | |
end | |
private | |
def __call__ | |
@thread.join | |
@attribute | |
end | |
end | |
module Kernel | |
def future(&block) | |
Future.new(&block) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This class implements lazy evaluation by transparently defer the execution of a block. | |
# The evaluation of the given block occurs if and when its result is first needed. | |
class Lazy < BasicObject | |
# As BasicObject does implement some methods, we must remove all of then to have a real | |
# proxy object. We just don't remove the methods whose starts and end with underlines, | |
# such as `__send__`. | |
instance_methods.each { |method| undef_method method unless method =~ /^(__.+__)$/ } | |
def initialize(&block) | |
@mutex = ::Mutex.new | |
@block = block | |
@called = false | |
end | |
# Here the magic happens, every method called on this object will be forwarded to the | |
# given block and then executed. | |
def method_missing(name, *args, &block) | |
__call__.__send__(name, *args, &block) | |
end | |
private | |
# This is where the block is evaluated. | |
def __call__ | |
# As multiple threads may execute the block, we need a mutex here to guarantee the thread safeness. | |
@mutex.synchronize do | |
# If the block was already called, we don't need to do anything | |
unless @called | |
begin | |
@result = @block.call | |
@called = true | |
rescue ::Exception => error # rubocop:disable Lint/RescueException | |
# We must store the possible error that the block could raise to re-raise it on the correct | |
# context | |
@error = error | |
end | |
end | |
end unless @called | |
# If the block raised an error, we should re-raise it here, otherwise, just return the block result. | |
@error ? ::Kernel.raise(@error) : @result | |
end | |
end | |
module Kernel | |
def lazy(&block) | |
Lazy.new(&block) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment