melomel 0.3.1 → 0.4.0

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.
@@ -3,70 +3,116 @@ require 'nokogiri'
3
3
  # These add-on methods enable the message encoding and decoding.
4
4
  module Melomel
5
5
  class Bridge
6
+ ###########################################################################
7
+ #
8
+ # Basic Messages
9
+ #
10
+ ###########################################################################
11
+
6
12
  # Creates an object in the Flash virtual machine and returns the reference
7
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.
8
18
  def create_object(class_name)
9
- send("<create class=\"#{class_name}\"/>")
10
- parse_message_value(Nokogiri::XML(receive()).root)
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)
11
26
  end
12
27
 
13
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.
14
33
  def get_class(class_name)
15
- send("<get-class name=\"#{class_name}\"/>")
16
- parse_message_value(Nokogiri::XML(receive()).root)
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)
17
41
  end
18
42
 
19
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.
20
49
  def get_property(proxy_id, property)
21
- send("<get object=\"#{proxy_id}\" property=\"#{property}\"/>")
22
- parse_message_value(Nokogiri::XML(receive()).root)
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)
23
57
  end
24
58
 
25
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.
26
66
  def set_property(proxy_id, property, value)
27
- # Create message and format value to set
28
- xml = Nokogiri::XML("<set object=\"#{proxy_id}\" property=\"#{property}\"><arg/></set>")
29
- format_message_value(xml.at_xpath('/set/arg'), value)
30
-
31
- # Send & Receive
32
- send(xml.root.to_xml(:indent => 0))
33
- parse_message_value(Nokogiri::XML(receive()).root)
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)
34
74
  end
35
75
 
36
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.
37
83
  def invoke_method(proxy_id, method_name, *args)
38
- xml = Nokogiri::XML("<invoke object=\"#{proxy_id}\" method=\"#{method_name}\"><args/></invoke>")
39
-
40
- # Loop over and add arguments to call
41
- args_node = xml.at_xpath('invoke/args')
42
- args.each do |arg|
43
- arg_node = Nokogiri::XML::Node.new('arg', xml)
44
- format_message_value(arg_node, arg)
45
- args_node.add_child(arg_node)
46
- end
47
-
48
- # Send and receive
49
- send(xml.root.to_xml(:indent => 0))
50
- parse_message_value(Nokogiri::XML(receive()).root)
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)
51
91
  end
52
92
 
53
93
  # Invokes a package level function in the Flash virtual machine
54
- def invoke_function(function, *args)
55
- xml = Nokogiri::XML("<invoke-function name=\"#{function}\"><args/></invoke>")
56
-
57
- # Loop over and add arguments to call
58
- args_node = xml.at_xpath('invoke-function/args')
59
- args.each do |arg|
60
- arg_node = Nokogiri::XML::Node.new('arg', xml)
61
- format_message_value(arg_node, arg)
62
- args_node.add_child(arg_node)
63
- end
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
64
102
 
65
- # Send and receive
66
- send(xml.root.to_xml(:indent => 0))
67
- parse_message_value(Nokogiri::XML(receive()).root)
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)
68
107
  end
69
108
 
109
+
110
+ ###########################################################################
111
+ #
112
+ # Utility Methods
113
+ #
114
+ ###########################################################################
115
+
70
116
  # Creates an object proxy from a hash
71
117
  def create_hash(hash)
72
118
  proxy = create_object('Object')
@@ -106,24 +152,113 @@ module Melomel
106
152
 
107
153
  # Parses a return message and converts it into an appropriate type
108
154
  def parse_message_value(xml)
109
- value = xml['value']
110
- data_type = xml['dataType']
155
+ name = xml.name()
111
156
 
