Module: RocketAMF
- Defined in:
- lib/rocketamf.rb,
lib/rocketamf/ext.rb,
lib/rocketamf/pure.rb,
lib/rocketamf/remoting.rb,
lib/rocketamf/constants.rb,
lib/rocketamf/class_mapping.rb,
lib/rocketamf/pure/remoting.rb,
lib/rocketamf/pure/io_helpers.rb,
lib/rocketamf/pure/serializer.rb,
lib/rocketamf/values/messages.rb,
lib/rocketamf/pure/deserializer.rb,
lib/rocketamf/values/typed_hash.rb,
ext/rocketamf_ext/rocketamf_ext.c
Overview
RocketAMF is a full featured AMF0/3 serializer and deserializer with support for bi-directional Flash to Ruby class mapping, custom serialization and mapping, remoting gateway helpers that follow AMF0/3 messaging specs, and a suite of specs to ensure adherence to the specification documents put out by Adobe. If the C components compile, then RocketAMF automatically takes advantage of them to provide a substantial performance benefit. In addition, RocketAMF is fully compatible with Ruby 1.9.
Performance
RocketAMF provides native C extensions for serialization, deserialization, remoting, and class mapping. If your environment supports them, RocketAMF will automatically take advantage of the C serializer, deserializer, and remoting support. The C class mapper has some substantial performance optimizations that make it incompatible with the pure Ruby class mapper, and so it must be manually enabled. For more information see RocketAMF::ClassMapping
. Below are some benchmarks I took using using a simple little benchmarking utility I whipped up, which can be found in the root of the repository.
# 100000 objects
# Ruby 1.8
Testing native AMF0:
minimum serialize time: 1.229868s
minimum deserialize time: 0.86465s
Testing native AMF3:
minimum serialize time: 1.444652s
minimum deserialize time: 0.879407s
Testing pure AMF0:
minimum serialize time: 25.427931s
minimum deserialize time: 11.706084s
Testing pure AMF3:
minimum serialize time: 31.637864s
minimum deserialize time: 14.773969s
Serialization & Deserialization
RocketAMF provides two main methods - serialize
and deserialize
. Deserialization takes a String or StringIO object and the AMF version if different from the default. Serialization takes any Ruby object and the version if different from the default. Both default to AMF0, as it’s more widely supported and slightly faster, but AMF3 does a better job of not sending duplicate data. Which you choose depends on what you need to communicate with and how much serialized size matters.
Mapping Classes Between Flash and Ruby
RocketAMF provides a simple class mapping tool to facilitate serialization and deserialization of typed objects. Refer to the documentation of RocketAMF::ClassMapping
for more details. If the provided class mapping tool is not sufficient for your needs, you also have the option to replace it with a class mapper of your own devising that matches the documented API.
Remoting
You can use RocketAMF bare to write an AMF gateway using the following code. In addition, you can use rack-amf (github.com/rubyamf/rack-amf) or RubyAMF (github.com/rubyamf/rubyamf), both of which provide rack-compliant AMF gateways.
# helloworld.ru
require 'rocketamf'
class HelloWorldApp
APPLICATION_AMF = 'application/x-amf'.freeze
def call env
if is_amf?(env)
# Wrap request and response
env['rack.input'].rewind
request = RocketAMF::Envelope.new.populate_from_stream(env['rack.input'].read)
response = RocketAMF::Envelope.new
# Handle request
response.each_method_call request do |method, args|
raise "Service #{method} does not exists" unless method == 'App.helloWorld'
'Hello world'
end
# Pass back response
response_str = response.serialize
return [200, {'Content-Type' => APPLICATION_AMF, 'Content-Length' => response_str.length.to_s}, [response_str]]
else
return [200, {'Content-Type' => 'text/plain', 'Content-Length' => '16' }, ["Rack AMF gateway"]]
end
end
private
def is_amf? env
return false unless env['CONTENT_TYPE'] == APPLICATION_AMF
return false unless env['PATH_INFO'] == '/amf'
return true
end
end
run HelloWorldApp.new
Advanced Serialization (encode_amf and IExternalizable)
RocketAMF provides some additional functionality to support advanced serialization techniques. If you define an encode_amf
method on your object, it will get called during serialization. It is passed a single argument, the serializer, and it can use the serializer stream, the serialize
method, the write_array
method, the write_object
method, and the serializer version. Below is a simple example that uses write_object
to customize the property hash that is used for serialization.
Example:
class TestObject
def encode_amf ser
ser.write_object self, @attributes
end
end
If you plan on using the serialize
method, make sure to pass in the current serializer version, or you could create a message that cannot be deserialized.
Example:
class VariableObject
def encode_amf ser
if ser.version == 0
ser.serialize 0, true
else
ser.serialize 3, false
end
end
end
If you wish to send and receive IExternalizable objects, you will need to implement encode_amf
, read_external
, and write_external
. Below is an example of a ResultSet class that extends Array and serializes as an array collection. RocketAMF can automatically serialize arrays as ArrayCollection objects, so this is just an example of how you might implement an object that conforms to IExternalizable.
Example:
class ResultSet < Array
def encode_amf ser
if ser.version == 0
# Serialize as simple array in AMF0
ser.write_array self
else
# Serialize as an ArrayCollection object
# It conforms to IExternalizable, does not have any dynamic properties,
# and has no "sealed" members. See the AMF3 specs for more details about
# object traits.
ser.write_object self, nil, {
:class_name => "flex.messaging.io.ArrayCollection",
:externalizable => true,
:dynamic => false,
:members => []
}
end
end
# Write self as array to stream
def write_external ser
ser.write_array(self)
end
# Read array out and replace data with deserialized array.
def read_external des
replace(des.read_object)
end
end
Defined Under Namespace
Modules: Ext, Pure, Values Classes: AMFError, ClassMapping, Envelope, Header, MappingSet, Message
Constant Summary collapse
- Deserializer =
:stopdoc: Import serializer/deserializer
RocketAMF::Pure::Deserializer
- Serializer =
RocketAMF::Pure::Serializer
- AMF0_NUMBER_MARKER =
AMF0 Type Markers
0x00
- AMF0_BOOLEAN_MARKER =
“000”
0x01
- AMF0_STRING_MARKER =
“001”
0x02
- AMF0_OBJECT_MARKER =
“002”
0x03
- AMF0_MOVIE_CLIP_MARKER =
“003”
0x04
- AMF0_NULL_MARKER =
“004” # Unused
0x05
- AMF0_UNDEFINED_MARKER =
“005”
0x06
- AMF0_REFERENCE_MARKER =
“006”
0x07
- AMF0_HASH_MARKER =
“a”
0x08
- AMF0_OBJECT_END_MARKER =
“b”
0x09
- AMF0_STRICT_ARRAY_MARKER =
“t”
0x0A
- AMF0_DATE_MARKER =
“n”
0x0B
- AMF0_LONG_STRING_MARKER =
“v”
0x0C
- AMF0_UNSUPPORTED_MARKER =
“f”
0x0D
- AMF0_RECORDSET_MARKER =
“r”
0x0E
- AMF0_XML_MARKER =
“016” # Unused
0x0F
- AMF0_TYPED_OBJECT_MARKER =
“017”
0x10
- AMF0_AMF3_MARKER =
“020”
0x11
- AMF3_UNDEFINED_MARKER =
AMF3 Type Markers
0x00
- AMF3_NULL_MARKER =
“000”
0x01
- AMF3_FALSE_MARKER =
“001”
0x02
- AMF3_TRUE_MARKER =
“002”
0x03
- AMF3_INTEGER_MARKER =
“003”
0x04
- AMF3_DOUBLE_MARKER =
“004”
0x05
- AMF3_STRING_MARKER =
“005”
0x06
- AMF3_XML_DOC_MARKER =
“006”
0x07
- AMF3_DATE_MARKER =
“a”
0x08
- AMF3_ARRAY_MARKER =
“b”
0x09
- AMF3_OBJECT_MARKER =
“t”
0x0A
- AMF3_XML_MARKER =
“n”
0x0B
- AMF3_BYTE_ARRAY_MARKER =
“v”
0x0C
- AMF3_VECTOR_INT_MARKER =
“f”
0x0D
- AMF3_VECTOR_UINT_MARKER =
“r”
0x0E
- AMF3_VECTOR_DOUBLE_MARKER =
“016”
0x0F
- AMF3_VECTOR_OBJECT_MARKER =
“017”
0x10
- AMF3_DICT_MARKER =
“020”
0x11
- AMF3_EMPTY_STRING =
Other AMF3 Markers
0x01
- AMF3_CLOSE_DYNAMIC_OBJECT =
0x01
- AMF3_CLOSE_DYNAMIC_ARRAY =
0x01
- MAX_INTEGER =
Other Constants
268435455
- MIN_INTEGER =
-268435456
Class Method Summary collapse
-
.const_missing(const) ⇒ Object
We use const_missing to define the active ClassMapper at runtime.
-
.deserialize(source, amf_version = 0) ⇒ Object
Deserialize the AMF string source of the given AMF version into a Ruby data structure and return it.
-
.serialize(obj, amf_version = 0) ⇒ Object
Serialize the given Ruby data structure obj into an AMF stream using the given AMF version.
Class Method Details
.const_missing(const) ⇒ Object
We use const_missing to define the active ClassMapper at runtime. This way, heavy modification of class mapping functionality is still possible without forcing extenders to redefine the constant.
206 207 208 209 210 211 212 |
# File 'lib/rocketamf.rb', line 206 def self.const_missing const #:nodoc: if const == :ClassMapper RocketAMF.const_set(:ClassMapper, RocketAMF::ClassMapping) else super(const) end end |
.deserialize(source, amf_version = 0) ⇒ Object
Deserialize the AMF string source of the given AMF version into a Ruby data structure and return it. Creates an instance of RocketAMF::Deserializer
with a new instance of RocketAMF::ClassMapper
and calls deserialize on it with the given source and amf version, returning the result.
189 190 191 192 |
# File 'lib/rocketamf.rb', line 189 def self.deserialize source, amf_version = 0 des = RocketAMF::Deserializer.new(RocketAMF::ClassMapper.new) des.deserialize(amf_version, source) end |
.serialize(obj, amf_version = 0) ⇒ Object
Serialize the given Ruby data structure obj into an AMF stream using the given AMF version. Creates an instance of RocketAMF::Serializer
with a new instance of RocketAMF::ClassMapper
and calls serialize on it with the given object and amf version, returning the result.
198 199 200 201 |
# File 'lib/rocketamf.rb', line 198 def self.serialize obj, amf_version = 0 ser = RocketAMF::Serializer.new(RocketAMF::ClassMapper.new) ser.serialize(amf_version, obj) end |