activerecord 4.1.15 → 4.2.11.3

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 (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1792
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +4 -0
  5. data/lib/active_record/aggregations.rb +15 -8
  6. data/lib/active_record/association_relation.rb +13 -0
  7. data/lib/active_record/associations.rb +158 -49
  8. data/lib/active_record/associations/alias_tracker.rb +3 -12
  9. data/lib/active_record/associations/association.rb +16 -4
  10. data/lib/active_record/associations/association_scope.rb +83 -38
  11. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  12. data/lib/active_record/associations/builder/association.rb +15 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  16. data/lib/active_record/associations/builder/has_many.rb +1 -1
  17. data/lib/active_record/associations/builder/has_one.rb +2 -2
  18. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  19. data/lib/active_record/associations/collection_association.rb +63 -27
  20. data/lib/active_record/associations/collection_proxy.rb +29 -35
  21. data/lib/active_record/associations/foreign_association.rb +11 -0
  22. data/lib/active_record/associations/has_many_association.rb +83 -22
  23. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  24. data/lib/active_record/associations/has_one_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  27. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/preloader/association.rb +14 -11
  30. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  31. data/lib/active_record/associations/singular_association.rb +17 -2
  32. data/lib/active_record/associations/through_association.rb +5 -12
  33. data/lib/active_record/attribute.rb +163 -0
  34. data/lib/active_record/attribute_assignment.rb +19 -11
  35. data/lib/active_record/attribute_decorators.rb +66 -0
  36. data/lib/active_record/attribute_methods.rb +56 -94
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  39. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  40. data/lib/active_record/attribute_methods/query.rb +1 -1
  41. data/lib/active_record/attribute_methods/read.rb +22 -59
  42. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  44. data/lib/active_record/attribute_methods/write.rb +9 -24
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attribute_set/builder.rb +106 -0
  47. data/lib/active_record/attributes.rb +147 -0
  48. data/lib/active_record/autosave_association.rb +19 -12
  49. data/lib/active_record/base.rb +13 -24
  50. data/lib/active_record/callbacks.rb +6 -6
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  55. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  63. data/lib/active_record/connection_adapters/column.rb +29 -240
  64. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  71. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  100. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  101. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  102. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  104. data/lib/active_record/connection_handling.rb +1 -1
  105. data/lib/active_record/core.rb +163 -39
  106. data/lib/active_record/counter_cache.rb +60 -6
  107. data/lib/active_record/enum.rb +9 -11
  108. data/lib/active_record/errors.rb +53 -30
  109. data/lib/active_record/explain.rb +1 -1
  110. data/lib/active_record/explain_subscriber.rb +1 -1
  111. data/lib/active_record/fixtures.rb +55 -69
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +35 -10
  114. data/lib/active_record/integration.rb +4 -4
  115. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  116. data/lib/active_record/locking/optimistic.rb +46 -26
  117. data/lib/active_record/migration.rb +71 -46
  118. data/lib/active_record/migration/command_recorder.rb +19 -2
  119. data/lib/active_record/migration/join_table.rb +1 -1
  120. data/lib/active_record/model_schema.rb +52 -58
  121. data/lib/active_record/nested_attributes.rb +5 -5
  122. data/lib/active_record/no_touching.rb +1 -1
  123. data/lib/active_record/persistence.rb +46 -26
  124. data/lib/active_record/query_cache.rb +3 -3
  125. data/lib/active_record/querying.rb +10 -7
  126. data/lib/active_record/railtie.rb +18 -11
  127. data/lib/active_record/railties/databases.rake +50 -51
  128. data/lib/active_record/readonly_attributes.rb +0 -1
  129. data/lib/active_record/reflection.rb +273 -114
  130. data/lib/active_record/relation.rb +57 -25
  131. data/lib/active_record/relation/batches.rb +0 -2
  132. data/lib/active_record/relation/calculations.rb +41 -37
  133. data/lib/active_record/relation/finder_methods.rb +70 -47
  134. data/lib/active_record/relation/merger.rb +39 -29
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  137. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  138. data/lib/active_record/relation/query_methods.rb +114 -65
  139. data/lib/active_record/relation/spawn_methods.rb +3 -0
  140. data/lib/active_record/result.rb +18 -7
  141. data/lib/active_record/sanitization.rb +12 -2
  142. data/lib/active_record/schema.rb +0 -1
  143. data/lib/active_record/schema_dumper.rb +59 -28
  144. data/lib/active_record/schema_migration.rb +5 -4
  145. data/lib/active_record/scoping/default.rb +6 -4
  146. data/lib/active_record/scoping/named.rb +4 -0
  147. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  148. data/lib/active_record/statement_cache.rb +95 -10
  149. data/lib/active_record/store.rb +5 -5
  150. data/lib/active_record/tasks/database_tasks.rb +61 -6
  151. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
  152. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  153. data/lib/active_record/timestamp.rb +9 -7
  154. data/lib/active_record/transactions.rb +53 -27
  155. data/lib/active_record/type.rb +23 -0
  156. data/lib/active_record/type/big_integer.rb +13 -0
  157. data/lib/active_record/type/binary.rb +50 -0
  158. data/lib/active_record/type/boolean.rb +31 -0
  159. data/lib/active_record/type/date.rb +50 -0
  160. data/lib/active_record/type/date_time.rb +54 -0
  161. data/lib/active_record/type/decimal.rb +64 -0
  162. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  163. data/lib/active_record/type/decorator.rb +14 -0
  164. data/lib/active_record/type/float.rb +19 -0
  165. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  166. data/lib/active_record/type/integer.rb +59 -0
  167. data/lib/active_record/type/mutable.rb +16 -0
  168. data/lib/active_record/type/numeric.rb +36 -0
  169. data/lib/active_record/type/serialized.rb +62 -0
  170. data/lib/active_record/type/string.rb +40 -0
  171. data/lib/active_record/type/text.rb +11 -0
  172. data/lib/active_record/type/time.rb +26 -0
  173. data/lib/active_record/type/time_value.rb +38 -0
  174. data/lib/active_record/type/type_map.rb +64 -0
  175. data/lib/active_record/type/unsigned_integer.rb +15 -0
  176. data/lib/active_record/type/value.rb +110 -0
  177. data/lib/active_record/validations.rb +25 -19
  178. data/lib/active_record/validations/associated.rb +5 -3
  179. data/lib/active_record/validations/presence.rb +5 -3
  180. data/lib/active_record/validations/uniqueness.rb +25 -29
  181. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  182. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +66 -11
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -57,20 +57,10 @@ module ActiveRecord
57
57
  end
58
58
 
59
59
  def aliased_table_for(table_name, aliased_name)
60
- table_alias = aliased_name_for(table_name, aliased_name)
61
-
62
- if table_alias == table_name
63
- Arel::Table.new(table_name)
64
- else
65
- Arel::Table.new(table_name).alias(table_alias)
66
- end
67
- end
68
-
69
- def aliased_name_for(table_name, aliased_name)
70
60
  if aliases[table_name].zero?
71
61
  # If it's zero, we can have our table_name
72
62
  aliases[table_name] = 1
73
- table_name
63
+ Arel::Table.new(table_name)
74
64
  else
75
65
  # Otherwise, we need to use an alias
76
66
  aliased_name = connection.table_alias_for(aliased_name)
@@ -78,11 +68,12 @@ module ActiveRecord
78
68
  # Update the count
79
69
  aliases[aliased_name] += 1
80
70
 
81
- if aliases[aliased_name] > 1
71
+ table_alias = if aliases[aliased_name] > 1
82
72
  "#{truncate(aliased_name)}_#{aliases[aliased_name]}"
83
73
  else
84
74
  aliased_name
85
75
  end
76
+ Arel::Table.new(table_name).alias(table_alias)
86
77
  end
87
78
  end
88
79
 
@@ -179,7 +179,7 @@ module ActiveRecord
179
179
  def creation_attributes
180
180
  attributes = {}
181
181
 
182
- if (reflection.macro == :has_one || reflection.macro == :has_many) && !options[:through]
182
+ if (reflection.has_one? || reflection.collection?) && !options[:through]
183
183
  attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
184
184
 
185
185
  if reflection.options[:as]
@@ -211,9 +211,12 @@ module ActiveRecord
211
211
  # the kind of the class of the associated objects. Meant to be used as
212
212
  # a sanity check when you are about to assign an associated record.
213
213
  def raise_on_type_mismatch!(record)
214
- unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
215
- message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
216
- raise ActiveRecord::AssociationTypeMismatch, message
214
+ unless record.is_a?(reflection.klass)
215
+ fresh_class = reflection.class_name.safe_constantize
216
+ unless fresh_class && record.is_a?(fresh_class)
217
+ message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
218
+ raise ActiveRecord::AssociationTypeMismatch, message
219
+ end
217
220
  end
218
221
  end
219
222
 
@@ -248,6 +251,15 @@ module ActiveRecord
248
251
  initialize_attributes(record)
249
252
  end
250
253
  end
254
+
255
+ # Returns true if statement cache should be skipped on the association reader.
256
+ def skip_statement_cache?
257
+ reflection.scope_chain.any?(&:any?) ||
258
+ scope.eager_loading? ||
259
+ klass.current_scope ||
260
+ klass.default_scopes.any? ||
261
+ reflection.source_reflection.active_record.default_scopes.any?
262
+ end
251
263
  end
252
264
  end
253
265
  end
@@ -1,12 +1,33 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class AssociationScope #:nodoc:
4
- INSTANCE = new
5
-
6
4
  def self.scope(association, connection)
7
5
  INSTANCE.scope association, connection
8
6
  end
9
7
 
8
+ class BindSubstitution
9
+ def initialize(block)
10
+ @block = block
11
+ end
12
+
13
+ def bind_value(scope, column, value, alias_tracker)
14
+ substitute = alias_tracker.connection.substitute_at(column)
15
+ scope.bind_values += [[column, @block.call(value)]]
16
+ substitute
17
+ end
18
+ end
19
+
20
+ def self.create(&block)
21
+ block = block ? block : lambda { |val| val }
22
+ new BindSubstitution.new(block)
23
+ end
24
+
25
+ def initialize(bind_substitution)
26
+ @bind_substitution = bind_substitution
27
+ end
28
+
29
+ INSTANCE = create
30
+
10
31
  def scope(association, connection)
11
32
  klass = association.klass
12
33
  reflection = association.reflection
@@ -22,6 +43,23 @@ module ActiveRecord
22
43
  Arel::Nodes::InnerJoin
23
44
  end
24
45
 
46
+ def self.get_bind_values(owner, chain)
47
+ binds = []
48
+ last_reflection = chain.last
49
+
50
+ binds << last_reflection.join_id_for(owner)
51
+ if last_reflection.type
52
+ binds << owner.class.base_class.name
53
+ end
54
+
55
+ chain.each_cons(2).each do |reflection, next_reflection|
56
+ if reflection.type
57
+ binds << next_reflection.klass.base_class.name
58
+ end
59
+ end
60
+ binds
61
+ end
62
+
25
63
  private
26
64
 
27
65
  def construct_tables(chain, klass, refl, alias_tracker)
@@ -49,10 +87,7 @@ module ActiveRecord
49
87
  end
50
88
 
51
89
  def bind_value(scope, column, value, alias_tracker)
52
- substitute = alias_tracker.connection.substitute_at(
53
- column, scope.bind_values.length)
54
- scope.bind_values += [[column, value]]
55
- substitute
90
+ @bind_substitution.bind_value scope, column, value, alias_tracker
56
91
  end
57
92
 
58
93
  def bind(scope, table_name, column_name, value, tracker)
@@ -60,47 +95,55 @@ module ActiveRecord
60
95
  bind_value scope, column, value, tracker
61
96
  end
62
97
 
98
+ def last_chain_scope(scope, table, reflection, owner, tracker, assoc_klass)
99
+ join_keys = reflection.join_keys(assoc_klass)
100
+ key = join_keys.key
101
+ foreign_key = join_keys.foreign_key
102
+
103
+ bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
104
+ scope = scope.where(table[key].eq(bind_val))
105
+
106
+ if reflection.type
107
+ value = owner.class.base_class.name
108
+ bind_val = bind scope, table.table_name, reflection.type, value, tracker
109
+ scope = scope.where(table[reflection.type].eq(bind_val))
110
+ else
111
+ scope
112
+ end
113
+ end
114
+
115
+ def next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
116
+ join_keys = reflection.join_keys(assoc_klass)
117
+ key = join_keys.key
118
+ foreign_key = join_keys.foreign_key
119
+
120
+ constraint = table[key].eq(foreign_table[foreign_key])
121
+
122
+ if reflection.type
123
+ value = next_reflection.klass.base_class.name
124
+ bind_val = bind scope, table.table_name, reflection.type, value, tracker
125
+ scope = scope.where(table[reflection.type].eq(bind_val))
126
+ end
127
+
128
+ scope = scope.joins(join(foreign_table, constraint))
129
+ end
130
+
63
131
  def add_constraints(scope, owner, assoc_klass, refl, tracker)
64
132
  chain = refl.chain
65
133
  scope_chain = refl.scope_chain
66
134
 
67
135
  tables = construct_tables(chain, assoc_klass, refl, tracker)
68
136
 
137
+ owner_reflection = chain.last
138
+ table = tables.last
139
+ scope = last_chain_scope(scope, table, owner_reflection, owner, tracker, assoc_klass)
140
+
69
141
  chain.each_with_index do |reflection, i|
70
142
  table, foreign_table = tables.shift, tables.first
71
143
 
72
- if reflection.source_macro == :belongs_to
73
- if reflection.options[:polymorphic]
74
- key = reflection.association_primary_key(assoc_klass)
75
- else
76
- key = reflection.association_primary_key
77
- end
78
-
79
- foreign_key = reflection.foreign_key
80
- else
81
- key = reflection.foreign_key
82
- foreign_key = reflection.active_record_primary_key
83
- end
84
-
85
- if reflection == chain.last
86
- bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
87
- scope = scope.where(table[key].eq(bind_val))
88
-
89
- if reflection.type
90
- value = owner.class.base_class.name
91
- bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
92
- scope = scope.where(table[reflection.type].eq(bind_val))
93
- end
94
- else
95
- constraint = table[key].eq(foreign_table[foreign_key])
96
-
97
- if reflection.type
98
- value = chain[i + 1].klass.base_class.name
99
- bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
100
- scope = scope.where(table[reflection.type].eq(bind_val))
101
- end
102
-
103
- scope = scope.joins(join(foreign_table, constraint))
144
+ unless reflection == chain.last
145
+ next_reflection = chain[i + 1]
146
+ scope = next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
104
147
  end
105
148
 
106
149
  is_first_chain = i == 0
@@ -119,7 +162,9 @@ module ActiveRecord
119
162
  scope.includes! item.includes_values
120
163
  end
121
164
 
165
+ scope.unscope!(*item.unscope_values)
122
166
  scope.where_values += item.where_values
167
+ scope.bind_values += item.bind_values
123
168
  scope.order_values |= item.order_values
124
169
  end
125
170
  end
@@ -31,6 +31,14 @@ module ActiveRecord
31
31
  @updated
32
32
  end
33
33
 
34
+ def decrement_counters # :nodoc:
35
+ with_cache_name { |name| decrement_counter name }
36
+ end
37
+
38
+ def increment_counters # :nodoc:
39
+ with_cache_name { |name| increment_counter name }
40
+ end
41
+
34
42
  private
35
43
 
36
44
  def find_target?
@@ -51,23 +59,28 @@ module ActiveRecord
51
59
  end
52
60
  end
53
61
 
54
- def decrement_counters
55
- with_cache_name { |name| decrement_counter name }
62
+ def decrement_counter(counter_cache_name)
63
+ if foreign_key_present?
64
+ klass.decrement_counter(counter_cache_name, target_id)
65
+ end
56
66
  end
57
67
 
58
- def decrement_counter counter_cache_name
68
+ def increment_counter(counter_cache_name)
59
69
  if foreign_key_present?
60
- klass.decrement_counter(counter_cache_name, target_id)
70
+ klass.increment_counter(counter_cache_name, target_id)
71
+ if target && !stale_target? && counter_cache_available_in_memory?(counter_cache_name)
72
+ target.increment(counter_cache_name)
73
+ end
61
74
  end
62
75
  end
63
76
 
64
77
  # Checks whether record is different to the current target, without loading it
65
78
  def different_target?(record)
66
- record.id != owner[reflection.foreign_key]
79
+ record.id != owner._read_attribute(reflection.foreign_key)
67
80
  end
68
81
 
69
82
  def replace_keys(record)
70
- owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)]
83
+ owner[reflection.foreign_key] = record._read_attribute(reflection.association_primary_key(record.class))
71
84
  end
