activerecord 5.0.7 → 5.1.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (219) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -2080
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record/aggregations.rb +244 -244
  8. data/lib/active_record/association_relation.rb +5 -5
  9. data/lib/active_record/associations/alias_tracker.rb +10 -11
  10. data/lib/active_record/associations/association.rb +23 -5
  11. data/lib/active_record/associations/association_scope.rb +95 -81
  12. data/lib/active_record/associations/belongs_to_association.rb +7 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +30 -16
  14. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  16. data/lib/active_record/associations/collection_association.rb +36 -205
  17. data/lib/active_record/associations/collection_proxy.rb +132 -63
  18. data/lib/active_record/associations/has_many_association.rb +10 -19
  19. data/lib/active_record/associations/has_many_through_association.rb +12 -4
  20. data/lib/active_record/associations/has_one_association.rb +24 -28
  21. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  22. data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
  23. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  24. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +121 -118
  26. data/lib/active_record/associations/preloader/association.rb +64 -64
  27. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  28. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  29. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  30. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  31. data/lib/active_record/associations/preloader/through_association.rb +41 -41
  32. data/lib/active_record/associations/preloader.rb +94 -94
  33. data/lib/active_record/associations/singular_association.rb +8 -25
  34. data/lib/active_record/associations/through_association.rb +2 -5
  35. data/lib/active_record/associations.rb +1591 -1562
  36. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute_assignment.rb +61 -61
  39. data/lib/active_record/attribute_decorators.rb +35 -13
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  41. data/lib/active_record/attribute_methods/dirty.rb +229 -46
  42. data/lib/active_record/attribute_methods/primary_key.rb +74 -73
  43. data/lib/active_record/attribute_methods/read.rb +39 -35
  44. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  46. data/lib/active_record/attribute_methods/write.rb +30 -33
  47. data/lib/active_record/attribute_methods.rb +56 -65
  48. data/lib/active_record/attribute_mutation_tracker.rb +63 -11
  49. data/lib/active_record/attribute_set/builder.rb +27 -33
  50. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  51. data/lib/active_record/attribute_set.rb +9 -6
  52. data/lib/active_record/attributes.rb +22 -22
  53. data/lib/active_record/autosave_association.rb +18 -13
  54. data/lib/active_record/base.rb +24 -22
  55. data/lib/active_record/callbacks.rb +56 -14
  56. data/lib/active_record/coders/yaml_column.rb +9 -11
  57. data/lib/active_record/collection_cache_key.rb +3 -4
  58. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
  59. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  60. data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
  61. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
  70. data/lib/active_record/connection_adapters/column.rb +26 -4
  71. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  72. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  91. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
  92. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
  97. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  99. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
  116. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
  117. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  118. data/lib/active_record/connection_handling.rb +14 -26
  119. data/lib/active_record/core.rb +109 -93
  120. data/lib/active_record/counter_cache.rb +60 -13
  121. data/lib/active_record/define_callbacks.rb +20 -0
  122. data/lib/active_record/dynamic_matchers.rb +80 -79
  123. data/lib/active_record/enum.rb +8 -6
  124. data/lib/active_record/errors.rb +64 -15
  125. data/lib/active_record/explain.rb +1 -2
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +7 -4
  128. data/lib/active_record/fixture_set/file.rb +11 -8
  129. data/lib/active_record/fixtures.rb +66 -53
  130. data/lib/active_record/gem_version.rb +1 -1
  131. data/lib/active_record/inheritance.rb +93 -79
  132. data/lib/active_record/integration.rb +7 -7
  133. data/lib/active_record/internal_metadata.rb +3 -16
  134. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  135. data/lib/active_record/locking/optimistic.rb +69 -74
  136. data/lib/active_record/locking/pessimistic.rb +10 -1
  137. data/lib/active_record/log_subscriber.rb +23 -28
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +100 -47
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/migration.rb +153 -155
  142. data/lib/active_record/model_schema.rb +94 -107
  143. data/lib/active_record/nested_attributes.rb +200 -199
  144. data/lib/active_record/null_relation.rb +11 -34
  145. data/lib/active_record/persistence.rb +65 -50
  146. data/lib/active_record/query_cache.rb +2 -6
  147. data/lib/active_record/querying.rb +3 -4
  148. data/lib/active_record/railtie.rb +16 -17
  149. data/lib/active_record/railties/controller_runtime.rb +6 -2
  150. data/lib/active_record/railties/databases.rake +105 -133
  151. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  152. data/lib/active_record/readonly_attributes.rb +2 -2
  153. data/lib/active_record/reflection.rb +154 -108
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  155. data/lib/active_record/relation/batches.rb +80 -51
  156. data/lib/active_record/relation/calculations.rb +169 -162
  157. data/lib/active_record/relation/delegation.rb +32 -31
  158. data/lib/active_record/relation/finder_methods.rb +197 -231
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  161. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  162. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  163. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  166. data/lib/active_record/relation/predicate_builder.rb +92 -89
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +255 -293
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +80 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/relation.rb +93 -119
  174. data/lib/active_record/result.rb +41 -32
  175. data/lib/active_record/runtime_registry.rb +3 -3
  176. data/lib/active_record/sanitization.rb +176 -192
  177. data/lib/active_record/schema.rb +3 -3
  178. data/lib/active_record/schema_dumper.rb +15 -38
  179. data/lib/active_record/schema_migration.rb +8 -4
  180. data/lib/active_record/scoping/default.rb +90 -90
  181. data/lib/active_record/scoping/named.rb +11 -11
  182. data/lib/active_record/scoping.rb +6 -6
  183. data/lib/active_record/secure_token.rb +2 -2
  184. data/lib/active_record/statement_cache.rb +13 -15
  185. data/lib/active_record/store.rb +31 -32
  186. data/lib/active_record/suppressor.rb +2 -1
  187. data/lib/active_record/table_metadata.rb +9 -5
  188. data/lib/active_record/tasks/database_tasks.rb +65 -55
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  192. data/lib/active_record/timestamp.rb +46 -25
  193. data/lib/active_record/touch_later.rb +1 -2
  194. data/lib/active_record/transactions.rb +97 -109
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +13 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/internal/abstract_json.rb +4 -0
  199. data/lib/active_record/type/serialized.rb +14 -8
  200. data/lib/active_record/type/text.rb +9 -0
  201. data/lib/active_record/type/time.rb +0 -1
  202. data/lib/active_record/type/type_map.rb +11 -15
  203. data/lib/active_record/type/unsigned_integer.rb +15 -0
  204. data/lib/active_record/type.rb +17 -13
  205. data/lib/active_record/type_caster/connection.rb +8 -6
  206. data/lib/active_record/type_caster/map.rb +3 -1
  207. data/lib/active_record/type_caster.rb +2 -2
  208. data/lib/active_record/validations/associated.rb +1 -1
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +8 -39
  211. data/lib/active_record/validations.rb +4 -4
  212. data/lib/active_record/version.rb +1 -1
  213. data/lib/active_record.rb +20 -20
  214. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  215. data/lib/rails/generators/active_record/migration.rb +1 -1
  216. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  217. data/lib/rails/generators/active_record.rb +4 -4
  218. metadata +24 -13
  219. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/array/wrap'
