rubyslim 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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