Skip to content

Instantly share code, notes, and snippets.

@pcapriotti
Created June 23, 2010 22:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pcapriotti/450648 to your computer and use it in GitHub Desktop.
Save pcapriotti/450648 to your computer and use it in GitHub Desktop.
Reactive programming in ruby
# 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