1
+ require "active_support/core_ext/array/wrap"
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  attr_reader :owner, :target, :reflection
20
20
  attr_accessor :inversed
21
21
 
22
- delegate :options, :to => :reflection
22
+ delegate :options, to: :reflection
23
23
 
24
24
  def initialize(owner, reflection)
25
25
  reflection.check_validity!
@@ -112,6 +112,15 @@ module ActiveRecord
112
112
  record
113
113
  end
114
114
 
115
+ # Remove the inverse association, if possible
116
+ def remove_inverse_instance(record)
117
+ if invertible_for?(record)
118
+ inverse = record.association(inverse_reflection_for(record).name)
119
+ inverse.target = nil
120
+ inverse.inversed = false
121
+ end
122
+ end
123
+
115
124
  # Returns the class of the target. belongs_to polymorphic overrides this to look at the
116
125
  # polymorphic_type field on the owner.
117
126
  def klass
@@ -176,13 +185,21 @@ module ActiveRecord
176
185
  def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
177
186
  except_from_scope_attributes ||= {}
178
187
  skip_assign = [reflection.foreign_key, reflection.type].compact
179
- assigned_keys = record.changed
188
+ assigned_keys = record.changed_attribute_names_to_save
180
189
  assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
181
190
  attributes = create_scope.except(*(assigned_keys - skip_assign))
