kolach-melomel 0.6.4

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/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