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