182
191
  record.assign_attributes(attributes)
183
192
  set_inverse_instance(record)
184
193
  end
185
194
 
195
+ def create(attributes = {}, &block)
196
+ _create_record(attributes, &block)
197
+ end
198
+
199
+ def create!(attributes = {}, &block)
200
+ _create_record(attributes, true, &block)
201
+ end
202
+
186
203
  private
187
204
 
188
205
  def find_target?
@@ -227,7 +244,8 @@ module ActiveRecord
227
244
  unless record.is_a?(reflection.klass)
228
245
  fresh_class = reflection.class_name.safe_constantize
229
246
  unless fresh_class && record.is_a?(fresh_class)
230
- message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
247
+ message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, "\
248
+ "got #{record.inspect} which is an instance of #{record.class}(##{record.class.object_id})"
231
249
  raise ActiveRecord::AssociationTypeMismatch, message
232
250
  end
233
251
  end
@@ -255,7 +273,7 @@ module ActiveRecord
255
273
  # so that when stale_state is different from the value stored on the last find_target,
256
274
  # the target is stale.
257
275
  #
258
- # This is only relevant to certain associations, which is why it returns nil by default.
276
+ # This is only relevant to certain associations, which is why it returns +nil+ by default.
259
277
  def stale_state
260
278
  end
261
279
 
@@ -21,11 +21,11 @@ module ActiveRecord
21
21
  reflection = association.reflection
22
22
  scope = klass.unscoped
23
23
  owner = association.owner
24
- alias_tracker = AliasTracker.create connection, association.klass.table_name, klass.type_caster
24
+ alias_tracker = AliasTracker.create connection, association.klass.table_name
25
25
  chain_head, chain_tail = get_chain(reflection, association, alias_tracker)
26
26
 
27
27
  scope.extending! reflection.extensions
28
- add_constraints(scope, owner, klass, reflection, chain_head, chain_tail)
28
+ add_constraints(scope, owner, reflection, chain_head, chain_tail)
29
29
  end
30
30
 
31
31
  def join_type
@@ -49,118 +49,132 @@ module ActiveRecord
49
49
  binds
50
50
  end
51
51
 
52
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
53
+ # Workaround for Ruby 2.2 "private attribute?" warning.
52
54
  protected
53
55
 
54
- attr_reader :value_transformation
56
+ attr_reader :value_transformation
55
57
 
56
58
  private
57
- def join(table, constraint)
58
- table.create_join(table, table.create_on(constraint), join_type)
59
- end
59
+ def join(table, constraint)
60
+ table.create_join(table, table.create_on(constraint), join_type)
61
+ end
60
62
 
61
- def last_chain_scope(scope, table, reflection, owner, association_klass)
62
- join_keys = reflection.join_keys(association_klass)
63
- key = join_keys.key
64
- foreign_key = join_keys.foreign_key
63
+ def last_chain_scope(scope, table, reflection, owner)
64
+ join_keys = reflection.join_keys
65
+ key = join_keys.key
66
+ foreign_key = join_keys.foreign_key
65
67
 
66
- value = transform_value(owner[foreign_key])
67
- scope = scope.where(table.name => { key => value })
68
+ value = transform_value(owner[foreign_key])
69
+ scope = apply_scope(scope, table, key, value)
68
70
 
