Class: Melomel::Bridge

Inherits:
Object show all
Defined in:
lib/melomel/bridge.rb,
lib/melomel/bridge/ui.rb,
lib/melomel/bridge/messaging.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host = '127.0.0.1', port = 10101) ⇒ Bridge

Returns a new instance of Bridge.



11
12
13
14
15
# File 'lib/melomel/bridge.rb', line 11

def initialize(host='127.0.0.1', port=10101)
  self.host = host
  self.port = port
  server, @socket = nil, nil
end

Instance Attribute Details

#hostObject

Returns the value of attribute host.



9
10
11
# File 'lib/melomel/bridge.rb', line 9

def host
  @host
end

#portObject

Returns the value of attribute port.



9
10
11
# File 'lib/melomel/bridge.rb', line 9

def port
  @port
end

Instance Method Details

#busy!Object

Checks if the Flash virtual machine is currently busy. See Melomel#busy in the Melomel asdocs for more information.

Returns true if the Flash virtual machine is busy. Otherwise returns false.



19
20
21
# File 'lib/melomel/bridge/ui.rb', line 19

def busy!
  get_class!('Melomel').busy!
end

#click(component, properties = {}) ⇒ Object

Imitates a click on a component



118
119
120
# File 'lib/melomel/bridge/ui.rb', line 118

def click(component, properties={})
  get_class('melomel.core.UI').click(component, properties)
end

#connectObject

Opens a socket connection to listen for incoming bridge connections. This method automatically handles requests for the policy file.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/melomel/bridge.rb', line 19

def connect()
  disconnect()
  
  # Listen for connections
  server = TCPServer.open(host, port)
  
  # Retrieve socket and check for initial handshake
  while(@socket.nil?) do
    socket = server.accept()
    data = socket.gets("\x00").chomp("\x00")
    
    # Send policy file if requested.
    if(data == '<policy-file-request/>')
      send_policy_file(socket)
    # Otherwise open connection and continue
    elsif(data == '<connect/>')
      @socket = socket
    end
  end

  server.close();
end

#create_object(class_name) ⇒ Object

Creates an object in the Flash virtual machine and returns the reference to it.

class_name - The name of the class to instantiate.

Returns an instance of the class if the class is found. Otherwise, nil.



18
19
20
# File 'lib/melomel/bridge/messaging.rb', line 18

def create_object(class_name)
  send_create_object(class_name, false)
end

#create_object!(class_name) ⇒ Object