112
- if data_type == 'null'
113
- return nil
114
- elsif data_type == 'int'
115
- return value.to_i
116
- elsif data_type == 'float'
117
- return value.to_f
118
- elsif data_type == 'boolean'
119
- return value == 'true'
120
- elsif data_type == 'object'
121
- return Melomel::ObjectProxy.new(self, value.to_i)
122
- elsif data_type == 'string' || data_type.nil?
123
- return value
157
+ # If we receive an error back then raise it in the Ruby VM.
158
+ if name == 'error'
159
+ stack_trace_xml = xml.at_xpath('stack-trace')
160
+ object = Melomel::ObjectProxy.new(self, xml['proxyId'].to_i)
161
+ error_id = xml['errorId'].to_i
162
+ message = xml['message']
163
+ name = xml['name']
164
+ stack_trace = stack_trace_xml ? stack_trace_xml.to_str : nil
165
+ $stderr.puts(stack_trace)
166
+ raise Melomel::Error.new(object, error_id, message, name, stack_trace), message
167
+
168
+ # Otherwise we have a return value so we should parse it.
124
169
  else
125
- raise UnrecognizedTypeError, "Unknown type: #{data_type}"
170
+ value = xml['value']
171
+ data_type = xml['dataType']
172
+
173
+ if data_type == 'null'
174
+ return nil
175
+ elsif data_type == 'int'
176
+ return value.to_i
177
+ elsif data_type == 'float'
178
+ return value.to_f
179
+ elsif data_type == 'boolean'
180
+ return value == 'true'
181
+ elsif data_type == 'object'
182
+ return Melomel::ObjectProxy.new(self, value.to_i)
183
+ elsif data_type == 'string' || data_type.nil?
184
+ return value
185
+ else
186
+ raise UnrecognizedTypeError, "Unknown type: #{data_type}"
187
+ end
126
188
  end
127
189
  end
190
+
191
+
192
+ ###########################################################################
193
+ #
194
+ # Private Methods
195
+ #
196
+ ###########################################################################
197
+
198
+ private
199
+
200
+ # Creates an object in the Flash virtual machine and returns the reference
201
+ # to it.
202
+ def send_create_object(class_name, throwable=true)
203
+ send("<create class=\"#{class_name}\" throwable=\"#{throwable}\"/>")
204
+ parse_message_value(Nokogiri::XML(receive()).root)
205
+ end
206
+
207
+ # Retrieves a reference to a class in the Flash virtual machine.
208
+ def send_get_class(class_name, throwable=true)
209
+ send("<get-class name=\"#{class_name}\" throwable=\"#{throwable}\"/>")
210
+ parse_message_value(Nokogiri::XML(receive()).root)
211
+ end
212
+
213
+ # Retrieves a property of an object in the Flash virtual machine
214
+ def send_get_property(proxy_id, property, throwable)
215
+ send("<get object=\"#{proxy_id}\" property=\"#{property}\" throwable=\"#{throwable}\"/>")
216
+ parse_message_value(Nokogiri::XML(receive()).root)
217
+ end
218
+
219
+ # Sets a property on an object in the Flash virtual machine
220
+ def send_set_property(proxy_id, property, value, throwable)
221
+ # Create message and format value to set
222
+ xml = Nokogiri::XML("<set object=\"#{proxy_id}\" property=\"#{property}\" throwable=\"#{throwable}\"><arg/></set>")
223
+ format_message_value(xml.at_xpath('/set/arg'), value)
224
+
225
+ # Send & Receive
226
+ send(xml.root.to_xml(:indent => 0))
227
+ parse_message_value(Nokogiri::XML(receive()).root)
228
+ end
229
+
230
+ # Invokes a method on an object in the Flash virtual machine
231
+ def send_invoke_method(proxy_id, method_name, args, throwable)
232
+ xml = Nokogiri::XML("<invoke object=\"#{proxy_id}\" method=\"#{method_name}\" throwable=\"#{throwable}\"><args/></invoke>")
233
+
234
+ # Loop over and add arguments to call
235
+ args_node = xml.at_xpath('invoke/args')
236
+ args.each do |arg|
237
+ arg_node = Nokogiri::XML::Node.new('arg', xml)
238
+ format_message_value(arg_node, arg)
239
+ args_node.add_child(arg_node)
240
+ end
241
+
242
+ # Send and receive
243
+ send(xml.root.to_xml(:indent => 0))
244
+ parse_message_value(Nokogiri::XML(receive()).root)
245
+ end
246
+
247
+ # Invokes a package level function in the Flash virtual machine
248
+ def send_invoke_function(function_name, args, throwable)
249
+ xml = Nokogiri::XML("<invoke-function name=\"#{function_name}\" throwable=\"#{throwable}\"><args/></invoke>")
250
+
251
+ # Loop over and add arguments to call
252
+ args_node = xml.at_xpath('invoke-function/args')
253
+ args.each do |arg|
254
+ arg_node = Nokogiri::XML::Node.new('arg', xml)
255
+ format_message_value(arg_node, arg)
256
+ args_node.add_child(arg_node)
257
+ end
258
+
259
+ # Send and receive
260
+ send(xml.root.to_xml(:indent => 0))
261
+ parse_message_value(Nokogiri::XML(receive()).root)
262
+ end
128
263
  end
