Class: ActiveRecord::Base

Inherits:
Object show all
Defined in:
lib/delineate/map_attributes.rb

Overview

Extend the ActiveRecord::Base class to include methods for defining and reading/writing the model API attributes.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.attribute_map(map_name) ⇒ Object



118
119
120
# File 'lib/delineate/map_attributes.rb', line 118

def self.attribute_map(map_name)
  attribute_maps.try(:fetch, map_name, nil)
end

.map_attributes(map_name, options = {}, &blk) ⇒ Object

The map_attributes method lets an ActiveRecord model class define a set of attributes that are to be exposed through the model’s public interface. See the AttributeMap documentation for more information.

The map_name parameter names the attribute map, and must be unique within a model class.

class  < ActiveRecord::Base
  map_attributes :api do
    .
    .
  end

  map_attributes :csv_export do
    .
    .
  end
end

ActiveRecord STI subclasses inherit the attribute map of the same name from their superclass. If you want to include additional subclass attributes, just invoke map_attributes in the subclass and define the extra attributes and associations. If the subclass wants to completely override/replace the superclass map, do:

map_attributes :api, :override => :replace do
  .
  .
end

To access (read) a model instance’s attributes via an attribute map, you invoke a method on the instance named <map-name>_attributes. For example:

attrs = post.api_attributes

retrieves the attributes as specified in the Post model attribute map named :api. A hash of the API attributes is returned.

An optional options parameter lets you include attributes and associations that are defined as :optional in the attribute map. Following are some examples:

post.api_attributes(:include => :author)
post.api_attributes(:include => [:author, :comments])

Include the balance attribute and also the description attribute in the :type association:

.api_attributes(:include => [:balance, {:type => :description}])

Another exmpale: in the :type association, include the optional name attribute, the desc attribute of the category association, and all the mapped attributes of the :type association named :assoc2.

.api_attributes(:include => {:type => [:name, {:category => :desc}, :assoc2]})

Other include forms:

:include => :attr1
:include => :assoc1
:include => [:attr1, :attr2, :assoc1]
:include => {:assoc1 => {}, :assoc2 => {:include => [:attr1, :attr2]}}
:include => [:attr1, :attr2, {:assoc1 => {:include => :assoc2}}, :assoc3]

In addition to the :include option, you can specify:

:only     Restricts the attributes and associations to only those specified.
:except   Processes attributes and associations except those specified.

To update/set a model instance’s attributes via an attribute map, you invoke a setter method on the instance named <map_name>_attributes=. For example:

post.api_attributes = attr_hash

The input hash contains name/value pairs, including those for nested models as defined as writeable in the attribute map. The input attribute values are mapped to the appropriate model attributes and associations.

NOTE: Maps should pretty much be the last thing defined at the class level, but especially after the model class’s associations and accepts_nested_attributes_for.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/delineate/map_attributes.rb', line 97

def self.map_attributes(map_name, options = {}, &blk)
  map = Delineate::AttributeMap::AttributeMap.new(self.name, map_name, options)

  # If this is a CTI subclass, init this map with its base class attributes and associations
  if respond_to?(:is_cti_subclass) and is_cti_subclass? and options[:override] != :replace
    base_class_map = cti_base_class.attribute_map(map_name)
    raise "Base class for CTI subclass #{self.name} must specify attribute map #{map_name}" if base_class_map.nil?

    base_class_map.attributes.each { |attr, opts| map.attribute(attr, opts.dup) }
    base_class_map.associations.each do |name, assoc|
      map.association(name, assoc[:options].merge({:attr_map => assoc[:attr_map].try(:dup)})) unless assoc[:klass_name] == self.name
    end
  end

  # Parse the map specification DSL
  map.instance_eval(&blk)

  define_attribute_map_methods(map_name)      # define map accessor methods
  attribute_maps[map_name] = map
end

Instance Method Details

#attribute_map(map_name) ⇒ Object



122
123
124
# File 'lib/delineate/map_attributes.rb', line 122

def attribute_map(map_name)
  self.class.attribute_maps[map_name]
end

#mapped_attributes(map_name, format = :hash, options = {}) ⇒ Object

Returns the attributes as specified in the attribut map. The format paramater can be one of the following: :hash, :json, :xml, :csv.

The supported options hash keys are:

:include  Specifies which optional attributes and associations to output.
:only     Restricts the attributes and associations to only those specified.
:except   Processes attributes and associations except those specified.
:context  If this option is specified, then attribute readers and writers
          defined as symbols will be executed as instance methods on the
          specified context object.


138
139
140
141
142
143
# File 'lib/delineate/map_attributes.rb', line 138

def mapped_attributes(map_name, format = :hash, options = {})
  map = validate_parameters(map_name, format)
  @serializer_context = options[:context]

  serializer_class(format).new(self, map, options).serialize(options)
end

#mapped_attributes=(map_name, attrs, format = :hash, options = {}) ⇒ Object Also known as: set_mapped_attributes

Sets the model object’s attributes from the input hash. The hash contains name/value pairs, including those for nested models as defined as writeable in the attribute map. The input attribute names are mapped to the appropriate model attributes and associations.



150
151
152
153
154
155
# File 'lib/delineate/map_attributes.rb', line 150

def mapped_attributes=(map_name, attrs, format = :hash, options = {})
  map = validate_parameters(map_name, format)
  @serializer_context = options[:context]

  self.attributes = serializer_class(format).new(self, map).serialize_in(attrs, options)
end