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.
@@ -1,3 +1,10 @@
1
+ === 1.1.0 / 2012-10-04
2
+
3
+ * Redis supports sending a payload with `notify_observers`
4
+ Thanks Collin Miller!
5
+
6
+ * DRb observer added
7
+
1
8
  === 1.0.0 / 2012-07-18
2
9
 
3
10
  * 1 major enhancement
@@ -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
@@ -15,6 +15,7 @@ Currently, Tusk supports Redis and PostgreSQL as message bus back ends.
15
15
  * Send message across processes
16
16
  * Supports Redis as a message bus
17
17
  * Supports PostgreSQL as a message bus
18
+ * Supports DRb as a message bus
18
19
 
19
20
  ## SYNOPSIS:
20
21
 
@@ -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.0.0'
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, nil
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
@@ -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
- hash: 23
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
- date: 2012-07-22 00:00:00 Z
19
- dependencies:
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
- requirement: &id001 !ruby/object:Gem::Requirement
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
- hash: 21
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
- requirement: &id002 !ruby/object:Gem::Requirement
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
- requirement: &id003 !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
55
57
  none: false
56
- requirements:
58
+ requirements:
57
59
  - - ~>
58
- - !ruby/object:Gem::Version
59
- hash: 19
60
- segments:
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
- prerelease: false
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
- hash: 25
75
- segments:
76
- - 2
77
- - 13
78
- version: "2.13"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.0'
79
70
  type: :development
80
- version_requirements: *id004
81
- description: |-
82
- Tusk is a minimal pub / sub system with multiple observer strategies.
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
- Currently, Tusk supports Redis and PostgreSQL as message bus back ends.
87
- email:
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
- hash: 3
127
- segments:
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
- hash: 3
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.22
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