shuttlecraft 0.0.1
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/.autotest +25 -0
- data/.gemtest +0 -0
- data/History.txt +6 -0
- data/LICENSE +20 -0
- data/Manifest.txt +20 -0
- data/README.md +76 -0
- data/Rakefile +20 -0
- data/bin/mothership_app +5 -0
- data/bin/shuttlecraft_app +5 -0
- data/lib/shuttlecraft.rb +111 -0
- data/lib/shuttlecraft/comms.rb +79 -0
- data/lib/shuttlecraft/mothership.rb +94 -0
- data/lib/shuttlecraft/mothership_app.rb +46 -0
- data/lib/shuttlecraft/protocol.rb +14 -0
- data/lib/shuttlecraft/resolv.rb +178 -0
- data/lib/shuttlecraft/shuttlecraft_app.rb +164 -0
- data/lib/shuttlecraft/test.rb +43 -0
- data/ringserver.rb +8 -0
- data/test/test_shuttlecraft.rb +124 -0
- data/test/test_shuttlecraft_mothership.rb +97 -0
- data/test/test_shuttlecraft_protocol.rb +18 -0
- metadata +147 -0
data/.autotest
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require "autotest/restart"
|
4
|
+
|
5
|
+
# Autotest.add_hook :initialize do |at|
|
6
|
+
# at.testlib = "minitest/unit"
|
7
|
+
#
|
8
|
+
# at.extra_files << "../some/external/dependency.rb"
|
9
|
+
#
|
10
|
+
# at.libs << ":../some/external"
|
11
|
+
#
|
12
|
+
# at.add_exception "vendor"
|
13
|
+
#
|
14
|
+
# at.add_mapping(/dependency.rb/) do |f, _|
|
15
|
+
# at.files_matching(/test_.*rb$/)
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# %w(TestA TestB).each do |klass|
|
19
|
+
# at.extra_class_map[klass] = "test/test_misc.rb"
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
|
23
|
+
# Autotest.add_hook :run_command do |at|
|
24
|
+
# system "rake build"
|
25
|
+
# end
|
data/.gemtest
ADDED
File without changes
|
data/History.txt
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Davy Stevenson
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
.autotest
|
2
|
+
History.txt
|
3
|
+
LICENSE
|
4
|
+
Manifest.txt
|
5
|
+
README.md
|
6
|
+
Rakefile
|
7
|
+
bin/mothership_app
|
8
|
+
bin/shuttlecraft_app
|
9
|
+
lib/shuttlecraft.rb
|
10
|
+
lib/shuttlecraft/mothership.rb
|
11
|
+
lib/shuttlecraft/mothership_app.rb
|
12
|
+
lib/shuttlecraft/protocol.rb
|
13
|
+
lib/shuttlecraft/comms.rb
|
14
|
+
lib/shuttlecraft/resolv.rb
|
15
|
+
lib/shuttlecraft/shuttlecraft_app.rb
|
16
|
+
lib/shuttlecraft/test.rb
|
17
|
+
ringserver.rb
|
18
|
+
test/test_shuttlecraft.rb
|
19
|
+
test/test_shuttlecraft_mothership.rb
|
20
|
+
test/test_shuttlecraft_protocol.rb
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# shuttlecraft
|
2
|
+
|
3
|
+
* http://github.com/davy/shuttlecraft
|
4
|
+
|
5
|
+
## DESCRIPTION:
|
6
|
+
|
7
|
+
Shuttlecraft is an easy-to-use wrapper for much of the functionality in Rinda.
|
8
|
+
|
9
|
+
Create a Shuttlecraft::Mothership to manage the RingServer and RingProvider, and then many Shuttlecrafts can easily connect to the Mothership. Registration management is easy and automatic.
|
10
|
+
|
11
|
+
Easily broadcast messages to all registered services (ie. Shuttlecrafts) from either the Mothership or a particular Shuttlecraft.
|
12
|
+
|
13
|
+
|
14
|
+
## SYNOPSIS:
|
15
|
+
|
16
|
+
Running the apps
|
17
|
+
----------------
|
18
|
+
|
19
|
+
Requires [shoes4](https://github.com/shoes/shoes4). Shoes4 is under active development! Apps tested against commit [7d0a1ee](https://github.com/shoes/shoes4/commit/7d0a1eefea601917dd01419b14ded2812d0acb9f).
|
20
|
+
|
21
|
+
Make sure you are running a ring server (via [RingyDingy](https://github.com/drbrain/RingyDingy))
|
22
|
+
|
23
|
+
$ ring_server
|
24
|
+
|
25
|
+
Run Mothership
|
26
|
+
|
27
|
+
$ path/to/shoes bin/mothership_app
|
28
|
+
|
29
|
+
Run Shuttlecraft
|
30
|
+
|
31
|
+
$ path/to/shoes bin/shuttlecraft_app
|
32
|
+
|
33
|
+
Play!
|
34
|
+
|
35
|
+
* Run multiple Motherships or Shuttlecrafts
|
36
|
+
* Shuttlecraft will ask which Mothership to connect to
|
37
|
+
* Can rescan for Motherships if none are available initially
|
38
|
+
* Broadcast a message to all running Shuttlecrafts registered with a Mothership
|
39
|
+
|
40
|
+
## INSTALL:
|
41
|
+
|
42
|
+
$ gem install shuttlecraft
|
43
|
+
|
44
|
+
## DEVELOPERS:
|
45
|
+
|
46
|
+
After checking out the source, run:
|
47
|
+
|
48
|
+
$ rake newb
|
49
|
+
|
50
|
+
This task will install any missing dependencies, run the tests/specs,
|
51
|
+
and generate the RDoc.
|
52
|
+
|
53
|
+
## LICENSE:
|
54
|
+
|
55
|
+
(The MIT License)
|
56
|
+
|
57
|
+
Copyright (c) 2013 Davy Stevenson, Eric Hodel
|
58
|
+
|
59
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
60
|
+
a copy of this software and associated documentation files (the
|
61
|
+
'Software'), to deal in the Software without restriction, including
|
62
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
63
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
64
|
+
permit persons to whom the Software is furnished to do so, subject to
|
65
|
+
the following conditions:
|
66
|
+
|
67
|
+
The above copyright notice and this permission notice shall be
|
68
|
+
included in all copies or substantial portions of the Software.
|
69
|
+
|
70
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
71
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
72
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
73
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
74
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
75
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
76
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "hoe"
|
5
|
+
|
6
|
+
Hoe.plugin :minitest
|
7
|
+
Hoe.plugin :git
|
8
|
+
|
9
|
+
Hoe.spec 'shuttlecraft' do
|
10
|
+
developer 'Davy Stevenson', 'davy.stevenson@gmail.com'
|
11
|
+
developer 'Eric Hodel', 'drbrain@segment7.net'
|
12
|
+
|
13
|
+
extra_deps << ['RingyDingy', '~> 1.6']
|
14
|
+
|
15
|
+
self.readme_file = 'README.md'
|
16
|
+
|
17
|
+
license 'MIT' # this should match the license in the README
|
18
|
+
end
|
19
|
+
|
20
|
+
# vim: syntax=ruby
|
data/bin/mothership_app
ADDED
data/lib/shuttlecraft.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'rinda/ring'
|
2
|
+
|
3
|
+
require 'shuttlecraft/comms'
|
4
|
+
|
5
|
+
class Shuttlecraft
|
6
|
+
|
7
|
+
include Shuttlecraft::Comms
|
8
|
+
|
9
|
+
VERSION = '0.0.1'
|
10
|
+
|
11
|
+
# [:name, :Mothership, name, Rinda::TupleSpace:tuplespace]
|
12
|
+
PROVIDER_TEMPLATE = [:name, :Mothership, String, nil]
|
13
|
+
|
14
|
+
# [:name, name, drb_uri]
|
15
|
+
REGISTRATION_TEMPLATE = [:name, String, String]
|
16
|
+
|
17
|
+
attr_accessor :ring_server, :mothership, :name, :protocol
|
18
|
+
|
19
|
+
def initialize(opts={})
|
20
|
+
initialize_comms(opts)
|
21
|
+
|
22
|
+
@drb = DRb.start_service(nil, self)
|
23
|
+
|
24
|
+
@name = opts[:name] || self.class.default_name
|
25
|
+
@protocol = opts[:protocol] || Shuttlecraft::Protocol.default
|
26
|
+
@verbose = opts[:verbose] || false
|
27
|
+
|
28
|
+
@ring_server = Rinda::RingFinger.primary
|
29
|
+
@receive_loop = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.default_name
|
33
|
+
'Shuttlecraft'
|
34
|
+
end
|
35
|
+
|
36
|
+
def provider_template
|
37
|
+
temp = PROVIDER_TEMPLATE.dup
|
38
|
+
temp[1] = @protocol.service_name
|
39
|
+
temp
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_all_motherships
|
43
|
+
ring_server.read_all(provider_template).collect{|_,_,name,ts| {name: name, ts: ts}}
|
44
|
+
end
|
45
|
+
|
46
|
+
def initiate_communication_with_mothership(name=nil)
|
47
|
+
motherships = find_all_motherships
|
48
|
+
|
49
|
+
if name
|
50
|
+
provider = motherships.detect{|m| m[:name] == name}
|
51
|
+
else
|
52
|
+
provider = motherships.first
|
53
|
+
end
|
54
|
+
|
55
|
+
if provider
|
56
|
+
@mothership = Rinda::TupleSpaceProxy.new provider[:ts]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def register
|
61
|
+
update!
|
62
|
+
unless @mothership.nil? || registered?
|
63
|
+
begin
|
64
|
+
@mothership.write([:name, @name, DRb.uri])
|
65
|
+
rescue DRb::DRbConnError
|
66
|
+
# mothership went away =(
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def registered?
|
72
|
+
update!
|
73
|
+
return false unless @mothership
|
74
|
+
|
75
|
+
!registered_services.detect{|t| !t.nil? && t[0] == @name && t[1] == DRb.uri}.nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
def unregister
|
79
|
+
update!
|
80
|
+
if registered?
|
81
|
+
begin
|
82
|
+
@mothership.take([:name, @name, DRb.uri])
|
83
|
+
rescue DRb::DRbConnError
|
84
|
+
# mothership went away =(
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
##
|
92
|
+
# For Shuttlecraft::Comms
|
93
|
+
def tuplespace
|
94
|
+
@mothership
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
require 'shuttlecraft/mothership'
|
99
|
+
require 'shuttlecraft/protocol'
|
100
|
+
require 'shuttlecraft/mothership_app'
|
101
|
+
require 'shuttlecraft/shuttlecraft_app'
|
102
|
+
|
103
|
+
if __FILE__ == $0
|
104
|
+
s = Shuttlecraft.new
|
105
|
+
s.initate_communication_with_mothership
|
106
|
+
s.register
|
107
|
+
|
108
|
+
sleep(5)
|
109
|
+
|
110
|
+
s.unregister
|
111
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
##
|
2
|
+
# Must define tuplespace to read from
|
3
|
+
# def tuplespace
|
4
|
+
#
|
5
|
+
# Must call initialize_comms inside initialize
|
6
|
+
#
|
7
|
+
class Shuttlecraft
|
8
|
+
module Comms
|
9
|
+
|
10
|
+
def initialize_comms(opts={})
|
11
|
+
@registered_services_ary = []
|
12
|
+
@update_every = opts[:update_every] || 2
|
13
|
+
@last_update = Time.at 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def registered_services
|
17
|
+
update
|
18
|
+
@registered_services_ary
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Registered services are only updatable if they haven't been updated in the
|
23
|
+
# last @update_every seconds. This prevents DRb message spam.
|
24
|
+
def update?
|
25
|
+
(@last_update + @update_every) < Time.now
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Retrieve the last registration data from the TupleSpace.
|
30
|
+
def update
|
31
|
+
return unless update?
|
32
|
+
@last_update = Time.now
|
33
|
+
@registered_services_ary = read_registered_services
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Forces retrieval of registrations from the TupleSpace.
|
38
|
+
def update!
|
39
|
+
@last_update = Time.at 0
|
40
|
+
update
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Enumerates through each registered service's uri
|
45
|
+
def each_service_uri
|
46
|
+
return enum_for __method__ unless block_given?
|
47
|
+
|
48
|
+
registered_services.each do |_, uri|
|
49
|
+
yield uri
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Loops through each client and yields
|
55
|
+
# DRb object for that client
|
56
|
+
def each_client
|
57
|
+
each_service_uri do |uri|
|
58
|
+
begin
|
59
|
+
remote = DRbObject.new_with_uri(uri)
|
60
|
+
yield remote
|
61
|
+
rescue DRb::DRbConnError
|
62
|
+
rescue => e
|
63
|
+
puts "Error sending message to client: #{e.message}" if @verbose
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def read_registered_services
|
71
|
+
begin
|
72
|
+
tuplespace.read_all(Shuttlecraft::REGISTRATION_TEMPLATE).collect{|_,name,uri| [name,uri]}
|
73
|
+
rescue DRb::DRbConnError
|
74
|
+
[]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'rinda/ring'
|
2
|
+
require 'rinda/tuplespace'
|
3
|
+
|
4
|
+
class Shuttlecraft::Mothership
|
5
|
+
|
6
|
+
include Shuttlecraft::Comms
|
7
|
+
|
8
|
+
attr_reader :ts, :provider, :name, :protocol
|
9
|
+
|
10
|
+
def initialize(opts={})
|
11
|
+
initialize_comms(opts)
|
12
|
+
|
13
|
+
@drb = DRb.start_service
|
14
|
+
|
15
|
+
@protocol = opts[:protocol] || Shuttlecraft::Protocol.default
|
16
|
+
@name = opts[:name] || @protocol.name
|
17
|
+
@verbose = opts[:verbose] || false
|
18
|
+
|
19
|
+
@ts = Rinda::TupleSpace.new
|
20
|
+
|
21
|
+
renewer = Rinda::SimpleRenewer.new 10
|
22
|
+
|
23
|
+
@provider =
|
24
|
+
Rinda::RingProvider.new(@protocol.service_name, @name, @ts, renewer)
|
25
|
+
@provider.provide
|
26
|
+
|
27
|
+
notify_on_registration
|
28
|
+
notify_on_unregistration
|
29
|
+
notify_on_write
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
##
|
34
|
+
# Override this method to add custom registration restrictions
|
35
|
+
def allow_registration?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
def notify_on_registration
|
40
|
+
@registration_observer = @ts.notify('write', Shuttlecraft::REGISTRATION_TEMPLATE)
|
41
|
+
Thread.new do
|
42
|
+
@registration_observer.each do |reg|
|
43
|
+
puts "Recieved registration from #{reg[1][1]}" if @verbose
|
44
|
+
if allow_registration?
|
45
|
+
update!
|
46
|
+
send(:on_registration) if respond_to? :on_registration
|
47
|
+
else
|
48
|
+
puts "Registration not allowed" if @verbose
|
49
|
+
@ts.take(reg[1])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def notify_on_unregistration
|
56
|
+
@unregistration_observer = @ts.notify('take', Shuttlecraft::REGISTRATION_TEMPLATE)
|
57
|
+
Thread.new do
|
58
|
+
@unregistration_observer.each do |reg|
|
59
|
+
puts "Recieved unregistration from #{reg[1][1]}" if @verbose
|
60
|
+
update!
|
61
|
+
send(:on_unregistration) if respond_to? :on_unregistration
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def notify_on_write
|
67
|
+
@write_observer = @ts.notify 'write', [nil]
|
68
|
+
Thread.new do
|
69
|
+
@write_observer.each {|n| p n}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
##
|
76
|
+
# For Shuttlecraft::Comms
|
77
|
+
def tuplespace
|
78
|
+
@ts
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
if __FILE__ == $0
|
84
|
+
m = Shuttlecraft::Mothership.new
|
85
|
+
|
86
|
+
while(true)
|
87
|
+
|
88
|
+
puts "Registered services: #{m.registered_services.join(', ')}"
|
89
|
+
|
90
|
+
sleep(5)
|
91
|
+
end
|
92
|
+
|
93
|
+
DRb.thread.join
|
94
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Shuttlecraft::MothershipApp
|
2
|
+
|
3
|
+
def self.run
|
4
|
+
Shoes.app width: 360, height: 360, resizeable: false, title: 'Mothership' do
|
5
|
+
@mothership = nil
|
6
|
+
|
7
|
+
def launch_screen
|
8
|
+
clear do
|
9
|
+
background black
|
10
|
+
title "Build Mothership", stroke: white
|
11
|
+
edit_line text: 'Name' do |s|
|
12
|
+
@name = s.text
|
13
|
+
end
|
14
|
+
button('launch') {
|
15
|
+
@mothership = Shuttlecraft::Mothership.new(name: @name)
|
16
|
+
display_screen
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def display_screen
|
22
|
+
clear do
|
23
|
+
background "#ffffff"
|
24
|
+
|
25
|
+
stack :margin => 20 do
|
26
|
+
title "Mothership #{@mothership.name}"
|
27
|
+
|
28
|
+
stack do
|
29
|
+
para 'Registered Services:'
|
30
|
+
@registrations = para
|
31
|
+
end
|
32
|
+
end
|
33
|
+
animate(5) { @registrations.replace registrations_text }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def registrations_text
|
38
|
+
if @mothership
|
39
|
+
@mothership.registered_services.join(', ')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
launch_screen
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Shuttlecraft::Protocol
|
2
|
+
|
3
|
+
attr_reader :service_name, :name
|
4
|
+
|
5
|
+
def initialize(service_name=:Mothership, name='Mothership')
|
6
|
+
@service_name = service_name
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.default
|
11
|
+
@@default ||= Shuttlecraft::Protocol.new
|
12
|
+
@@default
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
|
3
|
+
class Resolv
|
4
|
+
|
5
|
+
class DNS
|
6
|
+
class Requester
|
7
|
+
|
8
|
+
remove_method :request
|
9
|
+
|
10
|
+
def request(sender, tout)
|
11
|
+
start = Time.now
|
12
|
+
timelimit = start + tout
|
13
|
+
begin
|
14
|
+
sender.send
|
15
|
+
rescue Errno::EHOSTUNREACH
|
16
|
+
# multi-homed IPv6 may generate this
|
17
|
+
raise ResolvTimeout
|
18
|
+
end
|
19
|
+
while true
|
20
|
+
before_select = Time.now
|
21
|
+
timeout = timelimit - before_select
|
22
|
+
if timeout <= 0
|
23
|
+
raise ResolvTimeout
|
24
|
+
end
|
25
|
+
select_result = IO.select(@socks, nil, nil, timeout)
|
26
|
+
if !select_result
|
27
|
+
after_select = Time.now
|
28
|
+
next if after_select < timelimit
|
29
|
+
raise ResolvTimeout
|
30
|
+
end
|
31
|
+
begin
|
32
|
+
reply, from = recv_reply(select_result[0])
|
33
|
+
rescue Errno::ECONNREFUSED, # GNU/Linux, FreeBSD
|
34
|
+
Errno::ECONNRESET # Windows
|
35
|
+
# No name server running on the server?
|
36
|
+
# Don't wait anymore.
|
37
|
+
raise ResolvTimeout
|
38
|
+
end
|
39
|
+
begin
|
40
|
+
msg = Message.decode(reply)
|
41
|
+
rescue DecodeError
|
42
|
+
next # broken DNS message ignored
|
43
|
+
end
|
44
|
+
if s = sender_for(from, msg)
|
45
|
+
break
|
46
|
+
else
|
47
|
+
# unexpected DNS message ignored
|
48
|
+
end
|
49
|
+
end
|
50
|
+
return msg, s.data
|
51
|
+
end
|
52
|
+
|
53
|
+
def sender_for(addr, msg)
|
54
|
+
@senders[[addr,msg.id]]
|
55
|
+
end
|
56
|
+
|
57
|
+
class MDNSOneShot < UnconnectedUDP # :nodoc:
|
58
|
+
def sender(msg, data, host, port=Port)
|
59
|
+
id = DNS.allocate_request_id(host, port)
|
60
|
+
request = msg.encode
|
61
|
+
request[0,2] = [id].pack('n')
|
62
|
+
sock = @socks_hash[host.index(':') ? "::" : "0.0.0.0"]
|
63
|
+
return @senders[id] =
|
64
|
+
UnconnectedUDP::Sender.new(request, data, sock, host, port)
|
65
|
+
end
|
66
|
+
|
67
|
+
def sender_for(addr, msg)
|
68
|
+
@senders[msg.id]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Resolv::MDNS is a one-shot Multicast DNS (mDNS) resolver. It blindly
|
76
|
+
# makes queries to the mDNS addresses without understanding anything about
|
77
|
+
# multicast ports.
|
78
|
+
#
|
79
|
+
# Information taken form the following places:
|
80
|
+
#
|
81
|
+
# * RFC 6762
|
82
|
+
|
83
|
+
class MDNS < DNS
|
84
|
+
|
85
|
+
##
|
86
|
+
# Default mDNS Port
|
87
|
+
|
88
|
+
Port = 5353
|
89
|
+
|
90
|
+
##
|
91
|
+
# Default IPv4 mDNS address
|
92
|
+
|
93
|
+
AddressV4 = '224.0.0.251'
|
94
|
+
|
95
|
+
##
|
96
|
+
# Default IPv6 mDNS address
|
97
|
+
|
98
|
+
AddressV6 = 'ff02::fb'
|
99
|
+
|
100
|
+
##
|
101
|
+
# Default mDNS addresses
|
102
|
+
|
103
|
+
Addresses = [
|
104
|
+
[AddressV4, Port],
|
105
|
+
[AddressV6, Port],
|
106
|
+
]
|
107
|
+
|
108
|
+
##
|
109
|
+
# Creates a new one-shot Multicast DNS (mDNS) resolver.
|
110
|
+
#
|
111
|
+
# +config_info+ can be:
|
112
|
+
#
|
113
|
+
# nil::
|
114
|
+
# Uses the default mDNS addresses
|
115
|
+
#
|
116
|
+
# Hash::
|
117
|
+
# Must contain :nameserver or :nameserver_port like
|
118
|
+
# Resolv::DNS#initialize.
|
119
|
+
|
120
|
+
def initialize(config_info=nil)
|
121
|
+
if config_info then
|
122
|
+
super({ nameserver_port: Addresses }.merge(config_info))
|
123
|
+
else
|
124
|
+
super(nameserver_port: Addresses)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Iterates over all IP addresses for +name+ retrieved from the mDNS
|
130
|
+
# resolver, provided name ends with "local". If the name does not end in
|
131
|
+
# "local" no records will be returned.
|
132
|
+
#
|
133
|
+
# +name+ can be a Resolv::DNS::Name or a String. Retrieved addresses will
|
134
|
+
# be a Resolv::IPv4 or Resolv::IPv6
|
135
|
+
|
136
|
+
def each_address(name)
|
137
|
+
name = Resolv::DNS::Name.create(name)
|
138
|
+
|
139
|
+
return unless name.to_a.last == 'local'
|
140
|
+
|
141
|
+
super(name)
|
142
|
+
end
|
143
|
+
|
144
|
+
def make_udp_requester # :nodoc:
|
145
|
+
nameserver_port = @config.nameserver_port
|
146
|
+
Requester::MDNSOneShot.new(*nameserver_port)
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
def DefaultResolver.replace_resolvers new_resolvers
|
152
|
+
@resolvers = new_resolvers
|
153
|
+
end
|
154
|
+
|
155
|
+
end unless Resolv.const_defined? :MDNS
|
156
|
+
|
157
|
+
require 'resolv-replace'
|
158
|
+
require 'tempfile'
|
159
|
+
|
160
|
+
broadcast_hosts = nil
|
161
|
+
|
162
|
+
Tempfile.open 'hosts' do |io|
|
163
|
+
io.puts '255.255.255.255 <broadcast>'
|
164
|
+
|
165
|
+
io.flush
|
166
|
+
|
167
|
+
broadcast_hosts = Resolv::Hosts.new io.path
|
168
|
+
end
|
169
|
+
|
170
|
+
resolvers = [
|
171
|
+
Resolv::MDNS.new,
|
172
|
+
Resolv::Hosts.new,
|
173
|
+
broadcast_hosts,
|
174
|
+
Resolv::DNS.new,
|
175
|
+
]
|
176
|
+
|
177
|
+
Resolv::DefaultResolver.replace_resolvers resolvers
|
178
|
+
|
@@ -0,0 +1,164 @@
|
|
1
|
+
class MyShuttlecraft < Shuttlecraft
|
2
|
+
|
3
|
+
attr_reader :msg_log
|
4
|
+
|
5
|
+
def initialize(opts={})
|
6
|
+
super(opts)
|
7
|
+
@msg_log = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def broadcast(msg)
|
11
|
+
for _, uri in registered_services
|
12
|
+
begin
|
13
|
+
remote = DRbObject.new_with_uri(uri)
|
14
|
+
remote.say(msg, DRb.uri)
|
15
|
+
rescue DRb::DRbConnError
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def say(msg, from)
|
21
|
+
@msg_log << msg
|
22
|
+
begin
|
23
|
+
remote = DRbObject.new_with_uri(from)
|
24
|
+
remote.message_reciept(@name)
|
25
|
+
rescue DRb::DRbConnError
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def message_reciept(from)
|
30
|
+
puts "reciept from #{from}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Shuttlecraft::ShuttlecraftApp
|
35
|
+
|
36
|
+
def self.run
|
37
|
+
@my_app = Shoes.app width: 360, height: 360, resizeable: false, title: 'Shuttlecraft' do
|
38
|
+
|
39
|
+
@shuttlecraft = nil
|
40
|
+
|
41
|
+
def display_screen
|
42
|
+
clear do
|
43
|
+
stack :margin => 20 do
|
44
|
+
title "Shuttlecraft #{@shuttlecraft.name}"
|
45
|
+
|
46
|
+
stack do @status = para end
|
47
|
+
|
48
|
+
@registered = nil
|
49
|
+
@updating_area = stack
|
50
|
+
@msg_stack = stack
|
51
|
+
end
|
52
|
+
|
53
|
+
animate(5) {
|
54
|
+
if @shuttlecraft
|
55
|
+
|
56
|
+
detect_registration_change
|
57
|
+
|
58
|
+
if @registered
|
59
|
+
@registrations.replace registrations_text
|
60
|
+
|
61
|
+
@msg_stack.clear do
|
62
|
+
for msg in @shuttlecraft.msg_log
|
63
|
+
para msg
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def detect_registration_change
|
73
|
+
if @registered != @shuttlecraft.registered?
|
74
|
+
@registered = @shuttlecraft.registered?
|
75
|
+
@status.replace "#{"Not " unless @registered}Registered"
|
76
|
+
@updating_area.clear do
|
77
|
+
if @registered
|
78
|
+
button("Unregister") { unregister }
|
79
|
+
|
80
|
+
el = edit_line
|
81
|
+
|
82
|
+
button("Send") {
|
83
|
+
@shuttlecraft.broadcast(el.text)
|
84
|
+
el.text = ''
|
85
|
+
}
|
86
|
+
stack do
|
87
|
+
para 'Registered Services:'
|
88
|
+
@registrations = para
|
89
|
+
end
|
90
|
+
else
|
91
|
+
button("Register") { register }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def launch_screen
|
98
|
+
clear do
|
99
|
+
background black
|
100
|
+
title "Build Shuttlecraft", stroke: white
|
101
|
+
edit_line text: 'Name' do |s|
|
102
|
+
@name = s.text
|
103
|
+
end
|
104
|
+
button('launch') {
|
105
|
+
@shuttlecraft = MyShuttlecraft.new(name: @name)
|
106
|
+
initiate_comms_screen
|
107
|
+
}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def initiate_comms_screen
|
112
|
+
clear do
|
113
|
+
background black
|
114
|
+
title "Initiate Comms", stroke: white
|
115
|
+
|
116
|
+
stack do
|
117
|
+
motherships = @shuttlecraft.find_all_motherships
|
118
|
+
|
119
|
+
if motherships.empty?
|
120
|
+
subtitle "No Motherships within range", stroke: white
|
121
|
+
else
|
122
|
+
subtitle "Select Mothership", stroke: white
|
123
|
+
end
|
124
|
+
for mothership in motherships
|
125
|
+
button(mothership[:name]) {|b|
|
126
|
+
begin
|
127
|
+
@shuttlecraft.initiate_communication_with_mothership(b.text)
|
128
|
+
rescue
|
129
|
+
initiate_comms_screen
|
130
|
+
end
|
131
|
+
display_screen
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
button('launch mothership') {
|
136
|
+
load File.dirname(__FILE__) + '/mothership_app.rb'
|
137
|
+
}
|
138
|
+
button('rescan') {
|
139
|
+
initiate_comms_screen
|
140
|
+
}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def register
|
146
|
+
@shuttlecraft.register if @shuttlecraft
|
147
|
+
end
|
148
|
+
|
149
|
+
def unregister
|
150
|
+
@shuttlecraft.unregister if @shuttlecraft
|
151
|
+
end
|
152
|
+
|
153
|
+
def registrations_text
|
154
|
+
if @shuttlecraft
|
155
|
+
@shuttlecraft.registered_services.join(', ')
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
launch_screen
|
160
|
+
end
|
161
|
+
ensure
|
162
|
+
@my_app.unregister if @my_app
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'shuttlecraft'
|
3
|
+
|
4
|
+
class Shuttlecraft::Test < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
class StubRingServer
|
7
|
+
def initialize; end
|
8
|
+
def read(*args); return 'foo'; end
|
9
|
+
def write(*args); return 'foo'; end
|
10
|
+
end
|
11
|
+
|
12
|
+
class StubMothership
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@tuples = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def write(*args)
|
19
|
+
@tuples << args
|
20
|
+
end
|
21
|
+
|
22
|
+
def take(*args)
|
23
|
+
@tuples.delete(args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_all(template)
|
27
|
+
@tuples.map{|t,r| t}.select do |t|
|
28
|
+
template[1].nil? or template[1] === t[1]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Rinda::RingFinger
|
35
|
+
class << self
|
36
|
+
remove_method :primary
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.primary
|
40
|
+
Shuttlecraft::Test::StubRingServer.new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
data/ringserver.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'shuttlecraft/test'
|
2
|
+
|
3
|
+
class Shuttlecraft::Override < Shuttlecraft
|
4
|
+
def self.default_name
|
5
|
+
'Override'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class TestShuttlecraft < Shuttlecraft::Test
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@shuttlecraft = Shuttlecraft.new(name: 'Galileo')
|
13
|
+
@shuttlecraft.mothership = Shuttlecraft::Test::StubMothership.new
|
14
|
+
@stub_mothership = @shuttlecraft.mothership
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_initialization
|
18
|
+
assert_equal false, @shuttlecraft.registered?
|
19
|
+
assert_equal 'Galileo', @shuttlecraft.name
|
20
|
+
assert_equal :Mothership, @shuttlecraft.protocol.service_name
|
21
|
+
assert_equal [], @shuttlecraft.registered_services
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_empty_initialization
|
25
|
+
@shuttlecraft = Shuttlecraft.new
|
26
|
+
assert_equal 'Shuttlecraft', @shuttlecraft.name
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_default_name_override
|
30
|
+
@shuttlecraft = Shuttlecraft::Override.new
|
31
|
+
assert_equal 'Override', @shuttlecraft.name
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_each_service_uri
|
35
|
+
@shuttlecraft.registered_services << ['name', DRb.uri]
|
36
|
+
|
37
|
+
e = @shuttlecraft.each_service_uri
|
38
|
+
|
39
|
+
assert_equal DRb.uri, e.next
|
40
|
+
|
41
|
+
uris = []
|
42
|
+
|
43
|
+
@shuttlecraft.each_service_uri do |uri|
|
44
|
+
uris << uri
|
45
|
+
end
|
46
|
+
|
47
|
+
refute_empty uris
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_each_client
|
51
|
+
@shuttlecraft.registered_services << ['name', 'druby://localhost:1234']
|
52
|
+
|
53
|
+
@shuttlecraft.each_client do |client_obj|
|
54
|
+
assert (DRbObject === client_obj)
|
55
|
+
assert_equal 'druby://localhost:1234', client_obj.__drburi
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_uses_default_protocol
|
60
|
+
assert_equal Shuttlecraft::Protocol.default, @shuttlecraft.protocol
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_update_eh
|
64
|
+
assert @shuttlecraft.update?
|
65
|
+
|
66
|
+
@shuttlecraft.update
|
67
|
+
|
68
|
+
refute @shuttlecraft.update?
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_update
|
72
|
+
make_registrations(%w[Davy Eric])
|
73
|
+
|
74
|
+
assert @shuttlecraft.update
|
75
|
+
refute @shuttlecraft.update
|
76
|
+
|
77
|
+
assert_equal %w[Davy Eric], @shuttlecraft.registered_services_ary.collect{|n,u| n}.sort
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_update_bang
|
81
|
+
make_registrations(%w[Davy Eric])
|
82
|
+
assert @shuttlecraft.update
|
83
|
+
|
84
|
+
assert_equal %w[Davy Eric], @shuttlecraft.registered_services.collect{|n,u| n}.sort
|
85
|
+
|
86
|
+
make_registrations(%w[Davy Eric Rein])
|
87
|
+
assert @shuttlecraft.update!
|
88
|
+
|
89
|
+
assert_equal %w[Davy Eric Rein], @shuttlecraft.registered_services.collect{|n,u| n}.sort
|
90
|
+
end
|
91
|
+
|
92
|
+
def make_registrations regs
|
93
|
+
|
94
|
+
@@regs = regs
|
95
|
+
|
96
|
+
class << @shuttlecraft
|
97
|
+
undef_method :read_registered_services
|
98
|
+
end
|
99
|
+
|
100
|
+
def @shuttlecraft.read_registered_services
|
101
|
+
@@regs.collect{|r| [r, DRb.uri]}
|
102
|
+
end
|
103
|
+
|
104
|
+
def @shuttlecraft.registered_services_ary
|
105
|
+
@registered_services_ary
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_registration
|
110
|
+
@shuttlecraft.register
|
111
|
+
|
112
|
+
assert_equal true, @shuttlecraft.registered?
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_unregistration
|
116
|
+
@stub_mothership.write([:name, @shuttlecraft.name, DRb.uri])
|
117
|
+
|
118
|
+
assert_equal true, @shuttlecraft.registered?
|
119
|
+
|
120
|
+
@shuttlecraft.unregister
|
121
|
+
|
122
|
+
assert_equal false, @shuttlecraft.registered?
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'shuttlecraft/test'
|
2
|
+
|
3
|
+
class TestShuttlecraftMothership < Shuttlecraft::Test
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@mothership = Shuttlecraft::Mothership.new(name: 'Enterprise')
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_initialization
|
10
|
+
assert_equal 'Enterprise', @mothership.name
|
11
|
+
assert_equal :Mothership, @mothership.protocol.service_name
|
12
|
+
assert_equal [], @mothership.registered_services
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_protocol
|
16
|
+
@mothership = Shuttlecraft::Mothership.new(protocol: Shuttlecraft::Protocol.new(:Foo, "Foobar"))
|
17
|
+
|
18
|
+
assert_equal 'Foobar', @mothership.name
|
19
|
+
assert_equal :Foo, @mothership.protocol.service_name
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_each_service_uri
|
23
|
+
@mothership.registered_services << ['name', DRb.uri]
|
24
|
+
|
25
|
+
e = @mothership.each_service_uri
|
26
|
+
|
27
|
+
assert_equal DRb.uri, e.next
|
28
|
+
|
29
|
+
uris = []
|
30
|
+
|
31
|
+
@mothership.each_service_uri do |uri|
|
32
|
+
uris << uri
|
33
|
+
end
|
34
|
+
|
35
|
+
refute_empty uris
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_each_client
|
39
|
+
@mothership.registered_services << ['name', 'druby://localhost:1234']
|
40
|
+
|
41
|
+
@mothership.each_client do |client_obj|
|
42
|
+
assert (DRbObject === client_obj)
|
43
|
+
assert_equal 'druby://localhost:1234', client_obj.__drburi
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_uses_default_protocol
|
48
|
+
assert_equal Shuttlecraft::Protocol.default, @mothership.protocol
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_update_eh
|
52
|
+
assert @mothership.update?
|
53
|
+
|
54
|
+
@mothership.update
|
55
|
+
|
56
|
+
refute @mothership.update?
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_update
|
60
|
+
make_registrations(%w[Davy Eric])
|
61
|
+
|
62
|
+
assert @mothership.update
|
63
|
+
refute @mothership.update
|
64
|
+
|
65
|
+
assert_equal %w[Davy Eric], @mothership.registered_services_ary.collect{|n,u| n}.sort
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_update_bang
|
69
|
+
make_registrations(%w[Davy Eric])
|
70
|
+
assert @mothership.update
|
71
|
+
|
72
|
+
assert_equal %w[Davy Eric], @mothership.registered_services.collect{|n,u| n}.sort
|
73
|
+
|
74
|
+
make_registrations(%w[Davy Eric Rein])
|
75
|
+
assert @mothership.update!
|
76
|
+
|
77
|
+
assert_equal %w[Davy Eric Rein], @mothership.registered_services.collect{|n,u| n}.sort
|
78
|
+
end
|
79
|
+
|
80
|
+
def make_registrations regs
|
81
|
+
|
82
|
+
@@regs = regs
|
83
|
+
|
84
|
+
class << @mothership
|
85
|
+
undef_method :read_registered_services
|
86
|
+
end
|
87
|
+
|
88
|
+
def @mothership.read_registered_services
|
89
|
+
@@regs.collect{|r| [r, DRb.uri]}
|
90
|
+
end
|
91
|
+
|
92
|
+
def @mothership.registered_services_ary
|
93
|
+
@registered_services_ary
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'shuttlecraft/test'
|
2
|
+
|
3
|
+
class TestShuttlecraftProtocol < Shuttlecraft::Test
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@protocol = Shuttlecraft::Protocol.new(:MySpecialProtocol, 'Special Snowflake')
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_initialization
|
10
|
+
assert_equal :MySpecialProtocol, @protocol.service_name
|
11
|
+
assert_equal "Special Snowflake", @protocol.name
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_default_protocol
|
15
|
+
assert_equal :Mothership, Shuttlecraft::Protocol.default.service_name
|
16
|
+
assert_equal "Mothership", Shuttlecraft::Protocol.default.name
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shuttlecraft
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Davy Stevenson
|
9
|
+
- Eric Hodel
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-11-08 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: RingyDingy
|
17
|
+
version_requirements: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.6'
|
22
|
+
none: false
|
23
|
+
requirement: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.6'
|
28
|
+
none: false
|
29
|
+
prerelease: false
|
30
|
+
type: :runtime
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: minitest
|
33
|
+
version_requirements: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '4.7'
|
38
|
+
none: false
|
39
|
+
requirement: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '4.7'
|
44
|
+
none: false
|
45
|
+
prerelease: false
|
46
|
+
type: :development
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rdoc
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '4.0'
|
54
|
+
none: false
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '4.0'
|
60
|
+
none: false
|
61
|
+
prerelease: false
|
62
|
+
type: :development
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: hoe
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.7'
|
70
|
+
none: false
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.7'
|
76
|
+
none: false
|
77
|
+
prerelease: false
|
78
|
+
type: :development
|
79
|
+
description: "Shuttlecraft is an easy-to-use wrapper for much of the functionality\
|
80
|
+
\ in Rinda. \n\nCreate a Shuttlecraft::Mothership to manage the RingServer and RingProvider,\
|
81
|
+
\ and then many Shuttlecrafts can easily connect to the Mothership. Registration\
|
82
|
+
\ management is easy and automatic.\n\nEasily broadcast messages to all registered\
|
83
|
+
\ services (ie. Shuttlecrafts) from either the Mothership or a particular Shuttlecraft."
|
84
|
+
email:
|
85
|
+
- davy.stevenson@gmail.com
|
86
|
+
- drbrain@segment7.net
|
87
|
+
executables:
|
88
|
+
- mothership_app
|
89
|
+
- shuttlecraft_app
|
90
|
+
extensions: []
|
91
|
+
extra_rdoc_files:
|
92
|
+
- History.txt
|
93
|
+
- Manifest.txt
|
94
|
+
- README.md
|
95
|
+
files:
|
96
|
+
- .autotest
|
97
|
+
- History.txt
|
98
|
+
- LICENSE
|
99
|
+
- Manifest.txt
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- bin/mothership_app
|
103
|
+
- bin/shuttlecraft_app
|
104
|
+
- lib/shuttlecraft.rb
|
105
|
+
- lib/shuttlecraft/mothership.rb
|
106
|
+
- lib/shuttlecraft/mothership_app.rb
|
107
|
+
- lib/shuttlecraft/protocol.rb
|
108
|
+
- lib/shuttlecraft/comms.rb
|
109
|
+
- lib/shuttlecraft/resolv.rb
|
110
|
+
- lib/shuttlecraft/shuttlecraft_app.rb
|
111
|
+
- lib/shuttlecraft/test.rb
|
112
|
+
- ringserver.rb
|
113
|
+
- test/test_shuttlecraft.rb
|
114
|
+
- test/test_shuttlecraft_mothership.rb
|
115
|
+
- test/test_shuttlecraft_protocol.rb
|
116
|
+
- .gemtest
|
117
|
+
homepage: http://github.com/davy/shuttlecraft
|
118
|
+
licenses:
|
119
|
+
- MIT
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options:
|
122
|
+
- --main
|
123
|
+
- README.md
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
none: false
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
none: false
|
138
|
+
requirements: []
|
139
|
+
rubyforge_project: shuttlecraft
|
140
|
+
rubygems_version: 1.8.24
|
141
|
+
signing_key:
|
142
|
+
specification_version: 3
|
143
|
+
summary: Shuttlecraft is an easy-to-use wrapper for much of the functionality in Rinda
|
144
|
+
test_files:
|
145
|
+
- test/test_shuttlecraft.rb
|
146
|
+
- test/test_shuttlecraft_mothership.rb
|
147
|
+
- test/test_shuttlecraft_protocol.rb
|