Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Melon::UDP #3

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language: ruby
rvm:
- 2.5.1
14 changes: 6 additions & 8 deletions lib/melon/drb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,18 @@ module Melon

def self.with_drb local_storage: Melon::LocalStorage.new, host: "localhost", port: 8484
@drb_servers << Melon::DRb::StorageServer.new(local_storage, host: host, port: port)
Melon::Paradigm.new(local_storage)
Melon::DRb::Paradigm.new(local_storage)
end

def self.stop_drb
@drb_servers.each(&:stop)
end

def self.servers
@drb_servers
end

class Paradigm
def add_remote host: "localhost", port: 8484
self.add_server Melon::DRb::RemoteStorage.new(host: host, port: port)
module DRb
class Paradigm < ::Melon::Paradigm
def add_remote host: "localhost", port: 8484
self.add_server Melon::DRb::RemoteStorage.new(host: host, port: port)
end
end
end
end
24 changes: 24 additions & 0 deletions lib/melon/udp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require_relative '../melon'
require_relative 'udp/remote_storage'
require_relative 'udp/storage_server'

module Melon
@udp_servers = []

def self.with_udp local_storage: Melon::LocalStorage.new, host: "localhost", port: 8484
@udp_servers << Melon::UDP::StorageServer.new(local_storage, host: host, port: port)
Melon::UDP::Paradigm.new(local_storage)
end

def self.stop_udp
@udp_servers.each(&:stop)
end

module UDP
class Paradigm < Melon::Paradigm
def add_remote host: "localhost", port: 8484
self.add_server Melon::UDP::RemoteStorage.new(host: host, port: port)
end
end
end
end
42 changes: 42 additions & 0 deletions lib/melon/udp/remote_storage.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'socket'

module Melon
module UDP
class RemoteStorage
def initialize host: "localhost", port: 8484
@socket = UDPSocket.new
@socket.connect host, port
end

def send_msg action, template, read_msgs = nil
msg = {
action: action,
template: template
}

msg[:read_msgs] = read_msgs if read_msgs

@socket.send Marshal.dump(msg), 0

reply, _ = @socket.recvfrom(6500)
Marshal.load reply
end

def find_unread template, read_msgs
send_msg :find_unread, template, read_msgs
end

def find_all_unread template, read_msgs
send_msg :find_all_unread, template, read_msgs
end

def find_and_take template
send_msg :find_and_take, template
end

def take_all template
send_msg :take_all, template
end
end
end
end
68 changes: 68 additions & 0 deletions lib/melon/udp/storage_server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require 'socket'

Thread.abort_on_exception = true

module Melon
module UDP
class StorageServer
def initialize storage, host: "localhost", port: 8484
@storage = storage

begin
@thread = Thread.new do
Socket.udp_server_loop(host, port) do |msg, sender|
handle_message msg, sender
end
end
rescue Errno::EADDRINUSE
port += 1
retry
end

warn "Started Melon server on #{host} #{port}"
end

def stop
@thread.kill
end

def handle_message msg, sender
m = Marshal.load msg
template = m[:template]
read_msgs = m[:read_msgs]

reply = case m[:action]
when :find_unread
find_unread template, read_msgs
when :find_all_unread
find_all_unread template, read_msgs
when :find_and_take
find_and_take template
when :take_all
take_all template
else
warn "Unknown message: #{m.inspect}"
{ error: "Unknown message: #{m.inspect}" }
end

sender.reply Marshal.dump(reply)
end

def find_unread template, read_msgs
@storage.find_unread template, read_msgs
end

def find_all_unread template, read_msgs
@storage.find_all_unread template, read_msgs
end

def find_and_take template
@storage.find_and_take template
end

def take_all template
@storage.take_all template
end
end
end
end
4 changes: 0 additions & 4 deletions test/test.rb

This file was deleted.

80 changes: 9 additions & 71 deletions test/test_drb_melon.rb
Original file line number Diff line number Diff line change
@@ -1,81 +1,19 @@
require_relative 'test_helper'
require 'melon/drb'

class TestDRbMelon < Minitest::Test
include CoreMelonTests

def teardown
Melon.stop_drb
::DRb.stop_service
end

def test_sanity
assert_kind_of Melon::Paradigm, Melon.with_drb
end

def test_store_take
m1 = Melon.with_drb port: 8484
m2 = Melon.with_drb port: 8585

m2.add_remote port: 8484
m1.add_remote port: 8585

m1.store ["hello", "world"]
m1.store ["hello", "world"]
msg1 = m2.take [String, "world"]
msg2 = m2.take [String, "world"]
msg3 = m2.take [String, "world"], false

assert_equal ["hello", "world"], msg1
assert_equal ["hello", "world"], msg2
assert_nil msg3
end

