RocketAMF 0.0.7 → 0.1.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.
- data/README.rdoc +8 -36
- data/Rakefile +3 -3
- data/lib/rocketamf.rb +68 -2
- data/lib/rocketamf/class_mapping.rb +7 -9
- data/lib/rocketamf/constants.rb +1 -2
- data/lib/rocketamf/pure/deserializer.rb +60 -17
- data/lib/rocketamf/pure/remoting.rb +15 -6
- data/lib/rocketamf/pure/serializer.rb +80 -18
- data/lib/rocketamf/values/array_collection.rb +4 -0
- data/spec/amf/class_mapping_spec.rb +10 -12
- data/spec/amf/deserializer_spec.rb +47 -16
- data/spec/amf/serializer_spec.rb +57 -17
- data/spec/amf/values/array_collection_spec.rb +15 -2
- data/spec/fixtures/objects/amf0-complexEncodedStringArray.bin +0 -0
- data/spec/fixtures/objects/amf0-xmlDoc.bin +0 -0
- data/spec/fixtures/objects/amf3-arrayCollection.bin +2 -0
- data/spec/fixtures/objects/amf3-complexEncodedStringArray.bin +1 -0
- data/spec/fixtures/objects/amf3-dictionary.bin +0 -0
- data/spec/fixtures/objects/amf3-emptyDictionary.bin +0 -0
- data/spec/fixtures/objects/amf3-encodedStringRef.bin +0 -0
- data/spec/fixtures/objects/amf3-intVector.bin +0 -0
- data/spec/fixtures/objects/amf3-objVector.bin +0 -0
- data/spec/fixtures/objects/amf3-xml.bin +1 -0
- data/spec/fixtures/objects/amf3-xmlDoc.bin +1 -0
- data/spec/fixtures/objects/amf3-xmlRef.bin +1 -0
- data/spec/spec_helper.rb +11 -3
- metadata +31 -8
- data/lib/rocketamf/version.rb +0 -9
data/README.rdoc
CHANGED
@@ -1,51 +1,23 @@
|
|
1
1
|
== DESCRIPTION:
|
2
2
|
|
3
|
-
|
3
|
+
RocketAMF is a full featured AMF0/3 serializer and deserializer with support for
|
4
|
+
Flash -> Ruby and Ruby -> Flash class mapping, custom serializers, remoting
|
5
|
+
gateway helpers that follow AMF0/3 messaging specs, and a suite of specs to
|
6
|
+
ensure adherence to the specification documents put out by Adobe.
|
4
7
|
|
5
8
|
== INSTALL:
|
6
9
|
|
7
10
|
gem install RocketAMF --source="http://gemcutter.org"
|
8
11
|
|
9
|
-
== EXAMPLE:
|
10
|
-
|
11
|
-
# helloworld.ru
|
12
|
+
== SIMPLE EXAMPLE:
|
12
13
|
|
13
14
|
require 'rocketamf'
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
def call env
|
19
|
-
if is_amf?(env)
|
20
|
-
# Wrap request and response
|
21
|
-
env['rack.input'].rewind
|
22
|
-
request = RocketAMF::Request.new.populate_from_stream(env['rack.input'].read)
|
23
|
-
response = RocketAMF::Response.new
|
24
|
-
|
25
|
-
# Handle request
|
26
|
-
response.each_method_call request do |method, args|
|
27
|
-
raise "Service #{method} does not exists" unless method == 'App.helloWorld'
|
28
|
-
'Hello world'
|
29
|
-
end
|
30
|
-
|
31
|
-
# Pass back response
|
32
|
-
response_str = response.serialize
|
33
|
-
return [200, {'Content-Type' => APPLICATION_AMF, 'Content-Length' => response_str.length.to_s}, [response_str]]
|
34
|
-
else
|
35
|
-
return [200, {'Content-Type' => 'text/plain', 'Content-Length' => '16' }, ["Rack AMF gateway"]]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
def is_amf? env
|
41
|
-
return false unless env['CONTENT_TYPE'] == APPLICATION_AMF
|
42
|
-
return false unless env['PATH_INFO'] == '/amf'
|
43
|
-
return true
|
44
|
-
end
|
16
|
+
hash = {:apple => "Apfel", :red => "Rot", :eyes => "Augen"}
|
17
|
+
File.open("amf.dat", 'w') do |f|
|
18
|
+
f.write RocketAMF.serialize(hash, 3) # Use AMF3 encoding because it's smaller
|
45
19
|
end
|
46
20
|
|
47
|
-
run HelloWorldApp.new
|
48
|
-
|
49
21
|
== LICENSE:
|
50
22
|
|
51
23
|
(The MIT License)
|
data/Rakefile
CHANGED
@@ -23,8 +23,8 @@ end
|
|
23
23
|
|
24
24
|
spec = Gem::Specification.new do |s|
|
25
25
|
s.name = 'RocketAMF'
|
26
|
-
s.version = '0.0
|
27
|
-
s.summary = 'Fast AMF serializer/deserializer
|
26
|
+
s.version = '0.1.0'
|
27
|
+
s.summary = 'Fast AMF serializer/deserializer with remoting request/response wrappers to simplify integration'
|
28
28
|
|
29
29
|
s.files = FileList['README.rdoc', 'Rakefile', 'lib/**/*.rb', 'spec/**/*.rb', 'spec/**/*.bin', 'spec/spec.opts']
|
30
30
|
s.require_path = 'lib'
|
@@ -34,7 +34,7 @@ spec = Gem::Specification.new do |s|
|
|
34
34
|
s.extra_rdoc_files = ['README.rdoc']
|
35
35
|
s.rdoc_options = ['--line-numbers', '--main', 'README.rdoc']
|
36
36
|
|
37
|
-
s.authors = ['
|
37
|
+
s.authors = ['Jacob Henry', 'Stephen Augenstein']
|
38
38
|
s.email = 'perl.programmer@gmail.com'
|
39
39
|
s.homepage = 'http://github.com/warhammerkid/rocket-amf'
|
40
40
|
|
data/lib/rocketamf.rb
CHANGED
@@ -1,11 +1,77 @@
|
|
1
1
|
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
2
|
$:.unshift "#{File.expand_path(File.dirname(__FILE__))}/rocketamf/"
|
3
3
|
|
4
|
-
require 'rocketamf/version'
|
5
4
|
require 'rocketamf/class_mapping'
|
6
5
|
require 'rocketamf/constants'
|
7
6
|
require 'rocketamf/remoting'
|
8
7
|
|
8
|
+
# RocketAMF is a full featured AMF0/3 serializer and deserializer with support
|
9
|
+
# for Flash -> Ruby and Ruby -> Flash class mapping, custom serializers,
|
10
|
+
# remoting gateway helpers that follow AMF0/3 messaging specs, and a suite of
|
11
|
+
# specs to ensure adherence to the specification documents put out by Adobe.
|
12
|
+
#
|
13
|
+
# == Serialization & Deserialization
|
14
|
+
#
|
15
|
+
# RocketAMF provides two main methods - <tt>RocketAMF.serialize(obj, amf_version=0)</tt>
|
16
|
+
# and <tt>RocketAMF.deserialize(source, amf_version=0)</tt>. To use, simple pass
|
17
|
+
# in the string to deserialize and the version if different from the default. To
|
18
|
+
# serialize an object, simply call <tt>RocketAMF.serialize</tt> with the object
|
19
|
+
# and the proper version. If you're working only with AS3, it is more effiecient
|
20
|
+
# to use the version 3 encoding, as it caches duplicate string to reduce
|
21
|
+
# serialized size. However for greater compatibility the default, AMF version 0,
|
22
|
+
# should work fine.
|
23
|
+
#
|
24
|
+
# == Mapping Classes Between Flash and Ruby
|
25
|
+
#
|
26
|
+
# RocketAMF provides a simple class mapping tool to facilitate serialization and
|
27
|
+
# deserialization of typed objects. Refer to the documentation of
|
28
|
+
# <tt>RocketAMF::ClassMapping</tt> for more details. If the provided class
|
29
|
+
# mapping tool is not sufficient for your needs, you also have the option to
|
30
|
+
# replace it with a class mapper of your own devising that matches the documented
|
31
|
+
# API.
|
32
|
+
#
|
33
|
+
# == Remoting
|
34
|
+
#
|
35
|
+
# You can use RocketAMF bare to write an AMF gateway using the following code.
|
36
|
+
# In addition, you can use rack-amf (http://github.com/warhammerkid/rack-amf)
|
37
|
+
# which simplifies the code necessary to set up a functioning AMF gateway.
|
38
|
+
#
|
39
|
+
# # helloworld.ru
|
40
|
+
# require 'rocketamf'
|
41
|
+
#
|
42
|
+
# class HelloWorldApp
|
43
|
+
# APPLICATION_AMF = 'application/x-amf'.freeze
|
44
|
+
#
|
45
|
+
# def call env
|
46
|
+
# if is_amf?(env)
|
47
|
+
# # Wrap request and response
|
48
|
+
# env['rack.input'].rewind
|
49
|
+
# request = RocketAMF::Request.new.populate_from_stream(env['rack.input'].read)
|
50
|
+
# response = RocketAMF::Response.new
|
51
|
+
#
|
52
|
+
# # Handle request
|
53
|
+
# response.each_method_call request do |method, args|
|
54
|
+
# raise "Service #{method} does not exists" unless method == 'App.helloWorld'
|
55
|
+
# 'Hello world'
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# # Pass back response
|
59
|
+
# response_str = response.serialize
|
60
|
+
# return [200, {'Content-Type' => APPLICATION_AMF, 'Content-Length' => response_str.length.to_s}, [response_str]]
|
61
|
+
# else
|
62
|
+
# return [200, {'Content-Type' => 'text/plain', 'Content-Length' => '16' }, ["Rack AMF gateway"]]
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# private
|
67
|
+
# def is_amf? env
|
68
|
+
# return false unless env['CONTENT_TYPE'] == APPLICATION_AMF
|
69
|
+
# return false unless env['PATH_INFO'] == '/amf'
|
70
|
+
# return true
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# run HelloWorldApp.new
|
9
75
|
module RocketAMF
|
10
76
|
begin
|
11
77
|
raise LoadError, 'C extensions not implemented'
|
@@ -40,7 +106,7 @@ module RocketAMF
|
|
40
106
|
# We use const_missing to define the active ClassMapper at runtime. This way,
|
41
107
|
# heavy modification of class mapping functionality is still possible without
|
42
108
|
# forcing extenders to redefine the constant.
|
43
|
-
def self.const_missing const
|
109
|
+
def self.const_missing const #:nodoc:
|
44
110
|
if const == :ClassMapper
|
45
111
|
RocketAMF.const_set(:ClassMapper, RocketAMF::ClassMapping.new)
|
46
112
|
else
|
@@ -3,8 +3,6 @@ require 'rocketamf/values/array_collection'
|
|
3
3
|
require 'rocketamf/values/messages'
|
4
4
|
|
5
5
|
module RocketAMF
|
6
|
-
# == Class Mapping
|
7
|
-
#
|
8
6
|
# Handles class name mapping between actionscript and ruby and assists in
|
9
7
|
# serializing and deserializing data between them. Simply map an AS class to a
|
10
8
|
# ruby class and when the object is (de)serialized it will end up as the
|
@@ -29,8 +27,6 @@ module RocketAMF
|
|
29
27
|
# Populators are processed in insert order and must respond to the <tt>can_handle?</tt>
|
30
28
|
# and <tt>populate</tt> methods.
|
31
29
|
#
|
32
|
-
# Example:
|
33
|
-
#
|
34
30
|
# class CustomPopulator
|
35
31
|
# def can_handle? obj
|
36
32
|
# true
|
@@ -43,11 +39,10 @@ module RocketAMF
|
|
43
39
|
# end
|
44
40
|
# RocketAMF::ClassMapper.object_populators << CustomPopulator.new
|
45
41
|
#
|
42
|
+
#
|
46
43
|
# Serializers are also processed in insert order and must respond to the
|
47
44
|
# <tt>can_handle?</tt> and <tt>serialize</tt> methods.
|
48
45
|
#
|
49
|
-
# Example:
|
50
|
-
#
|
51
46
|
# class CustomSerializer
|
52
47
|
# def can_handle? obj
|
53
48
|
# true
|
@@ -65,7 +60,9 @@ module RocketAMF
|
|
65
60
|
# mapping completely. In this case, simply assign an instance of your own class
|
66
61
|
# mapper to <tt>RocketAMF::ClassMapper</tt> after loading RocketAMF. Through
|
67
62
|
# the magic of <tt>const_missing</tt>, <tt>ClassMapper</tt> is only defined after
|
68
|
-
# the first access by default, so you get no annoying warning messages.
|
63
|
+
# the first access by default, so you get no annoying warning messages. Custom
|
64
|
+
# class mappers must implement the following methods: <tt>get_as_class_name</tt>,
|
65
|
+
# <tt>get_ruby_obj</tt>, <tt>populate_ruby_obj</tt>, <tt>props_for_serialization</tt>.
|
69
66
|
#
|
70
67
|
# Example:
|
71
68
|
#
|
@@ -163,7 +160,8 @@ module RocketAMF
|
|
163
160
|
end
|
164
161
|
|
165
162
|
# Instantiates a ruby object using the mapping configuration based on the
|
166
|
-
# source AS class name. If there is no mapping defined, it returns a
|
163
|
+
# source AS class name. If there is no mapping defined, it returns a
|
164
|
+
# <tt>RocketAMF::Values::TypedHash</tt> with the serialized class name.
|
167
165
|
def get_ruby_obj as_class_name
|
168
166
|
ruby_class_name = mappings.get_ruby_class_name as_class_name
|
169
167
|
if ruby_class_name.nil?
|
@@ -220,7 +218,7 @@ module RocketAMF
|
|
220
218
|
(ruby_obj.public_methods - @ignored_props).each do |method_name|
|
221
219
|
# Add them to the prop hash if they take no arguments
|
222
220
|
method_def = ruby_obj.method(method_name)
|
223
|
-
props[method_name] = ruby_obj.send(method_name) if method_def.arity == 0
|
221
|
+
props[method_name.to_s] = ruby_obj.send(method_name) if method_def.arity == 0
|
224
222
|
end
|
225
223
|
props
|
226
224
|
end
|
data/lib/rocketamf/constants.rb
CHANGED
@@ -33,11 +33,10 @@ module RocketAMF
|
|
33
33
|
AMF3_OBJECT_MARKER = 0x0A #"\n"
|
34
34
|
AMF3_XML_MARKER = 0x0B #"\v"
|
35
35
|
AMF3_BYTE_ARRAY_MARKER = 0x0C #"\f"
|
36
|
+
AMF3_DICT_MARKER = 0x11 #"\021"
|
36
37
|
|
37
38
|
# Other AMF3 Markers
|
38
39
|
AMF3_EMPTY_STRING = 0x01
|
39
|
-
AMF3_ANONYMOUS_OBJECT = 0x01
|
40
|
-
AMF3_DYNAMIC_OBJECT = 0x0B
|
41
40
|
AMF3_CLOSE_DYNAMIC_OBJECT = 0x01
|
42
41
|
AMF3_CLOSE_DYNAMIC_ARRAY = 0x01
|
43
42
|
|
@@ -39,7 +39,7 @@ module RocketAMF
|
|
39
39
|
when AMF0_UNSUPPORTED_MARKER
|
40
40
|
nil
|
41
41
|
when AMF0_XML_MARKER
|
42
|
-
|
42
|
+
read_string source, true
|
43
43
|
when AMF0_TYPED_OBJECT_MARKER
|
44
44
|
read_typed_object source
|
45
45
|
when AMF0_AMF3_MARKER
|
@@ -63,7 +63,9 @@ module RocketAMF
|
|
63
63
|
|
64
64
|
def read_string source, long=false
|
65
65
|
len = long ? read_word32_network(source) : read_word16_network(source)
|
66
|
-
source.read(len)
|
66
|
+
str = source.read(len)
|
67
|
+
str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
|
68
|
+
str
|
67
69
|
end
|
68
70
|
|
69
71
|
def read_object source, add_to_ref_cache=true
|
@@ -180,18 +182,18 @@ module RocketAMF
|
|
180
182
|
read_number source
|
181
183
|
when AMF3_STRING_MARKER
|
182
184
|
read_string source
|
183
|
-
when AMF3_XML_DOC_MARKER
|
184
|
-
|
185
|
+
when AMF3_XML_DOC_MARKER, AMF3_XML_MARKER
|
186
|
+
read_xml source
|
185
187
|
when AMF3_DATE_MARKER
|
186
188
|
read_date source
|
187
189
|
when AMF3_ARRAY_MARKER
|
188
190
|
read_array source
|
189
191
|
when AMF3_OBJECT_MARKER
|
190
192
|
read_object source
|
191
|
-
when AMF3_XML_MARKER
|
192
|
-
#read_amf3_xml
|
193
193
|
when AMF3_BYTE_ARRAY_MARKER
|
194
194
|
read_amf3_byte_array source
|
195
|
+
when AMF3_DICT_MARKER
|
196
|
+
read_dict source
|
195
197
|
else
|
196
198
|
raise AMFError, "Invalid type: #{type}"
|
197
199
|
end
|
@@ -245,12 +247,32 @@ module RocketAMF
|
|
245
247
|
str = ""
|
246
248
|
if length > 0
|
247
249
|
str = source.read(length)
|
250
|
+
str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
|
248
251
|
@string_cache << str
|
249
252
|
end
|
250
253
|
return str
|
251
254
|
end
|
252
255
|
end
|
253
256
|
|
257
|
+
def read_xml source
|
258
|
+
type = read_integer source
|
259
|
+
isReference = (type & 0x01) == 0
|
260
|
+
|
261
|
+
if isReference
|
262
|
+
reference = type >> 1
|
263
|
+
return @object_cache[reference]
|
264
|
+
else
|
265
|
+
length = type >> 1
|
266
|
+
str = ""
|
267
|
+
if length > 0
|
268
|
+
str = source.read(length)
|
269
|
+
str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
|
270
|
+
@object_cache << str
|
271
|
+
end
|
272
|
+
return str
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
254
276
|
def read_amf3_byte_array source
|
255
277
|
type = read_integer source
|
256
278
|
isReference = (type & 0x01) == 0
|
@@ -314,37 +336,39 @@ module RocketAMF
|
|
314
336
|
|
315
337
|
if class_is_reference
|
316
338
|
reference = class_type >> 1
|
317
|
-
|
339
|
+
traits = @trait_cache[reference]
|
318
340
|
else
|
319
|
-
class_name = read_string source
|
320
341
|
externalizable = (class_type & 0x02) != 0
|
321
342
|
dynamic = (class_type & 0x04) != 0
|
322
343
|
attribute_count = class_type >> 3
|
344
|
+
class_name = read_string source
|
323
345
|
|
324
346
|
class_attributes = []
|
325
347
|
attribute_count.times{class_attributes << read_string(source)} # Read class members
|
326
348
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
349
|
+
traits = {
|
350
|
+
:class_name => class_name,
|
351
|
+
:members => class_attributes,
|
352
|
+
:externalizable => externalizable,
|
353
|
+
:dynamic => dynamic
|
354
|
+
}
|
355
|
+
@trait_cache << traits
|
332
356
|
end
|
333
357
|
|
334
|
-
obj = RocketAMF::ClassMapper.get_ruby_obj
|
358
|
+
obj = RocketAMF::ClassMapper.get_ruby_obj traits[:class_name]
|
335
359
|
@object_cache << obj
|
336
360
|
|
337
|
-
if
|
361
|
+
if traits[:externalizable]
|
338
362
|
obj.externalized_data = deserialize(source)
|
339
363
|
else
|
340
364
|
props = {}
|
341
|
-
|
365
|
+
traits[:members].each do |key|
|
342
366
|
value = deserialize(source)
|
343
367
|
props[key.to_sym] = value
|
344
368
|
end
|
345
369
|
|
346
370
|
dynamic_props = nil
|
347
|
-
if
|
371
|
+
if traits[:dynamic]
|
348
372
|
dynamic_props = {}
|
349
373
|
while (key = read_string source) && key.length != 0 do # read next key
|
350
374
|
value = deserialize(source)
|
@@ -371,6 +395,25 @@ module RocketAMF
|
|
371
395
|
time
|
372
396
|
end
|
373
397
|
end
|
398
|
+
|
399
|
+
def read_dict source
|
400
|
+
type = read_integer source
|
401
|
+
# Currently duplicate dictionaries send false, but I'll leave this in here just in case
|
402
|
+
isReference = (type & 0x01) == 0
|
403
|
+
if isReference
|
404
|
+
reference = type >> 1
|
405
|
+
return @object_cache[reference]
|
406
|
+
else
|
407
|
+
dict = {}
|
408
|
+
@object_cache << dict
|
409
|
+
length = type >> 1
|
410
|
+
skip = read_integer source # TODO: Handle when specs are updated
|
411
|
+
0.upto(length - 1) do |i|
|
412
|
+
dict[deserialize(source)] = deserialize(source)
|
413
|
+
end
|
414
|
+
dict
|
415
|
+
end
|
416
|
+
end
|
374
417
|
end
|
375
418
|
end
|
376
419
|
end
|
@@ -20,6 +20,7 @@ module RocketAMF
|
|
20
20
|
header_count = read_word16_network stream
|
21
21
|
0.upto(header_count-1) do
|
22
22
|
name = stream.read(read_word16_network(stream))
|
23
|
+
name.force_encoding("UTF-8") if name.respond_to?(:force_encoding)
|
23
24
|
must_understand = read_int8(stream) != 0
|
24
25
|
length = read_word32_network stream
|
25
26
|
data = RocketAMF.deserialize stream
|
@@ -30,7 +31,9 @@ module RocketAMF
|
|
30
31
|
message_count = read_word16_network stream
|
31
32
|
0.upto(message_count-1) do
|
32
33
|
target_uri = stream.read(read_word16_network(stream))
|
34
|
+
target_uri.force_encoding("UTF-8") if target_uri.respond_to?(:force_encoding)
|
33
35
|
response_uri = stream.read(read_word16_network(stream))
|
36
|
+
response_uri.force_encoding("UTF-8") if response_uri.respond_to?(:force_encoding)
|
34
37
|
length = read_word32_network stream
|
35
38
|
data = RocketAMF.deserialize stream
|
36
39
|
if data.is_a?(Array) && data.length == 1 && data[0].is_a?(::RocketAMF::Values::AbstractMessage)
|
@@ -58,8 +61,10 @@ module RocketAMF
|
|
58
61
|
# Write headers
|
59
62
|
stream << pack_int16_network(@headers.length) # Header count
|
60
63
|
@headers.each do |h|
|
61
|
-
|
62
|
-
|
64
|
+
name_str = h.name
|
65
|
+
name_str.encode!("UTF-8").force_encoding("ASCII-8BIT") if name_str.respond_to?(:encode)
|
66
|
+
stream << pack_int16_network(name_str.bytesize)
|
67
|
+
stream << name_str
|
63
68
|
stream << pack_int8(h.must_understand ? 1 : 0)
|
64
69
|
stream << pack_word32_network(-1)
|
65
70
|
stream << RocketAMF.serialize(h.data, 0)
|
@@ -68,11 +73,15 @@ module RocketAMF
|
|
68
73
|
# Write messages
|
69
74
|
stream << pack_int16_network(@messages.length) # Message count
|
70
75
|
@messages.each do |m|
|
71
|
-
|
72
|
-
|
76
|
+
uri_str = m.target_uri
|
77
|
+
uri_str.encode!("UTF-8").force_encoding("ASCII-8BIT") if uri_str.respond_to?(:encode)
|
78
|
+
stream << pack_int16_network(uri_str.bytesize)
|
79
|
+
stream << uri_str
|
73
80
|
|
74
|
-
|
75
|
-
|
81
|
+
uri_str = m.response_uri
|
82
|
+
uri_str.encode!("UTF-8").force_encoding("ASCII-8BIT") if uri_str.respond_to?(:encode)
|
83
|
+
stream << pack_int16_network(uri_str.bytesize)
|
84
|
+
stream << uri_str
|
76
85
|
|
77
86
|
stream << pack_word32_network(-1)
|
78
87
|
stream << AMF0_AMF3_MARKER if @amf_version == 3
|
@@ -52,7 +52,8 @@ module RocketAMF
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def write_string str, stream
|
55
|
-
|
55
|
+
str = str.encode("UTF-8").force_encoding("ASCII-8BIT") if str.respond_to?(:encode)
|
56
|
+
len = str.bytesize
|
56
57
|
if len > 2**16-1
|
57
58
|
stream << AMF0_LONG_STRING_MARKER
|
58
59
|
stream << pack_word32_network(len)
|
@@ -100,8 +101,9 @@ module RocketAMF
|
|
100
101
|
# Is it a typed object?
|
101
102
|
class_name = RocketAMF::ClassMapper.get_as_class_name obj
|
102
103
|
if class_name
|
104
|
+
class_name = class_name.encode("UTF-8").force_encoding("ASCII-8BIT") if class_name.respond_to?(:encode)
|
103
105
|
stream << AMF0_TYPED_OBJECT_MARKER
|
104
|
-
stream << pack_int16_network(class_name.
|
106
|
+
stream << pack_int16_network(class_name.bytesize)
|
105
107
|
stream << class_name
|
106
108
|
else
|
107
109
|
stream << AMF0_OBJECT_MARKER
|
@@ -116,7 +118,8 @@ module RocketAMF
|
|
116
118
|
# Write prop list
|
117
119
|
props = RocketAMF::ClassMapper.props_for_serialization obj
|
118
120
|
props.sort.each do |key, value| # Sort keys before writing
|
119
|
-
|
121
|
+
key = key.encode("UTF-8").force_encoding("ASCII-8BIT") if key.respond_to?(:encode)
|
122
|
+
stream << pack_int16_network(key.bytesize)
|
120
123
|
stream << key
|
121
124
|
serialize value, stream
|
122
125
|
end
|
@@ -134,6 +137,7 @@ module RocketAMF
|
|
134
137
|
def initialize
|
135
138
|
@string_cache = SerializerCache.new :string
|
136
139
|
@object_cache = SerializerCache.new :object
|
140
|
+
@trait_cache = SerializerCache.new :trait
|
137
141
|
end
|
138
142
|
|
139
143
|
def version
|
@@ -159,6 +163,8 @@ module RocketAMF
|
|
159
163
|
write_date obj, stream
|
160
164
|
elsif obj.is_a?(StringIO)
|
161
165
|
write_byte_array obj, stream
|
166
|
+
elsif obj.is_a?(RocketAMF::Values::ArrayCollection)
|
167
|
+
write_array_collection obj, stream
|
162
168
|
elsif obj.is_a?(Array)
|
163
169
|
write_array obj, stream
|
164
170
|
elsif obj.is_a?(Hash) || obj.is_a?(Object)
|
@@ -229,6 +235,10 @@ module RocketAMF
|
|
229
235
|
end
|
230
236
|
end
|
231
237
|
|
238
|
+
def write_array_collection array, stream
|
239
|
+
write_object array, stream, {:class_name => RocketAMF::ClassMapper.get_as_class_name(array), :members => [], :externalizable => true, :dynamic => false}
|
240
|
+
end
|
241
|
+
|
232
242
|
def write_array array, stream
|
233
243
|
stream << AMF3_ARRAY_MARKER
|
234
244
|
if @object_cache[array] != nil
|
@@ -248,7 +258,7 @@ module RocketAMF
|
|
248
258
|
end
|
249
259
|
end
|
250
260
|
|
251
|
-
def write_object obj, stream
|
261
|
+
def write_object obj, stream, traits=nil
|
252
262
|
stream << AMF3_OBJECT_MARKER
|
253
263
|
if @object_cache[obj] != nil
|
254
264
|
write_reference @object_cache[obj], stream
|
@@ -256,26 +266,59 @@ module RocketAMF
|
|
256
266
|
# Cache object
|
257
267
|
@object_cache.add_obj obj
|
258
268
|
|
259
|
-
#
|
260
|
-
|
269
|
+
# Calculate traits if not given
|
270
|
+
if traits.nil?
|
271
|
+
traits = {
|
272
|
+
:class_name => RocketAMF::ClassMapper.get_as_class_name(obj),
|
273
|
+
:members => [],
|
274
|
+
:externalizable => false,
|
275
|
+
:dynamic => true
|
276
|
+
}
|
277
|
+
end
|
261
278
|
|
262
|
-
# Write
|
263
|
-
class_name
|
264
|
-
|
265
|
-
write_utf8_vr class_name, stream
|
279
|
+
# Write out traits
|
280
|
+
if traits[:class_name] && @trait_cache[traits] != nil
|
281
|
+
stream << pack_integer(@trait_cache[traits] << 2 | 0x01)
|
266
282
|
else
|
267
|
-
|
283
|
+
@trait_cache.add_obj traits if traits[:class_name]
|
284
|
+
|
285
|
+
# Write out trait header
|
286
|
+
header = 0x03 # Not object ref and not trait ref
|
287
|
+
header |= 0x02 << 2 if traits[:dynamic]
|
288
|
+
header |= 0x01 << 2 if traits[:externalizable]
|
289
|
+
header |= traits[:members].length << 4
|
290
|
+
stream << pack_integer(header)
|
291
|
+
|
292
|
+
# Write out class name
|
293
|
+
write_utf8_vr(traits[:class_name].to_s, stream)
|
294
|
+
|
295
|
+
# Write out members
|
296
|
+
traits[:members].each {|m| write_utf8_vr(m, stream)}
|
297
|
+
end
|
298
|
+
|
299
|
+
# If externalizable, take externalized data shortcut
|
300
|
+
if traits[:externalizable]
|
301
|
+
serialize obj.externalized_data, stream
|
302
|
+
return
|
268
303
|
end
|
269
304
|
|
270
|
-
# Write out properties
|
305
|
+
# Write out sealed properties
|
271
306
|
props = RocketAMF::ClassMapper.props_for_serialization obj
|
272
|
-
|
273
|
-
|
274
|
-
|
307
|
+
traits[:members].each do |m|
|
308
|
+
serialize props[m], stream
|
309
|
+
props.delete(m)
|
275
310
|
end
|
276
311
|
|
277
|
-
|
278
|
-
|
312
|
+
if traits[:dynamic]
|
313
|
+
# Write out dynamic properties
|
314
|
+
props.sort.each do |key, val| # Sort props until Ruby 1.9 becomes common
|
315
|
+
write_utf8_vr key.to_s, stream
|
316
|
+
serialize val, stream
|
317
|
+
end
|
318
|
+
|
319
|
+
# Write close
|
320
|
+
stream << AMF3_CLOSE_DYNAMIC_OBJECT
|
321
|
+
end
|
279
322
|
end
|
280
323
|
end
|
281
324
|
|
@@ -283,6 +326,8 @@ module RocketAMF
|
|
283
326
|
include RocketAMF::Pure::WriteIOHelpers
|
284
327
|
|
285
328
|
def write_utf8_vr str, stream
|
329
|
+
str = str.encode("UTF-8").force_encoding("ASCII-8BIT") if str.respond_to?(:encode)
|
330
|
+
|
286
331
|
if str == ''
|
287
332
|
stream << AMF3_EMPTY_STRING
|
288
333
|
elsif @string_cache[str] != nil
|
@@ -292,7 +337,7 @@ module RocketAMF
|
|
292
337
|
@string_cache.add_obj str
|
293
338
|
|
294
339
|
# Build AMF string
|
295
|
-
header = str.
|
340
|
+
header = str.bytesize << 1 # make room for a low bit of 1
|
296
341
|
header = header | 1 # set the low bit to 1
|
297
342
|
stream << pack_integer(header)
|
298
343
|
stream << str
|
@@ -306,6 +351,8 @@ module RocketAMF
|
|
306
351
|
StringCache.new
|
307
352
|
elsif type == :object
|
308
353
|
ObjectCache.new
|
354
|
+
elsif type == :trait
|
355
|
+
TraitCache.new
|
309
356
|
end
|
310
357
|
end
|
311
358
|
|
@@ -334,6 +381,21 @@ module RocketAMF
|
|
334
381
|
@cache_index += 1
|
335
382
|
end
|
336
383
|
end
|
384
|
+
|
385
|
+
class TraitCache < Hash #:nodoc:
|
386
|
+
def initialize
|
387
|
+
@cache_index = 0
|
388
|
+
end
|
389
|
+
|
390
|
+
def [] obj
|
391
|
+
super(obj[:class_name])
|
392
|
+
end
|
393
|
+
|
394
|
+
def add_obj obj
|
395
|
+
self[obj[:class_name]] = @cache_index
|
396
|
+
@cache_index += 1
|
397
|
+
end
|
398
|
+
end
|
337
399
|
end
|
338
400
|
end
|
339
401
|
end
|
@@ -1,14 +1,15 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
end
|
3
|
+
class ClassMappingTest
|
4
|
+
attr_accessor :prop_a
|
5
|
+
attr_accessor :prop_b
|
6
|
+
attr_accessor :prop_c
|
7
|
+
end
|
8
|
+
class ClassMappingTest2 < ClassMappingTest; end;
|
11
9
|
|
10
|
+
module ANamespace; class TestRubyClass; end; end
|
11
|
+
|
12
|
+
describe RocketAMF::ClassMapping do
|
12
13
|
before :each do
|
13
14
|
@mapper = RocketAMF::ClassMapping.new
|
14
15
|
@mapper.define do |m|
|
@@ -34,7 +35,6 @@ describe RocketAMF::ClassMapping do
|
|
34
35
|
end
|
35
36
|
|
36
37
|
it "should properly instantiate namespaced classes" do
|
37
|
-
module ANamespace; class TestRubyClass; end; end
|
38
38
|
@mapper.define {|m| m.map :as => 'ASClass', :ruby => 'ANamespace::TestRubyClass'}
|
39
39
|
@mapper.get_ruby_obj('ASClass').should be_a(ANamespace::TestRubyClass)
|
40
40
|
end
|
@@ -110,7 +110,7 @@ describe RocketAMF::ClassMapping do
|
|
110
110
|
|
111
111
|
describe "property extractor" do
|
112
112
|
it "should extract hash properties" do
|
113
|
-
hash = {:a => 'test1',
|
113
|
+
hash = {:a => 'test1', 'b' => 'test2'}
|
114
114
|
props = @mapper.props_for_serialization(hash)
|
115
115
|
props.should == {'a' => 'test1', 'b' => 'test2'}
|
116
116
|
end
|
@@ -125,8 +125,6 @@ describe RocketAMF::ClassMapping do
|
|
125
125
|
end
|
126
126
|
|
127
127
|
it "should extract inherited object properties" do
|
128
|
-
class ClassMappingTest2 < ClassMappingTest
|
129
|
-
end
|
130
128
|
obj = ClassMappingTest2.new
|
131
129
|
obj.prop_a = 'Test A'
|
132
130
|
obj.prop_b = 'Test B'
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
1
3
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
4
|
|
3
5
|
describe "when deserializing" do
|
@@ -18,7 +20,7 @@ describe "when deserializing" do
|
|
18
20
|
output.should === true
|
19
21
|
end
|
20
22
|
|
21
|
-
it "should deserialize strings" do
|
23
|
+
it "should deserialize UTF8 strings" do
|
22
24
|
input = object_fixture('amf0-string.bin')
|
23
25
|
output = RocketAMF.deserialize(input, 0)
|
24
26
|
output.should == "this is a テスト"
|
@@ -74,7 +76,11 @@ describe "when deserializing" do
|
|
74
76
|
output.should == Time.utc(2003, 2, 13, 5)
|
75
77
|
end
|
76
78
|
|
77
|
-
it "should deserialize XML"
|
79
|
+
it "should deserialize an XML document" do
|
80
|
+
input = object_fixture('amf0-xmlDoc.bin')
|
81
|
+
output = RocketAMF.deserialize(input, 0)
|
82
|
+
output.should == '<parent><child prop="test" /></parent>'
|
83
|
+
end
|
78
84
|
|
79
85
|
it "should deserialize an unmapped object as a dynamic anonymous object" do
|
80
86
|
input = object_fixture("amf0-typed-object.bin")
|
@@ -85,9 +91,6 @@ describe "when deserializing" do
|
|
85
91
|
end
|
86
92
|
|
87
93
|
it "should deserialize a mapped object as a mapped ruby class instance" do
|
88
|
-
class RubyClass
|
89
|
-
attr_accessor :foo, :baz
|
90
|
-
end
|
91
94
|
RocketAMF::ClassMapper.define {|m| m.map :as => 'org.rocketAMF.ASClass', :ruby => 'RubyClass'}
|
92
95
|
|
93
96
|
input = object_fixture("amf0-typed-object.bin")
|
@@ -167,8 +170,17 @@ describe "when deserializing" do
|
|
167
170
|
output.should == Time.at(0)
|
168
171
|
end
|
169
172
|
|
170
|
-
|
171
|
-
|
173
|
+
it "should deserialize XML" do
|
174
|
+
# XMLDocument tag
|
175
|
+
input = object_fixture("amf3-xmlDoc.bin")
|
176
|
+
output = RocketAMF.deserialize(input, 3)
|
177
|
+
output.should == '<parent><child prop="test" /></parent>'
|
178
|
+
|
179
|
+
# XML tag
|
180
|
+
input = object_fixture("amf3-xml.bin")
|
181
|
+
output = RocketAMF.deserialize(input, 3)
|
182
|
+
output.should == '<parent><child prop="test"/></parent>'
|
183
|
+
end
|
172
184
|
end
|
173
185
|
|
174
186
|
describe "objects" do
|
@@ -186,9 +198,6 @@ describe "when deserializing" do
|
|
186
198
|
end
|
187
199
|
|
188
200
|
it "should deserialize a mapped object as a mapped ruby class instance" do
|
189
|
-
class RubyClass
|
190
|
-
attr_accessor :foo, :baz
|
191
|
-
end
|
192
201
|
RocketAMF::ClassMapper.define {|m| m.map :as => 'org.rocketAMF.ASClass', :ruby => 'RubyClass'}
|
193
202
|
|
194
203
|
input = object_fixture("amf3-typedObject.bin")
|
@@ -231,8 +240,29 @@ describe "when deserializing" do
|
|
231
240
|
input = object_fixture("amf3-byteArray.bin")
|
232
241
|
output = RocketAMF.deserialize(input, 3)
|
233
242
|
|
234
|
-
output.should be_a
|
235
|
-
|
243
|
+
output.should be_a(StringIO)
|
244
|
+
expected = "\000\003これtest\100"
|
245
|
+
expected.force_encoding("ASCII-8BIT") if expected.respond_to?(:force_encoding)
|
246
|
+
output.string.should == expected
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should deserialize an empty dictionary" do
|
250
|
+
input = object_fixture("amf3-emptyDictionary.bin")
|
251
|
+
output = RocketAMF.deserialize(input, 3)
|
252
|
+
output.should == {}
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should deserialize a dictionary" do
|
256
|
+
input = object_fixture("amf3-dictionary.bin")
|
257
|
+
output = RocketAMF.deserialize(input, 3)
|
258
|
+
|
259
|
+
keys = output.keys
|
260
|
+
keys.length.should == 2
|
261
|
+
obj_key, str_key = keys[0].is_a?(RocketAMF::Values::TypedHash) ? [keys[0], keys[1]] : [keys[1], keys[0]]
|
262
|
+
obj_key.type.should == 'org.rocketAMF.ASClass'
|
263
|
+
output[obj_key].should == "asdf2"
|
264
|
+
str_key.should == "bar"
|
265
|
+
output[str_key].should == "asdf1"
|
236
266
|
end
|
237
267
|
end
|
238
268
|
|
@@ -274,9 +304,6 @@ describe "when deserializing" do
|
|
274
304
|
end
|
275
305
|
|
276
306
|
it "should keep reference of duplicate object traits" do
|
277
|
-
class RubyClass
|
278
|
-
attr_accessor :foo, :baz
|
279
|
-
end
|
280
307
|
RocketAMF::ClassMapper.define {|m| m.map :as => 'org.rocketAMF.ASClass', :ruby => 'RubyClass'}
|
281
308
|
|
282
309
|
input = object_fixture("amf3-traitRef.bin")
|
@@ -304,7 +331,11 @@ describe "when deserializing" do
|
|
304
331
|
output.should == [a,b,a,b]
|
305
332
|
end
|
306
333
|
|
307
|
-
it "should keep references of duplicate XML and XMLDocuments"
|
334
|
+
it "should keep references of duplicate XML and XMLDocuments" do
|
335
|
+
input = object_fixture("amf3-xmlRef.bin")
|
336
|
+
output = RocketAMF.deserialize(input, 3)
|
337
|
+
output.should == ['<parent><child prop="test"/></parent>', '<parent><child prop="test"/></parent>']
|
338
|
+
end
|
308
339
|
|
309
340
|
it "should keep references of duplicate byte arrays" do
|
310
341
|
input = object_fixture("amf3-byteArrayRef.bin")
|
data/spec/amf/serializer_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
1
3
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
4
|
|
3
5
|
require 'rexml/document'
|
@@ -34,9 +36,6 @@ describe "when serializing" do
|
|
34
36
|
end
|
35
37
|
|
36
38
|
it "should serialize references" do
|
37
|
-
class OtherClass
|
38
|
-
attr_accessor :foo, :bar
|
39
|
-
end
|
40
39
|
obj = OtherClass.new
|
41
40
|
obj.foo = "baz"
|
42
41
|
obj.bar = 3.14
|
@@ -51,14 +50,11 @@ describe "when serializing" do
|
|
51
50
|
end
|
52
51
|
|
53
52
|
it "should serialize hashes" do
|
54
|
-
output = RocketAMF.serialize({:a => 'b',
|
53
|
+
output = RocketAMF.serialize({:a => 'b', 'c' => 'd'}, 0)
|
55
54
|
output.should == object_fixture('amf0-hash.bin')
|
56
55
|
end
|
57
56
|
|
58
57
|
it "should serialize unmapped objects" do
|
59
|
-
class RubyClass
|
60
|
-
attr_accessor :foo, :baz
|
61
|
-
end
|
62
58
|
obj = RubyClass.new
|
63
59
|
obj.foo = "bar"
|
64
60
|
|
@@ -67,9 +63,6 @@ describe "when serializing" do
|
|
67
63
|
end
|
68
64
|
|
69
65
|
it "should serialize mapped objects" do
|
70
|
-
class RubyClass
|
71
|
-
attr_accessor :foo, :baz
|
72
|
-
end
|
73
66
|
obj = RubyClass.new
|
74
67
|
obj.foo = "bar"
|
75
68
|
RocketAMF::ClassMapper.define {|m| m.map :as => 'org.rocketAMF.ASClass', :ruby => 'RubyClass'}
|
@@ -77,6 +70,15 @@ describe "when serializing" do
|
|
77
70
|
output = RocketAMF.serialize(obj, 0)
|
78
71
|
output.should == object_fixture('amf0-typed-object.bin')
|
79
72
|
end
|
73
|
+
|
74
|
+
if "".respond_to?(:force_encoding)
|
75
|
+
it "should support multiple encodings" do
|
76
|
+
shift_str = "\x53\x68\x69\x66\x74\x20\x83\x65\x83\x58\x83\x67".force_encoding("Shift_JIS") # "Shift テスト"
|
77
|
+
utf_str = "\x55\x54\x46\x20\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding("UTF-8") # "UTF テスト"
|
78
|
+
output = RocketAMF.serialize({"0" => 5, "1" => shift_str, "2" => utf_str, "3" => 5}, 0)
|
79
|
+
output.should == object_fixture("amf0-complexEncodedStringArray.bin")
|
80
|
+
end
|
81
|
+
end
|
80
82
|
end
|
81
83
|
|
82
84
|
describe "AMF3" do
|
@@ -153,9 +155,6 @@ describe "when serializing" do
|
|
153
155
|
output = RocketAMF.serialize(input, 3)
|
154
156
|
output.should == expected
|
155
157
|
end
|
156
|
-
|
157
|
-
#BAH! Who sends XML over AMF?
|
158
|
-
it "should serialize XML"
|
159
158
|
end
|
160
159
|
|
161
160
|
describe "objects" do
|
@@ -188,7 +187,7 @@ describe "when serializing" do
|
|
188
187
|
it "should serialize a hash as a dynamic anonymous object" do
|
189
188
|
hash = {}
|
190
189
|
hash[:answer] = 42
|
191
|
-
hash[
|
190
|
+
hash['foo'] = "bar"
|
192
191
|
|
193
192
|
expected = object_fixture("amf3-hash.bin")
|
194
193
|
input = hash
|
@@ -278,6 +277,28 @@ describe "when serializing" do
|
|
278
277
|
output.should == expected
|
279
278
|
end
|
280
279
|
|
280
|
+
it "should keep reference of duplicate object traits" do
|
281
|
+
obj1 = RubyClass.new
|
282
|
+
obj1.foo = "foo"
|
283
|
+
def obj1.to_amf serializer
|
284
|
+
stream = ""
|
285
|
+
serializer.write_object(self, stream, {:class_name => 'org.rocketAMF.ASClass', :dynamic => false, :externalizable => false, :members => ["baz", "foo"]})
|
286
|
+
stream
|
287
|
+
end
|
288
|
+
obj2 = RubyClass.new
|
289
|
+
obj2.foo = "bar"
|
290
|
+
def obj2.to_amf serializer
|
291
|
+
stream = ""
|
292
|
+
serializer.write_object(self, stream, {:class_name => 'org.rocketAMF.ASClass', :dynamic => false, :externalizable => false, :members => ["baz", "foo"]})
|
293
|
+
stream
|
294
|
+
end
|
295
|
+
input = [obj1, obj2]
|
296
|
+
|
297
|
+
expected = object_fixture("amf3-traitRef.bin")
|
298
|
+
output = RocketAMF.serialize(input, 3)
|
299
|
+
output.should == expected
|
300
|
+
end
|
301
|
+
|
281
302
|
it "should keep references of duplicate arrays" do
|
282
303
|
a = [1,2,3]
|
283
304
|
b = %w{ a b c }
|
@@ -300,8 +321,6 @@ describe "when serializing" do
|
|
300
321
|
output.should == expected
|
301
322
|
end
|
302
323
|
|
303
|
-
it "should keep references of duplicate XML and XMLDocuments"
|
304
|
-
|
305
324
|
it "should keep references of duplicate byte arrays" do
|
306
325
|
b = StringIO.new "ASDF"
|
307
326
|
|
@@ -313,8 +332,8 @@ describe "when serializing" do
|
|
313
332
|
|
314
333
|
it "should serialize a deep object graph with circular references" do
|
315
334
|
class GraphMember
|
316
|
-
attr_accessor :parent
|
317
335
|
attr_accessor :children
|
336
|
+
attr_accessor :parent
|
318
337
|
|
319
338
|
def initialize
|
320
339
|
self.children = []
|
@@ -337,5 +356,26 @@ describe "when serializing" do
|
|
337
356
|
output.should == expected
|
338
357
|
end
|
339
358
|
end
|
359
|
+
|
360
|
+
if "".respond_to?(:force_encoding)
|
361
|
+
describe "and handling encodings" do
|
362
|
+
it "should support multiple encodings" do
|
363
|
+
shift_str = "\x53\x68\x69\x66\x74\x20\x83\x65\x83\x58\x83\x67".force_encoding("Shift_JIS") # "Shift テスト"
|
364
|
+
utf_str = "\x55\x54\x46\x20\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding("UTF-8") # "UTF テスト"
|
365
|
+
output = RocketAMF.serialize([5, shift_str, utf_str, 5], 3)
|
366
|
+
output.should == object_fixture("amf3-complexEncodedStringArray.bin")
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should keep references of duplicate strings with different encodings" do
|
370
|
+
# String is "this is a テスト"
|
371
|
+
shift_str = "\x74\x68\x69\x73\x20\x69\x73\x20\x61\x20\x83\x65\x83\x58\x83\x67".force_encoding("Shift_JIS")
|
372
|
+
utf_str = "\x74\x68\x69\x73\x20\x69\x73\x20\x61\x20\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding("UTF-8")
|
373
|
+
|
374
|
+
expected = object_fixture("amf3-encodedStringRef.bin")
|
375
|
+
output = RocketAMF.serialize([shift_str, utf_str], 3)
|
376
|
+
output.should == expected
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
340
380
|
end
|
341
381
|
end
|
@@ -1,6 +1,19 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
2
|
|
3
3
|
describe RocketAMF::Values::ArrayCollection do
|
4
|
-
it "should deserialize properly"
|
5
|
-
|
4
|
+
it "should deserialize properly" do
|
5
|
+
input = object_fixture("amf3-arrayCollection.bin")
|
6
|
+
output = RocketAMF.deserialize(input, 3)
|
7
|
+
|
8
|
+
output.should be_a(RocketAMF::Values::ArrayCollection)
|
9
|
+
output.should == ["foo", "bar"]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should serialize properly" do
|
13
|
+
expected = object_fixture('amf3-arrayCollection.bin')
|
14
|
+
input = RocketAMF::Values::ArrayCollection.new
|
15
|
+
input.push("foo", "bar")
|
16
|
+
output = RocketAMF.serialize(input, 3)
|
17
|
+
output.should == expected
|
18
|
+
end
|
6
19
|
end
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
Shift テストUTF テスト
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
K<parent><child prop="test"/></parent>
|
@@ -0,0 +1 @@
|
|
1
|
+
M<parent><child prop="test" /></parent>
|
@@ -0,0 +1 @@
|
|
1
|
+
K<parent><child prop="test"/></parent>
|
data/spec/spec_helper.rb
CHANGED
@@ -11,13 +11,21 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
|
|
11
11
|
require 'rocketamf'
|
12
12
|
|
13
13
|
def request_fixture(binary_path)
|
14
|
-
File.open(File.dirname(__FILE__) + '/fixtures/request/' + binary_path).read
|
14
|
+
data = File.open(File.dirname(__FILE__) + '/fixtures/request/' + binary_path).read
|
15
|
+
data.force_encoding("ASCII-8BIT") if data.respond_to?(:force_encoding)
|
16
|
+
data
|
15
17
|
end
|
16
18
|
|
17
19
|
def object_fixture(binary_path)
|
18
|
-
File.open(File.dirname(__FILE__) + '/fixtures/objects/' + binary_path).read
|
20
|
+
data = File.open(File.dirname(__FILE__) + '/fixtures/objects/' + binary_path).read
|
21
|
+
data.force_encoding("ASCII-8BIT") if data.respond_to?(:force_encoding)
|
22
|
+
data
|
19
23
|
end
|
20
24
|
|
21
25
|
def create_request(binary_path)
|
22
26
|
RocketAMF::Request.new.populate_from_stream(StringIO.new(request_fixture(binary_path)))
|
23
|
-
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Helper classes
|
30
|
+
class RubyClass; attr_accessor :baz, :foo; end;
|
31
|
+
class OtherClass; attr_accessor :bar, :foo; end;
|
metadata
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: RocketAMF
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
|
-
-
|
13
|
+
- Jacob Henry
|
8
14
|
- Stephen Augenstein
|
9
15
|
autorequire:
|
10
16
|
bindir: bin
|
11
17
|
cert_chain: []
|
12
18
|
|
13
|
-
date: 2010-
|
19
|
+
date: 2010-07-04 00:00:00 -04:00
|
14
20
|
default_executable:
|
15
21
|
dependencies: []
|
16
22
|
|
@@ -36,7 +42,6 @@ files:
|
|
36
42
|
- lib/rocketamf/values/array_collection.rb
|
37
43
|
- lib/rocketamf/values/messages.rb
|
38
44
|
- lib/rocketamf/values/typed_hash.rb
|
39
|
-
- lib/rocketamf/version.rb
|
40
45
|
- lib/rocketamf.rb
|
41
46
|
- spec/amf/class_mapping_spec.rb
|
42
47
|
- spec/amf/deserializer_spec.rb
|
@@ -47,6 +52,7 @@ files:
|
|
47
52
|
- spec/amf/values/messages_spec.rb
|
48
53
|
- spec/spec_helper.rb
|
49
54
|
- spec/fixtures/objects/amf0-boolean.bin
|
55
|
+
- spec/fixtures/objects/amf0-complexEncodedStringArray.bin
|
50
56
|
- spec/fixtures/objects/amf0-date.bin
|
51
57
|
- spec/fixtures/objects/amf0-ecma-ordinal-array.bin
|
52
58
|
- spec/fixtures/objects/amf0-hash.bin
|
@@ -59,20 +65,27 @@ files:
|
|
59
65
|
- spec/fixtures/objects/amf0-typed-object.bin
|
60
66
|
- spec/fixtures/objects/amf0-undefined.bin
|
61
67
|
- spec/fixtures/objects/amf0-untyped-object.bin
|
68
|
+
- spec/fixtures/objects/amf0-xmlDoc.bin
|
62
69
|
- spec/fixtures/objects/amf3-0.bin
|
70
|
+
- spec/fixtures/objects/amf3-arrayCollection.bin
|
63
71
|
- spec/fixtures/objects/amf3-arrayRef.bin
|
64
72
|
- spec/fixtures/objects/amf3-bigNum.bin
|
65
73
|
- spec/fixtures/objects/amf3-byteArray.bin
|
66
74
|
- spec/fixtures/objects/amf3-byteArrayRef.bin
|
75
|
+
- spec/fixtures/objects/amf3-complexEncodedStringArray.bin
|
67
76
|
- spec/fixtures/objects/amf3-date.bin
|
68
77
|
- spec/fixtures/objects/amf3-datesRef.bin
|
78
|
+
- spec/fixtures/objects/amf3-dictionary.bin
|
69
79
|
- spec/fixtures/objects/amf3-dynObject.bin
|
70
80
|
- spec/fixtures/objects/amf3-emptyArray.bin
|
71
81
|
- spec/fixtures/objects/amf3-emptyArrayRef.bin
|
82
|
+
- spec/fixtures/objects/amf3-emptyDictionary.bin
|
72
83
|
- spec/fixtures/objects/amf3-emptyStringRef.bin
|
84
|
+
- spec/fixtures/objects/amf3-encodedStringRef.bin
|
73
85
|
- spec/fixtures/objects/amf3-false.bin
|
74
86
|
- spec/fixtures/objects/amf3-graphMember.bin
|
75
87
|
- spec/fixtures/objects/amf3-hash.bin
|
88
|
+
- spec/fixtures/objects/amf3-intVector.bin
|
76
89
|
- spec/fixtures/objects/amf3-largeMax.bin
|
77
90
|
- spec/fixtures/objects/amf3-largeMin.bin
|
78
91
|
- spec/fixtures/objects/amf3-max.bin
|
@@ -80,6 +93,7 @@ files:
|
|
80
93
|
- spec/fixtures/objects/amf3-mixedArray.bin
|
81
94
|
- spec/fixtures/objects/amf3-null.bin
|
82
95
|
- spec/fixtures/objects/amf3-objRef.bin
|
96
|
+
- spec/fixtures/objects/amf3-objVector.bin
|
83
97
|
- spec/fixtures/objects/amf3-primArray.bin
|
84
98
|
- spec/fixtures/objects/amf3-string.bin
|
85
99
|
- spec/fixtures/objects/amf3-stringRef.bin
|
@@ -87,6 +101,9 @@ files:
|
|
87
101
|
- spec/fixtures/objects/amf3-traitRef.bin
|
88
102
|
- spec/fixtures/objects/amf3-true.bin
|
89
103
|
- spec/fixtures/objects/amf3-typedObject.bin
|
104
|
+
- spec/fixtures/objects/amf3-xml.bin
|
105
|
+
- spec/fixtures/objects/amf3-xmlDoc.bin
|
106
|
+
- spec/fixtures/objects/amf3-xmlRef.bin
|
90
107
|
- spec/fixtures/request/acknowledge-response.bin
|
91
108
|
- spec/fixtures/request/amf0-error-response.bin
|
92
109
|
- spec/fixtures/request/commandMessage.bin
|
@@ -106,24 +123,30 @@ rdoc_options:
|
|
106
123
|
require_paths:
|
107
124
|
- lib
|
108
125
|
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
109
127
|
requirements:
|
110
128
|
- - ">="
|
111
129
|
- !ruby/object:Gem::Version
|
130
|
+
hash: 3
|
131
|
+
segments:
|
132
|
+
- 0
|
112
133
|
version: "0"
|
113
|
-
version:
|
114
134
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
115
136
|
requirements:
|
116
137
|
- - ">="
|
117
138
|
- !ruby/object:Gem::Version
|
139
|
+
hash: 3
|
140
|
+
segments:
|
141
|
+
- 0
|
118
142
|
version: "0"
|
119
|
-
version:
|
120
143
|
requirements: []
|
121
144
|
|
122
145
|
rubyforge_project:
|
123
|
-
rubygems_version: 1.3.
|
146
|
+
rubygems_version: 1.3.7
|
124
147
|
signing_key:
|
125
148
|
specification_version: 3
|
126
|
-
summary: Fast AMF serializer/deserializer
|
149
|
+
summary: Fast AMF serializer/deserializer with remoting request/response wrappers to simplify integration
|
127
150
|
test_files:
|
128
151
|
- spec/amf/class_mapping_spec.rb
|
129
152
|
- spec/amf/deserializer_spec.rb
|
data/lib/rocketamf/version.rb
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
module RocketAMF
|
2
|
-
# AMF version
|
3
|
-
VERSION = '0.0.7'
|
4
|
-
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
|
5
|
-
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
|
6
|
-
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
|
7
|
-
VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
|
8
|
-
VARIANT_BINARY = false
|
9
|
-
end
|