Skip to content

Commit

Permalink
first real commit
Browse files Browse the repository at this point in the history
  • Loading branch information
unclebob committed Dec 2, 2008
1 parent 8ee99eb commit 9c7574b
Show file tree
Hide file tree
Showing 18 changed files with 555 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/init.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$: << File.expand_path(File.dirname(__FILE__) + "/../lib")
43 changes: 43 additions & 0 deletions lib/list_deserializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module ListDeserializer
class SyntaxError < Exception

end

def self.deserialize(string)
raise SyntaxError.new("Can't deserialize null") if string.nil?
raise SyntaxError.new("Can't deserialize empty string") if string.empty?
raise SyntaxError.new("Serialized list has no starting [") if string[0..0] != "["
raise SyntaxError.new("Serialized list has no ending ]") if string[-1..-1] != "]"
Deserializer.new(string).deserialize
end

class Deserializer
def initialize(string)
@string = string;
end

def deserialize
@pos = 1;
@list = []
number_of_items = get_length
number_of_items.times do
length_of_item = get_length
item = @string[@pos...@pos+length_of_item]
@pos += length_of_item+1
begin
sublist = ListDeserializer.deserialize(item)
@list << sublist
rescue ListDeserializer::SyntaxError
@list << item
end
end
@list
end

def get_length
length = @string[@pos...@pos+6].to_i
@pos += 7
length
end
end
end
5 changes: 5 additions & 0 deletions lib/list_executor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ListExecutor
def execute(instructions)
[["inv1", "wow"]]
end
end
18 changes: 18 additions & 0 deletions lib/list_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module ListSerializer
def self.serialize(list)
result = "["
result += length_string(list.length)
list.each do |item|
item = "null" if item.nil?
item = serialize(item) if item.is_a?(Array)
item = item.to_s
result += length_string(item.length)
result += item + ":"
end
result += "]"
end

def self.length_string(length)
sprintf("%06d:",length)
end
end
23 changes: 23 additions & 0 deletions lib/ruby_slim.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require File.expand_path(File.dirname(__FILE__) + "/init")
require "socket_service"

connected = true
port = ARGV[0].to_i
puts "port: #{port}"
socket_service = SocketService.new()
socket_service.serve(port) do |socket|
puts "Got connection"
socket.puts("Slim -- V0.0");
length = socket.read(6).to_i
socket.read(1); #skip colon
command = socket.read(length);
if command.downcase != "bye"
puts "Oh no, that wasn't a bye."
end
connected = false
end

while connected
sleep(0.1)
end

3 changes: 3 additions & 0 deletions lib/slim_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class SlimError < Exception

end
53 changes: 53 additions & 0 deletions lib/socket_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require 'socket'
require 'thread'


class SocketService

attr_reader :closed

def initialize()
@ropeSocket = nil
@group = ThreadGroup.new
@serviceThread = nil
end

def serve(port, &action)
@closed = false
@action = action
@ropeSocket = TCPserver.open(port)
@serviceThread = Thread.start {serviceTask}
@group.add(@serviceThread)
end

def pendingSessions
@group.list.size - ((@serviceThread != nil) ? 1 : 0)
end

def serviceTask
while true
Thread.start(@ropeSocket.accept) do |s|
serverTask(s)
end
end
end

def serverTask(s)
@action.call(s)
s.close
end

def close
@serviceThread.kill
@serviceThread = nil
@ropeSocket.close
waitForServers
@closed = true
end

def waitForServers
@group.list.each do |t|
t.join
end
end
end
70 changes: 70 additions & 0 deletions lib/statement_executor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
require "slim_error"

class StatementExecutor
def initialize
@instances = {}
end

def create(instance_name, class_name, constructor_arguments)
@instances[instance_name] = construct_instance(class_name, constructor_arguments);
"OK"
end

def construct_instance(class_name, constructor_arguments)
require_class(class_name);
construct(class_name, constructor_arguments);
end

def require_class(class_name)
path = make_path_to_class(class_name)
begin
require path
rescue LoadError => e
raise SlimError.new("message:<<COULD_NOT_INVOKE_CONSTRUCTOR #{path}>>")

end
end

def make_path_to_class(class_name)
module_names = split_class_name(class_name)
files = module_names.collect { |module_name| to_file_name(module_name) }
File.join(files)
end

def split_class_name(class_name)
class_name.split(/\.|\:\:/)
end

