drbdump 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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.autotest +8 -0
- data/.gemtest +0 -0
- data/History.rdoc +4 -0
- data/Manifest.txt +30 -0
- data/README.rdoc +71 -0
- data/Rakefile +30 -0
- data/bin/drbdump +6 -0
- data/example/ping.rb +241 -0
- data/example/service_primes.rb +212 -0
- data/example/tuplespace_primes.rb +196 -0
- data/lib/drbdump.rb +714 -0
- data/lib/drbdump/loader.rb +75 -0
- data/lib/drbdump/message.rb +72 -0
- data/lib/drbdump/message_result.rb +84 -0
- data/lib/drbdump/message_send.rb +146 -0
- data/lib/drbdump/statistic.rb +90 -0
- data/lib/drbdump/statistics.rb +318 -0
- data/lib/drbdump/test_case.rb +91 -0
- data/test/arg.dump +0 -0
- data/test/drb_fin.dump +0 -0
- data/test/http.dump +0 -0
- data/test/ping.dump +0 -0
- data/test/ring.dump +0 -0
- data/test/test_drbdump.rb +283 -0
- data/test/test_drbdump_loader.rb +88 -0
- data/test/test_drbdump_message.rb +31 -0
- data/test/test_drbdump_message_result.rb +73 -0
- data/test/test_drbdump_message_send.rb +105 -0
- data/test/test_drbdump_statistic.rb +98 -0
- data/test/test_drbdump_statistics.rb +264 -0
- data/test/too_large_packet.pcap +0 -0
- metadata +183 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b3f2cd54ef03808d4d62ec58bbf6988a5a40657e
|
4
|
+
data.tar.gz: c83c65fb484a4aa329aa943b08c6b63f95061d68
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f8a8f622a06fdadd9495f13136bfc9c8acbb8a1e43d2a427c0cba184830324919f235916ecfb6249cb8637d7207ad7ef5c27fb29d6e19e7552a0cc39d2674af7
|
7
|
+
data.tar.gz: 8ff6002c16d643c7e88300ab91546595a247dc0e5b46a666c8f42bf08e2d89b6ad69f6991db645b4d8a0acb2e41674aba752065ac0b04fde9460ad83547d957c
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
Binary file
|
data/.autotest
ADDED
data/.gemtest
ADDED
File without changes
|
data/History.rdoc
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
.autotest
|
2
|
+
History.rdoc
|
3
|
+
Manifest.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
bin/drbdump
|
7
|
+
example/ping.rb
|
8
|
+
example/service_primes.rb
|
9
|
+
example/tuplespace_primes.rb
|
10
|
+
lib/drbdump.rb
|
11
|
+
lib/drbdump/loader.rb
|
12
|
+
lib/drbdump/message.rb
|
13
|
+
lib/drbdump/message_result.rb
|
14
|
+
lib/drbdump/message_send.rb
|
15
|
+
lib/drbdump/statistic.rb
|
16
|
+
lib/drbdump/statistics.rb
|
17
|
+
lib/drbdump/test_case.rb
|
18
|
+
test/arg.dump
|
19
|
+
test/drb_fin.dump
|
20
|
+
test/http.dump
|
21
|
+
test/ping.dump
|
22
|
+
test/ring.dump
|
23
|
+
test/test_drbdump.rb
|
24
|
+
test/test_drbdump_loader.rb
|
25
|
+
test/test_drbdump_message.rb
|
26
|
+
test/test_drbdump_message_result.rb
|
27
|
+
test/test_drbdump_message_send.rb
|
28
|
+
test/test_drbdump_statistic.rb
|
29
|
+
test/test_drbdump_statistics.rb
|
30
|
+
test/too_large_packet.pcap
|
data/README.rdoc
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
= drbdump
|
2
|
+
|
3
|
+
home :: https://github.com/drbrain/drbdump
|
4
|
+
rdoc :: http://docs.seattlerb.org/drbdump
|
5
|
+
bugs :: https://github.com/drbrain/drbdump/issues
|
6
|
+
|
7
|
+
== Description
|
8
|
+
|
9
|
+
drbdump is a tcpdump-like tool for the dRuby protocol. It allows you to
|
10
|
+
inspect the message and analyze the messages sent between your DRb-using
|
11
|
+
processes.
|
12
|
+
|
13
|
+
== Features
|
14
|
+
|
15
|
+
* Displays DRb message sends and results
|
16
|
+
* Displays Rinda service discovery announcements
|
17
|
+
* Records DRb message statistics including allocations and latency
|
18
|
+
|
19
|
+
== Problems
|
20
|
+
|
21
|
+
* Does not handle out-of-order or resent TCP packets
|
22
|
+
* Does not handle mDNS
|
23
|
+
* Does not support filtering
|
24
|
+
* TCP only
|
25
|
+
|
26
|
+
== Usage
|
27
|
+
|
28
|
+
To show DRb messages:
|
29
|
+
|
30
|
+
sudo drbdump
|
31
|
+
|
32
|
+
See the DRbDump class documentation for a full description of drbdump
|
33
|
+
|
34
|
+
== Installation
|
35
|
+
|
36
|
+
sudo gem install drbdump
|
37
|
+
|
38
|
+
== Developers
|
39
|
+
|
40
|
+
After checking out the source, run:
|
41
|
+
|
42
|
+
$ rake newb
|
43
|
+
|
44
|
+
This task will install any missing dependencies, run the tests/specs,
|
45
|
+
and generate the RDoc.
|
46
|
+
|
47
|
+
== License
|
48
|
+
|
49
|
+
(The MIT License)
|
50
|
+
|
51
|
+
Copyright (c) Eric Hodel
|
52
|
+
|
53
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
54
|
+
a copy of this software and associated documentation files (the
|
55
|
+
'Software'), to deal in the Software without restriction, including
|
56
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
57
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
58
|
+
permit persons to whom the Software is furnished to do so, subject to
|
59
|
+
the following conditions:
|
60
|
+
|
61
|
+
The above copyright notice and this permission notice shall be
|
62
|
+
included in all copies or substantial portions of the Software.
|
63
|
+
|
64
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
65
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
66
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
67
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
68
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
69
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
70
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
71
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.plugin :git
|
7
|
+
Hoe.plugin :minitest
|
8
|
+
Hoe.plugin :travis
|
9
|
+
|
10
|
+
namespace :travis do
|
11
|
+
task :install_libpcap do
|
12
|
+
sh 'sudo apt-get install libpcap-dev'
|
13
|
+
end
|
14
|
+
|
15
|
+
task before: %w[install_libpcap]
|
16
|
+
end
|
17
|
+
|
18
|
+
Hoe.spec 'drbdump' do
|
19
|
+
developer 'Eric Hodel', 'drbrain@segment7.net'
|
20
|
+
|
21
|
+
rdoc_locations << 'docs.seattlerb.org:/data/www/docs.seattlerb.org/drbdump/'
|
22
|
+
|
23
|
+
self.readme_file = 'README.rdoc'
|
24
|
+
self.licenses << 'MIT'
|
25
|
+
|
26
|
+
self.extra_deps << ['capp', '~> 1.0']
|
27
|
+
self.extra_deps << ['marshal-structure', '~> 2.0']
|
28
|
+
end
|
29
|
+
|
30
|
+
# vim: syntax=ruby
|
data/bin/drbdump
ADDED
data/example/ping.rb
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'drb'
|
2
|
+
require 'drbdump'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
class Ping
|
6
|
+
|
7
|
+
class Responder
|
8
|
+
def ping i, data
|
9
|
+
return i, data
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.process_args argv
|
14
|
+
options = {
|
15
|
+
client: nil,
|
16
|
+
count: Float::INFINITY,
|
17
|
+
flood: false,
|
18
|
+
interval: 1,
|
19
|
+
reconnect: false,
|
20
|
+
server: false,
|
21
|
+
size: 0,
|
22
|
+
}
|
23
|
+
|
24
|
+
interval_set = false
|
25
|
+
|
26
|
+
op = OptionParser.new do |opt|
|
27
|
+
opt.banner = <<-BANNER
|
28
|
+
Usage: ping.rb [options] [druby://...]
|
29
|
+
|
30
|
+
With no arguments, spawns a child process and sends DRb messages to it
|
31
|
+
|
32
|
+
To ping across multiple machines start a server with:
|
33
|
+
|
34
|
+
ping.rb --server
|
35
|
+
|
36
|
+
And submit the given URI to a client:
|
37
|
+
|
38
|
+
ping.rb druby://...
|
39
|
+
BANNER
|
40
|
+
|
41
|
+
opt.separator nil
|
42
|
+
opt.separator 'Options:'
|
43
|
+
|
44
|
+
opt.on('-c', '--count COUNT', Integer,
|
45
|
+
'Number of packets to send') do |count|
|
46
|
+
options[:count] = count
|
47
|
+
end
|
48
|
+
|
49
|
+
opt.on('-f', '--flood',
|
50
|
+
'Send packets as fast as possible',
|
51
|
+
'Prints one . per 1000 messages') do
|
52
|
+
options[:flood] = true
|
53
|
+
end
|
54
|
+
|
55
|
+
opt.on('-i', '--interval SECONDS', Float,
|
56
|
+
'Time between non-flood packets') do |interval|
|
57
|
+
options[:interval] = interval
|
58
|
+
interval_set = true
|
59
|
+
end
|
60
|
+
|
61
|
+
opt.on('-s', '--packet-size SIZE', Integer,
|
62
|
+
'Size of extra data to send') do |size|
|
63
|
+
options[:size] = size
|
64
|
+
end
|
65
|
+
|
66
|
+
opt.on( '--reconnect',
|
67
|
+
'Reconnect for each ping') do
|
68
|
+
options[:reconnect] = true
|
69
|
+
end
|
70
|
+
|
71
|
+
opt.on( '--server',
|
72
|
+
'Run only as a server') do |value|
|
73
|
+
options[:server] = true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
op.parse! argv
|
78
|
+
|
79
|
+
raise OptionParser::ParseError, '--flood with --interval is nonsense' if
|
80
|
+
options[:flood] and interval_set
|
81
|
+
|
82
|
+
raise OptionParser::ParseError, '--server with --flood is nonsense' if
|
83
|
+
options[:server] and options[:flood]
|
84
|
+
|
85
|
+
raise OptionParser::ParseError, '--server with --count is nonsense' if
|
86
|
+
options[:server] and options[:count]
|
87
|
+
|
88
|
+
options[:client] = argv.shift if /\Adruby:/ =~ argv.first
|
89
|
+
|
90
|
+
raise OptionParser::ParseError, '--server with client URI is nonsense' if
|
91
|
+
options[:server] and options[:client]
|
92
|
+
|
93
|
+
options
|
94
|
+
rescue OptionParser::ParseError => e
|
95
|
+
$stderr.puts op
|
96
|
+
$stderr.puts
|
97
|
+
$stderr.puts e.message
|
98
|
+
|
99
|
+
abort
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.run argv = ARGV
|
103
|
+
options = process_args argv
|
104
|
+
|
105
|
+
ping = new options
|
106
|
+
|
107
|
+
ping.run
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize options
|
111
|
+
@count = options[:count]
|
112
|
+
@client = options[:client]
|
113
|
+
@flood = options[:flood]
|
114
|
+
@interval = options[:interval]
|
115
|
+
@reconnect = options[:reconnect]
|
116
|
+
@remote = nil
|
117
|
+
@server = options[:server]
|
118
|
+
@size = options[:size]
|
119
|
+
@uri = nil
|
120
|
+
|
121
|
+
@data = ('a'..'z').cycle.first(@size).join
|
122
|
+
end
|
123
|
+
|
124
|
+
def delay_ping
|
125
|
+
statistic = DRbDump::Statistic.new
|
126
|
+
seq = 0
|
127
|
+
|
128
|
+
until (seq += 1) > @count do
|
129
|
+
send_message seq, statistic
|
130
|
+
end
|
131
|
+
ensure
|
132
|
+
puts
|
133
|
+
puts delay_statistics statistic
|
134
|
+
end
|
135
|
+
|
136
|
+
def delay_statistics statistic
|
137
|
+
'%d messages, min/avg/max/stddev = %0.3f/%0.3f/%0.3f/%0.3f ms' % [
|
138
|
+
statistic.count, statistic.min, statistic.mean, statistic.max,
|
139
|
+
statistic.standard_deviation
|
140
|
+
]
|
141
|
+
end
|
142
|
+
|
143
|
+
def flood_ping
|
144
|
+
start = Time.now
|
145
|
+
seq = 0
|
146
|
+
|
147
|
+
until (seq += 1) > @count do
|
148
|
+
begin
|
149
|
+
@remote.ping seq, @data
|
150
|
+
rescue DRb::DRbConnError
|
151
|
+
end
|
152
|
+
|
153
|
+
reconnect
|
154
|
+
|
155
|
+
print '.' if seq % 1000 == 0
|
156
|
+
end
|
157
|
+
ensure
|
158
|
+
elapsed = Time.now - start
|
159
|
+
puts
|
160
|
+
puts flood_statistics(elapsed, seq - 1)
|
161
|
+
end
|
162
|
+
|
163
|
+
def flood_statistics elapsed, messages
|
164
|
+
'%d messages in %0.3f seconds, %d messages/sec' % [
|
165
|
+
messages, elapsed, messages / elapsed
|
166
|
+
]
|
167
|
+
end
|
168
|
+
|
169
|
+
def loopback
|
170
|
+
uri = DRb.uri
|
171
|
+
|
172
|
+
pid = fork do
|
173
|
+
DRb.stop_service
|
174
|
+
|
175
|
+
DRb.start_service
|
176
|
+
|
177
|
+
ping uri
|
178
|
+
end
|
179
|
+
|
180
|
+
trap 'INT' do Process.kill 'INT', pid end
|
181
|
+
trap 'TERM' do Process.kill 'TERM', pid end
|
182
|
+
|
183
|
+
Process.wait pid
|
184
|
+
end
|
185
|
+
|
186
|
+
def ping uri
|
187
|
+
@uri = uri
|
188
|
+
@remote = DRb::DRbObject.new_with_uri @uri
|
189
|
+
|
190
|
+
if @flood then
|
191
|
+
flood_ping
|
192
|
+
else
|
193
|
+
delay_ping
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def reconnect
|
198
|
+
return unless @reconnect
|
199
|
+
|
200
|
+
DRb::DRbConn.open @uri do [false, false] end
|
201
|
+
end
|
202
|
+
|
203
|
+
def run
|
204
|
+
DRb.start_service nil, Responder.new
|
205
|
+
|
206
|
+
if @server then
|
207
|
+
puts DRb.uri
|
208
|
+
|
209
|
+
DRb.thread.join
|
210
|
+
elsif @client then
|
211
|
+
ping @client
|
212
|
+
else
|
213
|
+
loopback
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def send_message seq, statistic
|
218
|
+
message = 'success'
|
219
|
+
start = Time.now
|
220
|
+
|
221
|
+
begin
|
222
|
+
@remote.ping seq, @data
|
223
|
+
rescue DRb::DRbConnError => e
|
224
|
+
message = e
|
225
|
+
end
|
226
|
+
|
227
|
+
elapsed = (Time.now - start) * 1000
|
228
|
+
|
229
|
+
statistic.add elapsed
|
230
|
+
|
231
|
+
puts '%s %s: seq=%d time=%0.3f ms' % [@uri, message, seq, elapsed]
|
232
|
+
|
233
|
+
reconnect
|
234
|
+
|
235
|
+
sleep @interval
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
Ping.run ARGV if $0 == __FILE__
|
241
|
+
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'drb'
|
2
|
+
require 'observer'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
##
|
6
|
+
# Generates primes using multiple independent services. This prime number
|
7
|
+
# generator is purposefully ridiculous in its separation of services.
|
8
|
+
#
|
9
|
+
# == Implementation Notes
|
10
|
+
#
|
11
|
+
# ServicePrimes contains the following services:
|
12
|
+
#
|
13
|
+
# :primes::
|
14
|
+
# NumberStore holding prime numbers and Observable for notification of newly
|
15
|
+
# added prime numbers.
|
16
|
+
# :candidates::
|
17
|
+
# Enumerator from 3 to infinity for candidates.
|
18
|
+
# :discover::
|
19
|
+
# Consumes an item from the :candidates service and adds the number to the
|
20
|
+
# :primes service if it is found to be prime.
|
21
|
+
# :show_primes::
|
22
|
+
# Observer for the :primes NumberStore that prints added primes to standard
|
23
|
+
# output
|
24
|
+
#
|
25
|
+
# ServicePrimes can be collapsed back to a single process by removing
|
26
|
+
# fork_child and assigning each service to the instance variable used by each
|
27
|
+
# service.
|
28
|
+
#
|
29
|
+
# == Analyzing with drbdump
|
30
|
+
#
|
31
|
+
# Setup and finding the first prime (3) occurs within the first 15 messages.
|
32
|
+
#
|
33
|
+
# Comparing short message capture (100 messages) at the beginning of prime
|
34
|
+
# generation followed by later (around 70001) shows test_prime doing more work
|
35
|
+
# as the number of +call+ messages becomes a larger fraction of the messages
|
36
|
+
# sent.
|
37
|
+
#
|
38
|
+
# Removing DrbUndumped from NumberStore and NumberShower gives an interesting
|
39
|
+
# failure where the Observer fails to register.
|
40
|
+
|
41
|
+
class ServicePrimes
|
42
|
+
|
43
|
+
##
|
44
|
+
# An observer for NumberStore that prints numbers as they are added.
|
45
|
+
|
46
|
+
class NumberShower
|
47
|
+
include DRb::DRbUndumped
|
48
|
+
|
49
|
+
##
|
50
|
+
# Prints the number to standard output
|
51
|
+
|
52
|
+
def update number
|
53
|
+
puts number
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Stores primes and allows access to generated primes. The NumberStore is
|
59
|
+
# pre-primed with the first prime number (2). The NumberStore is Observable
|
60
|
+
# and will notify observers when a new prime is added.
|
61
|
+
|
62
|
+
class NumberStore
|
63
|
+
include DRb::DRbUndumped
|
64
|
+
include Enumerable
|
65
|
+
include Observable
|
66
|
+
|
67
|
+
def initialize # :nodoc:
|
68
|
+
@primes = [2]
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Adds a number +value+ and notifies observers
|
73
|
+
|
74
|
+
def add value
|
75
|
+
changed
|
76
|
+
@primes << value
|
77
|
+
notify_observers value
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Iterates through stored primes
|
82
|
+
|
83
|
+
def each
|
84
|
+
@primes.each do |prime|
|
85
|
+
yield prime
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def initialize # :nodoc:
|
91
|
+
@services = {}
|
92
|
+
|
93
|
+
DRb.start_service nil, @services
|
94
|
+
|
95
|
+
@uri = DRb.uri
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Creates a candidate numbers service which is enumerator of integers from 3
|
100
|
+
# to infinity.
|
101
|
+
|
102
|
+
def candidates
|
103
|
+
fork_child :candidates do
|
104
|
+
@services[:candidates] = 3.upto Float::INFINITY
|
105
|
+
|
106
|
+
DRb.thread.join
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Creates a prime number discovery service which consumes a candidate number
|
112
|
+
# and uses +test_prime+ to determine if the candidate number is prime. If
|
113
|
+
# the candidate is prime it is added to the prime number list.
|
114
|
+
|
115
|
+
def discover
|
116
|
+
fork_child :discover do
|
117
|
+
candidates = @services[:candidates]
|
118
|
+
@primes = @services[:primes]
|
119
|
+
|
120
|
+
candidates.each do |candidate|
|
121
|
+
prime = test_prime candidate
|
122
|
+
|
123
|
+
@primes.add candidate if prime
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Forks a worker child named +service+ and waits for its registration before
|
130
|
+
# returning.
|
131
|
+
|
132
|
+
def fork_child service
|
133
|
+
Thread.start do
|
134
|
+
pid = fork do
|
135
|
+
DRb.stop_service
|
136
|
+
|
137
|
+
DRb.start_service
|
138
|
+
|
139
|
+
@services = DRb::DRbObject.new_with_uri @uri
|
140
|
+
|
141
|
+
yield
|
142
|
+
end
|
143
|
+
|
144
|
+
Process.wait pid
|
145
|
+
end
|
146
|
+
|
147
|
+
Thread.pass until @services[service]
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Creates a prime number storage service named primes.
|
152
|
+
|
153
|
+
def primes
|
154
|
+
fork_child :primes do
|
155
|
+
@services[:primes] = NumberStore.new
|
156
|
+
|
157
|
+
DRb.thread.join
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# Starts the prime number generator
|
163
|
+
|
164
|
+
def run
|
165
|
+
candidates
|
166
|
+
|
167
|
+
primes
|
168
|
+
|
169
|
+
show_primes
|
170
|
+
|
171
|
+
discover
|
172
|
+
|
173
|
+
DRb.thread.join
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Creates a prime number display service that observes the addition of new
|
178
|
+
# prime numbers to the primes service.
|
179
|
+
|
180
|
+
def show_primes
|
181
|
+
fork_child :show_primes do
|
182
|
+
prime_shower = NumberShower.new
|
183
|
+
|
184
|
+
@services[:primes].add_observer prime_shower
|
185
|
+
|
186
|
+
@services[:show_primes] = prime_shower
|
187
|
+
|
188
|
+
DRb.thread.join
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Determines if +candidate+ is a prime number by dividing it by all primes
|
194
|
+
# below the square root of the +candidate+.
|
195
|
+
|
196
|
+
def test_prime candidate
|
197
|
+
max = Math.sqrt(candidate).ceil
|
198
|
+
|
199
|
+
is_prime = @primes.each do |prime|
|
200
|
+
break true if prime > max
|
201
|
+
|
202
|
+
_, remainder = candidate.divmod prime
|
203
|
+
|
204
|
+
break false if remainder.zero?
|
205
|
+
end
|
206
|
+
|
207
|
+
is_prime
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
ServicePrimes.new.run if $0 == __FILE__
|