129
264
  end
@@ -0,0 +1,43 @@
1
+ # This class is used for all errors returned from the Flash virtual machine.
2
+ module Melomel
3
+ class Error < StandardError
4
+ ############################################################################
5
+ #
6
+ # Constructor
7
+ #
8
+ ############################################################################
9
+
10
+ def initialize(object, error_id, message, name, stack_trace)
11
+ @object = object
12
+ @error_id = error_id
13
+ @message = message
14
+ @name = name
15
+ @stack_trace = stack_trace
16
+ end
17
+
18
+
19
+ ############################################################################
20
+ #
21
+ # Public Properties
22
+ #
23
+ ############################################################################
24
+
25
+ # A proxied reference to the original Flash error object.
26
+ #
27
+ # Returns a Melomel::ObjectProxy.
28
+ attr_reader :object
29
+
30
+ # The error identifier of the Flash error.
31
+ attr_reader :error_id
32
+
33
+ # The Flash error message.
34
+ attr_reader :message
35
+
36
+ # The name of the Flash error.
37
+ attr_reader :name
38
+
39
+ # The Flash stack trace. This is only available when using the Flash debug
40
+ # player or the AIR Debug Launcher (ADL).
41
+ attr_reader :stack_trace
42
+ end
43
+ end
@@ -22,16 +22,29 @@ module Melomel
22
22
  @bridge.get_property(@proxy_id, name)
23
23
  end
24
24
 
25
+ def get_property!(name)
26
+ @bridge.get_property!(@proxy_id, name)
27
+ end
28
+
25
29
  # Sets the value of a property for the proxied object.
26
30
  def set_property(name, value)
27
31
  @bridge.set_property(@proxy_id, name, value)
28
32
  end
33
+
34
+ # Sets the value of a property for the proxied object.
35
+ def set_property!(name, value)
36
+ @bridge.set_property!(@proxy_id, name, value)
37
+ end
29
38
 
30
39
  # Invokes a method on the proxied object. Arguments passed into the method
31
40
  # are passed through to the invoked method
32
41
  def invoke_method(method_name, *args)
33
42
  @bridge.invoke_method(@proxy_id, method_name, *args)
34
43
  end
44
+
45
+ def invoke_method!(method_name, *args)
46
+ @bridge.invoke_method!(@proxy_id, method_name, *args)
47
+ end
35
48
 
36
49
  alias :invoke :invoke_method
37
50
 
@@ -45,10 +58,18 @@ module Melomel
45
58
  return set_property(method_name.chop, *args)
46
59
  # Methods with arguments are methods
47
60
  elsif args.length > 0
48
- return invoke_method(method_name, *args)
61
+ if last_char == '!'
62
+ return invoke_method!(method_name.chop, *args)
63
+ else
64
+ return invoke_method(method_name, *args)
65
+ end
49
66
  # Methods with no arguments are aliased to get_property
