sskirby-activerecord 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. data/CHANGELOG.md +6749 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +222 -0
  4. data/examples/associations.png +0 -0
  5. data/examples/performance.rb +177 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +147 -0
  8. data/lib/active_record/aggregations.rb +255 -0
  9. data/lib/active_record/associations.rb +1604 -0
  10. data/lib/active_record/associations/alias_tracker.rb +79 -0
  11. data/lib/active_record/associations/association.rb +239 -0
  12. data/lib/active_record/associations/association_scope.rb +119 -0
  13. data/lib/active_record/associations/belongs_to_association.rb +79 -0
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -0
  15. data/lib/active_record/associations/builder/association.rb +55 -0
  16. data/lib/active_record/associations/builder/belongs_to.rb +85 -0
  17. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -0
  19. data/lib/active_record/associations/builder/has_many.rb +71 -0
  20. data/lib/active_record/associations/builder/has_one.rb +62 -0
  21. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  22. data/lib/active_record/associations/collection_association.rb +574 -0
  23. data/lib/active_record/associations/collection_proxy.rb +132 -0
  24. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +62 -0
  25. data/lib/active_record/associations/has_many_association.rb +108 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +180 -0
  27. data/lib/active_record/associations/has_one_association.rb +73 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +214 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +154 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  33. data/lib/active_record/associations/join_helper.rb +55 -0
  34. data/lib/active_record/associations/preloader.rb +177 -0
  35. data/lib/active_record/associations/preloader/association.rb +127 -0
  36. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  37. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  38. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  39. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  40. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  41. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  42. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  43. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  44. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  45. data/lib/active_record/associations/singular_association.rb +64 -0
  46. data/lib/active_record/associations/through_association.rb +83 -0
  47. data/lib/active_record/attribute_assignment.rb +221 -0
  48. data/lib/active_record/attribute_methods.rb +272 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +31 -0
  50. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  51. data/lib/active_record/attribute_methods/dirty.rb +101 -0
  52. data/lib/active_record/attribute_methods/primary_key.rb +114 -0
  53. data/lib/active_record/attribute_methods/query.rb +39 -0
  54. data/lib/active_record/attribute_methods/read.rb +135 -0
  55. data/lib/active_record/attribute_methods/serialization.rb +93 -0
  56. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -0
  57. data/lib/active_record/attribute_methods/write.rb +69 -0
  58. data/lib/active_record/autosave_association.rb +422 -0
  59. data/lib/active_record/base.rb +716 -0
  60. data/lib/active_record/callbacks.rb +275 -0
  61. data/lib/active_record/coders/yaml_column.rb +41 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +188 -0
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +58 -0
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +388 -0
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -0
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +115 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +492 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +598 -0
  70. data/lib/active_record/connection_adapters/abstract_adapter.rb +296 -0
  71. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
  72. data/lib/active_record/connection_adapters/column.rb +270 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +288 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +426 -0
  75. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1261 -0
  76. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  77. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -0
  78. data/lib/active_record/connection_adapters/sqlite_adapter.rb +577 -0
  79. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  80. data/lib/active_record/counter_cache.rb +119 -0
  81. data/lib/active_record/dynamic_finder_match.rb +56 -0
  82. data/lib/active_record/dynamic_matchers.rb +79 -0
  83. data/lib/active_record/dynamic_scope_match.rb +23 -0
  84. data/lib/active_record/errors.rb +195 -0
  85. data/lib/active_record/explain.rb +85 -0
  86. data/lib/active_record/explain_subscriber.rb +21 -0
  87. data/lib/active_record/fixtures.rb +906 -0
  88. data/lib/active_record/fixtures/file.rb +65 -0
  89. data/lib/active_record/identity_map.rb +156 -0
  90. data/lib/active_record/inheritance.rb +167 -0
  91. data/lib/active_record/integration.rb +49 -0
  92. data/lib/active_record/locale/en.yml +40 -0
  93. data/lib/active_record/locking/optimistic.rb +183 -0
  94. data/lib/active_record/locking/pessimistic.rb +77 -0
  95. data/lib/active_record/log_subscriber.rb +68 -0
  96. data/lib/active_record/migration.rb +765 -0
  97. data/lib/active_record/migration/command_recorder.rb +105 -0
  98. data/lib/active_record/model_schema.rb +366 -0
  99. data/lib/active_record/nested_attributes.rb +469 -0
  100. data/lib/active_record/observer.rb +121 -0
  101. data/lib/active_record/persistence.rb +372 -0
  102. data/lib/active_record/query_cache.rb +74 -0
  103. data/lib/active_record/querying.rb +58 -0
  104. data/lib/active_record/railtie.rb +119 -0
  105. data/lib/active_record/railties/console_sandbox.rb +6 -0
  106. data/lib/active_record/railties/controller_runtime.rb +49 -0
  107. data/lib/active_record/railties/databases.rake +620 -0
  108. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  109. data/lib/active_record/readonly_attributes.rb +26 -0
  110. data/lib/active_record/reflection.rb +534 -0
  111. data/lib/active_record/relation.rb +534 -0
  112. data/lib/active_record/relation/batches.rb +90 -0
  113. data/lib/active_record/relation/calculations.rb +354 -0
  114. data/lib/active_record/relation/delegation.rb +49 -0
  115. data/lib/active_record/relation/finder_methods.rb +398 -0
  116. data/lib/active_record/relation/predicate_builder.rb +58 -0
  117. data/lib/active_record/relation/query_methods.rb +417 -0
  118. data/lib/active_record/relation/spawn_methods.rb +148 -0
  119. data/lib/active_record/result.rb +34 -0
  120. data/lib/active_record/sanitization.rb +194 -0
  121. data/lib/active_record/schema.rb +58 -0
  122. data/lib/active_record/schema_dumper.rb +204 -0
  123. data/lib/active_record/scoping.rb +152 -0
  124. data/lib/active_record/scoping/default.rb +142 -0
  125. data/lib/active_record/scoping/named.rb +202 -0
  126. data/lib/active_record/serialization.rb +18 -0
  127. data/lib/active_record/serializers/xml_serializer.rb +202 -0
  128. data/lib/active_record/session_store.rb +358 -0
  129. data/lib/active_record/store.rb +50 -0
  130. data/lib/active_record/test_case.rb +73 -0
  131. data/lib/active_record/timestamp.rb +113 -0
  132. data/lib/active_record/transactions.rb +360 -0
  133. data/lib/active_record/translation.rb +22 -0
  134. data/lib/active_record/validations.rb +83 -0
  135. data/lib/active_record/validations/associated.rb +43 -0
  136. data/lib/active_record/validations/uniqueness.rb +180 -0
  137. data/lib/active_record/version.rb +10 -0
  138. data/lib/rails/generators/active_record.rb +25 -0
  139. data/lib/rails/generators/active_record/migration.rb +15 -0
  140. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  141. data/lib/rails/generators/active_record/migration/templates/migration.rb +31 -0
  142. data/lib/rails/generators/active_record/model/model_generator.rb +43 -0
  143. data/lib/rails/generators/active_record/model/templates/migration.rb +15 -0
  144. data/lib/rails/generators/active_record/model/templates/model.rb +7 -0
  145. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  146. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  147. data/lib/rails/generators/active_record/observer/templates/observer.rb +4 -0
  148. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +25 -0
  149. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +12 -0
  150. metadata +242 -0
