rubyslim 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +16 -0
- data/README.md +187 -0
- data/Rakefile +21 -0
- data/bin/rubyslim +10 -0
- data/lib/rubyslim/list_deserializer.rb +67 -0
- data/lib/rubyslim/list_executor.rb +12 -0
- data/lib/rubyslim/list_serializer.rb +43 -0
- data/lib/rubyslim/ruby_slim.rb +61 -0
- data/lib/rubyslim/slim_error.rb +3 -0
- data/lib/rubyslim/slim_helper_library.rb +23 -0
- data/lib/rubyslim/socket_service.rb +53 -0
- data/lib/rubyslim/statement.rb +79 -0
- data/lib/rubyslim/statement_executor.rb +174 -0
- data/lib/rubyslim/table_to_hash_converter.rb +34 -0
- data/lib/test_module/library_new.rb +13 -0
- data/lib/test_module/library_old.rb +12 -0
- data/lib/test_module/should_not_find_test_slim_in_here/test_slim.rb +7 -0
- data/lib/test_module/simple_script.rb +9 -0
- data/lib/test_module/test_chain.rb +5 -0
- data/lib/test_module/test_slim.rb +86 -0
- data/lib/test_module/test_slim_with_arguments.rb +23 -0
- data/lib/test_module/test_slim_with_no_sut.rb +5 -0
- data/rubyslim.gemspec +21 -0
- data/spec/instance_creation_spec.rb +40 -0
- data/spec/it8f_spec.rb +9 -0
- data/spec/list_deserializer_spec.rb +67 -0
- data/spec/list_executor_spec.rb +187 -0
- data/spec/list_serialzer_spec.rb +39 -0
- data/spec/method_invocation_spec.rb +128 -0
- data/spec/slim_helper_library_spec.rb +80 -0
- data/spec/socket_service_spec.rb +98 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/statement_executor_spec.rb +50 -0
- data/spec/statement_spec.rb +17 -0
- data/spec/table_to_hash_converter_spec.rb +32 -0
- metadata +100 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
class SlimHelperLibrary
|
2
|
+
ACTOR_INSTANCE_NAME = "scriptTableActor"
|
3
|
+
attr_accessor :executor
|
4
|
+
|
5
|
+
def initialize(executor = nil)
|
6
|
+
@executor = executor
|
7
|
+
@fixtures = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_fixture
|
11
|
+
executor.instance(ACTOR_INSTANCE_NAME)
|
12
|
+
end
|
13
|
+
|
14
|
+
def push_fixture
|
15
|
+
@fixtures << get_fixture
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def pop_fixture
|
20
|
+
executor.set_instance(ACTOR_INSTANCE_NAME, @fixtures.pop)
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
|
5
|
+
class SocketService
|
6
|
+
|
7
|
+
attr_reader :closed
|
8
|
+
|
9
|
+
def initialize()
|
10
|
+
@ropeSocket = nil
|
11
|
+
@group = ThreadGroup.new
|
12
|
+
@serviceThread = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def serve(port, &action)
|
16
|
+
@closed = false
|
17
|
+
@action = action
|
18
|
+
@ropeSocket = TCPServer.open(port)
|
19
|
+
@serviceThread = Thread.start {serviceTask}
|
20
|
+
@group.add(@serviceThread)
|
21
|
+
end
|
22
|
+
|
23
|
+
def pendingSessions
|
24
|
+
@group.list.size - ((@serviceThread != nil) ? 1 : 0)
|
25
|
+
end
|
26
|
+
|
27
|
+
def serviceTask
|
28
|
+
while true
|
29
|
+
Thread.start(@ropeSocket.accept) do |s|
|
30
|
+
serverTask(s)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def serverTask(s)
|
36
|
+
@action.call(s)
|
37
|
+
s.close
|
38
|
+
end
|
39
|
+
|
40
|
+
def close
|
41
|
+
@serviceThread.kill
|
42
|
+
@serviceThread = nil
|
43
|
+
@ropeSocket.close
|
44
|
+
waitForServers
|
45
|
+
@closed = true
|
46
|
+
end
|
47
|
+
|
48
|
+
def waitForServers
|
49
|
+
@group.list.each do |t|
|
50
|
+
t.join
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "rubyslim/statement_executor"
|
2
|
+
|
3
|
+
class Statement
|
4
|
+
EXCEPTION_TAG = "__EXCEPTION__:"
|
5
|
+
def self.execute(statement, executor)
|
6
|
+
Statement.new(statement).exec(executor)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(statement)
|
10
|
+
@statement = statement
|
11
|
+
end
|
12
|
+
|
13
|
+
def exec(executor)
|
14
|
+
@executor = executor
|
15
|
+
begin
|
16
|
+
case(operation)
|
17
|
+
when "make"
|
18
|
+
instance_name = get_word(2)
|
19
|
+
class_name = slim_to_ruby_class(get_word(3))
|
20
|
+
[id, @executor.create(instance_name, class_name, get_args(4))]
|
21
|
+
when "import"
|
22
|
+
@executor.add_module(slim_to_ruby_class(get_word(2)))
|
23
|
+
[id, "OK"]
|
24
|
+
when "call"
|
25
|
+
call_method_at_index(2)
|
26
|
+
when "callAndAssign"
|
27
|
+
result = call_method_at_index(3)
|
28
|
+
@executor.set_symbol(get_word(2), result[1])
|
29
|
+
result
|
30
|
+
else
|
31
|
+
[id, EXCEPTION_TAG + "message:<<INVALID_STATEMENT: #{@statement.inspect}.>>"]
|
32
|
+
end
|
33
|
+
rescue SlimError => e
|
34
|
+
[id, EXCEPTION_TAG + e.message]
|
35
|
+
rescue Exception => e
|
36
|
+
[id, EXCEPTION_TAG + e.message + "\n" + e.backtrace.join("\n")]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def call_method_at_index(index)
|
42
|
+
instance_name = get_word(index)
|
43
|
+
method_name = slim_to_ruby_method(get_word(index+1))
|
44
|
+
args = get_args(index+2)
|
45
|
+
[id, @executor.call(instance_name, method_name, *args)]
|
46
|
+
end
|
47
|
+
|
48
|
+
def slim_to_ruby_class(class_name)
|
49
|
+
parts = class_name.split(/\.|\:\:/)
|
50
|
+
converted = parts.collect {|part| part[0..0].upcase+part[1..-1]}
|
51
|
+
converted.join("::")
|
52
|
+
end
|
53
|
+
|
54
|
+
def slim_to_ruby_method(method_name)
|
55
|
+
value = method_name[0..0].downcase + method_name[1..-1]
|
56
|
+
value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" }
|
57
|
+
end
|
58
|
+
|
59
|
+
def id
|
60
|
+
get_word(0)
|
61
|
+
end
|
62
|
+
|
63
|
+
def operation
|
64
|
+
get_word(1)
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_word(index)
|
68
|
+
check_index(index)
|
69
|
+
@statement[index]
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_args(index)
|
73
|
+
@statement[index..-1]
|
74
|
+
end
|
75
|
+
|
76
|
+
def check_index(index)
|
77
|
+
raise SlimError.new("message:<<MALFORMED_INSTRUCTION #{@statement.inspect}.>>") if index >= @statement.length
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require "rubyslim/slim_error"
|
2
|
+
require "rubyslim/statement"
|
3
|
+
require "rubyslim/table_to_hash_converter"
|
4
|
+
require "rubyslim/slim_helper_library"
|
5
|
+
|
6
|
+
class StatementExecutor
|
7
|
+
def initialize
|
8
|
+
@instances = {}
|
9
|
+
@modules = []
|
10
|
+
@symbols = {}
|
11
|
+
@libraries = [SlimHelperLibrary.new(self)]
|
12
|
+
end
|
13
|
+
|
14
|
+
def library?(instance_name)
|
15
|
+
library_prefix = "library"
|
16
|
+
instance_name[0, library_prefix.length] == library_prefix
|
17
|
+
end
|
18
|
+
|
19
|
+
def create(instance_name, class_name, constructor_arguments)
|
20
|
+
begin
|
21
|
+
instance = replace_symbol(class_name)
|
22
|
+
if instance.is_a?(String)
|
23
|
+
instance = construct_instance(instance, replace_symbols(constructor_arguments))
|
24
|
+
if library?(instance_name)
|
25
|
+
@libraries << instance
|
26
|
+
end
|
27
|
+
end
|
28
|
+
set_instance(instance_name, instance)
|
29
|
+
"OK"
|
30
|
+
rescue SlimError => e
|
31
|
+
Statement::EXCEPTION_TAG + e.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_instance(instance_name, instance)
|
36
|
+
@instances[instance_name] = instance
|
37
|
+
end
|
38
|
+
|
39
|
+
def construct_instance(class_name, constructor_arguments)
|
40
|
+
require_class(class_name);
|
41
|
+
construct(class_name, constructor_arguments);
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def make_path_to_class(class_name)
|
46
|
+
module_names = split_class_name(class_name)
|
47
|
+
files = module_names.collect { |module_name| to_file_name(module_name) }
|
48
|
+
File.join(files)
|
49
|
+
end
|
50
|
+
|
51
|
+
def split_class_name(class_name)
|
52
|
+
class_name.split(/\:\:/)
|
53
|
+
end
|
54
|
+
|
55
|
+
def replace_tables_with_hashes(constructor_arguments)
|
56
|
+
args = constructor_arguments.map do |arg|
|
57
|
+
TableToHashConverter.convert arg
|
58
|
+
end
|
59
|
+
return args
|
60
|
+
end
|
61
|
+
|
62
|
+
def construct(class_name, constructor_arguments)
|
63
|
+
class_object = get_class(class_name)
|
64
|
+
begin
|
65
|
+
class_object.new(*replace_tables_with_hashes(constructor_arguments))
|
66
|
+
rescue ArgumentError => e
|
67
|
+
raise SlimError.new("message:<<COULD_NOT_INVOKE_CONSTRUCTOR #{class_name}[#{constructor_arguments.length}]>>")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def with_each_fully_qualified_class_name(class_name, &block)
|
73
|
+
(@modules.map { |module_name| module_name + "::" + class_name } << class_name).reverse.each &block
|
74
|
+
end
|
75
|
+
|
76
|
+
def require_class(class_name)
|
77
|
+
with_each_fully_qualified_class_name(class_name) do |fully_qualified_name|
|
78
|
+
begin
|
79
|
+
require make_path_to_class(fully_qualified_name)
|
80
|
+
return
|
81
|
+
rescue LoadError
|
82
|
+
end
|
83
|
+
end
|
84
|
+
raise SlimError.new("message:<<COULD_NOT_INVOKE_CONSTRUCTOR #{class_name} failed to find in #{@modules.map { |mod| make_path_to_class(mod) }.inspect}>>")
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_class(class_name)
|
88
|
+
with_each_fully_qualified_class_name(class_name) do |fully_qualified_name|
|
89
|
+
begin
|
90
|
+
return eval(fully_qualified_name)
|
91
|
+
rescue NameError
|
92
|
+
end
|
93
|
+
end
|
94
|
+
raise SlimError.new("message:<<COULD_NOT_INVOKE_CONSTRUCTOR #{class_name} in any module #{@modules.inspect}>>")
|
95
|
+
end
|
96
|
+
|
97
|
+
def instance(instance_name)
|
98
|
+
@instances[instance_name]
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_file_name(module_name)
|
102
|
+
value = module_name[0..0].downcase + module_name[1..-1]
|
103
|
+
value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" }
|
104
|
+
end
|
105
|
+
|
106
|
+
def send_message_to_instance(instance, method, args)
|
107
|
+
symbols = replace_symbols(args)
|
108
|
+
instance.send(method, *replace_tables_with_hashes(symbols))
|
109
|
+
end
|
110
|
+
|
111
|
+
def method_to_call(instance, method_name)
|
112
|
+
return nil unless instance
|
113
|
+
return method_name.to_sym if instance.respond_to?(method_name)
|
114
|
+
return "#{$1}=".to_sym if method_name =~ /set_(\w+)/ && instance.respond_to?("#{$1}=")
|
115
|
+
return nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def call(instance_name, method_name, *args)
|
119
|
+
begin
|
120
|
+
instance = self.instance(instance_name)
|
121
|
+
if method = method_to_call(instance, method_name)
|
122
|
+
send_message_to_instance(instance, method, args)
|
123
|
+
elsif instance.respond_to?(:sut) && method = method_to_call(instance.sut, method_name)
|
124
|
+
send_message_to_instance(instance.sut, method, args)
|
125
|
+
else
|
126
|
+
@libraries.reverse_each do |library|
|
127
|
+
method = method_to_call(library, method_name)
|
128
|
+
return send_message_to_instance(library, method, args) if method
|
129
|
+
end
|
130
|
+
raise SlimError.new("message:<<NO_INSTANCE #{instance_name}>>") if instance.nil?
|
131
|
+
raise SlimError.new("message:<<NO_METHOD_IN_CLASS #{method_name}[#{args.length}] #{instance.class.name}.>>")
|
132
|
+
end
|
133
|
+
rescue SlimError => e
|
134
|
+
Statement::EXCEPTION_TAG + e.to_s
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def add_module(module_name)
|
139
|
+
@modules << module_name.gsub(/\./, '::')
|
140
|
+
end
|
141
|
+
|
142
|
+
def set_symbol(name, value)
|
143
|
+
@symbols[name] = value
|
144
|
+
end
|
145
|
+
|
146
|
+
def get_symbol(name)
|
147
|
+
@symbols[name]
|
148
|
+
end
|
149
|
+
|
150
|
+
def acquire_symbol(symbol_text)
|
151
|
+
symbol = get_symbol(symbol_text[1..-1])
|
152
|
+
symbol = symbol_text if symbol.nil?
|
153
|
+
symbol
|
154
|
+
end
|
155
|
+
|
156
|
+
def replace_symbol(item)
|
157
|
+
match = item.match(/\A\$\w*\z/)
|
158
|
+
return acquire_symbol(match[0]) if match
|
159
|
+
|
160
|
+
item.gsub(/\$\w*/) do |match|
|
161
|
+
acquire_symbol(match)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def replace_symbols(list)
|
166
|
+
list.map do |item|
|
167
|
+
if item.kind_of?(Array)
|
168
|
+
replace_symbols(item)
|
169
|
+
else
|
170
|
+
replace_symbol(item)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
class TableToHashConverter
|
4
|
+
def self.convert(string)
|
5
|
+
begin
|
6
|
+
doc = REXML::Document.new(string.strip)
|
7
|
+
return html_to_hash(doc)
|
8
|
+
rescue
|
9
|
+
return string
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.html_to_hash(doc)
|
14
|
+
table = doc.elements['table']
|
15
|
+
raise ArgumentError if table.nil?
|
16
|
+
create_hash_from_rows(table)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.create_hash_from_rows(rows)
|
20
|
+
hash = {}
|
21
|
+
rows.elements.each('tr') do |row|
|
22
|
+
add_row_to_hash(hash, row)
|
23
|
+
end
|
24
|
+
hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.add_row_to_hash(hash, row)
|
28
|
+
columns = row.get_elements('td')
|
29
|
+
raise ArgumentError if columns.size != 2
|
30
|
+
# Handle empty values.
|
31
|
+
columns[1].text = '' if columns[1].text == nil
|
32
|
+
hash[columns[0].text.strip.to_sym] = columns[1].text.strip
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module TestModule
|
4
|
+
class SystemUnderTest
|
5
|
+
attr_accessor :attribute
|
6
|
+
def sut_method
|
7
|
+
true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class TestSlim
|
12
|
+
attr_reader :sut
|
13
|
+
attr_accessor :string
|
14
|
+
|
15
|
+
def initialize(generation = 0)
|
16
|
+
@generation = generation
|
17
|
+
@sut = SystemUnderTest.new
|
18
|
+
@string = "string"
|
19
|
+
end
|
20
|
+
|
21
|
+
def return_string
|
22
|
+
@string
|
23
|
+
end
|
24
|
+
|
25
|
+
def returnString #Should not ever be called.
|
26
|
+
"blah"
|
27
|
+
end
|
28
|
+
|
29
|
+
def add(a, b)
|
30
|
+
a+b
|
31
|
+
end
|
32
|
+
|
33
|
+
def echo(x)
|
34
|
+
x
|
35
|
+
end
|
36
|
+
|
37
|
+
def null
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def echo_int i
|
42
|
+
i
|
43
|
+
end
|
44
|
+
|
45
|
+
def echo_string s
|
46
|
+
s
|
47
|
+
end
|
48
|
+
|
49
|
+
def syntax_error
|
50
|
+
eval "1,2"
|
51
|
+
end
|
52
|
+
|
53
|
+
def utf8
|
54
|
+
"Espa\357\277\275ol"
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_test_slim_with_string(string)
|
58
|
+
slim = TestSlim.new(@generation + 1)
|
59
|
+
slim.string = string
|
60
|
+
slim
|
61
|
+
end
|
62
|
+
|
63
|
+
def new_with_string(string)
|
64
|
+
s = TestSlim.new
|
65
|
+
s.string = string
|
66
|
+
s
|
67
|
+
end
|
68
|
+
|
69
|
+
def echo_object(method, string)
|
70
|
+
OpenStruct.new(method.to_sym => string)
|
71
|
+
end
|
72
|
+
|
73
|
+
def call_on(method, object)
|
74
|
+
object.send(method.to_sym)
|
75
|
+
end
|
76
|
+
|
77
|
+
# def is_same(other)
|
78
|
+
# self === other
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# def get_string_from_other other
|
82
|
+
# other.get_string_arg
|
83
|
+
# end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|