Same as ‘create_object` except that an error is thrown if the class is not found.



24
25
26
# File 'lib/melomel/bridge/messaging.rb', line 24

def create_object!(class_name)
  send_create_object(class_name, true)
end

#disconnectObject

Closes any open connection to a Flash virtual machine.



43
44
45
46
47
48
49
50
# File 'lib/melomel/bridge.rb', line 43

def disconnect()
  begin
    @socket.close()
  rescue
  end

  @socket = nil
end

#double_click(component, properties = {}) ⇒ Object

Imitates a double click on a component



123
124
125
# File 'lib/melomel/bridge/ui.rb', line 123

def double_click(component, properties={})
  get_class('melomel.core.UI').doubleClick(component, properties)
end

#find(class_name, root = {}, properties = {}) ⇒ Object

Finds the first display object matching a class and hash of properties.

class_name - The type of object to search for. root - The object to start searching from. (Defaults to the stage). properties - A list of properties to match on the object.

Example:

bridge.find('mx.controls.Button', :label => 'Click me')
# => <Melomel::ObjectProxy>

Returns the first display object contained by root that matches the properties and class specified.



66
67
68
69
70
71
72
73
74
75
# File 'lib/melomel/bridge/ui.rb', line 66

def find(class_name, root={}, properties={})
  # Merge hashes if no root is specified
  if root.is_a?(Hash)
    properties.merge!(root)
    root = nil
  end
  
  # Retrieve object
  get_class('melomel.core.UI').find(class_name, root, properties)
end

#find!(class_name, root = {}, properties = {}) ⇒ Object

Raises:



77
78
79
80
81
# File 'lib/melomel/bridge/ui.rb', line 77

def find!(class_name, root={}, properties={})
  object = find(class_name, root, properties)
  raise MelomelError.new("No object found") if object.nil?
  return object
end

#find_all(class_name, root = {}, properties = {}) ⇒ Object

Finds a list of display objects matching a class and hash of properties.

class_name - The type of objects to search for. root - The object to start searching from. (Defaults to the stage). properties - A list of properties to match on each object.

Example:

bridge.find_all('mx.controls.Button', :label => 'Click me')
# => [<Melomel::ObjectProxy>, <Melomel::ObjectProxy>]

Returns a list of display objects contained by root that match the properties and class specified.



36
37
38
39
40
41
42
43
44
45
# File 'lib/melomel/bridge/ui.rb', line 36

def find_all(class_name, root={}, properties={})
  # Merge hashes if no root is specified
  if root.is_a?(Hash)
    properties.merge!(root)
    root = nil
  end
  
  # Retrieve object
  get_class('melomel.core.UI').findAll(class_name, root, properties)
end

#find_all!(class_name, root = {}, properties = {}) ⇒ Object

Raises:



47
48
49
50
51
# File 'lib/melomel/bridge/ui.rb', line 47

def find_all!(class_name, root={}, properties={})
  objects = find_all(class_name, root, properties)
  raise MelomelError.new("No objects found") if objects.empty?
  return objects
end

#find_labeled(class_name, label_text, root = {}, properties = {}) ⇒ Object

Finds a component based on the label of a nearby component. This works by first finding a Halo or Spark label component and then recursively searching the label’s parent’s children for a component of a given class.

class_name - The type of object to search for. label_text - The label text to search for. root - The object to start searching from. (Defaults to the stage). properties - A list of properties to match on the object.

Example:

bridge.find_labeled('mx.controls.TextInput', 'First Name')
# => <Melomel::ObjectProxy>

Returns the first display object which is inside the parent of a given label.



99
100
101
102
103
104
105
106
107
108
# File 'lib/melomel/bridge/ui.rb', line 99

def find_labeled(class_name, label_text, root={}, properties={})
  # Merge hashes if no root is specified
  if root.is_a?(Hash)
    properties.merge!(root)
    root = nil
  end
  
  # Retrieve object
  get_class('melomel.core.UI').findLabeled(class_name, label_text, root, properties)
end

#find_labeled!(class_name, label_text, root = {}, properties = {}) ⇒ Object

Raises:



110
111
112
113
114
# File 'lib/melomel/bridge/ui.rb', line 110

def find_labeled!(class_name, label_text, root={}, properties={})
  object = find_labeled(class_name, label_text, root, properties)
  raise MelomelError.new("No object found") if object.nil?
  return object
end

#format_message_value(xml, value) ⇒ Object

Formats a Ruby value into an XML message



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/melomel/bridge/messaging.rb', line 117

def format_message_value(xml, value)
  # Automatically convert simple objects to proxies.
  value = value.to_object_proxy(self) unless value.nil?
  
  if value.nil?
    xml['dataType'] = 'null'
  elsif value.class == Fixnum || value.class == Bignum
    xml['value'] = value.to_s
    xml['dataType'] = 'int'
  elsif value.class == Float
    xml['value'] = value.to_s
    xml['dataType'] = 'float'
  elsif value == true || value == false
    xml['value'] = value.to_s
    xml['dataType'] = 'boolean'
  elsif value.class == Melomel::ObjectProxy
    xml['value'] = value.proxy_id.to_s
    xml['dataType'] = 'object'
  elsif value.class == String
    xml['value'] = value
    xml['dataType'] = 'string'
  end
end

#get_class(class_name) ⇒ Object

Retrieves a reference to a class in the Flash virtual machine.

class_name - The name of the class to retrieve a reference to.

Returns a reference to the class if it exists. Otherwise, nil.



33
34
35
# File 'lib/melomel/bridge/messaging.rb', line 33

def get_class(class_name)
  send_get_class(class_name, false)
end

#get_class!(class_name) ⇒ Object

Same as ‘get_class` except that an error is thrown if the class is not found.



39
40
41
# File 'lib/melomel/bridge/messaging.rb', line 39

def get_class!(class_name)
  send_get_class(class_name, true)
end

#get_property(proxy_id, property) ⇒ Object

Retrieves a property of an object in the Flash virtual machine

proxy_id - The identifier for the object proxy. property - The name of the property to access.

Returns the value of the property if it exists. Otherwise, nil.



49
50
51
# File 'lib/melomel/bridge/messaging.rb', line 49

def get_property(proxy_id, property)
  send_get_property(proxy_id, property, false)
end

#get_property!(proxy_id, property) ⇒ Object

Same as ‘get_property` except that an error is thrown if the property does not exist.



55
56
57
# File 'lib/melomel/bridge/messaging.rb', line 55

def get_property!(proxy_id, property)
  send_get_property(proxy_id, property, true)
end

#invoke_function(function_name, *args) ⇒ Object

Invokes a package level function in the Flash virtual machine

function_name - The fully qualified name of the function to invoke. *args - List of arguments passed to the function.

Returns the return value of the Flash function.



99
100
101
# File 'lib/melomel/bridge/messaging.rb', line 99

def invoke_function(function_name, *args)
  send_invoke_function(function_name, args, false)
end

#invoke_function!(function_name, *args) ⇒ Object

Same as ‘invoke_function` except that an error is thrown if the method does not exist.



105
106
107
# File 'lib/melomel/bridge/messaging.rb', line 105