72
85
 
73
86
  def remove_keys
@@ -75,26 +88,31 @@ module ActiveRecord
75
88
  end
76
89
 
77
90
  def foreign_key_present?
78
- owner[reflection.foreign_key]
91
+ owner._read_attribute(reflection.foreign_key)
79
92
  end
80
93
 
81
94
  # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
82
95
  # has_one associations.
83
96
  def invertible_for?(record)
84
97
  inverse = inverse_reflection_for(record)
85
- inverse && inverse.macro == :has_one
98
+ inverse && inverse.has_one?
86
99
  end
87
100
 
88
101
  def target_id
89
102
  if options[:primary_key]
90
103
  owner.send(reflection.name).try(:id)
91
104
  else
92
- owner[reflection.foreign_key]
105
+ owner._read_attribute(reflection.foreign_key)
93
106
  end
94
107
  end
95
108
 
96
109
  def stale_state
97
- owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s
110
+ result = owner._read_attribute(reflection.foreign_key)
111
+ result && result.to_s
112
+ end
113
+
114
+ def counter_cache_available_in_memory?(counter_cache_name)
115
+ target.respond_to?(counter_cache_name)
98
116
  end
99
117
  end
100
118
  end
@@ -36,6 +36,7 @@ module ActiveRecord::Associations::Builder
36
36
  reflection = builder.build(model)
