| 1 |
module ActiveRecord |
|---|
| 2 |
module Reflection |
|---|
| 3 |
def self.included(base) |
|---|
| 4 |
base.extend(ClassMethods) |
|---|
| 5 |
end |
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
module ClassMethods |
|---|
| 13 |
def create_reflection(macro, name, options, active_record) |
|---|
| 14 |
case macro |
|---|
| 15 |
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many |
|---|
| 16 |
reflection = AssociationReflection.new(macro, name, options, active_record) |
|---|
| 17 |
when :composed_of |
|---|
| 18 |
reflection = AggregateReflection.new(macro, name, options, active_record) |
|---|
| 19 |
end |
|---|
| 20 |
write_inheritable_hash :reflections, name => reflection |
|---|
| 21 |
reflection |
|---|
| 22 |
end |
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
def reflections |
|---|
| 31 |
read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {}) |
|---|
| 32 |
end |
|---|
| 33 |
|
|---|
| 34 |
|
|---|
| 35 |
def reflect_on_all_aggregations |
|---|
| 36 |
reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) } |
|---|
| 37 |
end |
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 |
|
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 |
def reflect_on_aggregation(aggregation) |
|---|
| 44 |
reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil |
|---|
| 45 |
end |
|---|
| 46 |
|
|---|
| 47 |
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 |
|
|---|
| 53 |
|
|---|
| 54 |
def reflect_on_all_associations(macro = nil) |
|---|
| 55 |
association_reflections = reflections.values.select { |reflection| reflection.is_a?(AssociationReflection) } |
|---|
| 56 |
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections |
|---|
| 57 |
end |
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 |
|
|---|
| 64 |
def reflect_on_association(association) |
|---|
| 65 |
reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil |
|---|
| 66 |
end |
|---|
| 67 |
end |
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 |
|
|---|
| 71 |
|
|---|
| 72 |
class MacroReflection |
|---|
| 73 |
attr_reader :active_record |
|---|
| 74 |
|
|---|
| 75 |
def initialize(macro, name, options, active_record) |
|---|
| 76 |
@macro, @name, @options, @active_record = macro, name, options, active_record |
|---|
| 77 |
end |
|---|
| 78 |
|
|---|
| 79 |
|
|---|
| 80 |
|
|---|
| 81 |
def name |
|---|
| 82 |
@name |
|---|
| 83 |
end |
|---|
| 84 |
|
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 |
def macro |
|---|
| 88 |
@macro |
|---|
| 89 |
end |
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 |
|
|---|
| 93 |
|
|---|
| 94 |
def options |
|---|
| 95 |
@options |
|---|
| 96 |
end |
|---|
| 97 |
|
|---|
| 98 |
|
|---|
| 99 |
|
|---|
| 100 |
def klass |
|---|
| 101 |
@klass ||= class_name.constantize |
|---|
| 102 |
end |
|---|
| 103 |
|
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
def class_name |
|---|
| 107 |
@class_name ||= options[:class_name] || derive_class_name |
|---|
| 108 |
end |
|---|
| 109 |
|
|---|
| 110 |
|
|---|
| 111 |
|
|---|
| 112 |
def ==(other_aggregation) |
|---|
| 113 |
name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record |
|---|
| 114 |
end |
|---|
| 115 |
|
|---|
| 116 |
private |
|---|
| 117 |
def derive_class_name |
|---|
| 118 |
name.to_s.camelize |
|---|
| 119 |
end |
|---|
| 120 |
end |
|---|
| 121 |
|
|---|
| 122 |
|
|---|
| 123 |
|
|---|
| 124 |
class AggregateReflection < MacroReflection |
|---|
| 125 |
end |
|---|
| 126 |
|
|---|
| 127 |
|
|---|
| 128 |
class AssociationReflection < MacroReflection |
|---|
| 129 |
def klass |
|---|
| 130 |
@klass ||= active_record.send(:compute_type, class_name) |
|---|
| 131 |
end |
|---|
| 132 |
|
|---|
| 133 |
def table_name |
|---|
| 134 |
@table_name ||= klass.table_name |
|---|
| 135 |
end |
|---|
| 136 |
|
|---|
| 137 |
def quoted_table_name |
|---|
| 138 |
@quoted_table_name ||= klass.quoted_table_name |
|---|
| 139 |
end |
|---|
| 140 |
|
|---|
| 141 |
def primary_key_name |
|---|
| 142 |
@primary_key_name ||= options[:foreign_key] || derive_primary_key_name |
|---|
| 143 |
end |
|---|
| 144 |
|
|---|
| 145 |
def association_foreign_key |
|---|
| 146 |
@association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key |
|---|
| 147 |
end |
|---|
| 148 |
|
|---|
| 149 |
def counter_cache_column |
|---|
| 150 |
if options[:counter_cache] == true |
|---|
| 151 |
"#{active_record.name.underscore.pluralize}_count" |
|---|
| 152 |
elsif options[:counter_cache] |
|---|
| 153 |
options[:counter_cache] |
|---|
| 154 |
end |
|---|
| 155 |
end |
|---|
| 156 |
|
|---|
| 157 |
def through_reflection |
|---|
| 158 |
@through_reflection ||= options[:through] ? active_record.reflect_on_association(options[:through]) : false |
|---|
| 159 |
end |
|---|
| 160 |
|
|---|
| 161 |
|
|---|
| 162 |
|
|---|
| 163 |
|
|---|
| 164 |
|
|---|
| 165 |
def source_reflection_names |
|---|
| 166 |
@source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym } |
|---|
| 167 |
end |
|---|
| 168 |
|
|---|
| 169 |
|
|---|
| 170 |
|
|---|
| 171 |
|
|---|
| 172 |
|
|---|
| 173 |
|
|---|
| 174 |
|
|---|
| 175 |
|
|---|
| 176 |
def source_reflection |
|---|
| 177 |
return nil unless through_reflection |
|---|
| 178 |
@source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first |
|---|
| 179 |
end |
|---|
| 180 |
|
|---|
| 181 |
def check_validity! |
|---|
| 182 |
if options[:through] |
|---|
| 183 |
if through_reflection.nil? |
|---|
| 184 |
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self) |
|---|
| 185 |
end |
|---|
| 186 |
|
|---|
| 187 |
if source_reflection.nil? |
|---|
| 188 |
raise HasManyThroughSourceAssociationNotFoundError.new(self) |
|---|
| 189 |
end |
|---|
| 190 |
|
|---|
| 191 |
if options[:source_type] && source_reflection.options[:polymorphic].nil? |
|---|
| 192 |
raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection) |
|---|
| 193 |
end |
|---|
| 194 |
|
|---|
| 195 |
if source_reflection.options[:polymorphic] && options[:source_type].nil? |
|---|
| 196 |
raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection) |
|---|
| 197 |
end |
|---|
| 198 |
|
|---|
| 199 |
unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil? |
|---|
| 200 |
raise HasManyThroughSourceAssociationMacroError.new(self) |
|---|
| 201 |
end |
|---|
| 202 |
end |
|---|
| 203 |
end |
|---|
| 204 |
|
|---|
| 205 |
private |
|---|
| 206 |
def derive_class_name |
|---|
| 207 |
|
|---|
| 208 |
if through_reflection |
|---|
| 209 |
options[:source_type] || source_reflection.class_name |
|---|
| 210 |
else |
|---|
| 211 |
class_name = name.to_s.camelize |
|---|
| 212 |
class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro) |
|---|
| 213 |
class_name |
|---|
| 214 |
end |
|---|
| 215 |
end |
|---|
| 216 |
|
|---|
| 217 |
def derive_primary_key_name |
|---|
| 218 |
if macro == :belongs_to |
|---|
| 219 |
"#{name}_id" |
|---|
| 220 |
elsif options[:as] |
|---|
| 221 |
"#{options[:as]}_id" |
|---|
| 222 |
else |
|---|
| 223 |
active_record.name.foreign_key |
|---|
| 224 |
end |
|---|
| 225 |
end |
|---|
| 226 |
end |
|---|
| 227 |
end |
|---|
| 228 |
end |
|---|