GBS::SignalSlot v. 1.0.0

Greenblack Software signal/slot pattern implementation for Ruby language

GBS::SignalSlot User’s Guide

  1. Introduction

  2. Connecting slots

  3. Disconnecting slots

  4. Class signals

  5. MIT License

Introduction

GBS::SignalSlot is a signal/slot mechanism implementation done in Ruby. The signal/slot pattern is a way of implementing the Observer design pattern. Such implementation was introduced by Trolltech in their famous QT library. Indeed the goal was achieved and resulted in code size reduction, especially so called boilerplate code.

With the Ruby facilities of meta-programming the whole implementation can be done within one single file with just a couple of lines. Such implementation was proposed by Niklas Frykholm and improved by Nobuyoshi Nakada on comp.lang.ruby usenet group (01-02-2002). Their work was the entry point to the Greenblack Software implementation. In fact, the idea and main tricks are the same. Our improvements were concerning on class methods support, better error checking, some additional features and clean output.

Connecting slots

The best way to see what GBS::SignalSlot is and to get into it a bit more, is to look thoroughly at the following examples.

First, let we create a class, a container for a value. Of course with signals our container will emit.

require “rubygems” require “gbs_signal_slot” class ValueContainer include GBS::SignalSlot attr_reader :value signal :new_value # signals are defined just like readers or writers

def initialize(v) @value = v end

def value=(v) @value = v new_value(v) # signal activation end end As you can guess, the signals are defined just like obvious attributes and emitted by calling a method named after the symbol indicating a signal. Their parameters are matter of our arbitrary choice, however we should stick to our once defined convention. Here, we are passing the new value that our container gets.

Particularly, the _new_value(v)_ method does not exist in the code. It is added by the module behind the scene, as a private method.

Next, we can use some mixed-in methods that will allow us to connect and disconnect signals and slots. The simplest way is to connect to anonymous closures:

vc = ValueContainer.new(0) vc.connect(:new_value) { |v| puts “New value is #v” } Now, each use of the value writer will result in sending relevant string to the output:

vc.value = 1 # >> New value is 1

But to get more fun let’s make a Tracker object with both class and instance methods which will act as slots for our new_value signal.

class Tracker def track_value(v) puts “value changed to #v!” end def self.track_value(v) puts “class slot method executed” end end

Now we may connect our signal to newly created tracker’s slots.

t = Tracker.new

		vc.connect(:new_value, :track_value, t)       # track_value instance method

vc.connect(:new_value, :track_value, Tracker) # track_value class method vc.value = 2

# >> New value is 2 # >> value changed to 2 # >> class slot method excecuted The last possibility is to connect signal to a global method. Like below:

def track_fun(v) puts “Global function” end vc.connect(:new_value, :track_fun) # that’s all vc.value = 3 # >> New value is 3 # >> value changed to 3 # >> class slot method excecuted # >> Global function It is so easy because the obj parameter is set to Object by default. Every global method we create belongs to the Object class, so we do not have to specify anything else at all.

Disconnecting slots

Ok, now it is time to clean everything up. The connected slots can be disconnected using a somewhat contrary method: disconnect. Let’s get the rid of the global function:

vc.disconnect(:new_value, :track_fun) vc.value = 4 # >> New value is 4 # >> value changed to 4 # >> class slot method excecuted As we see the global function call is removed. Similarly we may cut off other slot methods:

vc.disconnect(:new_value, :track_value, t) vc.disconnect(:new_value, :track_value, Tracker) vc.value = 5 # >> New value is 5 Ok. But how can we deal with the anonymous closure? There are two ways. The first one is to get the name, a reference saying precisely, to the proc object. The second way is to cut off all slots at once. Here is the action:

blah_proc = vc.connect(:new_value) { |v| puts “blah” } # returns the proc object vc.value = 6 # >> New value is 6 # >> blah vc.disconnect(:new_value, &blah_proc) # disconnecting the proc object vc.value = 7 # >> New value is 7 If we do not have the proc object, the only way is to disconnect all slots.

vc.disconnect(:new_value) # disconnects all slots connected to the :new_value vc.value = 8 # Nothing to print.

Class signals

GBS::SignalSlot module can be used with class methods as well (within classes or modules). The only difference is the use of class_signal method instead of signal.

class SomeClass include GBS::SignalSlot

signal :signal1 class_signal :signal2

def some_method signal1 # activation end

def self.some_other_method signal2 # class signal activation end end

some_instance.connect(:signal1) { puts “instance signal” } SomeClass.connect(:signal2) { puts “class signal” } # etc.

The way we activate signals is still identical. It is even possible to use the same signal symbol for both contexts (class and instance) at the same time. The contexts will not be messed up and the right signals will activate right slots as we had defined before.

Such distinction (between signal and class_signal) is due to readiness of class/module definitions. From the technical point of view it is easy to embed class and instance level behaviors into one method. The other reason is the fewer amount of code generated and inserted into classes.

MIT License

The MIT License

Copyright © 2008 Greenblack Software

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.