Module: Replicate::AR::InstanceMethods

Defined in:
lib/replicate/active_record.rb

Overview

Mixin for the ActiveRecord instance.

Instance Method Summary collapse

Instance Method Details

#dump_all_association_replicants(dumper, association_type) ⇒ Object

Dump all associations of a given type.

dumper - The Dumper object used to dump additional objects. association_type - :has_one, :belongs_to, :has_many

Returns nothing.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/replicate/active_record.rb', line 123

def dump_all_association_replicants(dumper, association_type)
  self.class.reflect_on_all_associations(association_type).each do |reflection|
    next if omitted_attributes.include?(reflection.name)

    # bail when this object has already been dumped
    next if (info = replicate_reflection_info(reflection)) &&
      (replicant_id = info[:replicant_id]) &&
      dumper.dumped?(replicant_id)

    next if (dependent = __send__(reflection.name)).nil?

    case dependent
    when ActiveRecord::Base, Array
      dumper.dump(dependent)

      # clear reference to allow GC
      if respond_to?(:association)
        association(reflection.name).reset
      elsif respond_to?(:association_instance_set, true)
        association_instance_set(reflection.name, nil)
      end
    else
      warn "warn: #{self.class}##{reflection.name} #{association_type} association " \
           "unexpectedly returned a #{dependent.class}. skipping."
    end
  end
end

#dump_association_replicants(dumper, association) ⇒ Object

Dump objects associated with an AR object through an association name.

object - AR object instance. association - Name of the association whose objects should be dumped.

Returns nothing.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/replicate/active_record.rb', line 157

def dump_association_replicants(dumper, association)
  if reflection = self.class.reflect_on_association(association)
    objects = __send__(reflection.name)
    dumper.dump(objects)
    if reflection.macro == :has_and_belongs_to_many
      dump_has_and_belongs_to_many_replicant(dumper, reflection)
    end
    object = __send__(reflection.name) # clear to allow GC
    if object.respond_to?(:reset)
      object.reset
    end
  else
    warn "error: #{self.class}##{association} is invalid"
  end
end

#dump_has_and_belongs_to_many_replicant(dumper, reflection) ⇒ Object

Dump the special Habtm object used to establish many-to-many relationships between objects that have already been dumped. Note that this object and all objects referenced must have already been dumped before calling this method.



177
178
179
# File 'lib/replicate/active_record.rb', line 177

def dump_has_and_belongs_to_many_replicant(dumper, reflection)
  dumper.dump Habtm.new(self, reflection)
end

#dump_replicant(dumper, opts = {}) ⇒ Object

Replicate::Dumper calls this method on objects to trigger dumping a replicant object tuple. The default implementation dumps all belongs_to associations, then self, then all has_one associations, then any has_many or has_and_belongs_to_many associations declared with the replicate_associations macro.

dumper - Dumper object whose #write method must be called with the

type, id, and attributes hash.

Returns nothing.



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/replicate/active_record.rb', line 21

def dump_replicant(dumper, opts={})
  @replicate_opts = opts
  @replicate_opts[:associations] ||= []
  @replicate_opts[:omit] ||= []
  dump_all_association_replicants dumper, :belongs_to
  dumper.write self.class.to_s, id, replicant_attributes, self
  dump_all_association_replicants dumper, :has_one
  included_associations.each do |association|
    dump_association_replicants dumper, association
  end
end

#included_associationsObject

List of associations to explicitly include when dumping this object.



34
35
36
# File 'lib/replicate/active_record.rb', line 34

def included_associations
  (self.class.replicate_associations + @replicate_opts[:associations]).uniq
end

#omitted_attributesObject

List of attributes and associations to omit when dumping this object.



39
40
41
# File 'lib/replicate/active_record.rb', line 39

def omitted_attributes
  (self.class.replicate_omit_attributes + @replicate_opts[:omit]).uniq
end

#replicant_attributesObject

Attributes hash used to persist this object. This consists of simply typed values (no complex types or objects) with the exception of special foreign key values. When an attribute value is [:id, “SomeClass:1234”], the loader will handle translating the id value to the local system’s version of the same object.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/replicate/active_record.rb', line 48

def replicant_attributes
  attributes = self.attributes.dup

  omitted_attributes.each { |omit| attributes.delete(omit.to_s) }
  self.class.reflect_on_all_associations(:belongs_to).each do |reflection|
    next if omitted_attributes.include?(reflection.name)
    if info = replicate_reflection_info(reflection)
      if replicant_id = info[:replicant_id]
        foreign_key = info[:foreign_key].to_s
        attributes[foreign_key] = [:id, *replicant_id]
      end
    end
  end

  attributes
end

#replicant_idObject

The replicant id is a two tuple containing the class and object id. This is used by Replicant::Dumper to determine if the object has already been dumped or not.



113
114
115
# File 'lib/replicate/active_record.rb', line 113

def replicant_id
  [self.class.name, id]
end

#replicate_reflection_info(reflection) ⇒ Object

Retrieve information on a reflection’s associated class and various keys.

Returns an info hash with these keys:

:class - The class object the association points to.
:primary_key  - The string primary key column name.
:foreign_key  - The string foreign key column name.
:replicant_id - The [classname, id] tuple identifying the record.

Returns nil when the reflection can not be linked to a model.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/replicate/active_record.rb', line 75

def replicate_reflection_info(reflection)
  options = reflection.options
  if options[:polymorphic]
    reference_class = attributes[reflection.foreign_type]
    return if reference_class.nil?

    klass = reference_class.constantize
    primary_key = klass.primary_key
    foreign_key = "#{reflection.name}_id"
  else
    klass = reflection.klass
    primary_key = (options[:primary_key] || klass.primary_key).to_s
    foreign_key = (options[:foreign_key] || "#{reflection.name}_id").to_s
  end

  info = {
    :class       => klass,
    :primary_key => primary_key,
    :foreign_key => foreign_key
  }

  if primary_key == klass.primary_key
    if id = attributes[foreign_key]
      info[:replicant_id] = [klass.to_s, id]
    else
      # nil value in association reference
    end
  else
    # association uses non-primary-key foreign key. no special key
    # conversion needed.
  end

  info
end