def test_store_take_all
m1 = Melon.with_drb port: 8489
m2 = Melon.with_drb port: 8580

m2.add_remote port: 8489
m1.add_remote port: 8580

m1.store ["hello", "world"]
m1.store ["hello", "world"]
msgs1 = m2.take_all [String, "world"]
msgs2 = m1.take_all [String, "world"], false

assert_equal [["hello", "world"], ["hello", "world"]], msgs1
assert msgs2.empty?
end

def test_write_read
m1 = Melon.with_drb port: 8486
m2 = Melon.with_drb port: 8587

m2.add_remote port: 8486
m1.add_remote port: 8587

m1.write ["hello", "world"]

msg1 = m2.read [String, "world"]
msg2 = m1.read [String, "world"]

assert_equal ["hello", "world"], msg1
assert_equal ["hello", "world"], msg2
end

def test_write_read_all
m1 = Melon.with_drb port: 8488
m2 = Melon.with_drb port: 8589

m2.add_remote port: 8488
m1.add_remote port: 8589

m1.write ["hello", "world"]
m2.write ["hello", "everyone"]

msgs1 = m2.read_all ["hello", String]
msgs2 = m1.read_all ["hello", String]

assert msgs1.include? ["hello", "world"]
assert msgs2.include? ["hello", "world"]
assert msgs1.include? ["hello", "everyone"]
assert msgs2.include? ["hello", "everyone"]
def make_melon port: nil
if port
Melon.with_drb port: port
else
Melon.with_drb
end
end
end
93 changes: 93 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
require 'minitest'
require 'minitest/autorun'
require 'minitest/pride'
require_relative '../lib/melon'

module CoreMelonTests
@port = 8000

def self.port
@port += 1
end

def test_sanity
assert_kind_of Melon::Paradigm, make_melon
end

def port1
@port1 ||= CoreMelonTests.port
end

def port2
@port2 ||= CoreMelonTests.port
end

def test_store_take
m1 = make_melon port: port1
m2 = make_melon port: port2

m2.add_remote port: port1
m1.add_remote port: port2

m1.store ["hello", "world"]
m1.store ["hello", "world"]
msg1 = m2.take [String, "world"]
msg2 = m2.take [String, "world"]
msg3 = m2.take [String, "world"], false

assert_equal ["hello", "world"], msg1
assert_equal ["hello", "world"], msg2
assert_nil msg3
end

def test_store_take_all
m1 = make_melon port: port1
m2 = make_melon port: port2

m2.add_remote port: port1
m1.add_remote port: port2

m1.store ["hello", "world"]
m1.store ["hello", "world"]
msgs1 = m2.take_all [String, "world"]
msgs2 = m1.take_all [String, "world"], false

assert_equal [["hello", "world"], ["hello", "world"]], msgs1
assert msgs2.empty?
end

def test_write_read
m1 = make_melon port: port1
m2 = make_melon port: port2

m2.add_remote port: port1
m1.add_remote port: port2

m1.write ["hello", "world"]

msg1 = m2.read [String, "world"]
msg2 = m1.read [String, "world"]

assert_equal ["hello", "world"], msg1
assert_equal ["hello", "world"], msg2
end

def test_write_read_all
m1 = make_melon port: port1
m2 = make_melon port: port2

m2.add_remote port: port1
m1.add_remote port: port2

m1.write ["hello", "world"]
m2.write ["hello", "everyone"]

msgs1 = m2.read_all ["hello", String]
msgs2 = m1.read_all ["hello", String]

assert msgs1.include? ["hello", "world"]
assert msgs2.include? ["hello", "world"]
assert msgs1.include? ["hello", "everyone"]
assert msgs2.include? ["hello", "everyone"]
end
end
2 changes: 2 additions & 0 deletions test/test_local_storage.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require_relative 'test_helper'

class TestLocalStorage < Minitest::Test
def setup
@ls = Melon::LocalStorage.new
Expand Down
1 change: 1 addition & 0 deletions test/test_paradigm.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require_relative 'test_helper'
require_relative '../lib/melon/paradigm'

class TestParadigm < Minitest::Test
Expand Down
2 changes: 2 additions & 0 deletions test/test_stored_message.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require_relative 'test_helper'

class TestStoredMessage < Minitest::Test
def test_creation
m = ["hello", "world"]
Expand Down
18 changes: 18 additions & 0 deletions test/test_udp_melon.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require_relative 'test_helper'
require 'melon/udp'

class TestUDPMelon < Minitest::Test
include CoreMelonTests

def teardown
Melon.stop_udp
end

def make_melon port: nil
if port
Melon.with_udp port: port
else
Melon.with_udp
end
end
end