Created
June 23, 2010 22:21
-
-
Save pcapriotti/450648 to your computer and use it in GitHub Desktop.
Reactive programming in ruby
This file contains 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 is a simple example of how to employ reactive programming techniques | |
# in ruby using Qt. | |
# | |
# This example uses Kaya's toolkit framework to keep things more compact, but | |
# it's possible to translate it back to pure QtRuby very easily. | |
# | |
# To try it out, download Kaya (http://github.com/pcapriotti/kaya), copy this | |
# file in its root directory and run: | |
# ruby -Ilib reactor.rb | |
require 'toolkit' | |
def main | |
# Create a Qt application, nothing fancy here | |
app = Qt::Application.new(ARGV) | |
# This creates, layouts and populates a window with a single push button. | |
# Kaya's toolkit abstraction allows us to express everything in a simple | |
# declarative DSL. | |
win = Qt::Widget.new.tap do |w| | |
w.setGUI(KDE::autogui(:main) do |b| | |
b.layout(:vertical) do |l| | |
l.widget :test, | |
:factory => Qt::PushButton, | |
:properties => { :text => "Test" } | |
end | |
end) | |
w.show | |
end | |
# Ok, here is the nice part. We handle button clicks in a synchronous way, | |
# waiting for a click at a time, and printing a sequential number on the | |
# console. | |
# No callbacks, no state machine. Note that wait_for doesn't block, and | |
# everything runs on a single thread. | |
# | |
Reactor.react do |r| | |
i = 0 | |
loop do | |
i += 1 | |
r.wait_for(win.test, :clicked) | |
puts i | |
end | |
end | |
# Finally, run the application | |
app.exec | |
end | |
# This is what makes everything work. Under the hood, the calls to react and | |
# wait_for juggle continuations to give us the illusion that our code is in | |
# control. | |
# | |
# What actually happens is that wait_for connects a signal, and then returns | |
# control to the main loop. When the signal is emitted, our code resumes from | |
# that point and is able to respond to the event, and possibly wait for others. | |
# | |
class Reactor | |
def self.react(&blk) | |
callcc do |cc| | |
r = new(cc) | |
blk[r] | |
r.outer.call | |
end | |
end | |
attr_reader :outer | |
def initialize(outer) | |
@outer = outer | |
end | |
def wait_for(obj, name) | |
callcc do |context| | |
connection = nil | |
connection = obj.on(name) do | |
connection.disconnect! | |
callcc do |@outer| | |
context.call | |
end | |
end | |
@outer.call | |
end | |
end | |
end | |
# This was a very simple example, and maybe doesn't even qualify as reactive | |
# programming, but it gives an idea of what kind of things are possible. | |
# Starting from this, one can think of implementing event streams, signals, | |
# and all that good stuff. | |
main if $0 == __FILE__ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment