Module | RubyAMF::Model |
In: |
lib/rubyamf/model.rb
|
Simply include in your ruby object to enable advanced serialization features like an in-model mapping API, customizable initialization after deserialization, scoped property configuration for serialization, and several other things. See RubyAMF::Model::ClassMethods for details of in-model mapping API.
Example:
class SerializableObject include RubyAMF::Model as_class "com.rubyamf.ASObject" map_amf :only => "prop_a" attr_accessor :prop_a, :prop_b end
If the object you include RubyAMF::Model into implements attributes and attributes=, those two methods will be automatically used to determine serializable properties and to set them after deserialization. If you do not implement those methods, attributes will be guessed by going through all methods that don‘t take arguments, and attribute setters will be used rather than attributes=.
For most ORMs, the provided rubyamf_init, rubyamf_hash, and rubyamf_retrieve_association should work correctly. However, they can be overridden to provide custom behavior if the default has issues with the ORM you are using. See RubyAMF::Rails::Model for an example of ORM-specific customization.
Like serializable_hash, rubyamf_hash returns a hash for serialization calculated from the given options. Supported options are :only, :except, :methods, and :include. This method is automatically called by RubyAMF::ClassMapping on serialization with the pre-configured options for whatever the current scope is.
# File lib/rubyamf/model.rb, line 132 132: def rubyamf_hash options=nil 133: # Process options 134: options ||= {} 135: only = Array.wrap(options[:only]).map(&:to_s) 136: except = Array.wrap(options[:except]).map(&:to_s) 137: method_names = [] 138: Array.wrap(options[:methods]).each do |name| 139: method_names << name.to_s if respond_to?(name) 140: end 141: 142: # Get list of attributes 143: if respond_to?(:attributes) 144: attrs = send(:attributes) 145: else 146: attrs = {} 147: ignored_props = Object.new.public_methods 148: (self.public_methods - ignored_props).each do |method_name| 149: # Add them to the attrs hash if they take no arguments 150: method_def = self.method(method_name) 151: attrs[method_name.to_s] = send(method_name) if method_def.arity == 0 152: end 153: end 154: attribute_names = attrs.keys.sort 155: if only.any? 156: attribute_names &= only 157: elsif except.any? 158: attribute_names -= except 159: end 160: 161: # Remove ignore_fields unless in only 162: RubyAMF.configuration.ignore_fields.each do |field| 163: attribute_names.delete(field) unless only.include?(field) 164: end 165: 166: # Build hash from attributes and methods 167: hash = {} 168: attribute_names.each {|name| hash[name] = attrs[name]} 169: method_names.each {|name| hash[name] = send(name)} 170: 171: # Add associations using ActiveRecord::Serialization style options 172: # processing 173: if include_associations = options.delete(:include) 174: # Process options 175: base_only_or_except = {:except => options[:except], :only => options[:only]} 176: include_has_options = include_associations.is_a?(Hash) 177: associations = include_has_options ? include_associations.keys : Array.wrap(include_associations) 178: 179: # Call to_amf on each object in the association, passing processed options 180: associations.each do |association| 181: records = rubyamf_retrieve_association(association) 182: if records 183: opts = include_has_options ? include_associations[association] : nil 184: if records.is_a?(Enumerable) 185: hash[association.to_s] = records.map {|r| opts.nil? ? r : r.to_amf(opts)} 186: else 187: hash[association.to_s] = opts.nil? ? records : records.to_amf(opts) 188: end 189: end 190: end 191: 192: options[:include] = include_associations 193: end 194: 195: hash 196: end
Populates the object after deserialization. By default it calls initialize, calls setters for keys not in attributes, and calls attributes= for the remaining properties if it‘s implemented. Override if necessary to support your ORM.
# File lib/rubyamf/model.rb, line 98 98: def rubyamf_init props, dynamic_props = nil 99: initialize # warhammerkid: Call initialize by default - good decision? 100: 101: props.merge!(dynamic_props) if dynamic_props 102: if respond_to?(:attributes=) 103: attrs = self.attributes 104: rubyamf_set_non_attributes props, attrs 105: self.attributes = props # Populate using attributes setter 106: else 107: rubyamf_set_non_attributes props, {} # Calls setters for all props it finds setters for 108: end 109: end
Override if necessary to support your ORM‘s system of retrieving objects in an association.
# File lib/rubyamf/model.rb, line 200 200: def rubyamf_retrieve_association association 201: # Naive implementation that should work for most cases without 202: # need for overriding 203: send(association) 204: end
Calls setters for all keys in the given hash not found in the base attributes hash and deletes those keys from the hash. Performs some simple checks on the keys to hopefully prevent more private setters from being called.
# File lib/rubyamf/model.rb, line 114 114: def rubyamf_set_non_attributes attrs, base_attrs 115: not_attributes = attrs.keys.select {|k| !base_attrs.include?(k)} 116: not_attributes.each do |k| 117: setter = "#{k}=" 118: next if setter !~ /^[a-z][A-Za-z0-9_]+=/ # Make sure setter doesn't start with capital, dollar, or underscore to make this safer 119: if respond_to?(setter) 120: send(setter, attrs.delete(k)) 121: else 122: RubyAMF.logger.warn("RubyAMF: Cannot call setter for non-attribute on #{self.class.name}: #{k}") 123: end 124: end 125: end
Stores the given options and object in an IntermediateObject so that the default serialization mapping options can be overriden if necessary.
# File lib/rubyamf/model.rb, line 208 208: def to_amf options=nil 209: RubyAMF::IntermediateObject.new(self, options) 210: end