gbs-signal-slot 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|