RocketAMF 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|