RocketAMF-ouvrages 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/README.rdoc +47 -0
  2. data/Rakefile +59 -0
  3. data/RocketAMF.gemspec +20 -0
  4. data/benchmark.rb +74 -0
  5. data/ext/rocketamf_ext/class_mapping.c +484 -0
  6. data/ext/rocketamf_ext/constants.h +52 -0
  7. data/ext/rocketamf_ext/deserializer.c +770 -0
  8. data/ext/rocketamf_ext/deserializer.h +27 -0
  9. data/ext/rocketamf_ext/extconf.rb +18 -0
  10. data/ext/rocketamf_ext/remoting.c +184 -0
  11. data/ext/rocketamf_ext/rocketamf_ext.c +38 -0
  12. data/ext/rocketamf_ext/serializer.c +834 -0
  13. data/ext/rocketamf_ext/serializer.h +29 -0
  14. data/ext/rocketamf_ext/utility.h +4 -0
  15. data/lib/rocketamf.rb +216 -0
  16. data/lib/rocketamf/class_mapping.rb +237 -0
  17. data/lib/rocketamf/constants.rb +50 -0
  18. data/lib/rocketamf/ext.rb +28 -0
  19. data/lib/rocketamf/extensions.rb +22 -0
  20. data/lib/rocketamf/pure.rb +24 -0
  21. data/lib/rocketamf/pure/deserializer.rb +455 -0
  22. data/lib/rocketamf/pure/io_helpers.rb +94 -0
  23. data/lib/rocketamf/pure/remoting.rb +117 -0
  24. data/lib/rocketamf/pure/serializer.rb +474 -0
  25. data/lib/rocketamf/remoting.rb +196 -0
  26. data/lib/rocketamf/values/messages.rb +214 -0
  27. data/lib/rocketamf/values/typed_hash.rb +13 -0
  28. data/spec/class_mapping_spec.rb +110 -0
  29. data/spec/deserializer_spec.rb +449 -0
  30. data/spec/fast_class_mapping_spec.rb +144 -0
  31. data/spec/fixtures/objects/amf0-boolean.bin +1 -0
  32. data/spec/fixtures/objects/amf0-complex-encoded-string.bin +0 -0
  33. data/spec/fixtures/objects/amf0-date.bin +0 -0
  34. data/spec/fixtures/objects/amf0-ecma-ordinal-array.bin +0 -0
  35. data/spec/fixtures/objects/amf0-hash.bin +0 -0
  36. data/spec/fixtures/objects/amf0-null.bin +1 -0
  37. data/spec/fixtures/objects/amf0-number.bin +0 -0
  38. data/spec/fixtures/objects/amf0-object.bin +0 -0
  39. data/spec/fixtures/objects/amf0-ref-test.bin +0 -0
  40. data/spec/fixtures/objects/amf0-strict-array.bin +0 -0
  41. data/spec/fixtures/objects/amf0-string.bin +0 -0
  42. data/spec/fixtures/objects/amf0-time.bin +0 -0
  43. data/spec/fixtures/objects/amf0-typed-object.bin +0 -0
  44. data/spec/fixtures/objects/amf0-undefined.bin +1 -0
  45. data/spec/fixtures/objects/amf0-untyped-object.bin +0 -0
  46. data/spec/fixtures/objects/amf0-xml-doc.bin +0 -0
  47. data/spec/fixtures/objects/amf3-0.bin +0 -0
  48. data/spec/fixtures/objects/amf3-array-collection.bin +2 -0
  49. data/spec/fixtures/objects/amf3-array-ref.bin +1 -0
  50. data/spec/fixtures/objects/amf3-associative-array.bin +1 -0
  51. data/spec/fixtures/objects/amf3-bigNum.bin +0 -0
  52. data/spec/fixtures/objects/amf3-byte-array-ref.bin +1 -0
  53. data/spec/fixtures/objects/amf3-byte-array.bin +0 -0
  54. data/spec/fixtures/objects/amf3-complex-array-collection.bin +6 -0
  55. data/spec/fixtures/objects/amf3-complex-encoded-string-array.bin +1 -0
  56. data/spec/fixtures/objects/amf3-date-ref.bin +0 -0
  57. data/spec/fixtures/objects/amf3-date.bin +0 -0
  58. data/spec/fixtures/objects/amf3-dictionary.bin +0 -0
  59. data/spec/fixtures/objects/amf3-dynamic-object.bin +2 -0
  60. data/spec/fixtures/objects/amf3-empty-array-ref.bin +1 -0
  61. data/spec/fixtures/objects/amf3-empty-array.bin +1 -0
  62. data/spec/fixtures/objects/amf3-empty-dictionary.bin +0 -0
  63. data/spec/fixtures/objects/amf3-empty-string-ref.bin +1 -0
  64. data/spec/fixtures/objects/amf3-encoded-string-ref.bin +0 -0
  65. data/spec/fixtures/objects/amf3-externalizable.bin +0 -0
  66. data/spec/fixtures/objects/amf3-false.bin +1 -0
  67. data/spec/fixtures/objects/amf3-float.bin +0 -0
  68. data/spec/fixtures/objects/amf3-graph-member.bin +0 -0
  69. data/spec/fixtures/objects/amf3-hash.bin +2 -0
  70. data/spec/fixtures/objects/amf3-large-max.bin +0 -0
  71. data/spec/fixtures/objects/amf3-large-min.bin +0 -0
  72. data/spec/fixtures/objects/amf3-max.bin +1 -0
  73. data/spec/fixtures/objects/amf3-min.bin +0 -0
  74. data/spec/fixtures/objects/amf3-mixed-array.bin +10 -0
  75. data/spec/fixtures/objects/amf3-null.bin +1 -0
  76. data/spec/fixtures/objects/amf3-object-ref.bin +0 -0
  77. data/spec/fixtures/objects/amf3-primitive-array.bin +1 -0
  78. data/spec/fixtures/objects/amf3-string-ref.bin +0 -0
  79. data/spec/fixtures/objects/amf3-string.bin +1 -0
  80. data/spec/fixtures/objects/amf3-symbol.bin +1 -0
  81. data/spec/fixtures/objects/amf3-trait-ref.bin +3 -0
  82. data/spec/fixtures/objects/amf3-true.bin +1 -0
  83. data/spec/fixtures/objects/amf3-typed-object.bin +2 -0
  84. data/spec/fixtures/objects/amf3-vector-double.bin +0 -0
  85. data/spec/fixtures/objects/amf3-vector-int.bin +0 -0
  86. data/spec/fixtures/objects/amf3-vector-object.bin +0 -0
  87. data/spec/fixtures/objects/amf3-vector-uint.bin +0 -0
  88. data/spec/fixtures/objects/amf3-xml-doc.bin +1 -0
  89. data/spec/fixtures/objects/amf3-xml-ref.bin +1 -0
  90. data/spec/fixtures/objects/amf3-xml.bin +1 -0
  91. data/spec/fixtures/request/acknowledge-response.bin +0 -0
  92. data/spec/fixtures/request/amf0-error-response.bin +0 -0
  93. data/spec/fixtures/request/blaze-response.bin +0 -0
  94. data/spec/fixtures/request/commandMessage.bin +0 -0
  95. data/spec/fixtures/request/flex-request.bin +0 -0
  96. data/spec/fixtures/request/multiple-simple-request.bin +0 -0
  97. data/spec/fixtures/request/remotingMessage.bin +0 -0
  98. data/spec/fixtures/request/simple-request.bin +0 -0
  99. data/spec/fixtures/request/simple-response.bin +0 -0
  100. data/spec/fixtures/request/unsupportedCommandMessage.bin +0 -0
  101. data/spec/messages_spec.rb +39 -0
  102. data/spec/remoting_spec.rb +196 -0
  103. data/spec/serializer_spec.rb +503 -0
  104. data/spec/spec_helper.rb +55 -0
  105. metadata +167 -0
