forked from unclebob/rubyslim
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
unclebob
committed
Dec 2, 2008
1 parent
8ee99eb
commit 9c7574b
Showing
18 changed files
with
555 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
$: << File.expand_path(File.dirname(__FILE__) + "/../lib") |
This file contains 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
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 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class ListExecutor | ||
def execute(instructions) | ||
[["inv1", "wow"]] | ||
end | ||
end |
This file contains 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
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 |
This file contains 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
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 | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
class SlimError < Exception | ||
|
||
end |
This file contains 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
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 |
This file contains 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
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 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module TestModule | ||
class TestSlim | ||
|
||
end | ||
end |
This file contains 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
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 |
This file contains 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
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 |
This file contains 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
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 |
This file contains 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
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 |
Oops, something went wrong.