require 'rocketamf/mapping/mapping_set' module RocketAMF # Handles class name mapping between AS and ruby and assists in # serializing and deserializing data between them. Simply map an AS class to a # ruby class and when the object is (de)serialized it will end up as the # appropriate class. # # Example: # # RocketAMF::CLASS_MAPPER.define do |m| # m.map as: 'AsClass', ruby: 'RubyClass' # m.map as: 'vo.User', ruby: 'Model::User' # end # # == Object Population/Serialization # # In addition to handling class name mapping, it also provides helper methods # for populating ruby objects from AMF and extracting properties from ruby objects # for serialization. Support for hash-like objects and objects using # attr_accessor for properties is currently built in, but custom classes # may require subclassing the class mapper to add support. # # == Complete Replacement # # In some cases, it may be beneficial to replace the default provider of class # mapping completely. In this case, simply assign your class mapper class to # RocketAMF::CLASS_MAPPER after loading RocketAMF. Through the magic of # const_missing, CLASS_MAPPER is only defined after the first # access by default, so you get no annoying warning messages. Custom class mappers # must implement the following methods on instances: use_array_collection, # get_as_class_name, get_ruby_obj, populate_ruby_obj, # and props_for_serialization. In addition, it should have a class level # mappings method that returns the mapping set it's using, although its # not required. If you'd like to see an example of what complete replacement # offers, check out RubyAMF (http://github.com/rubyamf/rubyamf). # # Example: # # require 'rubygems' # require 'rocketamf' # # RocketAMF.const_set(CLASS_MAPPER, MyCustomClassMapper) # # == C ClassMapper # # The C class mapper, RocketAMF::Ext::FastClassMapping, has the same # public API that RubyAMF::ClassMapping does, but has some additional # performance optimizations that may interfere with the proper serialization of # objects. To reduce the cost of processing public methods for every object, # its implementation of props_for_serialization caches valid properties # by class, using the class as the hash key for property lookup. This means that # adding and removing properties from instances while serializing using a given # class mapper instance will result in the changes not being detected. As such, # it's not enabled by default. So long as you aren't planning on modifying # classes during serialization using encode_amf, the faster C class # mapper should be perfectly safe to use. # # Activating the C Class Mapper: # # require 'rubygems' # require 'rocketamf' #todo:review # RocketAMF::ClassMapper = RocketAMF::Ext::FastClassMapping class ClassMapping class << self # Global configuration variable for sending Arrays as ArrayCollections. # Defaults to false. attr_accessor :use_array_collection # Returns the mapping set with all the class mappings that is currently # being used. def mappings @mappings ||= MappingSet.new end # Define class mappings in the block. Block is passed a MappingSet object # as the first parameter. # # Example: # # RocketAMF::CLASS_MAPPER.define do |m| # m.map as: 'AsClass', ruby: 'RubyClass' # end def define(&block) #:yields: mapping_set yield mappings end # Reset all class mappings except the defaults and return # use_array_collection to false def reset @use_array_collection = false @mappings = nil end end # # Properties # attr_reader :use_array_collection # # Methods # # Copies configuration from class level configs to populate object public def initialize @mappings = self.class.mappings @use_array_collection = self.class.use_array_collection === true end # Returns the ActionScript class name for the given ruby object. Will also # take a string containing the ruby class name. def get_as_class_name(obj) # Get class name if obj.is_a?(String) ruby_class_name = obj elsif obj.is_a?(Types::TypedHash) ruby_class_name = obj.type elsif obj.is_a?(Hash) return nil else ruby_class_name = obj.class.name end # Get mapped AS class name @mappings.get_as_class_name(ruby_class_name) end # Instantiates a ruby object using the mapping configuration based on the # source ActionScript class name. If there is no mapping defined, it returns # a RocketAMF::Types::TypedHash with the serialized class name. public def get_ruby_obj(as_class_name) result = nil ruby_class_name = @mappings.get_ruby_class_name(as_class_name) if ruby_class_name.nil? # Populate a simple hash, since no mapping result = Types::TypedHash.new(as_class_name) else ruby_class = ruby_class_name.split('::').inject(Kernel) { |scope, const_name| scope.const_get(const_name) } result = ruby_class.new end result end # Populates the ruby object using the given properties. props will be hashes with symbols for keys. public def populate_ruby_obj(target, props) # Don't even bother checking if it responds to setter methods if it's a TypedHash if target.is_a?(Types::TypedHash) target.merge! props return target end # Some type of object hash_like = target.respond_to?("[]=") props.each do |key, value| if target.respond_to?("#{key}=") target.send("#{key}=", value) elsif hash_like target[key] = value end end target end # Extracts all exportable properties from the given ruby object and returns # them in a hash. If overriding, make sure to return a hash wth string keys # unless you are only going to be using the native C extensions, as the pure # ruby serializer performs a sort on the keys to acheive consistent, testable # results. public def props_for_serialization(ruby_obj) result = {} # Handle hashes if ruby_obj.is_a?(Hash) # Stringify keys to make it easier later on and allow sorting ruby_obj.each { |k, v| result[k.to_s] = v } else # Generic object serializer @ignored_props ||= Object.new.public_methods (ruby_obj.public_methods - @ignored_props).each do |method_name| # Add them to the prop hash if they take no arguments method_def = ruby_obj.method(method_name) result[method_name.to_s] = ruby_obj.send(method_name) if method_def.arity == 0 end end result end # props_for_serialization end #ClassMapping end #RocketAMF