def invoke_function!(function_name, *args)
  send_invoke_function(function_name, args, true)
end

#invoke_method(proxy_id, method_name, *args) ⇒ Object

Invokes a method on an object in the Flash virtual machine

proxy_id - The identifier of the object proxy. method_name - The name of the method to invoke. *args - List of arguments passed to the method.

Returns the value returned from the Flash method.



83
84
85
# File 'lib/melomel/bridge/messaging.rb', line 83

def invoke_method(proxy_id, method_name, *args)
  send_invoke_method(proxy_id, method_name, args, false)
end

#invoke_method!(proxy_id, method_name, *args) ⇒ Object

Same as ‘invoke_method` except that an error is thrown if the method does not exist.



89
90
91
# File 'lib/melomel/bridge/messaging.rb', line 89

def invoke_method!(proxy_id, method_name, *args)
  send_invoke_method(proxy_id, method_name, args, true)
end

#items_to_labels!(component, data) ⇒ Object

Generates a list of labels created by a data control or column based on a data set.

component - The control or column which has an itemToLabel() method. data - The data set to generate labels from.

Returns a Ruby array of labels.



151
152
153
# File 'lib/melomel/bridge/ui.rb', line 151

def items_to_labels!(component, data)
  get_class('melomel.core.UI').itemsToLabels!(component, data)
end

#key_down(component, char, properties = {}) ⇒ Object

Imitates a key down on a component



129
130
131
# File 'lib/melomel/bridge/ui.rb', line 129

def key_down(component, char, properties={})
  get_class('melomel.core.UI').keyDown(component, char, properties)
end

#key_press(component, char, properties = {}) ⇒ Object

Imitates a key press on a component



139
140
141
# File 'lib/melomel/bridge/ui.rb', line 139

def key_press(component, char, properties={})
  get_class('melomel.core.UI').keyPress(component, char, properties)
end

#key_up(component, char, properties = {}) ⇒ Object

Imitates a key up on a component



134
135
136
# File 'lib/melomel/bridge/ui.rb', line 134

def key_up(component, char, properties={})
  get_class('melomel.core.UI').keyUp(component, char, properties)
end

#parse_message_value(xml) ⇒ Object

Parses a return message and converts it into an appropriate type



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/melomel/bridge/messaging.rb', line 142

def parse_message_value(xml)
  name = xml.name()
  
  # If we receive an error back then raise it in the Ruby VM.
  if name == 'error'
    stack_trace_xml = xml.at_xpath('stack-trace')
    object      = Melomel::ObjectProxy.new(self, xml['proxyId'].to_i)
    error_id    = xml['errorId'].to_i
    message     = xml['message']
    name        = xml['name']
    stack_trace = stack_trace_xml ? stack_trace_xml.to_str : nil
    $stderr.puts(stack_trace)
    raise Melomel::Error.new(object, error_id, message, name, stack_trace), message

  # Otherwise we have a return value so we should parse it.
  else
    value = xml['value']
    data_type = xml['dataType']
  
    if data_type == 'null'
      return nil
    elsif data_type == 'int'
      return value.to_i
    elsif data_type == 'float'
      return value.to_f
    elsif data_type == 'boolean'
      return value == 'true'
    elsif data_type == 'object'
      return Melomel::ObjectProxy.new(self, value.to_i)
    elsif data_type == 'string' || data_type.nil?
      return value
    else
      raise UnrecognizedTypeError, "Unknown type: #{data_type}"
    end
  end
end

#receiveObject

Receives a message from the Flash bridge. This is a blocking call.



58
59
60
# File 'lib/melomel/bridge.rb', line 58

def receive()
  @socket.gets("\x00").chomp("\x00")
end

#send(message) ⇒ Object

Sends a message over to the Flash bridge.



53
54
55
# File 'lib/melomel/bridge.rb', line 53

def send(message)
  @socket.puts("#{message}\x00")
end

#set_property(proxy_id, property, value) ⇒ Object

Sets a property on an object in the Flash virtual machine

proxy_id - The identifier of the object proxy. property - The name of the property to mutate. value - The value to set the property to.

Returns the value of the property after being set.



66
67
68
# File 'lib/melomel/bridge/messaging.rb', line 66

def set_property(proxy_id, property, value)
  send_set_property(proxy_id, property, value, false)
end

#set_property!(proxy_id, property, value) ⇒ Object

Same as ‘set_property` except that an error is thrown if the property does not exist.



72
73
74
# File 'lib/melomel/bridge/messaging.rb', line 72

def set_property!(proxy_id, property, value)
  send_set_property(proxy_id, property, value, true)
end

#waitObject

Blocks until Melomel.busy in the Flash virtual machine is false.

Returns nothing.



7
8
9
10
11
12
# File 'lib/melomel/bridge/ui.rb', line 7

def wait()
  loop do 
    break unless busy!
    sleep 0.1
  end
end