37
37
  define_accessors model, reflection
38
38
  define_callbacks model, reflection
39
+ define_validations model, reflection
39
40
  builder.define_extensions model
40
41
  reflection
41
42
  end
@@ -85,7 +86,11 @@ module ActiveRecord::Associations::Builder
85
86
  end
86
87
 
87
88
  def self.define_callbacks(model, reflection)
88
- add_before_destroy_callbacks(model, reflection) if reflection.options[:dependent]
89
+ if dependent = reflection.options[:dependent]
90
+ check_dependent_options(dependent)
91
+ add_destroy_callbacks(model, reflection)
92
+ end
93
+
89
94
  Association.extensions.each do |extension|
90
95
  extension.build model, reflection
91
96
  end
@@ -120,17 +125,23 @@ module ActiveRecord::Associations::Builder
120
125
  CODE
121
126
  end
122
127
 
128
+ def self.define_validations(model, reflection)
129
+ # noop
130
+ end
131
+
123
132
  def self.valid_dependent_options
124
133
  raise NotImplementedError
125
134
  end
126
135
 
127
136
  private
128
137
 
129
- def self.add_before_destroy_callbacks(model, reflection)
130
- unless valid_dependent_options.include? reflection.options[:dependent]
131
- raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{reflection.options[:dependent]}"
138
+ def self.check_dependent_options(dependent)
139
+ unless valid_dependent_options.include? dependent
140
+ raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
132
141
  end
142
+ end
133
143
 
144
+ def self.add_destroy_callbacks(model, reflection)
134
145
  name = reflection.name
135
146
  model.before_destroy lambda { |o| o.association(name).handle_dependency }
136
147
  end
@@ -26,28 +26,9 @@ module ActiveRecord::Associations::Builder
26
26
  private
27
27
 
28
28
  def self.add_counter_cache_methods(mixin)
29
- return if mixin.method_defined? :belongs_to_counter_cache_after_create
29
+ return if mixin.method_defined? :belongs_to_counter_cache_after_update
30
30
 
31
31
  mixin.class_eval do
32
- def belongs_to_counter_cache_after_create(reflection)
33
- if record = send(reflection.name)
34
- cache_column = reflection.counter_cache_column
35
- record.class.increment_counter(cache_column, record.id)
36
- @_after_create_counter_called = true
37
- end
38
- end
39
-
40
- def belongs_to_counter_cache_before_destroy(reflection)
41
- foreign_key = reflection.foreign_key.to_sym
42
- unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
43
- record = send reflection.name
44
- if record && !self.destroyed?
45
- cache_column = reflection.counter_cache_column
46
- record.class.decrement_counter(cache_column, record.id)
47
- end
48
- end
49
- end
50
-
51
32
  def belongs_to_counter_cache_after_update(reflection)