69
- if reflection.type
70
- polymorphic_type = transform_value(owner.class.base_class.name)
71
- scope = scope.where(table.name => { reflection.type => polymorphic_type })
71
+ if reflection.type
72
+ polymorphic_type = transform_value(owner.class.base_class.name)
73
+ scope = apply_scope(scope, table, reflection.type, polymorphic_type)
74
+ end
75
+
76
+ scope
72
77
  end
73
78
 
74
- scope
75
- end
79
+ def transform_value(value)
80
+ value_transformation.call(value)
81
+ end
76
82
 
77
- def transform_value(value)
78
- value_transformation.call(value)
79
- end
83
+ def next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
84
+ join_keys = reflection.join_keys
85
+ key = join_keys.key
86
+ foreign_key = join_keys.foreign_key
80
87
 
81
- def next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
82
- join_keys = reflection.join_keys(association_klass)
83
- key = join_keys.key
84
- foreign_key = join_keys.foreign_key
88
+ constraint = table[key].eq(foreign_table[foreign_key])
85
89
 
86
- constraint = table[key].eq(foreign_table[foreign_key])
90
+ if reflection.type
91
+ value = transform_value(next_reflection.klass.base_class.name)
92
+ scope = apply_scope(scope, table, reflection.type, value)
93
+ end
87
94
 
88
- if reflection.type
89
- value = transform_value(next_reflection.klass.base_class.name)
90
- scope = scope.where(table.name => { reflection.type => value })
95
+ scope.joins!(join(foreign_table, constraint))
91
96
  end
92
97
 
93
- scope = scope.joins(join(foreign_table, constraint))
94
- end
98
+ class ReflectionProxy < SimpleDelegator # :nodoc:
99
+ attr_accessor :next
100
+ attr_reader :alias_name
95
101
 
96
- class ReflectionProxy < SimpleDelegator # :nodoc:
97
- attr_accessor :next
98
- attr_reader :alias_name
102
+ def initialize(reflection, alias_name)
103
+ super(reflection)
104
+ @alias_name = alias_name
105
+ end
99
106
 
100
- def initialize(reflection, alias_name)
101
- super(reflection)
102
- @alias_name = alias_name
107
+ def all_includes; nil; end
103
108
  end
104
109
 
105
- def all_includes; nil; end
106
- end
107
-
108
- def get_chain(reflection, association, tracker)
109
- name = reflection.name
110
- runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
111
- previous_reflection = runtime_reflection
112
- reflection.chain.drop(1).each do |refl|
113
- alias_name = tracker.aliased_table_for(refl.table_name, refl.alias_candidate(name))
114
- proxy = ReflectionProxy.new(refl, alias_name)
115
- previous_reflection.next = proxy
116
- previous_reflection = proxy
110
+ def get_chain(reflection, association, tracker)
111
+ name = reflection.name
112
+ runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
113
+ previous_reflection = runtime_reflection
114
+ reflection.chain.drop(1).each do |refl|
115
+ alias_name = tracker.aliased_table_for(
116
+ refl.table_name,
117
+ refl.alias_candidate(name),
118
+ refl.klass.type_caster
119
+ )
120
+ proxy = ReflectionProxy.new(refl, alias_name)
121
+ previous_reflection.next = proxy
122
+ previous_reflection = proxy
123
+ end
124
+ [runtime_reflection, previous_reflection]
117
125
  end
118
- [runtime_reflection, previous_reflection]
119
- end
120
126
 
121
- def add_constraints(scope, owner, association_klass, refl, chain_head, chain_tail)
122
- owner_reflection = chain_tail
123
- table = owner_reflection.alias_name
124
- scope = last_chain_scope(scope, table, owner_reflection, owner, association_klass)
127
+ def add_constraints(scope, owner, refl, chain_head, chain_tail)
128
+ owner_reflection = chain_tail
129
+ table = owner_reflection.alias_name
130
+ scope = last_chain_scope(scope, table, owner_reflection, owner)
125
131
 
