tusk 1.0.0 → 1.1.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/CHANGELOG.rdoc +7 -0
- data/Manifest.txt +2 -0
- data/README.markdown +1 -0
- data/lib/tusk.rb +3 -2
- data/lib/tusk/observable/drb.rb +178 -0
- data/lib/tusk/observable/redis.rb +14 -10
- data/test/helper.rb +18 -0
- data/test/observable/test_drb.rb +76 -0
- metadata +80 -88
data/CHANGELOG.rdoc
CHANGED
data/Manifest.txt
CHANGED
@@ -5,9 +5,11 @@ README.markdown
|
|
5
5
|
Rakefile
|
6
6
|
lib/tusk.rb
|
7
7
|
lib/tusk/latch.rb
|
8
|
+
lib/tusk/observable/drb.rb
|
8
9
|
lib/tusk/observable/pg.rb
|
9
10
|
lib/tusk/observable/redis.rb
|
10
11
|
test/helper.rb
|
12
|
+
test/observable/test_drb.rb
|
11
13
|
test/observable/test_pg.rb
|
12
14
|
test/observable/test_redis.rb
|
13
15
|
test/redis-test.conf
|
data/README.markdown
CHANGED
data/lib/tusk.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
#
|
4
4
|
# Tusk::Observers::Redis offers an Observer API with Redis as the
|
5
5
|
# message bus. Tusk::Observers::PG offers and Observer API with
|
6
|
-
# PostgreSQL as the message bus.
|
6
|
+
# PostgreSQL as the message bus. Tusk::Observers::DRb offers an
|
7
|
+
# Observer API with DRb as the message bus.
|
7
8
|
module Tusk
|
8
|
-
VERSION = '1.
|
9
|
+
VERSION = '1.1.0'
|
9
10
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'drb'
|
2
|
+
require 'digest/md5'
|
3
|
+
|
4
|
+
module Tusk
|
5
|
+
module Observable
|
6
|
+
###
|
7
|
+
# An observer implementation for DRb. This module requires that
|
8
|
+
# you start a DRb server, which can be done via Server.start
|
9
|
+
#
|
10
|
+
# This observer works across processes.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# require 'tusk/observable/drb'
|
15
|
+
#
|
16
|
+
# class Timer
|
17
|
+
# include Tusk::Observable::DRb
|
18
|
+
#
|
19
|
+
# # Start the DRb server. Do this once
|
20
|
+
# Thread.new { Server.start }
|
21
|
+
#
|
22
|
+
# def tick
|
23
|
+
# changed
|
24
|
+
# notify_observers
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# class Listener
|
29
|
+
# def update
|
30
|
+
# puts "got update"
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# timer = Timer.new
|
35
|
+
#
|
36
|
+
# fork do
|
37
|
+
# timer.add_observer Listener.new
|
38
|
+
# sleep # put the process to sleep so it doesn't exit
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# loop do
|
42
|
+
# timer.tick
|
43
|
+
# sleep 1
|
44
|
+
# end
|
45
|
+
module DRb
|
46
|
+
class Server
|
47
|
+
URI = 'druby://localhost:8787'
|
48
|
+
|
49
|
+
def self.start
|
50
|
+
::DRb.start_service URI, new
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.stop
|
54
|
+
::DRb.stop_service
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize
|
58
|
+
@channels = Hash.new { |h,k| h[k] = {} }
|
59
|
+
end
|
60
|
+
|
61
|
+
def watch channel, proxy
|
62
|
+
@channels[channel][proxy] = proxy
|
63
|
+
end
|
64
|
+
|
65
|
+
def signal channel, args
|
66
|
+
@channels[channel].each { |proxy,|
|
67
|
+
proxy.notify args
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete_observer channel, o
|
72
|
+
@channels[channel].delete o
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete channel
|
76
|
+
@channels.delete channel
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Proxy # :nodoc:
|
81
|
+
include ::DRb::DRbUndumped
|
82
|
+
|
83
|
+
def initialize d, func
|
84
|
+
@delegate = d
|
85
|
+
@func = func
|
86
|
+
end
|
87
|
+
|
88
|
+
def notify args
|
89
|
+
@delegate.send(@func, *args)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.extended klass
|
94
|
+
super
|
95
|
+
|
96
|
+
klass.instance_eval do
|
97
|
+
@bus = DRbObject.new_with_uri uri
|
98
|
+
@observer_state = false
|
99
|
+
@subscribers = {}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def initialize *args
|
104
|
+
super
|
105
|
+
|
106
|
+
@bus = DRbObject.new_with_uri uri
|
107
|
+
@observer_state = false
|
108
|
+
@subscribers = {}
|
109
|
+
end
|
110
|
+
|
111
|
+
# Add +observer+ as an observer to this object. The +object+ will
|
112
|
+
# receive a notification when #changed? returns true and #notify_observers
|
113
|
+
# is called.
|
114
|
+
#
|
115
|
+
# +func+ method is called on +object+ when notifications are sent.
|
116
|
+
def add_observer object, func = :update
|
117
|
+
unless ::DRb.thread && ::DRb.thread.alive?
|
118
|
+
::DRb.start_service
|
119
|
+
end
|
120
|
+
|
121
|
+
proxy = Proxy.new object, func
|
122
|
+
@subscribers[object] = proxy
|
123
|
+
@bus.watch channel, proxy
|
124
|
+
end
|
125
|
+
|
126
|
+
# If this object's #changed? state is true, this method will notify
|
127
|
+
# observing objects.
|
128
|
+
def notify_observers(*args)
|
129
|
+
return unless changed?
|
130
|
+
@bus.signal channel, args
|
131
|
+
changed false
|
132
|
+
end
|
133
|
+
|
134
|
+
# Remove all observers associated with this object *in the current
|
135
|
+
# process*. This method will not impact observers of this object in
|
136
|
+
# other processes.
|
137
|
+
def delete_observers
|
138
|
+
@bus.delete channel
|
139
|
+
@subscribers.clear
|
140
|
+
end
|
141
|
+
|
142
|
+
# Remove +observer+ so that it will no longer receive notifications.
|
143
|
+
def delete_observer o
|
144
|
+
proxy = @subscribers.delete o
|
145
|
+
@bus.delete_observer channel, proxy
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns true if this object's state has been changed since the last
|
149
|
+
# call to #notify_observers.
|
150
|
+
def changed?
|
151
|
+
@observer_state
|
152
|
+
end
|
153
|
+
|
154
|
+
# Set the changed state of this object. Notifications will be sent only
|
155
|
+
# if the changed +state+ is a truthy object.
|
156
|
+
def changed state = true
|
157
|
+
@observer_state = state
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns the number of observers associated with this object *in the
|
161
|
+
# current process*. If the object is observed across multiple processes,
|
162
|
+
# the returned count will not reflect the other processes.
|
163
|
+
def count_observers
|
164
|
+
@subscribers.length
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def uri
|
170
|
+
Server::URI
|
171
|
+
end
|
172
|
+
|
173
|
+
def channel
|
174
|
+
"a" + Digest::MD5.hexdigest("#{self.class.name}#{object_id}")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -15,33 +15,33 @@ module Tusk
|
|
15
15
|
#
|
16
16
|
# require 'redis'
|
17
17
|
# require 'tusk/observable/redis'
|
18
|
-
#
|
18
|
+
#
|
19
19
|
# class Timer
|
20
20
|
# include Tusk::Observable::Redis
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# def tick
|
23
23
|
# changed
|
24
24
|
# notify_observers
|
25
25
|
# end
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# def connection
|
28
28
|
# Thread.current[:conn] ||= ::Redis.new
|
29
29
|
# end
|
30
30
|
# end
|
31
|
-
#
|
31
|
+
#
|
32
32
|
# class Listener
|
33
33
|
# def update
|
34
34
|
# puts "got update"
|
35
35
|
# end
|
36
36
|
# end
|
37
|
-
#
|
37
|
+
#
|
38
38
|
# timer = Timer.new
|
39
|
-
#
|
39
|
+
#
|
40
40
|
# fork do
|
41
41
|
# timer.add_observer Listener.new
|
42
42
|
# sleep # put the process to sleep so it doesn't exit
|
43
43
|
# end
|
44
|
-
#
|
44
|
+
#
|
45
45
|
# loop do
|
46
46
|
# timer.tick
|
47
47
|
# sleep 1
|
@@ -100,9 +100,9 @@ module Tusk
|
|
100
100
|
|
101
101
|
# If this object's #changed? state is true, this method will notify
|
102
102
|
# observing objects.
|
103
|
-
def notify_observers
|
103
|
+
def notify_observers(*args)
|
104
104
|
return unless changed?
|
105
|
-
connection.publish channel,
|
105
|
+
connection.publish channel, payload_coder.dump(args)
|
106
106
|
changed false
|
107
107
|
end
|
108
108
|
|
@@ -148,6 +148,10 @@ module Tusk
|
|
148
148
|
"a" + Digest::MD5.hexdigest("#{self.class.name}#{object_id}")
|
149
149
|
end
|
150
150
|
|
151
|
+
def payload_coder
|
152
|
+
Marshal
|
153
|
+
end
|
154
|
+
|
151
155
|
def start_listener latch
|
152
156
|
connection.subscribe(channel, control_channel) do |on|
|
153
157
|
on.subscribe { |c| latch.release }
|
@@ -158,7 +162,7 @@ module Tusk
|
|
158
162
|
else
|
159
163
|
@sub_lock.synchronize do
|
160
164
|
subscribers.fetch(c, {}).each do |object,m|
|
161
|
-
object.send m
|
165
|
+
object.send m, *payload_coder.load(message)
|
162
166
|
end
|
163
167
|
end
|
164
168
|
end
|
data/test/helper.rb
CHANGED
@@ -13,6 +13,12 @@ module Tusk
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
class PayloadQueueingObserver < QueueingObserver
|
17
|
+
def update(*args)
|
18
|
+
@q.push(args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
16
22
|
def test_changed?
|
17
23
|
o = build_observable
|
18
24
|
refute o.changed?
|
@@ -69,6 +75,18 @@ module Tusk
|
|
69
75
|
assert_equal :foo, q.pop
|
70
76
|
end
|
71
77
|
|
78
|
+
def test_notification_payload
|
79
|
+
o = build_observable
|
80
|
+
q = Queue.new
|
81
|
+
|
82
|
+
o.add_observer PayloadQueueingObserver.new q
|
83
|
+
|
84
|
+
o.changed
|
85
|
+
o.notify_observers :payload
|
86
|
+
|
87
|
+
assert_equal [:payload], q.pop
|
88
|
+
end
|
89
|
+
|
72
90
|
def test_multiple_observers
|
73
91
|
o = build_observable
|
74
92
|
q = Queue.new
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'tusk/observable/drb'
|
3
|
+
|
4
|
+
module Tusk
|
5
|
+
module Observable
|
6
|
+
class TestDRb < TestCase
|
7
|
+
include ObserverTests
|
8
|
+
|
9
|
+
class Timer
|
10
|
+
include Tusk::Observable::DRb
|
11
|
+
|
12
|
+
def tick
|
13
|
+
changed
|
14
|
+
notify_observers
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def setup
|
19
|
+
super
|
20
|
+
DRb::Server.start
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
super
|
25
|
+
DRb::Server.stop
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_no_connection
|
29
|
+
skip "not implementing for now"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def build_observable
|
35
|
+
Timer.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def observer_module
|
39
|
+
Tusk::Observable::DRb
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class TestClassDRb < TestCase
|
44
|
+
include ObserverTests
|
45
|
+
|
46
|
+
def setup
|
47
|
+
super
|
48
|
+
DRb::Server.start
|
49
|
+
end
|
50
|
+
|
51
|
+
def teardown
|
52
|
+
super
|
53
|
+
DRb::Server.stop
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_observable
|
57
|
+
Class.new {
|
58
|
+
extend Tusk::Observable::DRb
|
59
|
+
|
60
|
+
def self.tick
|
61
|
+
changed
|
62
|
+
notify_observers
|
63
|
+
end
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_no_connection
|
68
|
+
skip "not implementing for now"
|
69
|
+
end
|
70
|
+
|
71
|
+
def observer_module
|
72
|
+
Tusk::Observable::DRb
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
metadata
CHANGED
@@ -1,100 +1,97 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: tusk
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 0
|
10
|
-
version: 1.0.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Aaron Patterson
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-10-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: minitest
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '4.0'
|
22
|
+
type: :development
|
22
23
|
prerelease: false
|
23
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rdoc
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
24
33
|
none: false
|
25
|
-
requirements:
|
34
|
+
requirements:
|
26
35
|
- - ~>
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 2
|
31
|
-
- 11
|
32
|
-
version: "2.11"
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.10'
|
33
38
|
type: :development
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: pg
|
37
39
|
prerelease: false
|
38
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.10'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: pg
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
|
-
requirements:
|
50
|
+
requirements:
|
41
51
|
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
hash: 39
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
- 14
|
47
|
-
- 0
|
52
|
+
- !ruby/object:Gem::Version
|
48
53
|
version: 0.14.0
|
49
54
|
type: :development
|
50
|
-
version_requirements: *id002
|
51
|
-
- !ruby/object:Gem::Dependency
|
52
|
-
name: rdoc
|
53
55
|
prerelease: false
|
54
|
-
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
55
57
|
none: false
|
56
|
-
requirements:
|
58
|
+
requirements:
|
57
59
|
- - ~>
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
|
60
|
-
|
61
|
-
- 3
|
62
|
-
- 10
|
63
|
-
version: "3.10"
|
64
|
-
type: :development
|
65
|
-
version_requirements: *id003
|
66
|
-
- !ruby/object:Gem::Dependency
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.14.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
67
63
|
name: hoe
|
68
|
-
|
69
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
70
65
|
none: false
|
71
|
-
requirements:
|
66
|
+
requirements:
|
72
67
|
- - ~>
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
|
75
|
-
segments:
|
76
|
-
- 2
|
77
|
-
- 13
|
78
|
-
version: "2.13"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.0'
|
79
70
|
type: :development
|
80
|
-
|
81
|
-
|
82
|
-
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.0'
|
78
|
+
description: ! 'Tusk is a minimal pub / sub system with multiple observer strategies.
|
79
|
+
|
83
80
|
Tusk builds upon the Observer API from stdlib in order to provide a mostly
|
81
|
+
|
84
82
|
consistent API for building cross thread or process pub / sub systems.
|
85
|
-
|
86
|
-
|
87
|
-
|
83
|
+
|
84
|
+
|
85
|
+
Currently, Tusk supports Redis and PostgreSQL as message bus back ends.'
|
86
|
+
email:
|
88
87
|
- aaron@tenderlovemaking.com
|
89
88
|
executables: []
|
90
|
-
|
91
89
|
extensions: []
|
92
|
-
|
93
|
-
extra_rdoc_files:
|
94
|
-
- Manifest.txt
|
90
|
+
extra_rdoc_files:
|
95
91
|
- CHANGELOG.rdoc
|
92
|
+
- Manifest.txt
|
96
93
|
- README.markdown
|
97
|
-
files:
|
94
|
+
files:
|
98
95
|
- .autotest
|
99
96
|
- CHANGELOG.rdoc
|
100
97
|
- Manifest.txt
|
@@ -102,47 +99,42 @@ files:
|
|
102
99
|
- Rakefile
|
103
100
|
- lib/tusk.rb
|
104
101
|
- lib/tusk/latch.rb
|
102
|
+
- lib/tusk/observable/drb.rb
|
105
103
|
- lib/tusk/observable/pg.rb
|
106
104
|
- lib/tusk/observable/redis.rb
|
107
105
|
- test/helper.rb
|
106
|
+
- test/observable/test_drb.rb
|
108
107
|
- test/observable/test_pg.rb
|
109
108
|
- test/observable/test_redis.rb
|
110
109
|
- test/redis-test.conf
|
111
110
|
- .gemtest
|
112
111
|
homepage: http://github.com/tenderlove/tusk
|
113
112
|
licenses: []
|
114
|
-
|
115
113
|
post_install_message:
|
116
|
-
rdoc_options:
|
114
|
+
rdoc_options:
|
117
115
|
- --main
|
118
116
|
- README.markdown
|
119
|
-
require_paths:
|
117
|
+
require_paths:
|
120
118
|
- lib
|
121
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
120
|
none: false
|
123
|
-
requirements:
|
124
|
-
- -
|
125
|
-
- !ruby/object:Gem::Version
|
126
|
-
|
127
|
-
|
128
|
-
- 0
|
129
|
-
version: "0"
|
130
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
126
|
none: false
|
132
|
-
requirements:
|
133
|
-
- -
|
134
|
-
- !ruby/object:Gem::Version
|
135
|
-
|
136
|
-
segments:
|
137
|
-
- 0
|
138
|
-
version: "0"
|
127
|
+
requirements:
|
128
|
+
- - ! '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
139
131
|
requirements: []
|
140
|
-
|
141
132
|
rubyforge_project: tusk
|
142
|
-
rubygems_version: 1.8.
|
133
|
+
rubygems_version: 1.8.24
|
143
134
|
signing_key:
|
144
135
|
specification_version: 3
|
145
136
|
summary: Tusk is a minimal pub / sub system with multiple observer strategies
|
146
|
-
test_files:
|
137
|
+
test_files:
|
138
|
+
- test/observable/test_drb.rb
|
147
139
|
- test/observable/test_pg.rb
|
148
140
|
- test/observable/test_redis.rb
|