drbdump 1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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__
|