126
- reflection = chain_head
127
- loop do
128
- break unless reflection
129
- table = reflection.alias_name
130
-
131
- unless reflection == chain_tail
132
+ reflection = chain_head
133
+ while reflection
134
+ table = reflection.alias_name
132
135
  next_reflection = reflection.next
133
- foreign_table = next_reflection.alias_name
134
- scope = next_chain_scope(scope, table, reflection, association_klass, foreign_table, next_reflection)
135
- end
136
136
 
137
- # Exclude the scope of the association itself, because that
138
- # was already merged in the #scope method.
139
- reflection.constraints.each do |scope_chain_item|
140
- item = eval_scope(reflection.klass, scope_chain_item, owner)
141
-
142
- if scope_chain_item == refl.scope
143
- scope.merge! item.except(:where, :includes)
137
+ unless reflection == chain_tail
138
+ foreign_table = next_reflection.alias_name
139
+ scope = next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
144
140
  end
145
141
 
146
- reflection.all_includes do
147
- scope.includes! item.includes_values
142
+ # Exclude the scope of the association itself, because that
143
+ # was already merged in the #scope method.
144
+ reflection.constraints.each do |scope_chain_item|
145
+ item = eval_scope(reflection.klass, table, scope_chain_item, owner)
146
+
147
+ if scope_chain_item == refl.scope
148
+ scope.merge! item.except(:where, :includes)
149
+ end
150
+
151
+ reflection.all_includes do
152
+ scope.includes! item.includes_values
153
+ end
154
+
155
+ scope.unscope!(*item.unscope_values)
156
+ scope.where_clause += item.where_clause
157
+ scope.order_values |= item.order_values
148
158
  end
149
159
 
150
- scope.unscope!(*item.unscope_values)
151
- scope.where_clause += item.where_clause
152
- scope.order_values |= item.order_values
160
+ reflection = next_reflection
153
161
  end
154
162
 
155
- reflection = reflection.next
163
+ scope
156
164
  end
157
165
 
158
- scope
159
- end
166
+ def apply_scope(scope, table, key, value)
167
+ if scope.table == table
168
+ scope.where!(key => value)
169
+ else
170
+ scope.where!(table.name => { key => value })
171
+ end
172
+ end
160
173
 
161
- def eval_scope(klass, scope, owner)
162
- klass.unscoped.instance_exec(owner, &scope)
163
- end
174
+ def eval_scope(klass, table, scope, owner)
175
+ predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
176
+ ActiveRecord::Relation.create(klass, table, predicate_builder).instance_exec(owner, &scope)
177
+ end
164
178
  end
165
179
  end
166
180
  end
@@ -2,7 +2,6 @@ module ActiveRecord
2
2
  # = Active Record Belongs To Association
3
3
  module Associations
4
4
  class BelongsToAssociation < SingularAssociation #:nodoc:
5
-
6
5
  def handle_dependency
7
6
  target.send(options[:dependent]) if load_target
8
7
  end
@@ -22,6 +21,10 @@ module ActiveRecord
22
21
  self.target = record
23
22
  end
24
23
 
24
+ def default(&block)
25
+ writer(owner.instance_exec(&block)) if reader.nil?
26
+ end
27
+
25
28
  def reset
26
29
  super
27
30
  @updated = false
@@ -44,9 +47,9 @@ module ActiveRecord
44
47
  def update_counters(by)
45
48
  if require_counter_update? && foreign_key_present?
46
49
  if target && !stale_target?
47
- target.increment!(reflection.counter_cache_column, by)
50
+ target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
48
51
  else
49
- klass.update_counters(target_id, reflection.counter_cache_column => by)
52
+ klass.update_counters(target_id, reflection.counter_cache_column => by, touch: reflection.options[:touch])
50
53
  end
51
54
  end
52
55
  end
@@ -62,7 +65,7 @@ module ActiveRecord
62
65
  def update_counters_on_replace(record)
63
66
  if require_counter_update? && different_target?(record)
64
67
  owner.instance_variable_set :@_after_replace_counter_called, true
65
- record.increment!(reflection.counter_cache_column)
68
+ record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
66
69
  decrement_counters
