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.
Files changed (38) hide show
  1. data/.gitignore +7 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +16 -0
  4. data/README.md +187 -0
  5. data/Rakefile +21 -0
  6. data/bin/rubyslim +10 -0
  7. data/lib/rubyslim/list_deserializer.rb +67 -0
  8. data/lib/rubyslim/list_executor.rb +12 -0
  9. data/lib/rubyslim/list_serializer.rb +43 -0
  10. data/lib/rubyslim/ruby_slim.rb +61 -0
  11. data/lib/rubyslim/slim_error.rb +3 -0
  12. data/lib/rubyslim/slim_helper_library.rb +23 -0
  13. data/lib/rubyslim/socket_service.rb +53 -0
  14. data/lib/rubyslim/statement.rb +79 -0
  15. data/lib/rubyslim/statement_executor.rb +174 -0
  16. data/lib/rubyslim/table_to_hash_converter.rb +34 -0
  17. data/lib/test_module/library_new.rb +13 -0
  18. data/lib/test_module/library_old.rb +12 -0
  19. data/lib/test_module/should_not_find_test_slim_in_here/test_slim.rb +7 -0
  20. data/lib/test_module/simple_script.rb +9 -0
  21. data/lib/test_module/test_chain.rb +5 -0
  22. data/lib/test_module/test_slim.rb +86 -0
  23. data/lib/test_module/test_slim_with_arguments.rb +23 -0
  24. data/lib/test_module/test_slim_with_no_sut.rb +5 -0
  25. data/rubyslim.gemspec +21 -0
  26. data/spec/instance_creation_spec.rb +40 -0
  27. data/spec/it8f_spec.rb +9 -0
  28. data/spec/list_deserializer_spec.rb +67 -0
  29. data/spec/list_executor_spec.rb +187 -0
  30. data/spec/list_serialzer_spec.rb +39 -0
  31. data/spec/method_invocation_spec.rb +128 -0
  32. data/spec/slim_helper_library_spec.rb +80 -0
  33. data/spec/socket_service_spec.rb +98 -0
  34. data/spec/spec_helper.rb +2 -0
  35. data/spec/statement_executor_spec.rb +50 -0
  36. data/spec/statement_spec.rb +17 -0
  37. data/spec/table_to_hash_converter_spec.rb +32 -0
  38. 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,13 @@
1
+ module TestModule
2
+ class LibraryNew
3
+ attr_reader :called
4
+ attr_accessor :lib_attribute
5
+ def method_on_library_new
6
+ @called = true;
7
+ end
8
+
9
+ def a_method
10
+ @called = true;
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module TestModule
2
+ class LibraryOld
3
+ attr_reader :called
4
+ def method_on_library_old
5
+ @called = true;
6
+ end
7
+
8
+ def a_method
9
+ @called = true
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module TestModule::ShouldNotFindTestSlimInHere
2
+ class TestSlim
3
+ def return_string
4
+ "blah"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ class SimpleScript
2
+ def set_arg(arg)
3
+ @arg = arg
4
+ end
5
+
6
+ def get_arg
7
+ @arg
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class TestChain
2
+ def echo_boolean(b)
3
+ b
4
+ end
5
+ 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