aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb
blob: cd16ae5a8b25146906fa5a07cc9a5bc809e42758 (plain) (blame)
1
2
3
4
5
6
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
68
69
70
71
72
73
74
75
76
77
78
79
80
module ActiveRecord
  module Associations
    module ClassMethods
      class JoinDependency # :nodoc:
        # A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited
        # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which
        # everything else is being joined onto. A JoinAssociation represents an association which
        # is joining to the base. A JoinAssociation may result in more than one actual join
        # operations (for example a has_and_belongs_to_many JoinAssociation would result in
        # two; one for the join table and one for the target table).
        class JoinPart # :nodoc:
          # The Active Record class which this join part is associated 'about'; for a JoinBase
          # this is the actual base model, for a JoinAssociation this is the target model of the
          # association.
          attr_reader :active_record

          delegate :table_name, :column_names, :primary_key, :reflections, :arel_engine, :to => :active_record

          def initialize(active_record)
            @active_record = active_record
            @cached_record = {}
            @column_names_with_alias = nil
          end

          def aliased_table
            Arel::Nodes::TableAlias.new aliased_table_name, table
          end

          def ==(other)
            raise NotImplementedError
          end

          # An Arel::Table for the active_record
          def table
            raise NotImplementedError
          end

          # The prefix to be used when aliasing columns in the active_record's table
          def aliased_prefix
            raise NotImplementedError
          end

          # The alias for the active_record's table
          def aliased_table_name
            raise NotImplementedError
          end

          # The alias for the primary key of the active_record's table
          def aliased_primary_key
            "#{aliased_prefix}_r0"
          end

          # An array of [column_name, alias] pairs for the table
          def column_names_with_alias
            unless @column_names_with_alias
              @column_names_with_alias = []

              ([primary_key] + (column_names - [primary_key])).each_with_index do |column_name, i|
                @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
              end
            end
            @column_names_with_alias
          end

          def extract_record(row)
            Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}]
          end

          def record_id(row)
            row[aliased_primary_key]
          end

          def instantiate(row)
            @cached_record[record_id(row)] ||= active_record.send(:instantiate, extract_record(row))
          end
        end
      end
    end
  end
end