rubyslim 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|