Created
August 10, 2016 23:09
-
-
Save seansu4you87/8f153c3920f6f1cb181ac1e5654c5da4 to your computer and use it in GitHub Desktop.
A naive implementation of gen_server in Ruby, for demonstration purposes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "ostruct" | |
require "securerandom" | |
module GenServer | |
class << self | |
def call(pid, method, *args) | |
entry = fetch_entry(pid) | |
value, state = entry.module.send(method, entry.state, *args) | |
entry.state = state | |
update_entry(pid, entry) | |
value | |
end | |
def cast(pid, method, *args) | |
entry = fetch_entry(pid) | |
entry.state = entry.module.send(method, entry.state, *args) | |
update_entry(pid, entry) | |
nil | |
end | |
def start_link(mod, *args) | |
state = mod.init(*args) | |
add_entry(mod, state) | |
end | |
def terminate(pid) | |
remove_entry(pid) | |
end | |
private | |
def add_entry(mod, state) | |
SecureRandom.uuid.tap do |uuid| | |
entries[uuid] = OpenStruct.new( | |
:module => mod, | |
:state => state | |
) | |
end | |
end | |
def entries | |
@entries ||= {} | |
end | |
def fetch_entry(pid) | |
entries[pid] | |
end | |
def remove_entry(pid) | |
entries.delete(pid) | |
end | |
def update_entry(pid, entry) | |
entries[pid] = entry | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "spec_helper" | |
module Stack | |
def self.init(state) | |
state | |
end | |
def self.pop(state) | |
[state.last, state[0..-2]] | |
end | |
def self.push(state, item) | |
state + [item] | |
end | |
end | |
describe GenServer do | |
describe "call" do | |
it "calls the given method on the provided module while keeping track of state" do | |
pid = GenServer.start_link(Stack, [1]) | |
popped1 = GenServer.call(pid, :pop) | |
popped2 = GenServer.call(pid, :pop) | |
expect(popped1).to eql(1) | |
expect(popped2).to be_nil | |
GenServer.terminate(pid) | |
end | |
end | |
describe "cast" do | |
it "calls the method and updates state but returns no value" do | |
pid = GenServer.start_link(Stack, [1]) | |
GenServer.cast(pid, :push, 2) | |
popped = GenServer.call(pid, :pop) | |
expect(popped).to eql(2) | |
GenServer.terminate(pid) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment