gbs-signal-slot 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +215 -0
- data/lib/gbs_signal_slot.rb +159 -0
- data/test/tc_gbs_signal_slot.rb +175 -0
- metadata +55 -0
data/README
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
= GBS::SignalSlot v. 1.0.0
|
2
|
+
Greenblack Software signal/slot pattern implementation for Ruby language
|
3
|
+
|
4
|
+
== GBS::SignalSlot User's Guide
|
5
|
+
|
6
|
+
1. Introduction
|
7
|
+
2. Connecting slots
|
8
|
+
3. Disconnecting slots
|
9
|
+
4. Class signals
|
10
|
+
5. MIT License
|
11
|
+
|
12
|
+
=== Introduction
|
13
|
+
|
14
|
+
GBS::SignalSlot is a signal/slot mechanism implementation done in Ruby.
|
15
|
+
The signal/slot pattern is a way of implementing the Observer design pattern.
|
16
|
+
Such implementation was introduced by Trolltech in their famous QT library.
|
17
|
+
Indeed the goal was achieved and resulted in code size reduction,
|
18
|
+
especially so called boilerplate code.
|
19
|
+
|
20
|
+
With the Ruby facilities of meta-programming the whole implementation can be
|
21
|
+
done within one single file with just a couple of lines. Such implementation was
|
22
|
+
proposed by Niklas Frykholm and improved by Nobuyoshi Nakada on <b>comp.lang.ruby</b>
|
23
|
+
usenet group (01-02-2002). Their work was the entry point to the Greenblack Software
|
24
|
+
implementation. In fact, the idea and main tricks are the same. Our improvements
|
25
|
+
were concerning on class methods support, better error checking, some additional
|
26
|
+
features and clean output.
|
27
|
+
|
28
|
+
=== Connecting slots
|
29
|
+
|
30
|
+
The best way to see what GBS::SignalSlot is and to get into it a bit more, is to look
|
31
|
+
thoroughly at the following examples.
|
32
|
+
|
33
|
+
First, let we create a class, a container for a value. Of course with signals
|
34
|
+
our container will emit.
|
35
|
+
|
36
|
+
require "rubygems"
|
37
|
+
require "gbs_signal_slot"
|
38
|
+
|
39
|
+
class ValueContainer
|
40
|
+
include GBS::SignalSlot
|
41
|
+
attr_reader :value
|
42
|
+
signal :new_value # signals are defined just like readers or writers
|
43
|
+
|
44
|
+
def initialize(v)
|
45
|
+
@value = v
|
46
|
+
end
|
47
|
+
|
48
|
+
def value=(v)
|
49
|
+
@value = v
|
50
|
+
new_value(v) # signal activation
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
As you can guess, the signals are defined just like obvious attributes and
|
55
|
+
emitted by calling a method named after the symbol indicating a signal.
|
56
|
+
Their parameters are matter of our arbitrary choice, however we should
|
57
|
+
stick to our once defined convention. Here, we are passing the new
|
58
|
+
value that our container gets.
|
59
|
+
|
60
|
+
Particularly, the _new_value(v)_ method does not exist in the code.
|
61
|
+
It is added by the module behind the scene, as a private method.
|
62
|
+
|
63
|
+
Next, we can use some mixed-in methods that will allow us to connect and
|
64
|
+
disconnect signals and slots. The simplest way is to connect to anonymous closures:
|
65
|
+
|
66
|
+
vc = ValueContainer.new(0)
|
67
|
+
vc.connect(:new_value) { |v| puts "New value is #{v}" }
|
68
|
+
|
69
|
+
Now, each use of the value writer will result in sending relevant string
|
70
|
+
to the output:
|
71
|
+
|
72
|
+
vc.value = 1
|
73
|
+
# >> New value is 1
|
74
|
+
|
75
|
+
But to get more fun let's make a _Tracker_ object with both class
|
76
|
+
and instance methods which will act as slots for our _new_value_ signal.
|
77
|
+
|
78
|
+
class Tracker
|
79
|
+
def track_value(v)
|
80
|
+
puts "value changed to #{v}!"
|
81
|
+
end
|
82
|
+
def self.track_value(v)
|
83
|
+
puts "class slot method executed"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Now we may connect our signal to newly created tracker's slots.
|
88
|
+
|
89
|
+
t = Tracker.new
|
90
|
+
vc.connect(:new_value, :track_value, t) # track_value instance method
|
91
|
+
vc.connect(:new_value, :track_value, Tracker) # track_value class method
|
92
|
+
vc.value = 2
|
93
|
+
|
94
|
+
# >> New value is 2
|
95
|
+
# >> value changed to 2
|
96
|
+
# >> class slot method excecuted
|
97
|
+
|
98
|
+
The last possibility is to connect signal to a global method. Like below:
|
99
|
+
|
100
|
+
def track_fun(v)
|
101
|
+
puts "Global function"
|
102
|
+
end
|
103
|
+
|
104
|
+
vc.connect(:new_value, :track_fun) # that's all
|
105
|
+
vc.value = 3
|
106
|
+
|
107
|
+
# >> New value is 3
|
108
|
+
# >> value changed to 3
|
109
|
+
# >> class slot method excecuted
|
110
|
+
# >> Global function
|
111
|
+
|
112
|
+
It is so easy because the _obj_ parameter is set to _Object_ by default. Every global
|
113
|
+
method we create belongs to the _Object_ class, so we do not have to specify
|
114
|
+
anything else at all.
|
115
|
+
|
116
|
+
=== Disconnecting slots
|
117
|
+
|
118
|
+
Ok, now it is time to clean everything up. The connected slots can be disconnected
|
119
|
+
using a somewhat contrary method: _disconnect_. Let's get the rid of the global function:
|
120
|
+
|
121
|
+
vc.disconnect(:new_value, :track_fun)
|
122
|
+
vc.value = 4
|
123
|
+
|
124
|
+
# >> New value is 4
|
125
|
+
# >> value changed to 4
|
126
|
+
# >> class slot method excecuted
|
127
|
+
|
128
|
+
As we see the global function call is removed. Similarly we may cut off other slot methods:
|
129
|
+
|
130
|
+
vc.disconnect(:new_value, :track_value, t)
|
131
|
+
vc.disconnect(:new_value, :track_value, Tracker)
|
132
|
+
vc.value = 5
|
133
|
+
|
134
|
+
# >> New value is 5
|
135
|
+
|
136
|
+
Ok. But how can we deal with the anonymous closure? There are two ways. The first one
|
137
|
+
is to get the name, a reference saying precisely, to the proc object. The second way
|
138
|
+
is to cut off all slots at once. Here is the action:
|
139
|
+
|
140
|
+
blah_proc = vc.connect(:new_value) { |v| puts "blah" } # returns the proc object
|
141
|
+
vc.value = 6
|
142
|
+
|
143
|
+
# >> New value is 6
|
144
|
+
# >> blah
|
145
|
+
|
146
|
+
vc.disconnect(:new_value, &blah_proc) # disconnecting the proc object
|
147
|
+
vc.value = 7
|
148
|
+
|
149
|
+
# >> New value is 7
|
150
|
+
|
151
|
+
If we do not have the proc object, the only way is to disconnect all slots.
|
152
|
+
|
153
|
+
vc.disconnect(:new_value) # disconnects all slots connected to the :new_value
|
154
|
+
vc.value = 8
|
155
|
+
|
156
|
+
# Nothing to print.
|
157
|
+
|
158
|
+
=== Class signals
|
159
|
+
|
160
|
+
GBS::SignalSlot module can be used with class methods as well
|
161
|
+
(within classes or modules). The only difference is the use of
|
162
|
+
_class_signal_ method instead of _signal_.
|
163
|
+
|
164
|
+
class SomeClass
|
165
|
+
include GBS::SignalSlot
|
166
|
+
|
167
|
+
signal :signal1
|
168
|
+
class_signal :signal2
|
169
|
+
|
170
|
+
def some_method
|
171
|
+
signal1 # activation
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.some_other_method
|
175
|
+
signal2 # class signal activation
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
some_instance.connect(:signal1) { puts "instance signal" }
|
180
|
+
SomeClass.connect(:signal2) { puts "class signal" }
|
181
|
+
# etc.
|
182
|
+
|
183
|
+
The way we activate signals is still identical. It is even possible
|
184
|
+
to use the same signal symbol for both contexts (class and instance)
|
185
|
+
at the same time. The contexts will not be messed up and the right
|
186
|
+
signals will activate right slots as we had defined before.
|
187
|
+
|
188
|
+
Such distinction (between _signal_ and _class_signal_) is due to readiness
|
189
|
+
of class/module definitions. From the technical point of view it is easy to
|
190
|
+
embed class and instance level behaviors into one method. The other reason is
|
191
|
+
the fewer amount of code generated and inserted into classes.
|
192
|
+
|
193
|
+
=== MIT License
|
194
|
+
|
195
|
+
The MIT License
|
196
|
+
|
197
|
+
Copyright (c) 2008 Greenblack Software
|
198
|
+
|
199
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
200
|
+
of this software and associated documentation files (the "Software"), to deal
|
201
|
+
in the Software without restriction, including without limitation the rights
|
202
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
203
|
+
copies of the Software, and to permit persons to whom the Software is
|
204
|
+
furnished to do so, subject to the following conditions:
|
205
|
+
|
206
|
+
The above copyright notice and this permission notice shall be included in
|
207
|
+
all copies or substantial portions of the Software.
|
208
|
+
|
209
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
210
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
211
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
212
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
213
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
214
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
215
|
+
THE SOFTWARE.
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# Copyright (c) 2008 Greenblack Software. Licensed under the MIT License.
|
2
|
+
# For more info please read the README or visit www.greenblacksoftware.com
|
3
|
+
# ----
|
4
|
+
# Based on Niklas Frykholm and Nobuyoshi Nakada signal/slot pattern implementations
|
5
|
+
# posted to comp.lang.ruby usenet group on 01-02-2002
|
6
|
+
|
7
|
+
# A default namespace used by Greenblack Software projects.
|
8
|
+
module GBS
|
9
|
+
|
10
|
+
# A module providing signal/slot mechanism.
|
11
|
+
# ----
|
12
|
+
# Copyright (c) 2008 Greenblack Software. Licensed under the MIT License.
|
13
|
+
# For more info please read the README or visit www.greenblacksoftware.com
|
14
|
+
# ----
|
15
|
+
# Based on Niklas Frykholm and Nobuyoshi Nakada signal/slot pattern implementations
|
16
|
+
# posted to comp.lang.ruby usenet group on 01-02-2002
|
17
|
+
module SignalSlot
|
18
|
+
|
19
|
+
# Connects a signal to a slot. The slot might be either a method referenced as
|
20
|
+
# a symbol (often together with a class or an instance) or just an anonymous closure
|
21
|
+
# passed as a block.
|
22
|
+
#
|
23
|
+
# :call-seq:
|
24
|
+
# sth.connect(signal) { ... } -> proc
|
25
|
+
# sth.connect(signal, slot) -> global slot method
|
26
|
+
# sth.connect(signal, slot, obj) -> obj.slot method
|
27
|
+
#
|
28
|
+
# === Parameters
|
29
|
+
# [+signal+]
|
30
|
+
# symbol defined in a class with the +signal+ or +class_signal+ method
|
31
|
+
# [+slot+]
|
32
|
+
# a method name referenced as a symbol object. If ommited the +connect+
|
33
|
+
# will try to bind a block
|
34
|
+
# [+obj+]
|
35
|
+
# an instance (for instance method), a class (for class methods)
|
36
|
+
# or nothing for global methods
|
37
|
+
#
|
38
|
+
# === Returns
|
39
|
+
# * determined procedure (method) object or +nil+ if only the +signal+ parameter
|
40
|
+
# was passed (without a block or a slot method name). It is important for
|
41
|
+
# disconnecting anonymous proc objects because this is the only way to obtain
|
42
|
+
# the reference. See also +GBS::SignalSlot#disconnect+
|
43
|
+
# ----
|
44
|
+
# === Examples
|
45
|
+
# [+sth.connect(:value_changed, :update_control, self)+]
|
46
|
+
# if the signal +:value_changed+ is emitted, the +self#update_control+
|
47
|
+
# will be executed
|
48
|
+
# [+sth.connect(:value_changed, update_status, MyClass)+]
|
49
|
+
# similar example but with +MyClass.update_status+ class method instead
|
50
|
+
# [+sth.connect(:value_changed, :global_update)+]
|
51
|
+
# now we are reffering to a global function (to something that
|
52
|
+
# belongs to the +Object+ class in fact as the default value of +obj+
|
53
|
+
# is set to +Object+)
|
54
|
+
# [+sth.connect(:value_changed) { |value| puts value }+]
|
55
|
+
# passing anonymous closure
|
56
|
+
def connect(signal, slot = nil, obj = Object, &pr)
|
57
|
+
s = slot ? obj.method(slot) : pr
|
58
|
+
raise ArgumentError, "No slot provided" unless s
|
59
|
+
((@_gbs_signals ||= {})[signal] ||= []) << s
|
60
|
+
s
|
61
|
+
end
|
62
|
+
|
63
|
+
# Disconnects a signal from a slot. The slot might be either a method referenced as
|
64
|
+
# a symbol together with a class or an instance or just an closure passed as a parameter
|
65
|
+
# (with & prefix)
|
66
|
+
#
|
67
|
+
# :call-seq:
|
68
|
+
# sth.disconnect(signal)
|
69
|
+
# sth.disconnect(signal, &proc)
|
70
|
+
# sth.disconnect(signal, slot)
|
71
|
+
# sth.disconnect(signal, slot, obj)
|
72
|
+
#
|
73
|
+
# === Parameters
|
74
|
+
# [+signal+]
|
75
|
+
# symbol defined in a class with the +signal+ or +class_signal+ method
|
76
|
+
# [+slot+]
|
77
|
+
# a method name referenced as a symbol object. If ommited the +connect+
|
78
|
+
# will try to bind the +&proc+. If the +&proc+ parameter
|
79
|
+
# is also omitted, all connections to the signal will be erased.
|
80
|
+
# It disconnects all slots.
|
81
|
+
# [+obj+]
|
82
|
+
# an instance (for instance method), a class (for class methods)
|
83
|
+
# or nothing for global methods
|
84
|
+
# [+&proc+]
|
85
|
+
# a proc object appended with the & prefix. This object can be
|
86
|
+
# obtained by the +GBS::SignalSlot#connect+ method with anonymous closure
|
87
|
+
# passed.
|
88
|
+
# ----
|
89
|
+
# === Examples
|
90
|
+
# [+sth.disconnect(:value_changed, :update_control, self)+]
|
91
|
+
# if the signal +:value_changed+ is emitted,
|
92
|
+
# the +self#update_control+ will not be executed anymore
|
93
|
+
# [+sth.disconnect(:value_changed, update_status, MyClass)+]
|
94
|
+
# similar example but with +MyClass.update_status+ class method instead
|
95
|
+
# [+sth.disconnect(:value_changed, :global_update)+]
|
96
|
+
# now we are reffering to a global function (to something that
|
97
|
+
# belongs to the +Object+ class in fact as the default value of +obj+
|
98
|
+
# is set to +Object+)
|
99
|
+
# [+sth.disconnect(:value_changed, &proc)+]
|
100
|
+
# the +proc+ object will not be called anymore on
|
101
|
+
# +:value_changed+ signal activation.
|
102
|
+
# [+sth.disconnect(:value_changed)+]
|
103
|
+
# +:value_changed+ totally disconnected from all slots
|
104
|
+
def disconnect(signal, slot = nil, obj = Object, &pr)
|
105
|
+
s = slot ? obj.method(slot) : pr
|
106
|
+
return unless @_gbs_signals and @_gbs_signals[signal]
|
107
|
+
if s
|
108
|
+
@_gbs_signals[signal].delete(s)
|
109
|
+
else
|
110
|
+
@_gbs_signals[signal] = []
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def _gbs_activate_signal(signal, *args, &pr)
|
115
|
+
return unless @_gbs_signals and @_gbs_signals[signal]
|
116
|
+
# little support for asynchronous connection
|
117
|
+
# adding/removing during execution
|
118
|
+
slots = @_gbs_signals[signal].clone
|
119
|
+
slots.each { |slot| slot.call(*args, &pr) }
|
120
|
+
end
|
121
|
+
|
122
|
+
module Sender
|
123
|
+
|
124
|
+
# Defines signals allowed to emit by an instance methods.
|
125
|
+
# Signals should be provided as symbols and activated by calling instance methods
|
126
|
+
# named after corresponding symbols. Those methods are appended dynamically.
|
127
|
+
def signal(*signals) # :doc:
|
128
|
+
signals.each { |s| module_eval method_str(s, false) }
|
129
|
+
end
|
130
|
+
|
131
|
+
# Defines signals allowed to emit by class methods.
|
132
|
+
# Signals should be provided as symbols and activated by calling class methods
|
133
|
+
# named after corresponding symbols. Those methods are appended dynamically.
|
134
|
+
def class_signal(*signals) # :doc:
|
135
|
+
signals.each { |s| module_eval method_str(s, true) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def method_str(signal, klass)
|
139
|
+
%Q{
|
140
|
+
def #{klass ? "self." : ""}#{signal}(*args, &pr)
|
141
|
+
_gbs_activate_signal(:#{signal}, *args, &pr)
|
142
|
+
end
|
143
|
+
private#{klass ? "_class_method" : ""} :#{signal}
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
private :signal, :class_signal, :method_str
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.append_features(klass)
|
151
|
+
super
|
152
|
+
klass.extend(Sender)
|
153
|
+
klass.extend(SignalSlot)
|
154
|
+
end
|
155
|
+
|
156
|
+
private :_gbs_activate_signal
|
157
|
+
private_class_method :append_features
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# Copyright (c) 2008 Greenblack Software. Licensed under the MIT License.
|
2
|
+
# For more info please read the README or visit www.greenblacksoftware.com
|
3
|
+
|
4
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
5
|
+
|
6
|
+
require "test/unit"
|
7
|
+
require "gbs_signal_slot"
|
8
|
+
|
9
|
+
$global_mock_value = nil
|
10
|
+
|
11
|
+
def global_mock_method(text)
|
12
|
+
$global_mock_value = text
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestSignalSlot < Test::Unit::TestCase
|
16
|
+
@@class_mock_value = nil
|
17
|
+
|
18
|
+
def setup
|
19
|
+
@val1 = nil
|
20
|
+
@val2 = nil
|
21
|
+
@@class_mock_value = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
class InstanceTest
|
25
|
+
include GBS::SignalSlot
|
26
|
+
|
27
|
+
signal :test_signal
|
28
|
+
|
29
|
+
def fire_test_sig(t)
|
30
|
+
test_signal(t)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module ModuleTest
|
35
|
+
include GBS::SignalSlot
|
36
|
+
|
37
|
+
class_signal :class_test_signal
|
38
|
+
|
39
|
+
def self.fire_class_test_sig(t)
|
40
|
+
class_test_signal(t)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class InstanceClassTest
|
45
|
+
include GBS::SignalSlot
|
46
|
+
|
47
|
+
signal :test_signal
|
48
|
+
class_signal :class_test_signal
|
49
|
+
|
50
|
+
def fire_test_sig(t)
|
51
|
+
test_signal(t)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.fire_class_test_sig(t)
|
55
|
+
class_test_signal(t)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def mock_slot_method(text)
|
60
|
+
@val1 = text
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.class_mock_method(text)
|
64
|
+
@@class_mock_value = text
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_connect_instance
|
68
|
+
it = InstanceTest.new
|
69
|
+
it.connect(:test_signal) { |text| @val2 = text }
|
70
|
+
it.connect(:test_signal, :mock_slot_method, self)
|
71
|
+
it.connect(:test_signal, :class_mock_method, TestSignalSlot)
|
72
|
+
it.connect(:test_signal, :global_mock_method)
|
73
|
+
it.fire_test_sig("a")
|
74
|
+
assert_equal("a", @val2)
|
75
|
+
assert_equal("a", @val1)
|
76
|
+
assert_equal("a", @@class_mock_value)
|
77
|
+
assert_equal("a", $global_mock_value)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_connect_module
|
81
|
+
ModuleTest.connect(:class_test_signal) { |text| @val2 = text }
|
82
|
+
ModuleTest.connect(:class_test_signal, :mock_slot_method, self)
|
83
|
+
ModuleTest.fire_class_test_sig("b")
|
84
|
+
assert_equal("b", @val2)
|
85
|
+
assert_equal("b", @val1)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_connect_mixed
|
89
|
+
obj = InstanceClassTest.new
|
90
|
+
obj.connect(:test_signal) { |text| @val2 = text }
|
91
|
+
obj.connect(:test_signal, :mock_slot_method, self)
|
92
|
+
obj.fire_test_sig("a")
|
93
|
+
assert_equal("a", @val2)
|
94
|
+
assert_equal("a", @val1)
|
95
|
+
InstanceClassTest.connect(:class_test_signal) { |text| @val2 = text }
|
96
|
+
InstanceClassTest.connect(:class_test_signal, :mock_slot_method, self)
|
97
|
+
InstanceClassTest.fire_class_test_sig("b")
|
98
|
+
assert_equal("b", @val2)
|
99
|
+
assert_equal("b", @val1)
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_disconnect
|
103
|
+
it = InstanceTest.new
|
104
|
+
proc = it.connect(:test_signal) { |text| @val2 = text }
|
105
|
+
it.connect(:test_signal, :mock_slot_method, self)
|
106
|
+
it.fire_test_sig(1)
|
107
|
+
assert_equal(1, @val2)
|
108
|
+
assert_equal(1, @val1)
|
109
|
+
@val2 = @val1 = nil
|
110
|
+
it.disconnect(:test_signal, &proc)
|
111
|
+
it.fire_test_sig(1)
|
112
|
+
assert_nil(@val2)
|
113
|
+
assert_not_nil(@val1)
|
114
|
+
@val2 = @val1 = nil
|
115
|
+
it.disconnect(:test_signal, :mock_slot_method, self)
|
116
|
+
it.fire_test_sig(1)
|
117
|
+
assert_nil(@val2)
|
118
|
+
assert_nil(@val1)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_disconnect_all
|
122
|
+
it = InstanceTest.new
|
123
|
+
it.connect(:test_signal) { |t| @val2 = t }
|
124
|
+
it.connect(:test_signal) { |t| @val1 = t }
|
125
|
+
it.fire_test_sig(1)
|
126
|
+
assert_equal(1, @val2)
|
127
|
+
assert_equal(1, @val1)
|
128
|
+
it.disconnect(:test_signal)
|
129
|
+
it.fire_test_sig(2)
|
130
|
+
assert_not_equal(2, @val2)
|
131
|
+
assert_not_equal(2, @val1)
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_signal_class_instance
|
135
|
+
ic = InstanceClassTest.new
|
136
|
+
ic.connect(:test_signal) { |t| @val1 = t }
|
137
|
+
InstanceClassTest.connect(:test_signal) { |t| @val2 = t}
|
138
|
+
ic.fire_test_sig(1)
|
139
|
+
assert_equal(1, @val1)
|
140
|
+
assert_nil(@val2)
|
141
|
+
InstanceClassTest.fire_class_test_sig(2)
|
142
|
+
assert_equal(1, @val1)
|
143
|
+
assert_nil(@val2)
|
144
|
+
end
|
145
|
+
|
146
|
+
class SpecialInstanceClassTest
|
147
|
+
include GBS::SignalSlot
|
148
|
+
signal :test_signal
|
149
|
+
class_signal :test_signal
|
150
|
+
|
151
|
+
def self.fire_test_sig(n)
|
152
|
+
test_signal(n)
|
153
|
+
end
|
154
|
+
def fire_test_sig(n)
|
155
|
+
test_signal(n)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_special_case
|
160
|
+
SpecialInstanceClassTest.connect(:test_signal) { |v| @val1 = v }
|
161
|
+
s = SpecialInstanceClassTest.new
|
162
|
+
s.connect(:test_signal) { |v| @val2 = v }
|
163
|
+
SpecialInstanceClassTest.fire_test_sig(10)
|
164
|
+
assert_equal(10, @val1)
|
165
|
+
assert_nil(@val2)
|
166
|
+
s.fire_test_sig(3)
|
167
|
+
assert_equal(10, @val1)
|
168
|
+
assert_equal(3, @val2)
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_bad_use
|
172
|
+
it = InstanceTest.new
|
173
|
+
assert_raise(ArgumentError) { it.connect(:test_signal) }
|
174
|
+
end
|
175
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gbs-signal-slot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Greenblack Software
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-04-06 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: info@greenblacksoftware.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- test/tc_gbs_signal_slot.rb
|
26
|
+
- lib/gbs_signal_slot.rb
|
27
|
+
- README
|
28
|
+
has_rdoc: true
|
29
|
+
homepage: www.greenblacksoftware.com
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: "0"
|
40
|
+
version:
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
requirements: []
|
48
|
+
|
49
|
+
rubyforge_project: gbs-signal-slot
|
50
|
+
rubygems_version: 1.1.0
|
51
|
+
signing_key:
|
52
|
+
specification_version: 2
|
53
|
+
summary: GBS::SignalSlot - a signal/slot mechanism in Ruby
|
54
|
+
test_files:
|
55
|
+
- test/tc_gbs_signal_slot.rb
|