Class: ActiveRecord::Base

Inherits:
Object show all
Extended by:
Associations::PolymorphicClassMethods
Defined in:
lib/has_many_polymorphs/base.rb,
lib/has_many_polymorphs/support_methods.rb,
lib/has_many_polymorphs.rb

Constant Summary collapse

COLLECTION_METHODS =
[:belongs_to, :has_many, :has_and_belongs_to_many, :has_one,
    :has_many_polymorphs, :acts_as_double_polymorphic_join].each do |method_name|
  alias_method "original_#{method_name}".to_sym, method_name
  undef_method method_name
end
GENERATED_CODE_DIR =
"#{base_dir}/generated_models"

Constants included from Associations::PolymorphicClassMethods

Associations::PolymorphicClassMethods::RESERVED_DOUBLES_KEYS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Associations::PolymorphicClassMethods

acts_as_double_polymorphic_join, create_has_many_polymorphs_reflection, has_many_polymorphs

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/has_many_polymorphs/debugging_tools.rb', line 56

def method_missing(method_name, *args, &block)
  if COLLECTION_METHODS.include? method_name.to_sym
    Dir.chdir GENERATED_CODE_DIR do
      filename = "#{demodulate(self.name.underscore)}.rb"
      contents = File.open(filename).read rescue "\nclass #{self.name}\n\nend\n"
      callfile, callline = caller[2][%r!/.*/(.*?:\d+)!, 1].split(':')
      contents[-5..-5] = "\n  #{method_name} #{args[0..-2].inspect[1..-2]},\n     #{args[-1].inspect[1..-2].gsub(" :", "\n     :").gsub("=>", " => ")}\n#{ block ? "     #{block.inspect.sub(/\@.*\//, '@')}\n" : ""}     # called from #{callfile}, line #{callline}\n\n"
      File.open(filename, "w") do |file|
        file.puts contents
      end
    end
    # doesn't actually display block contents
    self.send("original_#{method_name}", *args, &block)
  else
    self.send(:original_method_missing, method_name, *args, &block)
  end
end

Class Method Details

.instantiate_with_polymorphic_checks(record) ⇒ Object

Interprets a polymorphic row from a unified SELECT, returning the appropriate ActiveRecord instance. Overrides ActiveRecord::Base.instantiate_without_callbacks.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/has_many_polymorphs/base.rb', line 7

def instantiate_with_polymorphic_checks(record)
  if record['polymorphic_parent_class']
    reflection = record['polymorphic_parent_class'].constantize.reflect_on_association(
      record['polymorphic_association_id'].to_sym
    )
    # _logger_debug "Instantiating a polymorphic row for #{
    #   record['polymorphic_parent_class']
    # }.reflect_on_association(:#{
    #   record['polymorphic_association_id']
    # })"

    # rewrite the record with the right column names
    table_aliases = reflection.options[:table_aliases].dup
    record = Hash[*table_aliases.keys.map {|key| [key, record[table_aliases[key]]] }.flatten]

    # find the real child class
    klass = record["#{self.table_name}.#{reflection.options[:polymorphic_type_key]}"].constantize
    if sti_klass = record["#{klass.table_name}.#{klass.inheritance_column}"]
      klass = klass.class_eval do compute_type(sti_klass) end # in case of namespaced STI models

      # copy the data over to the right structure
      klass.columns.map(&:name).each do |col|
        record["#{klass.table_name}.#{col}"] = record["#{klass.base_class.table_name}.#{col}"]
      end
    end

    # check that the join actually joined to something
    child_id = record["#{self.table_name}.#{reflection.options[:polymorphic_key]}"]
    unless child_id == record["#{klass.table_name}.#{klass.primary_key}"]
      msg = []
      msg << "Referential integrity violation"
      msg << "child <#{klass.name}:#{child_id}> was not found for #{reflection.name.inspect}"
      raise ActiveRecord::Associations::PolymorphicError, msg.join('; ')
    end

    # eject the join keys
    # XXX not very readable
    record = Hash[*record._select do |column, value|
      column[/^#{klass.table_name}/]
    end.map do |column, value|
      [column[/\.(.*)/, 1], value]
    end.flatten]

    # allocate and assign values
    klass.allocate.tap do |obj|
      obj.instance_variable_set("@attributes", record)
      obj.instance_variable_set("@attributes_cache", Hash.new)

      if obj.respond_to_without_attributes?(:after_find)
        obj.send(:callback, :after_find)
      end

      if obj.respond_to_without_attributes?(:after_initialize)
        obj.send(:callback, :after_initialize)
      end

    end
  else
    instantiate_without_polymorphic_checks(record)
  end
end

Instance Method Details

#_base_class_nameObject

Return the base class name as a string.



86
87
88
# File 'lib/has_many_polymorphs/support_methods.rb', line 86

def _base_class_name
  self.class.base_class.name.to_s
end

#original_method_missingObject



55
# File 'lib/has_many_polymorphs/debugging_tools.rb', line 55

alias :original_method_missing :method_missing