kolach-melomel 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,35 @@
1
+ v0.6.4
2
+ * added `I should see no data in the "([^"]*)" data grid'
3
+
4
+ v0.6.3
5
+ * minor changes in working with data grids
6
+
7
+ v0.6.2 contribution
8
+ * added Advanced Data Grid class support for grid steps
9
+ * minor issue fixed: nil.trim!
10
+ * added `should not see' for text label
11
+
12
+ v0.6.0
13
+ * Added wait interface.
14
+ * Added focus to components in Cucumber steps.
15
+
16
+ v0.5.0
17
+ * Added Cucumber support.
18
+
19
+ v0.4.0
20
+ * Added error serialization with stack tracing from Flash to Ruby.
21
+
22
+
23
+ v0.3.1
24
+ * Removed requirement for adding a bang to the end of no-arg methods.
25
+
26
+ v0.3.0
27
+ * Added `Melomel.invoke_function()` for package level functions invocation. (Nikita Dudnik)
28
+
29
+ v0.2.0
30
+ * Moved `Melomel::UI` methods into `Melomel`.
31
+ * Changed Flex build from `ant compile` to `ant build`.
32
+ * Made `build` the default ant target.
33
+
34
+ v0.1.0
35
+ * Initial alpha version.
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ melomel.rb -- A Ruby interface to Melomel
2
+ =========================================
3
+
4
+ ## DESCRIPTION
5
+
6
+ Melomel.rb is a library that allows Ruby to communicate with an application
7
+ running within the Flash virtual machine. For more information on Melomel,
8
+ visit the Melomel site:
9
+
10
+ http://melomel.info
11
+
12
+ Melomel.rb follows the rules of [Semantic Versioning](http://semver.org/) and
13
+ uses [TomDoc](http://tomdoc.org/) for inline documentation.
14
+
15
+
16
+ ## INSTALLATION
17
+
18
+ To install Melomel.rb simply use RubyGems:
19
+
20
+ $ [sudo] gem install melomel
21
+
22
+ Melomel needs to be embedded and running in the application you're trying to
23
+ connect to. Please see the Melomel repository for instructions on installation.
24
+
25
+
26
+ ## HACKING
27
+
28
+ To get started with working with the source, start by running Bundler to get all
29
+ your dependencies:
30
+
31
+ [sudo] bundle install
32
+
33
+ If you are adding new features, please add test coverage to all code that you
34
+ write. First compile the Flex application and then run the test suite with
35
+ `rake`:
36
+
37
+ ant
38
+ rake test
39
+
40
+
41
+ ## CONTRIBUTE
42
+
43
+ If you'd like to contribute to Melomel.rb, please fork the repo on GitHub:
44
+
45
+ http://github.com/benbjohnson/melomel.rb
46
+
47
+ To get all of the dependencies, install the gem first. The best way to get
48
+ your changes merged back into core is as follows:
49
+
50
+ 1. Clone your fork locally
51
+ 1. Create a named topic branch to contain your change
52
+ 1. Code
53
+ 1. All code must have RSpec test coverage. Run `rake` to ensure everything
54
+ passes.
55
+ 1. If you are adding new functionality, document it in the README
56
+ 1. If necessary, rebase your commits into logical chunks, without errors
57
+ 1. Push the branch up to GitHub
58
+ 1. Send me a pull request for your branch
data/lib/melomel.rb ADDED
@@ -0,0 +1,48 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.index(File.dirname(__FILE__))
2
+
3
+ require 'object'
4
+ require 'melomel/bridge'
5
+ require 'melomel/date'
6
+ require 'melomel/error'
7
+ require 'melomel/flex'
8
+ require 'melomel/object_proxy'
9
+ require 'melomel/version'
10
+
11
+ # This class acts as a singleton instance of the bridge. This is typically the
12
+ # only class you'll need to use. If multiple instances of the bridge are needed
13
+ # (to connect to multiple SWF files), you will need to instantiate and manage
14
+ # the bridge objects manually.
15
+ module Melomel
16
+ class MelomelError < StandardError; end
17
+ class UnrecognizedTypeError < MelomelError; end
18
+
19
+ class << self
20
+ attr_reader :bridge
21
+
22
+ # Opens a bridge connection to the SWF file. This is a blocking call and
23
+ # will wait until a SWF connects before continuing.
24
+ def connect(host='localhost', port=10101)
25
+ @bridge = Melomel::Bridge.new(host, port)
26
+ @bridge.connect();
27
+ end
28
+
29
+ def method_missing(method, *args)
30
+ @bridge.__send__(method.to_sym, *args)
31
+ end
32
+
33
+ # Retrieves a reference to a class
34
+ def get_class(*args)
35
+ @bridge.get_class(*args)
36
+ end
37
+
38
+ # Creates an object in the virtual machine.
39
+ def create_object(class_name)
40
+ @bridge.create_object(class_name)
41
+ end
42
+
43
+ # Invokes package level function
44
+ def invoke_function(function, *args)
45
+ @bridge.invoke_function(function, *args)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,76 @@
1
+ require 'socket'
2
+ require 'melomel/bridge/messaging'
3
+ require 'melomel/bridge/ui'
4
+
5
+ # The bridge manages the connection to the Flash virtual machine. All messages
6
+ # sent to the virtual machine are passed through this object.
7
+ module Melomel
8
+ class Bridge
9
+ attr_accessor :host, :port
10
+
11
+ def initialize(host='localhost', port=10101)
12
+ self.host = host
13
+ self.port = port
14
+ server, @socket = nil, nil
15
+ end
16
+
17
+ # Opens a socket connection to listen for incoming bridge connections. This
18
+ # method automatically handles requests for the policy file.
19
+ def connect()
20
+ disconnect()
21
+
22
+ # Listen for connections
23
+ server = TCPServer.open(host, port)
24
+
25
+ # Retrieve socket and check for initial handshake
26
+ while(@socket.nil?) do
27
+ socket = server.accept()
28
+ data = socket.gets("\x00").chomp("\x00")
29
+
30
+ # Send policy file if requested.
31
+ if(data == '<policy-file-request/>')
32
+ send_policy_file(socket)
33
+ # Otherwise open connection and continue
34
+ elsif(data == '<connect/>')
35
+ @socket = socket
36
+ end
37
+ end
38
+
39
+ server.close();
40
+ end
41
+
42
+ # Closes any open connection to a Flash virtual machine.
43
+ def disconnect()
44
+ begin
45
+ @socket.close()
46
+ rescue
47
+ end
48
+
49
+ @socket = nil
50
+ end
51
+
52
+ # Sends a message over to the Flash bridge.
53
+ def send(message)
54
+ @socket.puts("#{message}\x00")
55
+ end
56
+
57
+ # Receives a message from the Flash bridge. This is a blocking call.
58
+ def receive()
59
+ @socket.gets("\x00").chomp("\x00")
60
+ end
61
+
62
+
63
+ private
64
+ def send_policy_file(socket)
65
+ policy = ''
66
+ policy << '<?xml version="1.0"?>'
67
+ policy << '<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">'
68
+ policy << '<cross-domain-policy>'
69
+ policy << "<allow-access-from domain=\"#{host}\" to-ports=\"#{port}\"/>"
70
+ policy << '</cross-domain-policy>'
71
+ socket.send(policy, 0)
72
+ socket.flush()
73
+ socket.close()
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,252 @@
1
+ require 'nokogiri'
2
+
3
+ # These add-on methods enable the message encoding and decoding.
4
+ module Melomel
5
+ class Bridge
6
+ ###########################################################################
7
+ #
8
+ # Basic Messages
9
+ #
10
+ ###########################################################################
11
+
12
+ # Creates an object in the Flash virtual machine and returns the reference
13
+ # to it.
14
+ #
15
+ # class_name - The name of the class to instantiate.
16
+ #
17
+ # Returns an instance of the class if the class is found. Otherwise, nil.
18
+ def create_object(class_name)
19
+ send_create_object(class_name, false)
20
+ end
21
+
22
+ # Same as `create_object` except that an error is thrown if the class is
23
+ # not found.
24
+ def create_object!(class_name)
25
+ send_create_object(class_name, true)
26
+ end
27
+
28
+ # Retrieves a reference to a class in the Flash virtual machine.
29
+ #
30
+ # class_name - The name of the class to retrieve a reference to.
31
+ #
32
+ # Returns a reference to the class if it exists. Otherwise, nil.
33
+ def get_class(class_name)
34
+ send_get_class(class_name, false)
35
+ end
36
+
37
+ # Same as `get_class` except that an error is thrown if the class is not
38
+ # found.
39
+ def get_class!(class_name)
40
+ send_get_class(class_name, true)
41
+ end
42
+
43
+ # Retrieves a property of an object in the Flash virtual machine
44
+ #
45
+ # proxy_id - The identifier for the object proxy.
46
+ # property - The name of the property to access.
47
+ #
48
+ # Returns the value of the property if it exists. Otherwise, nil.
49
+ def get_property(proxy_id, property)
50
+ send_get_property(proxy_id, property, false)
51
+ end
52
+
53
+ # Same as `get_property` except that an error is thrown if the property
54
+ # does not exist.
55
+ def get_property!(proxy_id, property)
56
+ send_get_property(proxy_id, property, true)
57
+ end
58
+
59
+ # Sets a property on an object in the Flash virtual machine
60
+ #
61
+ # proxy_id - The identifier of the object proxy.
62
+ # property - The name of the property to mutate.
63
+ # value - The value to set the property to.
64
+ #
65
+ # Returns the value of the property after being set.
66
+ def set_property(proxy_id, property, value)
67
+ send_set_property(proxy_id, property, value, false)
68
+ end
69
+
70
+ # Same as `set_property` except that an error is thrown if the property
71
+ # does not exist.
72
+ def set_property!(proxy_id, property, value)
73
+ send_set_property(proxy_id, property, value, true)
74
+ end
75
+
76
+ # Invokes a method on an object in the Flash virtual machine
77
+ #
78
+ # proxy_id - The identifier of the object proxy.
79
+ # method_name - The name of the method to invoke.
80
+ # *args - List of arguments passed to the method.
81
+ #
82
+ # Returns the value returned from the Flash method.
83
+ def invoke_method(proxy_id, method_name, *args)
84
+ send_invoke_method(proxy_id, method_name, args, false)
85
+ end
86
+
87
+ # Same as `invoke_method` except that an error is thrown if the method
88
+ # does not exist.
89
+ def invoke_method!(proxy_id, method_name, *args)
90
+ send_invoke_method(proxy_id, method_name, args, true)
91
+ end
92
+
93
+ # Invokes a package level function in the Flash virtual machine
94
+ #
95
+ # function_name - The fully qualified name of the function to invoke.
96
+ # *args - List of arguments passed to the function.
97
+ #
98
+ # Returns the return value of the Flash function.
99
+ def invoke_function(function_name, *args)
100
+ send_invoke_function(function_name, args, false)
101
+ end
102
+
103
+ # Same as `invoke_function` except that an error is thrown if the method
104
+ # does not exist.
105
+ def invoke_function!(function_name, *args)
106
+ send_invoke_function(function_name, args, true)
107
+ end
108
+
109
+
110
+ ###########################################################################
111
+ #
112
+ # Utility Methods
113
+ #
114
+ ###########################################################################
115
+
116
+ # Formats a Ruby value into an XML message
117
+ def format_message_value(xml, value)
118
+ # Automatically convert simple objects to proxies.
119
+ value = value.to_object_proxy(self) unless value.nil?
120
+
121
+ if value.nil?
122
+ xml['dataType'] = 'null'
123
+ elsif value.class == Fixnum || value.class == Bignum
124
+ xml['value'] = value.to_s
125
+ xml['dataType'] = 'int'
126
+ elsif value.class == Float
127
+ xml['value'] = value.to_s
128
+ xml['dataType'] = 'float'
129
+ elsif value == true || value == false
130
+ xml['value'] = value.to_s
131
+ xml['dataType'] = 'boolean'
132
+ elsif value.class == Melomel::ObjectProxy
133
+ xml['value'] = value.proxy_id.to_s
134
+ xml['dataType'] = 'object'
135
+ elsif value.class == String
136
+ xml['value'] = value
137
+ xml['dataType'] = 'string'
138
+ end
139
+ end
140
+
141
+ # Parses a return message and converts it into an appropriate type
142
+ def parse_message_value(xml)
143
+ name = xml.name()
144
+
145
+ # If we receive an error back then raise it in the Ruby VM.
146
+ if name == 'error'
147
+ stack_trace_xml = xml.at_xpath('stack-trace')
148
+ object = Melomel::ObjectProxy.new(self, xml['proxyId'].to_i)
149
+ error_id = xml['errorId'].to_i
150
+ message = xml['message']
151
+ name = xml['name']
152
+ stack_trace = stack_trace_xml ? stack_trace_xml.to_str : nil
153
+ $stderr.puts(stack_trace)
154
+ raise Melomel::Error.new(object, error_id, message, name, stack_trace), message
155
+
156
+ # Otherwise we have a return value so we should parse it.
157
+ else
158
+ value = xml['value']
159
+ data_type = xml['dataType']
160
+
161
+ if data_type == 'null'
162
+ return nil
163
+ elsif data_type == 'int'
164
+ return value.to_i
165
+ elsif data_type == 'float'
166
+ return value.to_f
167
+ elsif data_type == 'boolean'
168
+ return value == 'true'
169
+ elsif data_type == 'object'
170
+ return Melomel::ObjectProxy.new(self, value.to_i)
171
+ elsif data_type == 'string' || data_type.nil?
172
+ return value
173
+ else
174
+ raise UnrecognizedTypeError, "Unknown type: #{data_type}"
175
+ end
176
+ end
177
+ end
178
+
179
+
180
+ ###########################################################################
181
+ #
182
+ # Private Methods
183
+ #
184
+ ###########################################################################
185
+
186
+ private
187
+
188
+ # Creates an object in the Flash virtual machine and returns the reference
189
+ # to it.
190
+ def send_create_object(class_name, throwable=true)
191
+ send("<create class=\"#{class_name}\" throwable=\"#{throwable}\"/>")
192
+ parse_message_value(Nokogiri::XML(receive()).root)
193
+ end
194
+
195
+ # Retrieves a reference to a class in the Flash virtual machine.
196
+ def send_get_class(class_name, throwable=true)
197
+ send("<get-class name=\"#{class_name}\" throwable=\"#{throwable}\"/>")
198
+ parse_message_value(Nokogiri::XML(receive()).root)
199
+ end
200
+
201
+ # Retrieves a property of an object in the Flash virtual machine
202
+ def send_get_property(proxy_id, property, throwable)
203
+ send("<get object=\"#{proxy_id}\" property=\"#{property}\" throwable=\"#{throwable}\"/>")
204
+ parse_message_value(Nokogiri::XML(receive()).root)
205
+ end
206
+
207
+ # Sets a property on an object in the Flash virtual machine
208
+ def send_set_property(proxy_id, property, value, throwable)
209
+ # Create message and format value to set
210
+ xml = Nokogiri::XML("<set object=\"#{proxy_id}\" property=\"#{property}\" throwable=\"#{throwable}\"><arg/></set>")
211
+ format_message_value(xml.at_xpath('/set/arg'), value)
212
+
213
+ # Send & Receive
214
+ send(xml.root.to_xml(:indent => 0))
215
+ parse_message_value(Nokogiri::XML(receive()).root)
216
+ end
217
+
218
+ # Invokes a method on an object in the Flash virtual machine
219
+ def send_invoke_method(proxy_id, method_name, args, throwable)
220
+ xml = Nokogiri::XML("<invoke object=\"#{proxy_id}\" method=\"#{method_name}\" throwable=\"#{throwable}\"><args/></invoke>")
221
+
222
+ # Loop over and add arguments to call
223
+ args_node = xml.at_xpath('invoke/args')
224
+ args.each do |arg|
225
+ arg_node = Nokogiri::XML::Node.new('arg', xml)
226
+ format_message_value(arg_node, arg)
227
+ args_node.add_child(arg_node)
228
+ end
229
+
230
+ # Send and receive
231
+ send(xml.root.to_xml(:indent => 0))
232
+ parse_message_value(Nokogiri::XML(receive()).root)
233
+ end
234
+
235
+ # Invokes a package level function in the Flash virtual machine
236
+ def send_invoke_function(function_name, args, throwable)
237
+ xml = Nokogiri::XML("<invoke-function name=\"#{function_name}\" throwable=\"#{throwable}\"><args/></invoke>")
238
+
239
+ # Loop over and add arguments to call
240
+ args_node = xml.at_xpath('invoke-function/args')
241
+ args.each do |arg|
242
+ arg_node = Nokogiri::XML::Node.new('arg', xml)
243
+ format_message_value(arg_node, arg)
244
+ args_node.add_child(arg_node)
245
+ end
246
+
247
+ # Send and receive
248
+ send(xml.root.to_xml(:indent => 0))
249
+ parse_message_value(Nokogiri::XML(receive()).root)
250
+ end
251
+ end
252
+ end