50
67
  else
51
- return get_property(method_name)
68
+ if last_char == '!'
69
+ return get_property!(method_name.chop)
70
+ else
71
+ return get_property(method_name)
72
+ end
52
73
  end
53
74
  end
54
75
  end
@@ -1,3 +1,3 @@
1
1
  module Melomel
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/melomel.rb CHANGED
@@ -1,4 +1,7 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.index(File.dirname(__FILE__))
2
+
1
3
  require 'melomel/bridge'
4
+ require 'melomel/error'
2
5
  require 'melomel/object_proxy'
3
6
  require 'melomel/ui'
4
7
  require 'melomel/version'
data/test/sandbox.rb ADDED
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), *%w[helper])
2
+
3
+ class SandboxTestCase < RunnerTestCase
4
+ def setup
5
+ start_runner
6
+ Melomel.connect()
7
+ end
8
+
9
+ def teardown
10
+ stop_runner
11
+ end
12
+
13
+ # Add tests here.
14
+ end
@@ -20,6 +20,25 @@ class IntegrationTestCase < RunnerTestCase
20
20
  assert_equal 'bar', runner.foo
21
21
  end
22
22
 
23
+ def test_should_get_missing_property_as_nil
24
+ app = Melomel.get_class('mx.core.FlexGlobals').topLevelApplication
25
+ assert_nil app.no_such_property
26
+ end
27
+
28
+ def test_should_throw_error_when_getting_missing_instance_property!
29
+ app = Melomel.get_class('mx.core.FlexGlobals').topLevelApplication
30
+ assert_raises Melomel::Error do
31
+ app.no_such_property!
32
+ end
33
+ end
34
+
35
+ def test_should_throw_error_when_getting_missing_static_property!
36
+ runner = Melomel.get_class('MelomelRunner')
37
+ assert_raises Melomel::Error do
38
+ runner.no_such_property!
39
+ end
40
+ end
41
+
23
42
  # Tests the ability for a get_property to call a no-arg method if unavailable.
24
43
  def test_should_get_property_passthrough
25
44
  app = Melomel.get_class('mx.core.FlexGlobals').topLevelApplication
@@ -30,7 +49,7 @@ class IntegrationTestCase < RunnerTestCase
30
49
  runner = Melomel.get_class('MelomelRunner')
31
50
  runner.name = 'Susy'
32
51
  assert_equal 'Susy', runner.name
33
- runner.name = 'John' # TODO: Do not make other tests dependent on this
52
+ runner.name = 'John'
34
53
  end
35
54
 
36
55
  def test_should_invoke_method
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: melomel
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 1
10
- version: 0.3.1
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ben Johnson
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-09-27 00:00:00 -06:00
18
+ date: 2010-09-29 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -79,6 +79,7 @@ files:
79
79
  - lib/melomel/bridge/messaging.rb
80
80
  - lib/melomel/bridge/ui.rb
81
81
  - lib/melomel/bridge.rb
82
+ - lib/melomel/error.rb
82
83
  - lib/melomel/object_proxy.rb
83
84
  - lib/melomel/ui.rb
84
85
  - lib/melomel/version.rb
@@ -86,6 +87,7 @@ files:
86
87
  - README.md
87
88
  - CHANGELOG.md
88
89
  - test/helper.rb
90
+ - test/sandbox.rb
89
91
  - test/test_bridge.rb
90
92
  - test/test_integration.rb
91
93
  - test/test_messaging.rb
@@ -127,6 +129,7 @@ specification_version: 3
127
129
  summary: A Ruby interface to Melomel
128
130
  test_files:
129
131
  - test/helper.rb
132
+ - test/sandbox.rb
130
133
  - test/test_bridge.rb
131
134
  - test/test_integration.rb
132
135
  - test/test_messaging.rb