@@ -0,0 +1,29 @@
1
+ #include <ruby.h>
2
+ #ifdef HAVE_RB_STR_ENCODE
3
+ #include <ruby/st.h>
4
+ #include <ruby/encoding.h>
5
+ #else
6
+ #include <st.h>
7
+ #endif
8
+
9
+ typedef struct {
10
+ int version;
11
+ VALUE class_mapper;
12
+ VALUE stream;
13
+ long depth;
14
+ st_table* str_cache;
15
+ long str_index;
16
+ st_table* trait_cache;
17
+ long trait_index;
18
+ st_table* obj_cache;
19
+ long obj_index;
20
+ } AMF_SERIALIZER;
21
+
22
+ void ser_write_byte(AMF_SERIALIZER *ser, char byte);
23
+ void ser_write_int(AMF_SERIALIZER *ser, int num);
24
+ void ser_write_uint16(AMF_SERIALIZER *ser, long num);
25
+ void ser_write_uint32(AMF_SERIALIZER *ser, long num);
26
+ void ser_write_double(AMF_SERIALIZER *ser, double num);
27
+ void ser_get_string(VALUE obj, VALUE encode, char** str, long* len);
28
+
29
+ VALUE ser_serialize(VALUE self, VALUE ver, VALUE obj);
@@ -0,0 +1,4 @@
1
+ // Before RFLOAT_VALUE, value was in a different place in the struct
2
+ #ifndef RFLOAT_VALUE
3
+ #define RFLOAT_VALUE(v) (RFLOAT(v)->value)
4
+ #endif
@@ -0,0 +1,216 @@
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 "date"
5
+ require "stringio"
6
+ require 'rocketamf/extensions'
7
+ require 'rocketamf/class_mapping'
8
+ require 'rocketamf/constants'
9
+ require 'rocketamf/remoting'
10
+
11
+ # RocketAMF is a full featured AMF0/3 serializer and deserializer with support for
12
+ # bi-directional Flash to Ruby class mapping, custom serialization and mapping,
13
+ # remoting gateway helpers that follow AMF0/3 messaging specs, and a suite of specs
14
+ # to ensure adherence to the specification documents put out by Adobe. If the C
15
+ # components compile, then RocketAMF automatically takes advantage of them to
16
+ # provide a substantial performance benefit. In addition, RocketAMF is fully
17
+ # compatible with Ruby 1.9.
18
+ #
19
+ # == Performance
20
+ #
21
+ # RocketAMF provides native C extensions for serialization, deserialization,
22
+ # remoting, and class mapping. If your environment supports them, RocketAMF will
23
+ # automatically take advantage of the C serializer, deserializer, and remoting
24
+ # support. The C class mapper has some substantial performance optimizations that
25
+ # make it incompatible with the pure Ruby class mapper, and so it must be manually
26
+ # enabled. For more information see <tt>RocketAMF::ClassMapping</tt>. Below are
27
+ # some benchmarks I took using using a simple little benchmarking utility I whipped
28
+ # up, which can be found in the root of the repository.
29
+ #
30
+ # # 100000 objects
31
+ # # Ruby 1.8
32
+ # Testing native AMF0:
33
+ # minimum serialize time: 1.229868s
34
+ # minimum deserialize time: 0.86465s
35
+ # Testing native AMF3:
36
+ # minimum serialize time: 1.444652s
37
+ # minimum deserialize time: 0.879407s
38
+ # Testing pure AMF0:
39
+ # minimum serialize time: 25.427931s
40
+ # minimum deserialize time: 11.706084s
41
+ # Testing pure AMF3:
42
+ # minimum serialize time: 31.637864s
43
+ # minimum deserialize time: 14.773969s
44
+ #
45
+ # == Serialization & Deserialization
46
+ #
47
+ # RocketAMF provides two main methods - <tt>serialize</tt> and <tt>deserialize</tt>.
48
+ # Deserialization takes a String or StringIO object and the AMF version if different
49
+ # from the default. Serialization takes any Ruby object and the version if different
50
+ # from the default. Both default to AMF0, as it's more widely supported and slightly
51
+ # faster, but AMF3 does a better job of not sending duplicate data. Which you choose
52
+ # depends on what you need to communicate with and how much serialized size matters.
53
+ #
54
+ # == Mapping Classes Between Flash and Ruby
55
+ #
56
+ # RocketAMF provides a simple class mapping tool to facilitate serialization and
57
+ # deserialization of typed objects. Refer to the documentation of
58
+ # <tt>RocketAMF::ClassMapping</tt> for more details. If the provided class
59
+ # mapping tool is not sufficient for your needs, you also have the option to
60
+ # replace it with a class mapper of your own devising that matches the documented
61
+ # API.
62
+ #
63
+ # == Remoting
64
+ #
65
+ # You can use RocketAMF bare to write an AMF gateway using the following code.
66
+ # In addition, you can use rack-amf (http://github.com/rubyamf/rack-amf) or
67
+ # RubyAMF (http://github.com/rubyamf/rubyamf), both of which provide rack-compliant
68
+ # AMF gateways.
69
+ #
70
+ # # helloworld.ru
71
+ # require 'rocketamf'
72
+ #
73
+ # class HelloWorldApp
74
+ # APPLICATION_AMF = 'application/x-amf'.freeze
75
+ #
76
+ # def call env
77
+ # if is_amf?(env)
78
+ # # Wrap request and response
79
+ # env['rack.input'].rewind
80
+ # request = RocketAMF::Envelope.new.populate_from_stream(env['rack.input'].read)
81
+ # response = RocketAMF::Envelope.new
82
+ #
83
+ # # Handle request
84
+ # response.each_method_call request do |method, args|
85
+ # raise "Service #{method} does not exists" unless method == 'App.helloWorld'
86
+ # 'Hello world'
87
+ # end
88
+ #
89
+ # # Pass back response
90
+ # response_str = response.serialize
91
+ # return [200, {'Content-Type' => APPLICATION_AMF, 'Content-Length' => response_str.length.to_s}, [response_str]]
92
+ # else
93
+ # return [200, {'Content-Type' => 'text/plain', 'Content-Length' => '16' }, ["Rack AMF gateway"]]
94
+ # end
95
+ # end
96
+ #
97
+ # private
98
+ # def is_amf? env
99
+ # return false unless env['CONTENT_TYPE'] == APPLICATION_AMF
100
+ # return false unless env['PATH_INFO'] == '/amf'
101
+ # return true
102
+ # end
103
+ # end
104
+ #
105
+ # run HelloWorldApp.new
106
+ #
107
+ # == Advanced Serialization (encode_amf and IExternalizable)
108
+ #
109
+ # RocketAMF provides some additional functionality to support advanced
110
+ # serialization techniques. If you define an <tt>encode_amf</tt> method on your
111
+ # object, it will get called during serialization. It is passed a single argument,
112
+ # the serializer, and it can use the serializer stream, the <tt>serialize</tt>
113
+ # method, the <tt>write_array</tt> method, the <tt>write_object</tt> method, and
114
+ # the serializer version. Below is a simple example that uses <tt>write_object</tt>
115
+ # to customize the property hash that is used for serialization.
116
+ #
117
+ # Example:
118
+ #
119
+ # class TestObject
120
+ # def encode_amf ser
121
+ # ser.write_object self, @attributes
122
+ # end
123
+ # end
124
+ #
125
+ # If you plan on using the <tt>serialize</tt> method, make sure to pass in the
126
+ # current serializer version, or you could create a message that cannot be deserialized.
127
+ #
128
+ # Example:
129
+ #
130
+ # class VariableObject
131
+ # def encode_amf ser
132
+ # if ser.version == 0
133
+ # ser.serialize 0, true
134
+ # else
135
+ # ser.serialize 3, false
136
+ # end
137
+ # end
138
+ # end
139
+ #
140
+ # If you wish to send and receive IExternalizable objects, you will need to
141
+ # implement <tt>encode_amf</tt>, <tt>read_external</tt>, and <tt>write_external</tt>.
142
+ # Below is an example of a ResultSet class that extends Array and serializes as
143
+ # an array collection. RocketAMF can automatically serialize arrays as
144
+ # ArrayCollection objects, so this is just an example of how you might implement
145
+ # an object that conforms to IExternalizable.
146
+ #
147
+ # Example:
148
+ #
149
+ # class ResultSet < Array
150
+ # def encode_amf ser
151
+ # if ser.version == 0
152
+ # # Serialize as simple array in AMF0
153
+ # ser.write_array self
154
+ # else
155
+ # # Serialize as an ArrayCollection object
156
+ # # It conforms to IExternalizable, does not have any dynamic properties,
157
+ # # and has no "sealed" members. See the AMF3 specs for more details about
158
+ # # object traits.
159
+ # ser.write_object self, nil, {
160
+ # :class_name => "flex.messaging.io.ArrayCollection",
161
+ # :externalizable => true,
162
+ # :dynamic => false,
163
+ # :members => []
164
+ # }
165
+ # end
166
+ # end
167
+ #
168
+ # # Write self as array to stream
169
+ # def write_external ser
170
+ # ser.write_array(self)
171
+ # end
172
+ #
173
+ # # Read array out and replace data with deserialized array.
174
+ # def read_external des
175
+ # replace(des.read_object)
176
+ # end
177
+ # end
178
+ module RocketAMF
179
+ begin
180
+ require 'rocketamf/ext'
181
+ rescue LoadError
182
+ require 'rocketamf/pure'
183
+ end
184
+
185
+ # Deserialize the AMF string _source_ of the given AMF version into a Ruby
186
+ # data structure and return it. Creates an instance of <tt>RocketAMF::Deserializer</tt>
187
+ # with a new instance of <tt>RocketAMF::ClassMapper</tt> and calls deserialize
188
+ # on it with the given source and amf version, returning the result.
189
+ def self.deserialize source, amf_version = 0
190
+ des = RocketAMF::Deserializer.new(RocketAMF::ClassMapper.new)
191
+ des.deserialize(amf_version, source)
192
+ end
193
+
194
+ # Serialize the given Ruby data structure _obj_ into an AMF stream using the
195
+ # given AMF version. Creates an instance of <tt>RocketAMF::Serializer</tt>
196
+ # with a new instance of <tt>RocketAMF::ClassMapper</tt> and calls serialize
197
+ # on it with the given object and amf version, returning the result.
198
+ def self.serialize obj, amf_version = 0
199
+ ser = RocketAMF::Serializer.new(RocketAMF::ClassMapper.new)
200
+ ser.serialize(amf_version, obj)
201
+ end
202
+
203
+ # We use const_missing to define the active ClassMapper at runtime. This way,
204
+ # heavy modification of class mapping functionality is still possible without
205
+ # forcing extenders to redefine the constant.
206
+ def self.const_missing const #:nodoc:
207
+ if const == :ClassMapper
208
+ RocketAMF.const_set(:ClassMapper, RocketAMF::ClassMapping)
209
+ else
210
+ super(const)
211
+ end
212
+ end
213
+
214
+ # The base exception for AMF errors.
215
+ class AMFError < StandardError; end
216
+ end
@@ -0,0 +1,237 @@
1
+ require 'rocketamf/values/typed_hash'
2
+ require 'rocketamf/values/messages'
3
+
4
+ module RocketAMF
5
+ # Container for all mapped classes
6
+ class MappingSet
7
+ # Creates a mapping set object and populates the default mappings
8
+ def initialize
9
+ @as_mappings = {}
10
+ @ruby_mappings = {}
11
+ map_defaults
12
+ end
13
+
14
+ # Adds required mapping configs, calling map for the required base mappings.
15
+ # Designed to allow extenders to take advantage of required default mappings.
16
+ def map_defaults
17
+ map :as => 'flex.messaging.messages.AbstractMessage', :ruby => 'RocketAMF::Values::AbstractMessage'
18
+ map :as => 'flex.messaging.messages.RemotingMessage', :ruby => 'RocketAMF::Values::RemotingMessage'
19
+ map :as => 'flex.messaging.messages.AsyncMessage', :ruby => 'RocketAMF::Values::AsyncMessage'
20
+ map :as => 'DSA', :ruby => 'RocketAMF::Values::AsyncMessageExt'
21
+ map :as => 'flex.messaging.messages.CommandMessage', :ruby => 'RocketAMF::Values::CommandMessage'
22
+ map :as => 'DSC', :ruby => 'RocketAMF::Values::CommandMessageExt'
23
+ map :as => 'flex.messaging.messages.AcknowledgeMessage', :ruby => 'RocketAMF::Values::AcknowledgeMessage'
24
+ map :as => 'DSK', :ruby => 'RocketAMF::Values::AcknowledgeMessageExt'
25
+ map :as => 'flex.messaging.messages.ErrorMessage', :ruby => 'RocketAMF::Values::ErrorMessage'
26
+ self
27
+ end
28
+
29
+ # Map a given AS class to a ruby class.
30
+ #
31
+ # Use fully qualified names for both.
32
+ #
33
+ # Example:
34
+ #
35
+ # m.map :as => 'com.example.Date', :ruby => 'Example::Date'
36
+ def map params
37
+ [:as, :ruby].each {|k| params[k] = params[k].to_s} # Convert params to strings
38
+ @as_mappings[params[:as]] = params[:ruby]
39
+ @ruby_mappings[params[:ruby]] = params[:as]
40
+ end
41
+
42
+ # Returns the AS class name for the given ruby class name, returing nil if
43
+ # not found
44
+ def get_as_class_name class_name #:nodoc:
45
+ @ruby_mappings[class_name.to_s]
46
+ end
47
+
48
+ # Returns the ruby class name for the given AS class name, returing nil if
49
+ # not found
50
+ def get_ruby_class_name class_name #:nodoc:
51
+ @as_mappings[class_name.to_s]
52
+ end
53
+ end
54
+
55
+ # Handles class name mapping between actionscript and ruby and assists in
56
+ # serializing and deserializing data between them. Simply map an AS class to a
57
+ # ruby class and when the object is (de)serialized it will end up as the
58
+ # appropriate class.
59
+ #
60
+ # Example:
61
+ #
62
+ # RocketAMF::ClassMapper.define do |m|
63
+ # m.map :as => 'AsClass', :ruby => 'RubyClass'
64
+ # m.map :as => 'vo.User', :ruby => 'Model::User'
65
+ # end
66
+ #
67
+ # == Object Population/Serialization
68
+ #
69
+ # In addition to handling class name mapping, it also provides helper methods
70
+ # for populating ruby objects from AMF and extracting properties from ruby objects
71
+ # for serialization. Support for hash-like objects and objects using
72
+ # <tt>attr_accessor</tt> for properties is currently built in, but custom classes
73
+ # may require subclassing the class mapper to add support.
74
+ #
75
+ # == Complete Replacement
76
+ #
77
+ # In some cases, it may be beneficial to replace the default provider of class
78
+ # mapping completely. In this case, simply assign your class mapper class to
79
+ # <tt>RocketAMF::ClassMapper</tt> after loading RocketAMF. Through the magic of
80
+ # <tt>const_missing</tt>, <tt>ClassMapper</tt> is only defined after the first
81
+ # access by default, so you get no annoying warning messages. Custom class mappers
82
+ # must implement the following methods on instances: <tt>use_array_collection</tt>,
83
+ # <tt>get_as_class_name</tt>, <tt>get_ruby_obj</tt>, <tt>populate_ruby_obj</tt>,
84
+ # and <tt>props_for_serialization</tt>. In addition, it should have a class level
85
+ # <tt>mappings</tt> method that returns the mapping set it's using, although its
86
+ # not required. If you'd like to see an example of what complete replacement
87
+ # offers, check out RubyAMF (http://github.com/rubyamf/rubyamf).
88
+ #
89
+ # Example:
90
+ #
91
+ # require 'rubygems'
92
+ # require 'rocketamf'
93
+ #
94
+ # RocketAMF::ClassMapper = MyCustomClassMapper
95
+ # # No warning about already initialized constant ClassMapper
96
+ # RocketAMF::ClassMapper # MyCustomClassMapper
97
+ #
98
+ # == C ClassMapper
99
+ #
100
+ # The C class mapper, <tt>RocketAMF::Ext::FastClassMapping</tt>, has the same
101
+ # public API that <tt>RubyAMF::ClassMapping</tt> does, but has some additional
102
+ # performance optimizations that may interfere with the proper serialization of
103
+ # objects. To reduce the cost of processing public methods for every object,
104
+ # its implementation of <tt>props_for_serialization</tt> caches valid properties
105
+ # by class, using the class as the hash key for property lookup. This means that
106
+ # adding and removing properties from instances while serializing using a given
107
+ # class mapper instance will result in the changes not being detected. As such,
108
+ # it's not enabled by default. So long as you aren't planning on modifying
109
+ # classes during serialization using <tt>encode_amf</tt>, the faster C class
110
+ # mapper should be perfectly safe to use.
111
+ #
112
+ # Activating the C Class Mapper:
113
+ #
114
+ # require 'rubygems'
115
+ # require 'rocketamf'
116
+ # RocketAMF::ClassMapper = RocketAMF::Ext::FastClassMapping
117
+ class ClassMapping
118
+ class << self
119
+ # Global configuration variable for sending Arrays as ArrayCollections.
120
+ # Defaults to false.
121
+ attr_accessor :use_array_collection
122
+
123
+ # Returns the mapping set with all the class mappings that is currently
124
+ # being used.
125
+ def mappings
126
+ @mappings ||= MappingSet.new
127
+ end
128
+
129
+ # Define class mappings in the block. Block is passed a <tt>MappingSet</tt> object
130
+ # as 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 &block #:yields: mapping_set
138
+ yield mappings
139
+ end
140
+
141
+ # Reset all class mappings except the defaults and return
142
+ # <tt>use_array_collection</tt> to false
143
+ def reset
144
+ @use_array_collection = false
145
+ @mappings = nil
146
+ end
147
+ end
148
+
149
+ attr_reader :use_array_collection
150
+
151
+ # Copies configuration from class level configs to populate object
152
+ def initialize
153
+ @mappings = self.class.mappings
154
+ @use_array_collection = self.class.use_array_collection === true
155
+ end
156
+
157
+ # Returns the ActionScript class name for the given ruby object. Will also
158
+ # take a string containing the ruby class name.
159
+ def get_as_class_name obj
160
+ # Get class name
161
+ if obj.is_a?(String)
162
+ ruby_class_name = obj
163
+ elsif obj.is_a?(Values::TypedHash)
164
+ ruby_class_name = obj.type
165
+ elsif obj.is_a?(Hash)
166
+ return nil
167
+ else
168
+ ruby_class_name = obj.class.name
169
+ end
170
+
171
+ # Get mapped AS class name
172
+ @mappings.get_as_class_name ruby_class_name
173
+ end
174
+
175
+ # Instantiates a ruby object using the mapping configuration based on the
176
+ # source ActionScript class name. If there is no mapping defined, it returns
177
+ # a <tt>RocketAMF::Values::TypedHash</tt> with the serialized class name.
178
+ def get_ruby_obj as_class_name
179
+ ruby_class_name = @mappings.get_ruby_class_name as_class_name
180
+ if ruby_class_name.nil?
181
+ # Populate a simple hash, since no mapping
182
+ return Values::TypedHash.new(as_class_name)
183
+ else
184
+ ruby_class = ruby_class_name.split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)}
185
+ return ruby_class.new
186
+ end
187
+ end
188
+
189
+ # Populates the ruby object using the given properties. props and
190
+ # dynamic_props will be hashes with symbols for keys.
191
+ def populate_ruby_obj obj, props, dynamic_props=nil
192
+ props.merge! dynamic_props if dynamic_props
193
+
194
+ # Don't even bother checking if it responds to setter methods if it's a TypedHash
195
+ if obj.is_a?(Values::TypedHash)
196
+ obj.merge! props
197
+ return obj
198
+ end
199
+
200
+ # Some type of object
201
+ hash_like = obj.respond_to?("[]=")
202
+ props.each do |key, value|
203
+ if obj.respond_to?("#{key}=")
204
+ obj.send("#{key}=", value)
205
+ elsif hash_like
206
+ obj[key] = value
207
+ end
208
+ end
209
+ obj
210
+ end
211
+
212
+ # Extracts all exportable properties from the given ruby object and returns
213
+ # them in a hash. If overriding, make sure to return a hash wth string keys
214
+ # unless you are only going to be using the native C extensions, as the pure
215
+ # ruby serializer performs a sort on the keys to acheive consistent, testable
216
+ # results.
217
+ def props_for_serialization ruby_obj
218
+ # Handle hashes
219
+ if ruby_obj.is_a?(Hash)
220
+ # Stringify keys to make it easier later on and allow sorting
221
+ h = {}
222
+ ruby_obj.each {|k,v| h[k.to_s] = v}
223
+ return h
224
+ end
225
+
226
+ # Generic object serializer
227
+ props = {}
228
+ @ignored_props ||= Object.new.public_methods
229
+ (ruby_obj.public_methods - @ignored_props).each do |method_name|
230
+ # Add them to the prop hash if they take no arguments
231
+ method_def = ruby_obj.method(method_name)
232
+ props[method_name.to_s] = ruby_obj.send(method_name) if method_def.arity == 0
233
+ end
234
+ props
235
+ end
236
+ end
237
+ end