def construct(class_name, constructor_arguments)
module_path = make_module_path(class_name)
class_object = eval(module_path)
begin
class_object.new(*constructor_arguments)
rescue ArgumentError => e
raise SlimError.new("message:<<COULD_NOT_INVOKE_CONSTRUCTOR #{module_path}[#{constructor_arguments.length}]>>")
end
end

def make_module_path(class_name)
module_names = split_class_name(class_name)
module_names.join("::");
end

def instance(instance_name)
@instances[instance_name]
end

def to_file_name(module_name)
value = module_name[0..0].downcase + module_name[1..-1]
value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" }
end

def call(instance_name, method_name, *args)
instance = @instances[instance_name]
method = method_name.to_sym
raise SlimError.new("message:<<NO_METHOD_IN_CLASS #{method}[#{args.length}] #{instance.class.name}.>>") if !instance.respond_to?(method)
instance.send(method, *args)
end


end
5 changes: 5 additions & 0 deletions lib/test_module/test_slim.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module TestModule
class TestSlim

end
end
11 changes: 11 additions & 0 deletions lib/test_module/test_slim_with_arguments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module TestModule
class TestSlimWithArguments
def initialize(arg)
@arg = arg
end

def arg
@arg
end
end
end
41 changes: 41 additions & 0 deletions spec/instance_creation_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
require "statement_executor"

describe StatementExecutor do
before do
@caller = StatementExecutor.new
end

it "can create an instance" do
response = @caller.create("x", "TestModule.TestSlim",[])
response.should == "OK"
x = @caller.instance("x")
x.class.name.should == "TestModule::TestSlim"
end

it "can create an instance with arguments" do
response = @caller.create("x", "TestModule.TestSlimWithArguments", ["3"])
response.should == "OK"
x = @caller.instance("x")
x.arg.should == "3"
end

it "can't create an instance with the wrong number of arguments" do
begin
@caller.create("x", "TestModule.TestSlim", ["noSuchArgument"])
fail("Shouldn't get here");
rescue SlimError => e
e.to_s.should == "message:<<COULD_NOT_INVOKE_CONSTRUCTOR TestModule::TestSlim[1]>>"
end
end


it "can't create an instance if there is no class" do
begin
@caller.create("x", "TestModule.NoSuchClass", [])
fail("Shouldn't get here");
rescue SlimError => e
e.to_s.should == "message:<<COULD_NOT_INVOKE_CONSTRUCTOR test_module/no_such_class>>"
end
end
end
52 changes: 52 additions & 0 deletions spec/list_deserializer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
require "list_serializer"
require "list_deserializer"

describe ListDeserializer do
before do
@list = []
end

it "can't deserialize a null string" do
proc {ListDeserializer.deserialize(nil)}.should raise_error(ListDeserializer::SyntaxError)
end

it "can't deserialize empty string" do
proc {ListDeserializer.deserialize("")}.should raise_error(ListDeserializer::SyntaxError)
end

it "can't deserialize string that doesn't start with an open bracket" do
proc {ListDeserializer.deserialize("hello")}.should raise_error(ListDeserializer::SyntaxError)
end

it "can't deserialize string that doesn't end with a bracket" do
proc {ListDeserializer.deserialize("[000000:")}.should raise_error(ListDeserializer::SyntaxError)
end

def check()
serialized = ListSerializer.serialize(@list)
deserialized = ListDeserializer.deserialize(serialized)
deserialized.should == @list
end

it "should deserialize and empty list" do
check
end

it "should deserialize a list with one element" do
@list = ["hello"]
check
end

it "should deserialize a list with two elements" do
@list = ["hello", "bob"]
check
end

it "should deserialize sublists" do
@list = ["hello", ["bob", "micah"], "today"]
check
end


end
29 changes: 29 additions & 0 deletions spec/list_executor_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
require "statement_executor"
require "list_executor"

describe ListExecutor do
before do
@executor = ListExecutor.new
@statements = []
@statements << ["i1", "import", "TestModule"]
@statements << ["m1", "make", "test_slim", "TestSlim"]
@expected_results = []
@expected_results << ["i1", "OK"]
@expected_results << ["m1", "OK"]
end

it "can't execute an invalid operation" do
@statements << ["inv1", "invalidOperation"]
results = @executor.execute(@statements)
result_map = pairs_to_map(results)
result = result_map["inv1"]
result.should include("__EXCEPTION__:")
end

def pairs_to_map(pairs)
map = {}
pairs.each {|pair| map[pair[0]] = pair[1]}
map
end
end
Loading

0 comments on commit 9c7574b

Please sign in to comment.