Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A naive implementation of gen_server in Ruby, for demonstration purposes.
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
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
You can’t perform that action at this time.