52
33
  foreign_key = reflection.foreign_key
53
34
  cache_column = reflection.counter_cache_column
@@ -73,14 +54,6 @@ module ActiveRecord::Associations::Builder
73
54
  def self.add_counter_cache_callbacks(model, reflection)
74
55
  cache_column = reflection.counter_cache_column
75
56
 
76
- model.after_create lambda { |record|
77
- record.belongs_to_counter_cache_after_create(reflection)
78
- }
79
-
80
- model.before_destroy lambda { |record|
81
- record.belongs_to_counter_cache_before_destroy(reflection)
82
- }
83
-
84
57
  model.after_update lambda { |record|
85
58
  record.belongs_to_counter_cache_after_update(reflection)
86
59
  }
@@ -130,9 +103,14 @@ module ActiveRecord::Associations::Builder
130
103
  BelongsTo.touch_record(record, foreign_key, n, touch)
131
104
  }
132
105
 
133
- model.after_save callback
106
+ model.after_save callback, if: :changed?
134
107
  model.after_touch callback
135
108
  model.after_destroy callback
136
109
  end
110
+
111
+ def self.add_destroy_callbacks(model, reflection)
112
+ name = reflection.name
113
+ model.after_destroy lambda { |o| o.association(name).handle_dependency }
114
+ end
137
115
  end
138
116
  end
@@ -82,7 +82,11 @@ module ActiveRecord::Associations::Builder
82
82
 
83
83
  def wrap_scope(scope, mod)
84
84
  if scope
85
- proc { |owner| instance_exec(owner, &scope).extending(mod) }
85
+ if scope.arity > 0
86
+ proc { |owner| instance_exec(owner, &scope).extending(mod) }
87
+ else
88
+ proc { instance_exec(&scope).extending(mod) }
89
+ end
86
90
  else
87
91
  proc { extending(mod) }
88
92
  end