scotttam-RocketAMF 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +45 -0
- data/Rakefile +54 -0
- data/lib/rocketamf.rb +128 -0
- data/lib/rocketamf/class_mapping.rb +231 -0
- data/lib/rocketamf/constants.rb +46 -0
- data/lib/rocketamf/pure.rb +28 -0
- data/lib/rocketamf/pure/deserializer.rb +419 -0
- data/lib/rocketamf/pure/io_helpers.rb +94 -0
- data/lib/rocketamf/pure/remoting.rb +134 -0
- data/lib/rocketamf/pure/serializer.rb +433 -0
- data/lib/rocketamf/remoting.rb +144 -0
- data/lib/rocketamf/values/array_collection.rb +13 -0
- data/lib/rocketamf/values/messages.rb +133 -0
- data/lib/rocketamf/values/typed_hash.rb +13 -0
- data/spec/amf/class_mapping_spec.rb +150 -0
- data/spec/amf/deserializer_spec.rb +367 -0
- data/spec/amf/remoting_spec.rb +132 -0
- data/spec/amf/serializer_spec.rb +384 -0
- data/spec/amf/values/array_collection_spec.rb +19 -0
- data/spec/amf/values/messages_spec.rb +31 -0
- data/spec/fixtures/objects/amf0-boolean.bin +1 -0
- data/spec/fixtures/objects/amf0-complexEncodedStringArray.bin +0 -0
- data/spec/fixtures/objects/amf0-date.bin +0 -0
- data/spec/fixtures/objects/amf0-ecma-ordinal-array.bin +0 -0
- data/spec/fixtures/objects/amf0-hash.bin +0 -0
- data/spec/fixtures/objects/amf0-null.bin +1 -0
- data/spec/fixtures/objects/amf0-number.bin +0 -0
- data/spec/fixtures/objects/amf0-object.bin +0 -0
- data/spec/fixtures/objects/amf0-ref-test.bin +0 -0
- data/spec/fixtures/objects/amf0-strict-array.bin +0 -0
- data/spec/fixtures/objects/amf0-string.bin +0 -0
- data/spec/fixtures/objects/amf0-typed-object.bin +0 -0
- data/spec/fixtures/objects/amf0-undefined.bin +1 -0
- data/spec/fixtures/objects/amf0-untyped-object.bin +0 -0
- data/spec/fixtures/objects/amf0-xmlDoc.bin +0 -0
- data/spec/fixtures/objects/amf3-0.bin +0 -0
- data/spec/fixtures/objects/amf3-arrayCollection.bin +2 -0
- data/spec/fixtures/objects/amf3-arrayRef.bin +1 -0
- data/spec/fixtures/objects/amf3-bigNum.bin +0 -0
- data/spec/fixtures/objects/amf3-byteArray.bin +0 -0
- data/spec/fixtures/objects/amf3-byteArrayRef.bin +1 -0
- data/spec/fixtures/objects/amf3-complexEncodedStringArray.bin +1 -0
- data/spec/fixtures/objects/amf3-date.bin +0 -0
- data/spec/fixtures/objects/amf3-datesRef.bin +0 -0
- data/spec/fixtures/objects/amf3-dictionary.bin +0 -0
- data/spec/fixtures/objects/amf3-dynObject.bin +2 -0
- data/spec/fixtures/objects/amf3-emptyArray.bin +1 -0
- data/spec/fixtures/objects/amf3-emptyArrayRef.bin +1 -0
- data/spec/fixtures/objects/amf3-emptyDictionary.bin +0 -0
- data/spec/fixtures/objects/amf3-emptyStringRef.bin +1 -0
- data/spec/fixtures/objects/amf3-encodedStringRef.bin +0 -0
- data/spec/fixtures/objects/amf3-false.bin +1 -0
- data/spec/fixtures/objects/amf3-float.bin +0 -0
- data/spec/fixtures/objects/amf3-graphMember.bin +0 -0
- data/spec/fixtures/objects/amf3-hash.bin +2 -0
- data/spec/fixtures/objects/amf3-largeMax.bin +0 -0
- data/spec/fixtures/objects/amf3-largeMin.bin +0 -0
- data/spec/fixtures/objects/amf3-max.bin +1 -0
- data/spec/fixtures/objects/amf3-min.bin +0 -0
- data/spec/fixtures/objects/amf3-mixedArray.bin +11 -0
- data/spec/fixtures/objects/amf3-null.bin +1 -0
- data/spec/fixtures/objects/amf3-objRef.bin +0 -0
- data/spec/fixtures/objects/amf3-primArray.bin +1 -0
- data/spec/fixtures/objects/amf3-string.bin +1 -0
- data/spec/fixtures/objects/amf3-stringRef.bin +0 -0
- data/spec/fixtures/objects/amf3-symbol.bin +1 -0
- data/spec/fixtures/objects/amf3-traitRef.bin +3 -0
- data/spec/fixtures/objects/amf3-true.bin +1 -0
- data/spec/fixtures/objects/amf3-typedObject.bin +2 -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/fixtures/request/acknowledge-response.bin +0 -0
- data/spec/fixtures/request/amf0-error-response.bin +0 -0
- data/spec/fixtures/request/commandMessage.bin +0 -0
- data/spec/fixtures/request/remotingMessage.bin +0 -0
- data/spec/fixtures/request/simple-response.bin +0 -0
- data/spec/fixtures/request/unsupportedCommandMessage.bin +0 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +31 -0
- metadata +153 -0
data/README.rdoc
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
== DESCRIPTION:
|
2
|
+
|
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. In addition,
|
7
|
+
it now fully supports Ruby 1.9.
|
8
|
+
|
9
|
+
== INSTALL:
|
10
|
+
|
11
|
+
gem install RocketAMF --source="http://gemcutter.org"
|
12
|
+
|
13
|
+
== SIMPLE EXAMPLE:
|
14
|
+
|
15
|
+
require 'rocketamf'
|
16
|
+
|
17
|
+
hash = {:apple => "Apfel", :red => "Rot", :eyes => "Augen"}
|
18
|
+
File.open("amf.dat", 'w') do |f|
|
19
|
+
f.write RocketAMF.serialize(hash, 3) # Use AMF3 encoding because it's smaller
|
20
|
+
end
|
21
|
+
|
22
|
+
== LICENSE:
|
23
|
+
|
24
|
+
(The MIT License)
|
25
|
+
|
26
|
+
Copyright (c) 2009 Stephen Augenstein and Jacob Smith
|
27
|
+
|
28
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
29
|
+
a copy of this software and associated documentation files (the
|
30
|
+
'Software'), to deal in the Software without restriction, including
|
31
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
32
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
33
|
+
permit persons to whom the Software is furnished to do so, subject to
|
34
|
+
the following conditions:
|
35
|
+
|
36
|
+
The above copyright notice and this permission notice shall be
|
37
|
+
included in all copies or substantial portions of the Software.
|
38
|
+
|
39
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
40
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
41
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
42
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
43
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
44
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
45
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
|
6
|
+
require 'spec/rake/spectask'
|
7
|
+
|
8
|
+
desc 'Default: run the specs.'
|
9
|
+
task :default => :spec
|
10
|
+
|
11
|
+
Spec::Rake::SpecTask.new do |t|
|
12
|
+
t.spec_opts = ['--options', 'spec/spec.opts']
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the RocketAMF plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'scotttam-RocketAMF'
|
19
|
+
rdoc.options << '--line-numbers' << '--main' << 'README.rdoc'
|
20
|
+
rdoc.rdoc_files.include('README.rdoc')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
spec = Gem::Specification.new do |s|
|
25
|
+
s.name = 'scotttam-RocketAMF'
|
26
|
+
s.version = '0.2.2'
|
27
|
+
s.summary = 'Fast AMF serializer/deserializer with remoting request/response wrappers to simplify integration'
|
28
|
+
|
29
|
+
s.files = FileList['README.rdoc', 'Rakefile', 'lib/**/*.rb', 'spec/**/*.rb', 'spec/**/*.bin', 'spec/spec.opts']
|
30
|
+
s.require_path = 'lib'
|
31
|
+
s.test_files = Dir[*['spec/**/*_spec.rb']]
|
32
|
+
|
33
|
+
s.has_rdoc = true
|
34
|
+
s.extra_rdoc_files = ['README.rdoc']
|
35
|
+
s.rdoc_options = ['--line-numbers', '--main', 'README.rdoc']
|
36
|
+
|
37
|
+
s.authors = ['Jacob Henry', 'Stephen Augenstein', "Joc O'Connor", "Scott Tamosunas"]
|
38
|
+
s.email = 'perl.programmer@gmail.com'
|
39
|
+
s.homepage = 'http://github.com/scotttam/rocket-amf'
|
40
|
+
|
41
|
+
s.platform = Gem::Platform::RUBY
|
42
|
+
end
|
43
|
+
|
44
|
+
Rake::GemPackageTask.new spec do |pkg|
|
45
|
+
pkg.need_tar = true
|
46
|
+
pkg.need_zip = true
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'Generate a gemspec file'
|
50
|
+
task :gemspec do
|
51
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
52
|
+
f.write spec.to_ruby
|
53
|
+
end
|
54
|
+
end
|
data/lib/rocketamf.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
$:.unshift "#{File.expand_path(File.dirname(__FILE__))}/rocketamf/"
|
3
|
+
|
4
|
+
require 'rocketamf/class_mapping'
|
5
|
+
require 'rocketamf/constants'
|
6
|
+
require 'rocketamf/remoting'
|
7
|
+
|
8
|
+
# Joc's monkeypatch for string bytesize (only available in 1.8.7+)
|
9
|
+
if !"amf".respond_to? :bytesize
|
10
|
+
class String
|
11
|
+
def bytesize
|
12
|
+
self.size
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# RocketAMF is a full featured AMF0/3 serializer and deserializer with support
|
18
|
+
# for Flash -> Ruby and Ruby -> Flash class mapping, custom serializers,
|
19
|
+
# remoting gateway helpers that follow AMF0/3 messaging specs, and a suite of
|
20
|
+
# specs to ensure adherence to the specification documents put out by Adobe.
|
21
|
+
#
|
22
|
+
# == Serialization & Deserialization
|
23
|
+
#
|
24
|
+
# RocketAMF provides two main methods - <tt>RocketAMF.serialize(obj, amf_version=0)</tt>
|
25
|
+
# and <tt>RocketAMF.deserialize(source, amf_version=0)</tt>. To use, simple pass
|
26
|
+
# in the string to deserialize and the version if different from the default. To
|
27
|
+
# serialize an object, simply call <tt>RocketAMF.serialize</tt> with the object
|
28
|
+
# and the proper version. If you're working only with AS3, it is more effiecient
|
29
|
+
# to use the version 3 encoding, as it caches duplicate string to reduce
|
30
|
+
# serialized size. However for greater compatibility the default, AMF version 0,
|
31
|
+
# should work fine.
|
32
|
+
#
|
33
|
+
# == Mapping Classes Between Flash and Ruby
|
34
|
+
#
|
35
|
+
# RocketAMF provides a simple class mapping tool to facilitate serialization and
|
36
|
+
# deserialization of typed objects. Refer to the documentation of
|
37
|
+
# <tt>RocketAMF::ClassMapping</tt> for more details. If the provided class
|
38
|
+
# mapping tool is not sufficient for your needs, you also have the option to
|
39
|
+
# replace it with a class mapper of your own devising that matches the documented
|
40
|
+
# API.
|
41
|
+
#
|
42
|
+
# == Remoting
|
43
|
+
#
|
44
|
+
# You can use RocketAMF bare to write an AMF gateway using the following code.
|
45
|
+
# In addition, you can use rack-amf (http://github.com/warhammerkid/rack-amf)
|
46
|
+
# which simplifies the code necessary to set up a functioning AMF gateway.
|
47
|
+
#
|
48
|
+
# # helloworld.ru
|
49
|
+
# require 'rocketamf'
|
50
|
+
#
|
51
|
+
# class HelloWorldApp
|
52
|
+
# APPLICATION_AMF = 'application/x-amf'.freeze
|
53
|
+
#
|
54
|
+
# def call env
|
55
|
+
# if is_amf?(env)
|
56
|
+
# # Wrap request and response
|
57
|
+
# env['rack.input'].rewind
|
58
|
+
# request = RocketAMF::Envelope.new.populate_from_stream(env['rack.input'].read)
|
59
|
+
# response = RocketAMF::Envelope.new
|
60
|
+
#
|
61
|
+
# # Handle request
|
62
|
+
# response.each_method_call request do |method, args|
|
63
|
+
# raise "Service #{method} does not exists" unless method == 'App.helloWorld'
|
64
|
+
# 'Hello world'
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# # Pass back response
|
68
|
+
# response_str = response.serialize
|
69
|
+
# return [200, {'Content-Type' => APPLICATION_AMF, 'Content-Length' => response_str.length.to_s}, [response_str]]
|
70
|
+
# else
|
71
|
+
# return [200, {'Content-Type' => 'text/plain', 'Content-Length' => '16' }, ["Rack AMF gateway"]]
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# private
|
76
|
+
# def is_amf? env
|
77
|
+
# return false unless env['CONTENT_TYPE'] == APPLICATION_AMF
|
78
|
+
# return false unless env['PATH_INFO'] == '/amf'
|
79
|
+
# return true
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# run HelloWorldApp.new
|
84
|
+
module RocketAMF
|
85
|
+
begin
|
86
|
+
raise LoadError, 'C extensions not implemented'
|
87
|
+
rescue LoadError
|
88
|
+
require 'rocketamf/pure'
|
89
|
+
end
|
90
|
+
|
91
|
+
# Deserialize the AMF string _source_ of the given AMF version into a Ruby
|
92
|
+
# data structure and return it
|
93
|
+
def self.deserialize source, amf_version = 0
|
94
|
+
if amf_version == 0
|
95
|
+
RocketAMF::Deserializer.new.deserialize(source)
|
96
|
+
elsif amf_version == 3
|
97
|
+
RocketAMF::AMF3Deserializer.new.deserialize(source)
|
98
|
+
else
|
99
|
+
raise AMFError, "unsupported version #{amf_version}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Serialize the given Ruby data structure _obj_ into an AMF stream using the
|
104
|
+
# given AMF version
|
105
|
+
def self.serialize obj, amf_version = 0
|
106
|
+
if amf_version == 0
|
107
|
+
RocketAMF::Serializer.new.serialize(obj)
|
108
|
+
elsif amf_version == 3
|
109
|
+
RocketAMF::AMF3Serializer.new.serialize(obj)
|
110
|
+
else
|
111
|
+
raise AMFError, "unsupported version #{amf_version}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# We use const_missing to define the active ClassMapper at runtime. This way,
|
116
|
+
# heavy modification of class mapping functionality is still possible without
|
117
|
+
# forcing extenders to redefine the constant.
|
118
|
+
def self.const_missing const #:nodoc:
|
119
|
+
if const == :ClassMapper
|
120
|
+
RocketAMF.const_set(:ClassMapper, RocketAMF::ClassMapping.new)
|
121
|
+
else
|
122
|
+
super(const)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# The base exception for AMF errors.
|
127
|
+
class AMFError < StandardError; end
|
128
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
require 'rocketamf/values/typed_hash'
|
2
|
+
require 'rocketamf/values/array_collection'
|
3
|
+
require 'rocketamf/values/messages'
|
4
|
+
|
5
|
+
module RocketAMF
|
6
|
+
# Handles class name mapping between actionscript and ruby and assists in
|
7
|
+
# serializing and deserializing data between them. Simply map an AS class to a
|
8
|
+
# ruby class and when the object is (de)serialized it will end up as the
|
9
|
+
# appropriate class.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# RocketAMF::ClassMapper.define do |m|
|
14
|
+
# m.map :as => 'AsClass', :ruby => 'RubyClass'
|
15
|
+
# m.map :as => 'vo.User', :ruby => 'Model::User'
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# == Object Population/Serialization
|
19
|
+
#
|
20
|
+
# In addition to handling class name mapping, it also provides helper methods
|
21
|
+
# for populating ruby objects from AMF and extracting properties from ruby objects
|
22
|
+
# for serialization. Support for hash-like objects and objects using
|
23
|
+
# <tt>attr_accessor</tt> for properties is currently built in, but custom classes
|
24
|
+
# may need custom support. As such, it is possible to create a custom populator
|
25
|
+
# or serializer.
|
26
|
+
#
|
27
|
+
# Populators are processed in insert order and must respond to the <tt>can_handle?</tt>
|
28
|
+
# and <tt>populate</tt> methods.
|
29
|
+
#
|
30
|
+
# class CustomPopulator
|
31
|
+
# def can_handle? obj
|
32
|
+
# true
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# def populate obj, props, dynamic_props
|
36
|
+
# obj.merge! props
|
37
|
+
# obj.merge!(dynamic_props) if dynamic_props
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
# RocketAMF::ClassMapper.object_populators << CustomPopulator.new
|
41
|
+
#
|
42
|
+
#
|
43
|
+
# Serializers are also processed in insert order and must respond to the
|
44
|
+
# <tt>can_handle?</tt> and <tt>serialize</tt> methods.
|
45
|
+
#
|
46
|
+
# class CustomSerializer
|
47
|
+
# def can_handle? obj
|
48
|
+
# true
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# def serialize obj
|
52
|
+
# {}
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
# RocketAMF::ClassMapper.object_serializers << CustomSerializer.new
|
56
|
+
#
|
57
|
+
# == Complete Replacement
|
58
|
+
#
|
59
|
+
# In some cases, it may be beneficial to replace the default provider of class
|
60
|
+
# mapping completely. In this case, simply assign an instance of your own class
|
61
|
+
# mapper to <tt>RocketAMF::ClassMapper</tt> after loading RocketAMF. Through
|
62
|
+
# the magic of <tt>const_missing</tt>, <tt>ClassMapper</tt> is only defined after
|
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>.
|
66
|
+
#
|
67
|
+
# Example:
|
68
|
+
#
|
69
|
+
# require 'rubygems'
|
70
|
+
# require 'rocketamf'
|
71
|
+
#
|
72
|
+
# RocketAMF::ClassMapper = MyCustomClassMapper.new
|
73
|
+
# # No warning about already initialized constant ClassMapper
|
74
|
+
# RocketAMF::ClassMapper.class # MyCustomClassMapper
|
75
|
+
class ClassMapping
|
76
|
+
# Container for all mapped classes
|
77
|
+
class MappingSet
|
78
|
+
def initialize #:nodoc:
|
79
|
+
@as_mappings = {}
|
80
|
+
@ruby_mappings = {}
|
81
|
+
|
82
|
+
# Map defaults
|
83
|
+
map :as => 'flex.messaging.messages.AbstractMessage', :ruby => 'RocketAMF::Values::AbstractMessage'
|
84
|
+
map :as => 'flex.messaging.messages.RemotingMessage', :ruby => 'RocketAMF::Values::RemotingMessage'
|
85
|
+
map :as => 'flex.messaging.messages.AsyncMessage', :ruby => 'RocketAMF::Values::AsyncMessage'
|
86
|
+
map :as => 'flex.messaging.messages.CommandMessage', :ruby => 'RocketAMF::Values::CommandMessage'
|
87
|
+
map :as => 'flex.messaging.messages.AcknowledgeMessage', :ruby => 'RocketAMF::Values::AcknowledgeMessage'
|
88
|
+
map :as => 'flex.messaging.messages.ErrorMessage', :ruby => 'RocketAMF::Values::ErrorMessage'
|
89
|
+
map :as => 'flex.messaging.io.ArrayCollection', :ruby => 'RocketAMF::Values::ArrayCollection'
|
90
|
+
end
|
91
|
+
|
92
|
+
# Map a given AS class to a ruby class.
|
93
|
+
#
|
94
|
+
# Use fully qualified names for both.
|
95
|
+
#
|
96
|
+
# Example:
|
97
|
+
#
|
98
|
+
# m.map :as => 'com.example.Date', :ruby => 'Example::Date'
|
99
|
+
def map params
|
100
|
+
[:as, :ruby].each {|k| params[k] = params[k].to_s} # Convert params to strings
|
101
|
+
@as_mappings[params[:as]] = params[:ruby]
|
102
|
+
@ruby_mappings[params[:ruby]] = params[:as]
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns the AS class name for the given ruby class name, returing nil if
|
106
|
+
# not found
|
107
|
+
def get_as_class_name class_name #:nodoc:
|
108
|
+
@ruby_mappings[class_name.to_s]
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns the ruby class name for the given AS class name, returing nil if
|
112
|
+
# not found
|
113
|
+
def get_ruby_class_name class_name #:nodoc:
|
114
|
+
@as_mappings[class_name.to_s]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Array of custom object populators.
|
119
|
+
attr_reader :object_populators
|
120
|
+
|
121
|
+
# Array of custom object serializers.
|
122
|
+
attr_reader :object_serializers
|
123
|
+
|
124
|
+
def initialize #:nodoc:
|
125
|
+
@object_populators = []
|
126
|
+
@object_serializers = []
|
127
|
+
end
|
128
|
+
|
129
|
+
# Define class mappings in the block. Block is passed a MappingSet object as
|
130
|
+
# the first parameter.
|
131
|
+
#
|
132
|
+
# Example:
|
133
|
+
#
|
134
|
+
# RocketAMF::ClassMapper.define do |m|
|
135
|
+
# m.map :as => 'AsClass', :ruby => 'RubyClass'
|
136
|
+
# end
|
137
|
+
def define #:yields: mapping_set
|
138
|
+
yield mappings
|
139
|
+
end
|
140
|
+
|
141
|
+
# Reset all class mappings except the defaults
|
142
|
+
def reset
|
143
|
+
@mappings = nil
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns the AS class name for the given ruby object. Will also take a string
|
147
|
+
# containing the ruby class name.
|
148
|
+
def get_as_class_name obj
|
149
|
+
# Get class name
|
150
|
+
if obj.is_a?(String)
|
151
|
+
ruby_class_name = obj
|
152
|
+
elsif obj.is_a?(Values::TypedHash)
|
153
|
+
ruby_class_name = obj.type
|
154
|
+
else
|
155
|
+
ruby_class_name = obj.class.name
|
156
|
+
end
|
157
|
+
|
158
|
+
# Get mapped AS class name
|
159
|
+
mappings.get_as_class_name ruby_class_name
|
160
|
+
end
|
161
|
+
|
162
|
+
# Instantiates a ruby object using the mapping configuration based on the
|
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.
|
165
|
+
def get_ruby_obj as_class_name
|
166
|
+
ruby_class_name = mappings.get_ruby_class_name as_class_name
|
167
|
+
if ruby_class_name.nil?
|
168
|
+
# Populate a simple hash, since no mapping
|
169
|
+
return Values::TypedHash.new(as_class_name)
|
170
|
+
else
|
171
|
+
ruby_class = ruby_class_name.split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)}
|
172
|
+
return ruby_class.new
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Populates the ruby object using the given properties
|
177
|
+
def populate_ruby_obj obj, props, dynamic_props=nil
|
178
|
+
# Process custom populators
|
179
|
+
@object_populators.each do |p|
|
180
|
+
next unless p.can_handle?(obj)
|
181
|
+
p.populate obj, props, dynamic_props
|
182
|
+
return obj
|
183
|
+
end
|
184
|
+
|
185
|
+
# Fallback populator
|
186
|
+
props.merge! dynamic_props if dynamic_props
|
187
|
+
hash_like = obj.respond_to?("[]=")
|
188
|
+
props.each do |key, value|
|
189
|
+
if obj.respond_to?("#{key}=")
|
190
|
+
obj.send("#{key}=", value)
|
191
|
+
elsif hash_like
|
192
|
+
obj[key.to_sym] = value
|
193
|
+
end
|
194
|
+
end
|
195
|
+
obj
|
196
|
+
end
|
197
|
+
|
198
|
+
# Extracts all exportable properties from the given ruby object and returns
|
199
|
+
# them in a hash
|
200
|
+
def props_for_serialization ruby_obj
|
201
|
+
# Proccess custom serializers
|
202
|
+
@object_serializers.each do |s|
|
203
|
+
next unless s.can_handle?(ruby_obj)
|
204
|
+
return s.serialize(ruby_obj)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Handle hashes
|
208
|
+
if ruby_obj.is_a?(Hash)
|
209
|
+
# Stringify keys to make it easier later on and allow sorting
|
210
|
+
h = {}
|
211
|
+
ruby_obj.each {|k,v| h[k.to_s] = v}
|
212
|
+
return h
|
213
|
+
end
|
214
|
+
|
215
|
+
# Fallback serializer
|
216
|
+
props = {}
|
217
|
+
@ignored_props ||= Object.new.public_methods
|
218
|
+
(ruby_obj.public_methods - @ignored_props).each do |method_name|
|
219
|
+
# Add them to the prop hash if they take no arguments
|
220
|
+
method_def = ruby_obj.method(method_name)
|
221
|
+
props[method_name.to_s] = ruby_obj.send(method_name) if method_def.arity == 0
|
222
|
+
end
|
223
|
+
props
|
224
|
+
end
|
225
|
+
|
226
|
+
private
|
227
|
+
def mappings
|
228
|
+
@mappings ||= MappingSet.new
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|