@@ -0,0 +1,73 @@
1
+ require 'active_support/core_ext/object/inclusion'
2
+
3
+ module ActiveRecord
4
+ # = Active Record Belongs To Has One Association
5
+ module Associations
6
+ class HasOneAssociation < SingularAssociation #:nodoc:
7
+ def replace(record, save = true)
8
+ raise_on_type_mismatch(record) if record
9
+ load_target
10
+
11
+ reflection.klass.transaction do
12
+ if target && target != record
13
+ remove_target!(options[:dependent]) unless target.destroyed?
14
+ end
15
+
16
+ if record
17
+ set_owner_attributes(record)
18
+ set_inverse_instance(record)
19
+
20
+ if owner.persisted? && save && !record.save
21
+ nullify_owner_attributes(record)
22
+ set_owner_attributes(target) if target
23
+ raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
24
+ end
25
+ end
26
+ end
27
+
28
+ self.target = record
29
+ end
30
+
31
+ def delete(method = options[:dependent])
32
+ if load_target
33
+ case method
34
+ when :delete
35
+ target.delete
36
+ when :destroy
37
+ target.destroy
38
+ when :nullify
39
+ target.update_attribute(reflection.foreign_key, nil)
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ # The reason that the save param for replace is false, if for create (not just build),
47
+ # is because the setting of the foreign keys is actually handled by the scoping when
48
+ # the record is instantiated, and so they are set straight away and do not need to be
49
+ # updated within replace.
50
+ def set_new_record(record)
51
+ replace(record, false)
52
+ end
53
+
54
+ def remove_target!(method)
55
+ if method.in?([:delete, :destroy])
56
+ target.send(method)
57
+ else
58
+ nullify_owner_attributes(target)
59
+
60
+ if target.persisted? && owner.persisted? && !target.save
61
+ set_owner_attributes(target)
62
+ raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
63
+ "The record failed to save when after its foreign key was set to nil."
64
+ end
65
+ end
66
+ end
67
+
68
+ def nullify_owner_attributes(record)
69
+ record[reflection.foreign_key] = nil
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,36 @@
1
+ module ActiveRecord
2
+ # = Active Record Has One Through Association
3
+ module Associations
4
+ class HasOneThroughAssociation < HasOneAssociation #:nodoc:
5
+ include ThroughAssociation
6
+
7
+ def replace(record)
8
+ create_through_record(record)
9
+ self.target = record
10
+ end
11
+
12
+ private
13
+
14
+ def create_through_record(record)
15
+ ensure_not_nested
16
+
17
+ through_proxy = owner.association(through_reflection.name)
18
+ through_record = through_proxy.send(:load_target)
19
+
20
+ if through_record && !record
21
+ through_record.destroy
22
+ elsif record
23
+ attributes = construct_join_attributes(record)
24
+
25
+ if through_record
26
+ through_record.update_attributes(attributes)
27
+ elsif owner.new_record?
28
+ through_proxy.build(attributes)
29
+ else
30
+ through_proxy.create(attributes)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,214 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class JoinDependency # :nodoc:
4
+ autoload :JoinPart, 'active_record/associations/join_dependency/join_part'
5
+ autoload :JoinBase, 'active_record/associations/join_dependency/join_base'
6
+ autoload :JoinAssociation, 'active_record/associations/join_dependency/join_association'
7
+
8
+ attr_reader :join_parts, :reflections, :alias_tracker, :active_record
9
+
10
+ def initialize(base, associations, joins)
11
+ @active_record = base
12
+ @table_joins = joins
13
+ @join_parts = [JoinBase.new(base)]
14
+ @associations = {}
15
+ @reflections = []
16
+ @alias_tracker = AliasTracker.new(joins)
17
+ @alias_tracker.aliased_name_for(base.table_name) # Updates the count for base.table_name to 1
18
+ build(associations)
19
+ end
20
+
21
+ def graft(*associations)
22
+ associations.each do |association|
23
+ join_associations.detect {|a| association == a} ||
24
+ build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type)
25
+ end
26
+ self
27
+ end
28
+
29
+ def join_associations
30
+ join_parts.last(join_parts.length - 1)
31
+ end
32
+
33
+ def join_base
34
+ join_parts.first
35
+ end
36
+
37
+ def columns
38
+ join_parts.collect { |join_part|
39
+ table = join_part.aliased_table
40
+ join_part.column_names_with_alias.collect{ |column_name, aliased_name|
41
+ table[column_name].as Arel.sql(aliased_name)
42
+ }
43
+ }.flatten
44
+ end
45
+
46
+ def instantiate(rows)
47
+ primary_key = join_base.aliased_primary_key
48
+ parents = {}
49
+
50
+ records = rows.map { |model|
51
+ primary_id = model[primary_key]
52
+ parent = parents[primary_id] ||= join_base.instantiate(model)
53
+ construct(parent, @associations, join_associations, model)
54
+ parent
55
+ }.uniq
56
+
57
+ remove_duplicate_results!(active_record, records, @associations)
58
+ records
59
+ end
60
+
61
+ def remove_duplicate_results!(base, records, associations)
62
+ case associations
63
+ when Symbol, String
64
+ reflection = base.reflections[associations]
65
+ remove_uniq_by_reflection(reflection, records)
66
+ when Array
67
+ associations.each do |association|
68
+ remove_duplicate_results!(base, records, association)
69
+ end
70
+ when Hash
71
+ associations.keys.each do |name|
72
+ reflection = base.reflections[name]
73
+ remove_uniq_by_reflection(reflection, records)
74
+
75
+ parent_records = []
76
+ records.each do |record|
77
+ if descendant = record.send(reflection.name)
78
+ if reflection.collection?
79
+ parent_records.concat descendant.target.uniq
80
+ else
81
+ parent_records << descendant
82
+ end
83
+ end
84
+ end
85
+
86
+ remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty?
87
+ end
88
+ end
89
+ end
90
+
91
+ protected
92
+
93
+ def cache_joined_association(association)
94
+ associations = []
95
+ parent = association.parent
96
+ while parent != join_base
97
+ associations.unshift(parent.reflection.name)
98
+ parent = parent.parent
99
+ end
100
+ ref = @associations
101
+ associations.each do |key|
102
+ ref = ref[key]
103
+ end
104
+ ref[association.reflection.name] ||= {}
105
+ end
106
+
107
+ def build(associations, parent = nil, join_type = Arel::InnerJoin)
108
+ parent ||= join_parts.last
109
+ case associations
110
+ when Symbol, String
111
+ reflection = parent.reflections[associations.to_s.intern] or
112
+ raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
113
+ unless join_association = find_join_association(reflection, parent)
114
+ @reflections << reflection
115
+ join_association = build_join_association(reflection, parent)
116
+ join_association.join_type = join_type
117
+ @join_parts << join_association
118
+ cache_joined_association(join_association)
119
+ end
120
+ join_association
121
+ when Array
122
+ associations.each do |association|
123
+ build(association, parent, join_type)
124
+ end
125
+ when Hash
126
+ associations.keys.sort_by { |a| a.to_s }.each do |name|
127
+ join_association = build(name, parent, join_type)
128
+ build(associations[name], join_association, join_type)
129
+ end
130
+ else
131
+ raise ConfigurationError, associations.inspect
132
+ end
133
+ end
134
+
135
+ def find_join_association(name_or_reflection, parent)
136
+ if String === name_or_reflection
137
+ name_or_reflection = name_or_reflection.to_sym
138
+ end
139
+
140
+ join_associations.detect { |j|
141
+ j.reflection == name_or_reflection && j.parent == parent
142
+ }
143
+ end
144
+
145
+ def remove_uniq_by_reflection(reflection, records)
146
+ if reflection && reflection.collection?
147
+ records.each { |record| record.send(reflection.name).target.uniq! }
148
+ end
149
+ end
150
+
151
+ def build_join_association(reflection, parent)
152
+ JoinAssociation.new(reflection, self, parent)
153
+ end
154
+
155
+ def construct(parent, associations, join_parts, row)
156
+ case associations
157
+ when Symbol, String
158
+ name = associations.to_s
159
+
160
+ join_part = join_parts.detect { |j|
161
+ j.reflection.name.to_s == name &&
162
+ j.parent_table_name == parent.class.table_name }
163
+
164
+ raise(ConfigurationError, "No such association") unless join_part
165
+
166
+ join_parts.delete(join_part)
167
+ construct_association(parent, join_part, row)
168
+ when Array
169
+ associations.each do |association|
170
+ construct(parent, association, join_parts, row)
171
+ end
172
+ when Hash
173
+ associations.sort_by { |k,_| k.to_s }.each do |association_name, assoc|
174
+ association = construct(parent, association_name, join_parts, row)
175
+ construct(association, assoc, join_parts, row) if association
176
+ end
177
+ else
178
+ raise ConfigurationError, associations.inspect
179
+ end
180
+ end
181
+
182
+ def construct_association(record, join_part, row)
183
+ return if record.id.to_s != join_part.parent.record_id(row).to_s
184
+
185
+ macro = join_part.reflection.macro
186
+ if macro == :has_one
187
+ return record.association(join_part.reflection.name).target if record.association_cache.key?(join_part.reflection.name)
188
+ association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil?
189
+ set_target_and_inverse(join_part, association, record)
190
+ else
191
+ association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil?
192
+ case macro
193
+ when :has_many, :has_and_belongs_to_many
194
+ other = record.association(join_part.reflection.name)
195
+ other.loaded!
196
+ other.target.push(association) if association
197
+ other.set_inverse_instance(association)
198
+ when :belongs_to
199
+ set_target_and_inverse(join_part, association, record)
200
+ else
201
+ raise ConfigurationError, "unknown macro: #{join_part.reflection.macro}"
202
+ end
203
+ end
204
+ association
205
+ end
206
+
207
+ def set_target_and_inverse(join_part, association, record)
208
+ other = record.association(join_part.reflection.name)
209
+ other.target = association
210
+ other.set_inverse_instance(association)
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,154 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class JoinDependency # :nodoc:
4
+ class JoinAssociation < JoinPart # :nodoc:
5
+ include JoinHelper
6
+
7
+ # The reflection of the association represented
8
+ attr_reader :reflection
9
+
10
+ # The JoinDependency object which this JoinAssociation exists within. This is mainly
11
+ # relevant for generating aliases which do not conflict with other joins which are
12
+ # part of the query.
13
+ attr_reader :join_dependency
14
+
15
+ # A JoinBase instance representing the active record we are joining onto.
16
+ # (So in Author.has_many :posts, the Author would be that base record.)
17
+ attr_reader :parent
18
+
19
+ # What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin
20
+ attr_accessor :join_type
21
+
22
+ # These implement abstract methods from the superclass
23
+ attr_reader :aliased_prefix
24
+
25
+ attr_reader :tables
26
+
27
+ delegate :options, :through_reflection, :source_reflection, :chain, :to => :reflection
28
+ delegate :table, :table_name, :to => :parent, :prefix => :parent
29
+ delegate :alias_tracker, :to => :join_dependency
30
+
31
+ alias :alias_suffix :parent_table_name
32
+
33
+ def initialize(reflection, join_dependency, parent = nil)
34
+ reflection.check_validity!
35
+
36
+ if reflection.options[:polymorphic]
37
+ raise EagerLoadPolymorphicError.new(reflection)
38
+ end
39
+
40
+ super(reflection.klass)
41
+
42
+ @reflection = reflection
43
+ @join_dependency = join_dependency
44
+ @parent = parent
45
+ @join_type = Arel::InnerJoin
46
+ @aliased_prefix = "t#{ join_dependency.join_parts.size }"
47
+ @tables = construct_tables.reverse
48
+ end
49
+
50
+ def ==(other)
51
+ other.class == self.class &&
52
+ other.reflection == reflection &&
53
+ other.parent == parent
54
+ end
55
+
56
+ def find_parent_in(other_join_dependency)
57
+ other_join_dependency.join_parts.detect do |join_part|
58
+ parent == join_part
59
+ end
60
+ end
61
+
62
+ def join_to(relation)
63
+ tables = @tables.dup
64
+ foreign_table = parent_table
65
+ foreign_klass = parent.active_record
66
+
67
+ # The chain starts with the target table, but we want to end with it here (makes
68
+ # more sense in this context), so we reverse
69
+ chain.reverse.each_with_index do |reflection, i|
70
+ table = tables.shift
71
+
72
+ case reflection.source_macro
73
+ when :belongs_to
74
+ key = reflection.association_primary_key
75
+ foreign_key = reflection.foreign_key
76
+ when :has_and_belongs_to_many
77
+ # Join the join table first...
78
+ relation.from(join(
79
+ table,
80
+ table[reflection.foreign_key].
81
+ eq(foreign_table[reflection.active_record_primary_key])
82
+ ))
83
+
84
+ foreign_table, table = table, tables.shift
85
+
86
+ key = reflection.association_primary_key
87
+ foreign_key = reflection.association_foreign_key
88
+ else
89
+ key = reflection.foreign_key
90
+ foreign_key = reflection.active_record_primary_key
91
+ end
92
+
93
+ constraint = build_constraint(reflection, table, key, foreign_table, foreign_key)
94
+
95
+ conditions = self.conditions[i].dup
96
+ conditions << { reflection.type => foreign_klass.base_class.name } if reflection.type
97
+
98
+ unless conditions.empty?
99
+ constraint = constraint.and(sanitize(conditions, table))
100
+ end
101
+
102
+ relation.from(join(table, constraint))
103
+
104
+ # The current table in this iteration becomes the foreign table in the next
105
+ foreign_table, foreign_klass = table, reflection.klass
106
+ end
107
+
108
+ relation
109
+ end
110
+
111
+ def build_constraint(reflection, table, key, foreign_table, foreign_key)
112
+ constraint = table[key].eq(foreign_table[foreign_key])
113
+
114
+ if reflection.klass.finder_needs_type_condition?
115
+ constraint = table.create_and([
116
+ constraint,
117
+ reflection.klass.send(:type_condition, table)
118
+ ])
119
+ end
120
+
121
+ constraint
122
+ end
123
+
124
+ def join_relation(joining_relation)
125
+ self.join_type = Arel::OuterJoin
126
+ joining_relation.joins(self)
127
+ end
128
+
129
+ def table
130
+ tables.last
131
+ end
132
+
133
+ def aliased_table_name
134
+ table.table_alias || table.name
135
+ end
136
+
137
+ def conditions
138
+ @conditions ||= reflection.conditions.reverse
139
+ end
140
+
141
+ private
142
+
143
+ def interpolate(conditions)
144
+ if conditions.respond_to?(:to_proc)
145
+ instance_eval(&conditions)
146
+ else
147
+ conditions
148
+ end
149
+ end
150
+
151
+ end
152
+ end
153
+ end
154
+ end