67
70
  end
68
71
  end
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
5
5
  end
6
6
 
7
7
  def self.valid_options(options)
8
- super + [:polymorphic, :touch, :counter_cache, :optional]
8
+ super + [:polymorphic, :touch, :counter_cache, :optional, :default]
9
9
  end
10
10
 
11
11
  def self.valid_dependent_options
@@ -16,6 +16,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
16
16
  super
17
17
  add_counter_cache_callbacks(model, reflection) if reflection.options[:counter_cache]
18
18
  add_touch_callbacks(model, reflection) if reflection.options[:touch]
19
+ add_default_callbacks(model, reflection) if reflection.options[:default]
19
20
  end
20
21
 
21
22
  def self.define_accessors(mixin, reflection)
@@ -35,17 +36,17 @@ module ActiveRecord::Associations::Builder # :nodoc:
35
36
  @_after_create_counter_called = false
36
37
  elsif (@_after_replace_counter_called ||= false)
37
38
  @_after_replace_counter_called = false
38
- elsif attribute_changed?(foreign_key) && !new_record?
39
+ elsif saved_change_to_attribute?(foreign_key) && !new_record?
39
40
  if reflection.polymorphic?
40
- model = attribute(reflection.foreign_type).try(:constantize)
41
- model_was = attribute_was(reflection.foreign_type).try(:constantize)
41
+ model = attribute_in_database(reflection.foreign_type).try(:constantize)
42
+ model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
42
43
  else
43
44
  model = reflection.klass
44
45
  model_was = reflection.klass
45
46
  end
46
47
 
47
- foreign_key_was = attribute_was foreign_key
48
- foreign_key = attribute foreign_key
48
+ foreign_key_was = attribute_before_last_save foreign_key
49
+ foreign_key = attribute_in_database foreign_key
49
50
 
50
51
  if foreign_key && model.respond_to?(:increment_counter)
51
52
  model.increment_counter(cache_column, foreign_key)
@@ -70,18 +71,21 @@ module ActiveRecord::Associations::Builder # :nodoc:
70
71
  klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
71
72
  end
72
73
 
73
- def self.touch_record(o, foreign_key, name, touch, touch_method) # :nodoc:
74
- old_foreign_id = o.changed_attributes[foreign_key]
74
+ def self.touch_record(o, changes, foreign_key, name, touch, touch_method) # :nodoc:
75
+ old_foreign_id = changes[foreign_key] && changes[foreign_key].first
75
76
 
76
77
  if old_foreign_id
77
78
  association = o.association(name)
78
79
  reflection = association.reflection
79
80
  if reflection.polymorphic?
80
- klass = o.public_send("#{reflection.foreign_type}_was").constantize
81
+ foreign_type = reflection.foreign_type
82
+ klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
83
+ klass = klass.constantize
81
84
  else
82
85
  klass = association.klass
83
86
  end
84
- old_record = klass.find_by(klass.primary_key => old_foreign_id)
87
+ primary_key = reflection.association_primary_key(klass)
88
+ old_record = klass.find_by(primary_key => old_foreign_id)
85
89
 
86
90
  if old_record
87
91
  if touch != true
@@ -107,13 +111,23 @@ module ActiveRecord::Associations::Builder # :nodoc:
107
111
  n = reflection.name
108
112
  touch = reflection.options[:touch]
109
113
 
110
- callback = lambda { |record|
111
- BelongsTo.touch_record(record, foreign_key, n, touch, belongs_to_touch_method)
112
- }
114
+ callback = lambda { |changes_method| lambda { |record|
115
+ BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
116
+ }}
117
+
118
+ unless reflection.counter_cache_column
119
+ model.after_create callback.(:saved_changes), if: :saved_changes?
120
+ model.after_destroy callback.(:changes_to_save)
121
+ end
113
122
 
