noam_lemma 0.2.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +20 -0
- data/Rakefile +11 -0
- data/examples/free_guest_publisher.rb +34 -0
- data/examples/free_guest_subscriber.rb +31 -0
- data/examples/lemma_verification.rb +43 -0
- data/examples/publisher.rb +47 -0
- data/examples/subscriber.rb +35 -0
- data/lib/noam_lemma/beacon.rb +36 -0
- data/lib/noam_lemma/lemma.rb +70 -0
- data/lib/noam_lemma/listener.rb +73 -0
- data/lib/noam_lemma/message/heard.rb +18 -0
- data/lib/noam_lemma/message/marco.rb +57 -0
- data/lib/noam_lemma/message/playable.rb +16 -0
- data/lib/noam_lemma/message/polo.rb +15 -0
- data/lib/noam_lemma/message/register.rb +17 -0
- data/lib/noam_lemma/message.rb +15 -0
- data/lib/noam_lemma/message_filter.rb +24 -0
- data/lib/noam_lemma/player.rb +78 -0
- data/lib/noam_lemma/version.rb +3 -0
- data/lib/noam_lemma.rb +16 -0
- data/noam_lemma.gemspec +26 -0
- data/spec/noam_lemma/beacon_spec.rb +26 -0
- data/spec/noam_lemma/lemma_spec.rb +88 -0
- data/spec/noam_lemma/message/heard_spec.rb +19 -0
- data/spec/noam_lemma/message/playable_spec.rb +17 -0
- data/spec/noam_lemma/message/register_spec.rb +17 -0
- data/spec/noam_lemma/message_filter_spec.rb +68 -0
- data/spec/noam_lemma/message_spec.rb +8 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/fake_server.rb +187 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 87f2a65c040cab0caea74d12a7e8d58d4ed36dd1
|
4
|
+
data.tar.gz: 0d9d0a71273809b6f28436a944fd69b2b9696c7e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1883fc59b41232681372d16811a62d4d73d135aca9fba538f74f924b693d7d3b06a3df0b8452a0db50b35a5a56b1f85ebbabdfbde15aafc75e577c677d7636d1
|
7
|
+
data.tar.gz: 4933af13dc4c016f0f2038cff34c1bff9d9d6af4b83e027c6238cd52b3b71aaae389dddc58fee98f8154234e780e2285529c2a5e252d840b8d79e515a829580c
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-r spec_helper
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 John Van Enk
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
noam\_lemma
|
2
|
+
===============
|
3
|
+
|
4
|
+
A Noam Lemma implementation for Ruby.
|
5
|
+
|
6
|
+
This library exposes the fundamental concepts in a Lemma to Ruby developers. It
|
7
|
+
handles registration, subscription, and message processing. All one needs to do
|
8
|
+
to create a new Lemma in a network is interact with the Noam::Lemma class. See
|
9
|
+
the `example/` directory in the project for further details on usage.
|
10
|
+
|
11
|
+
Build the ruby gem from noam lemma directory
|
12
|
+
`gem build noam_lemma.gemspec`
|
13
|
+
|
14
|
+
Install the gem
|
15
|
+
`gem install noam_lemma-version.gem`
|
16
|
+
|
17
|
+
Known Issues
|
18
|
+
------------
|
19
|
+
|
20
|
+
* Listening fails un-gracefully when the server goes down.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'noam_lemma'
|
2
|
+
|
3
|
+
# This is an example of a Ruby Lemma that publishes message and *also* uses the
|
4
|
+
# "Guest" model of connection. This Lemma will advertise that it's available on
|
5
|
+
# the local network and only begin speaking messages once a server requests a
|
6
|
+
# connection from the Lemma.
|
7
|
+
|
8
|
+
publisher = Noam::Lemma.new('example-guest-publisher', [], ["e3"])
|
9
|
+
|
10
|
+
# Using the `advertise` method asks the Lemma to announce it's presence and
|
11
|
+
# wait for a message from a server that may want to connect to it.
|
12
|
+
#
|
13
|
+
# The "local-test" parameter is the room name. Servers with a room name that's
|
14
|
+
# the same as the Lemma's advertised room name will connect automatically.
|
15
|
+
publisher.advertise("")
|
16
|
+
|
17
|
+
seq = 0
|
18
|
+
e = "e3"
|
19
|
+
loop do
|
20
|
+
# Construct a value to send with the event.
|
21
|
+
v = {"seq" => seq, "time" => Time.now.to_s}
|
22
|
+
|
23
|
+
# If `speak` returns false, we're unable to speak the message likely because
|
24
|
+
# the socket has closed. The connection would have to be restarted.
|
25
|
+
unless publisher.speak(e, v)
|
26
|
+
puts "Done"
|
27
|
+
break
|
28
|
+
end
|
29
|
+
puts "Wrote: #{e} -> #{v.inspect}"
|
30
|
+
|
31
|
+
seq += 1
|
32
|
+
# Sleep for a while so that we don't bog down the network.
|
33
|
+
sleep(1)
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'noam_lemma'
|
2
|
+
|
3
|
+
# This is an example of a Ruby Lemma that publishes message and *also* uses the
|
4
|
+
# "Guest" model of connection. This Lemma will advertise that it's available on
|
5
|
+
# the local network and only begin subscribing to messages once a server
|
6
|
+
# requests a connection from the Lemma.
|
7
|
+
|
8
|
+
subscriber = Noam::Lemma.new('example-guest-subscriber', ["e3"], [])
|
9
|
+
|
10
|
+
# Using the `advertise` method asks the Lemma to announce it's presence and
|
11
|
+
# wait for a message from a server that may want to connect to it.
|
12
|
+
#
|
13
|
+
# The "local-test" parameter is the room name. Servers with a room name that's
|
14
|
+
# the same as the Lemma's advertised room name will connect automatically.
|
15
|
+
subscriber.advertise("")
|
16
|
+
|
17
|
+
loop do
|
18
|
+
# The `listen` method will return an Event object once one is received by the
|
19
|
+
# Lemma. Until an event is heard, the `listen` method blocks.
|
20
|
+
m = subscriber.listen
|
21
|
+
|
22
|
+
# There's one special value that's returned from `listen`: the `:cancelled`
|
23
|
+
# symbol. If this shows up, it means some one else has called the `stop`
|
24
|
+
# method on the Lemma.
|
25
|
+
if :cancelled == m
|
26
|
+
puts "Done"
|
27
|
+
break
|
28
|
+
else
|
29
|
+
puts "Read: #{m.event} -> #{m.value.inspect}"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "noam_lemma"
|
2
|
+
|
3
|
+
class Noam::LemmaVerification
|
4
|
+
def self.run
|
5
|
+
echo
|
6
|
+
plus_one
|
7
|
+
sum
|
8
|
+
name
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.echo
|
12
|
+
lemma = Noam::Lemma.new("verification", ["Echo"], ["EchoVerify"])
|
13
|
+
lemma.advertise("lemma_verification")
|
14
|
+
event = lemma.listen
|
15
|
+
lemma.speak("EchoVerify", event.value)
|
16
|
+
lemma.stop
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.plus_one
|
20
|
+
lemma = Noam::Lemma.new("verification", ["PlusOne"], ["PlusOneVerify"])
|
21
|
+
lemma.advertise("lemma_verification")
|
22
|
+
event = lemma.listen
|
23
|
+
lemma.speak("PlusOneVerify", event.value + 1)
|
24
|
+
lemma.stop
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.sum
|
28
|
+
lemma = Noam::Lemma.new("verification", ["Sum"], ["SumVerify"])
|
29
|
+
lemma.advertise("lemma_verification")
|
30
|
+
event = lemma.listen
|
31
|
+
lemma.speak("SumVerify", event.value.inject {|sum, v| sum + v})
|
32
|
+
lemma.stop
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.name
|
36
|
+
lemma = Noam::Lemma.new("verification", ["Name"], ["NameVerify"])
|
37
|
+
lemma.advertise("lemma_verification")
|
38
|
+
event = lemma.listen
|
39
|
+
fullname = "#{event.value["firstName"]} #{event.value["lastName"]}"
|
40
|
+
lemma.speak("NameVerify", {fullName: fullname})
|
41
|
+
lemma.stop
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'noam_lemma'
|
2
|
+
|
3
|
+
# This is an example of a Ruby Lemma that publishes messages. It expects a Noam
|
4
|
+
# server to be running. It also expects that the noam-lemma.rb file is in the
|
5
|
+
# search path. If you run this example from the project root, the following
|
6
|
+
# command should work:
|
7
|
+
#
|
8
|
+
# ruby -Ilib example/publisher.rb
|
9
|
+
#
|
10
|
+
# This example _will not_ work on the same machine running the Noam server as
|
11
|
+
# both programs need to bind to UDP port 1030.
|
12
|
+
|
13
|
+
publisher = Noam::Lemma.new('example-publisher', [], ["e1", "e2"])
|
14
|
+
|
15
|
+
# Using the `discover` method asks the Lemma to proactively try and discover a
|
16
|
+
# server to connect to on the local network. Once the server is discovered, it
|
17
|
+
# will connect and send a Noam 'register' message. When `discover` returns, the
|
18
|
+
# Lemma is ready to send events.
|
19
|
+
publisher.discover
|
20
|
+
|
21
|
+
seq = 0
|
22
|
+
loop do
|
23
|
+
# This method block picks an event to send. It's randomized a bit so that we
|
24
|
+
# can see things change over time.
|
25
|
+
e = if 0.5 < rand()
|
26
|
+
"e1"
|
27
|
+
else
|
28
|
+
"e2"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Next, package the event sequence and the current time into a value.
|
32
|
+
v = {"seq" => seq, "time" => Time.now.to_s}
|
33
|
+
|
34
|
+
# Attempt to speak the chosen event with the value. Note, the event is either
|
35
|
+
# "e1" or "e2" based on how rand() returned.
|
36
|
+
unless publisher.speak(e, v)
|
37
|
+
# If `speak` returns false, we're unable to speak the message likely because
|
38
|
+
# the socket has closed. The connection would have to be restarted.
|
39
|
+
puts "Done"
|
40
|
+
break
|
41
|
+
end
|
42
|
+
puts "Wrote: #{e} -> #{v.inspect}"
|
43
|
+
|
44
|
+
seq += 1
|
45
|
+
# Sleep for a while so that we don't bog down the network.
|
46
|
+
sleep(1)
|
47
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'noam_lemma'
|
2
|
+
|
3
|
+
# This is an example of a Ruby Lemma that subscribes to messages. It expects a
|
4
|
+
# Noam server to be running. It also expects that the noam-lemma.rb file is in
|
5
|
+
# the search path. If you run this example from the project root, the following
|
6
|
+
# command should work:
|
7
|
+
#
|
8
|
+
# ruby -Ilib example/subscriber.rb
|
9
|
+
#
|
10
|
+
# This example _will not_ work on the same machine running the Noam server as
|
11
|
+
# both programs need to bind to UDP port 1030.
|
12
|
+
|
13
|
+
subscriber = Noam::Lemma.new('example-subscriber', ["e1", "e2"], [])
|
14
|
+
|
15
|
+
# Using the `discover` method asks the Lemma to proactively try and discover a
|
16
|
+
# server to connect to on the local network. Once the server is discovered, it
|
17
|
+
# will connect and send a Noam 'register' message. When `discover` returns, the
|
18
|
+
# Lemma is ready to receive events.
|
19
|
+
subscriber.discover
|
20
|
+
|
21
|
+
loop do
|
22
|
+
# The `listen` method will return an Event object once one is received by the
|
23
|
+
# Lemma. Until an event is heard, the `listen` method blocks.
|
24
|
+
m = subscriber.listen
|
25
|
+
|
26
|
+
# There's one special value that's returned from `listen`: the `:cancelled`
|
27
|
+
# symbol. If this shows up, it means some one else has called the `stop`
|
28
|
+
# method on the Lemma.
|
29
|
+
if :cancelled == m
|
30
|
+
puts "Done"
|
31
|
+
break
|
32
|
+
else
|
33
|
+
puts "Read: #{m.event} -> #{m.value.inspect}"
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Noam
|
2
|
+
class Beacon
|
3
|
+
attr_reader :name, :host, :port
|
4
|
+
|
5
|
+
def initialize(name, host, port)
|
6
|
+
@name = name
|
7
|
+
@host = host
|
8
|
+
@port = port
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.discover(net = "0.0.0.0")
|
12
|
+
socket = UDPSocket.new
|
13
|
+
begin
|
14
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
|
15
|
+
socket.bind(net, Noam::BEACON_PORT)
|
16
|
+
|
17
|
+
raise "Didn't see beacon after #{WAIT_TIME} seconds." unless message_received?(socket)
|
18
|
+
|
19
|
+
data, addr = socket.recvfrom(MAX_RESPONSE_LENGTH)
|
20
|
+
parsed_data = JSON.parse(data)
|
21
|
+
Beacon.new(parsed_data[1], addr[2], parsed_data[2])
|
22
|
+
ensure
|
23
|
+
socket.close
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
MAX_RESPONSE_LENGTH = 1600
|
30
|
+
WAIT_TIME = 10.0
|
31
|
+
|
32
|
+
def self.message_received?(socket)
|
33
|
+
IO.select([socket], [], [], WAIT_TIME) != nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "noam_lemma/message_filter"
|
2
|
+
|
3
|
+
module Noam
|
4
|
+
class Lemma
|
5
|
+
attr_reader :name, :listener, :player, :speaks
|
6
|
+
|
7
|
+
def initialize(name, hears = [], speaks = [])
|
8
|
+
@name = name
|
9
|
+
@speaks = speaks
|
10
|
+
@player = nil
|
11
|
+
@listener = nil
|
12
|
+
|
13
|
+
initialize_message_filter(hears)
|
14
|
+
end
|
15
|
+
|
16
|
+
def discover(beacon = nil)
|
17
|
+
beacon ||= Beacon.discover
|
18
|
+
start(beacon.host, beacon.port)
|
19
|
+
end
|
20
|
+
|
21
|
+
def advertise(room_name)
|
22
|
+
marco = Noam::Message::Marco.new(room_name, @name)
|
23
|
+
polo = marco.start
|
24
|
+
start(polo.host, polo.port)
|
25
|
+
end
|
26
|
+
|
27
|
+
def speak(event, value)
|
28
|
+
if @player
|
29
|
+
@player.put(Noam::Message::Playable.new(@name, event, value))
|
30
|
+
true
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def listen
|
37
|
+
@listener.take
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop
|
41
|
+
@player.stop if @player
|
42
|
+
@listener.stop if @listener
|
43
|
+
@player = nil
|
44
|
+
@listener = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def hears
|
48
|
+
@message_filter.hears
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_message_filter(message_filter)
|
52
|
+
@message_filter = message_filter
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def start(host, port)
|
58
|
+
@listener = Listener.new
|
59
|
+
@player = Player.new(host, port)
|
60
|
+
@player.put(Message::Register.new(@name, @listener.port, @message_filter.hears, @speaks))
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize_message_filter(hears)
|
64
|
+
@message_filter = MessageFilter.new
|
65
|
+
hears.each do |event_name|
|
66
|
+
@message_filter.hear(event_name) {}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Noam
|
4
|
+
class Listener
|
5
|
+
attr_reader :port
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@queue = Queue.new
|
9
|
+
@server = TCPServer.new(0)
|
10
|
+
@port = @server.addr[1]
|
11
|
+
|
12
|
+
manage_queue_on_thread
|
13
|
+
end
|
14
|
+
|
15
|
+
def take
|
16
|
+
@queue.pop
|
17
|
+
end
|
18
|
+
|
19
|
+
def stop
|
20
|
+
@exit_requested = true
|
21
|
+
@thread.join
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def manage_queue_on_thread
|
27
|
+
@thread = Thread.new do |t|
|
28
|
+
begin
|
29
|
+
loop_listen
|
30
|
+
ensure
|
31
|
+
@server.close
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def loop_listen
|
37
|
+
loop do
|
38
|
+
if client = listen_for_connection
|
39
|
+
read_from_client(client)
|
40
|
+
client.close
|
41
|
+
end
|
42
|
+
|
43
|
+
if exiting?
|
44
|
+
@queue.push(:cancelled)
|
45
|
+
break
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def listen_for_connection
|
51
|
+
timeout_sec = 0.1
|
52
|
+
available_ios = select([@server], nil, nil, timeout_sec)
|
53
|
+
@server.accept if available_ios
|
54
|
+
end
|
55
|
+
|
56
|
+
def read_from_client(client)
|
57
|
+
begin
|
58
|
+
loop do
|
59
|
+
message_length = client.read_nonblock(Message::MESSAGE_LENGTH_STRING_SIZE).to_i
|
60
|
+
message_content = client.read_nonblock(message_length)
|
61
|
+
@queue.push(Message::Heard.from_noam(message_content))
|
62
|
+
break if exiting?
|
63
|
+
end
|
64
|
+
rescue IO::WaitReadable
|
65
|
+
retry unless exiting?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def exiting?
|
70
|
+
return @exit_requested
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Noam
|
2
|
+
module Message
|
3
|
+
class Heard
|
4
|
+
attr_reader :source, :event, :value
|
5
|
+
|
6
|
+
def initialize(source, event, value)
|
7
|
+
@source = source
|
8
|
+
@event = event
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.from_noam(noam)
|
13
|
+
_, source, event, value = JSON.parse(noam)
|
14
|
+
Heard.new(source, event, value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Noam
|
5
|
+
module Message
|
6
|
+
class Marco
|
7
|
+
attr_reader :port
|
8
|
+
|
9
|
+
def initialize(room_name, lemma_name)
|
10
|
+
@room_name = room_name
|
11
|
+
@lemma_name = lemma_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
bcast_socket = UDPSocket.new
|
16
|
+
reply_socket = UDPSocket.new
|
17
|
+
reply_socket.bind("0.0.0.0", 0)
|
18
|
+
|
19
|
+
begin
|
20
|
+
bcast_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
|
21
|
+
|
22
|
+
loop do
|
23
|
+
bcast_socket.send(noam_encode, 0, "255.255.255.255", Noam::BEACON_PORT)
|
24
|
+
if message_received?(bcast_socket)
|
25
|
+
break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
get_polo_response(bcast_socket)
|
30
|
+
ensure
|
31
|
+
reply_socket.close
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def noam_encode
|
36
|
+
["marco", @lemma_name, @room_name, Noam::DEVICE_TYPE, Noam::VERSION].to_json
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
MAX_RESPONSE_LENGTH = 1600
|
42
|
+
WAIT_TIME = 5.0
|
43
|
+
|
44
|
+
def message_received?(socket)
|
45
|
+
IO.select([socket], [], [], WAIT_TIME)
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_polo_response(socket)
|
49
|
+
message, sockaddr = socket.recvfrom(MAX_RESPONSE_LENGTH)
|
50
|
+
_, _, noam_port = JSON.parse(message)
|
51
|
+
_, _, addr, _ = sockaddr
|
52
|
+
|
53
|
+
Polo.new(addr, noam_port)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Noam
|
2
|
+
module Message
|
3
|
+
class Playable
|
4
|
+
def initialize(host_id, event, value)
|
5
|
+
@host_id = host_id
|
6
|
+
@event = event
|
7
|
+
@value = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def noam_encode
|
11
|
+
j = ['event', @host_id, @event, @value].to_json
|
12
|
+
Noam::Message.encode_length(j.length) + j
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Noam
|
2
|
+
module Message
|
3
|
+
class Polo
|
4
|
+
attr_reader :host, :port
|
5
|
+
|
6
|
+
class InvalidHost < Exception; end
|
7
|
+
class InvalidPort < Exception; end
|
8
|
+
|
9
|
+
def initialize(host, port)
|
10
|
+
raise InvalidHost.new if (@host = host).nil?
|
11
|
+
raise InvalidPort.new if (@port = port).nil?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Noam
|
2
|
+
module Message
|
3
|
+
class Register
|
4
|
+
def initialize(device_id, port, hears, speaks)
|
5
|
+
@device_id = device_id
|
6
|
+
@port = port
|
7
|
+
@hears = hears
|
8
|
+
@speaks = speaks
|
9
|
+
end
|
10
|
+
|
11
|
+
def noam_encode
|
12
|
+
j = ["register", @device_id, @port.to_i, @hears, @speaks, Noam::DEVICE_TYPE, Noam::VERSION].to_json
|
13
|
+
Noam::Message.encode_length(j.length) + j
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Noam
|
2
|
+
module Message
|
3
|
+
MESSAGE_LENGTH_STRING_SIZE = 6
|
4
|
+
|
5
|
+
def self.encode_length(l)
|
6
|
+
("%06u" % l)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'noam_lemma/message/heard'
|
12
|
+
require 'noam_lemma/message/marco'
|
13
|
+
require 'noam_lemma/message/playable'
|
14
|
+
require 'noam_lemma/message/polo'
|
15
|
+
require 'noam_lemma/message/register'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Noam
|
2
|
+
class MessageFilter
|
3
|
+
def initialize
|
4
|
+
@hears = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def hear(event_name, &block)
|
8
|
+
@hears[event_name] ||= []
|
9
|
+
@hears[event_name] << block
|
10
|
+
end
|
11
|
+
|
12
|
+
def receive(message)
|
13
|
+
blocks = @hears[message.event] || []
|
14
|
+
blocks.each do |block|
|
15
|
+
block.call(message)
|
16
|
+
end
|
17
|
+
message
|
18
|
+
end
|
19
|
+
|
20
|
+
def hears
|
21
|
+
@hears.keys.uniq
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|