Module: RubyAMF::Model

Included in:
Rails::Model
Defined in:
lib/rubyamf/model.rb

Overview

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

Integration

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.

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

:nodoc:



34
35
36
# File 'lib/rubyamf/model.rb', line 34

def self.included base #:nodoc:
  base.send :extend, ClassMethods
end

Instance Method Details

#rubyamf_hash(options = nil) ⇒ Object

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.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/rubyamf/model.rb', line 132

def rubyamf_hash options=nil
  # Process options
  options ||= {}
  only = Array.wrap(options[:only]).map(&:to_s)
  except = Array.wrap(options[:except]).map(&:to_s)
  method_names = []
  Array.wrap(options[:methods]).each do |name|
    method_names << name.to_s if respond_to?(name)
  end

  # Get list of attributes
  if respond_to?(:attributes)
    attrs = send(:attributes)
  else
    attrs = {}
    ignored_props = Object.new.public_methods
    (self.public_methods - ignored_props).each do |method_name|
      # Add them to the attrs hash if they take no arguments
      method_def = self.method(method_name)
      attrs[method_name.to_s] = send(method_name) if method_def.arity == 0
    end
  end
  attribute_names = attrs.keys.sort
  if only.any?
    attribute_names &= only
  elsif except.any?
    attribute_names -= except
  end

  # Build hash from attributes and methods
  hash = {}
  attribute_names.each {|name| hash[name] = attrs[name]}
  method_names.each {|name| hash[name] = send(name)}

  # Add associations using ActiveRecord::Serialization style options
  # processing
  if include_associations = options.delete(:include)
    # Process options
    base_only_or_except = {:except => options[:except], :only => options[:only]}
    include_has_options = include_associations.is_a?(Hash)
    associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)

    # Call to_amf on each object in the association, passing processed options
    associations.each do |association|
      records = rubyamf_retrieve_association(association)
      if records
        opts = include_has_options ? include_associations[association] : nil
        if records.is_a?(Enumerable)
          hash[association.to_s] = records.map {|r| opts.nil? ? r : r.to_amf(opts)}
        else
          hash[association.to_s] = opts.nil? ? records : records.to_amf(opts)
        end
      end
    end

    options[:include] = include_associations
  end

  hash
end

#rubyamf_init(props, dynamic_props = nil) ⇒ Object

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.



98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/rubyamf/model.rb', line 98

def rubyamf_init props, dynamic_props = nil
  initialize # warhammerkid: Call initialize by default - good decision?

  props.merge!(dynamic_props) if dynamic_props
  if respond_to?(:attributes=)
    attrs = self.attributes
    rubyamf_set_non_attributes props, attrs
    self.attributes = props # Populate using attributes setter
  else
    rubyamf_set_non_attributes props, {} # Calls setters for all props it finds setters for
  end
end

#rubyamf_retrieve_association(association) ⇒ Object

Override if necessary to support your ORM’s system of retrieving objects in an association.



195
196
197
198
199
# File 'lib/rubyamf/model.rb', line 195

def rubyamf_retrieve_association association
  # Naive implementation that should work for most cases without
  # need for overriding
  send(association)
end

#rubyamf_set_non_attributes(attrs, base_attrs) ⇒ Object

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.



114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/rubyamf/model.rb', line 114

def rubyamf_set_non_attributes attrs, base_attrs
  not_attributes = attrs.keys.select {|k| !base_attrs.include?(k)}
  not_attributes.each do |k|
    setter = "#{k}="
    next if setter !~ /^[a-z][A-Za-z0-9_]+=/ # Make sure setter doesn't start with capital, dollar, or underscore to make this safer
    if respond_to?(setter)
      send(setter, attrs.delete(k))
    else
      RubyAMF.logger.warn("RubyAMF: Cannot call setter for non-attribute on #{self.class.name}: #{k}")
    end
  end
end

#to_amf(options = nil) ⇒ Object

Stores the given options and object in an IntermediateObject so that the default serialization mapping options can be overriden if necessary.



203
204
205
# File 'lib/rubyamf/model.rb', line 203

def to_amf options=nil
  RubyAMF::IntermediateObject.new(self, options)
end