114
- model.after_save callback, if: :changed?
115
- model.after_touch callback
116
- model.after_destroy callback
123
+ model.after_update callback.(:saved_changes), if: :saved_changes?
124
+ model.after_touch callback.(:changes_to_save)
125
+ end
126
+
127
+ def self.add_default_callbacks(model, reflection)
128
+ model.before_validation lambda { |o|
129
+ o.association(reflection.name).default(&reflection.options[:default])
130
+ }
117
131
  end
118
132
 
119
133
  def self.add_destroy_callbacks(model, reflection)
@@ -1,10 +1,9 @@
1
1
  # This class is inherited by the has_many and has_many_and_belongs_to_many association classes
2
2
 
3
- require 'active_record/associations'
3
+ require "active_record/associations"
4
4
 
5
5
  module ActiveRecord::Associations::Builder # :nodoc:
6
6
  class CollectionAssociation < Association #:nodoc:
7
-
8
7
  CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
9
8
 
10
9
  def self.valid_options(options)
@@ -16,9 +16,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
16
16
 
17
17
  private
18
18
 
19
- def klass
20
- @lhs_class.send(:compute_type, @rhs_class_name)
21
- end
19
+ def klass
20
+ @lhs_class.send(:compute_type, @rhs_class_name)
21
+ end
22
22
  end
23
23
 
24
24
  def self.build(lhs_class, name, options)
@@ -28,7 +28,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
28
28
  class_name = options.fetch(:class_name) {
29
29
  name.to_s.camelize.singularize
30
30
  }
31
- KnownClass.new lhs_class, class_name
31
+ KnownClass.new lhs_class, class_name.to_s
32
32
  end
33
33
  end
34
34
  end
@@ -78,9 +78,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
78
78
 
79
79
  private
80
80
 
81
- def self.suppress_composite_primary_key(pk)
82
- pk unless pk.is_a?(Array)
83
- end
81
+ def self.suppress_composite_primary_key(pk)
82
+ pk unless pk.is_a?(Array)
83
+ end
84
84
  }
85
85
 
86
86
  join_model.name = "HABTM_#{association_name.to_s.camelize}"
@@ -94,7 +94,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
94
94
 
95
95
  def middle_reflection(join_model)
96
96
  middle_name = [lhs_model.name.downcase.pluralize,
97
- association_name].join('_'.freeze).gsub('::'.freeze, '_'.freeze).to_sym
97
+ association_name].join("_".freeze).gsub("::".freeze, "_".freeze).to_sym
98
98
  middle_options = middle_options join_model
99
99
 
100
100
  HasMany.create_reflection(lhs_model,
@@ -105,29 +105,29 @@ module ActiveRecord::Associations::Builder # :nodoc:
105
105
 
106
106
  private
107
107
 
108
- def middle_options(join_model)
109
- middle_options = {}
110
- middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
111
- middle_options[:source] = join_model.left_reflection.name
112
- if options.key? :foreign_key
113
- middle_options[:foreign_key] = options[:foreign_key]
108
+ def middle_options(join_model)
109
+ middle_options = {}
110
+ middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
111
+ middle_options[:source] = join_model.left_reflection.name
112
+ if options.key? :foreign_key
113
+ middle_options[:foreign_key] = options[:foreign_key]
114
+ end
115
+ middle_options
114
116
  end
115
- middle_options
116
- end
117
117
 
118
- def belongs_to_options(options)
119
- rhs_options = {}
118
+ def belongs_to_options(options)
119
+ rhs_options = {}
120
120
 
121
- if options.key? :class_name
122
- rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
123
- rhs_options[:class_name] = options[:class_name]
124
- end
121
+ if options.key? :class_name
122
+ rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
123
+ rhs_options[:class_name] = options[:class_name]
124
+ end
125
125
 
126
- if options.key? :association_foreign_key
127
- rhs_options[:foreign_key] = options[:association_foreign_key]
128
- end
126
+ if options.key? :association_foreign_key
127
+ rhs_options[:foreign_key] = options[:association_foreign_key]
128
+ end
129
129
 
130
- rhs_options
131
- end
130
+ rhs_options
131
+ end
132
132
  end
133
133
  end