activerecord 3.1.10 → 4.2.11

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.

Potentially problematic release.


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

Files changed (237) hide show
  1. checksums.yaml +6 -6
  2. data/CHANGELOG.md +1837 -338
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +39 -43
  5. data/examples/performance.rb +51 -20
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +57 -43
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -39
  10. data/lib/active_record/associations/association.rb +71 -85
  11. data/lib/active_record/associations/association_scope.rb +138 -89
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
  14. data/lib/active_record/associations/builder/association.rb +125 -29
  15. data/lib/active_record/associations/builder/belongs_to.rb +91 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +22 -29
  21. data/lib/active_record/associations/collection_association.rb +294 -187
  22. data/lib/active_record/associations/collection_proxy.rb +961 -94
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +118 -23
  25. data/lib/active_record/associations/has_many_through_association.rb +115 -45
  26. data/lib/active_record/associations/has_one_association.rb +57 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -102
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +61 -32
  38. data/lib/active_record/associations/preloader.rb +113 -87
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +37 -19
  41. data/lib/active_record/associations.rb +505 -371
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +212 -0
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +141 -51
  47. data/lib/active_record/attribute_methods/primary_key.rb +87 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +74 -117
  50. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
  52. data/lib/active_record/attribute_methods/write.rb +60 -21
  53. data/lib/active_record/attribute_methods.rb +409 -48
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +279 -232
  58. data/lib/active_record/base.rb +84 -1969
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +422 -243
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +273 -170
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
  75. data/lib/active_record/connection_adapters/column.rb +33 -221
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +445 -902
  114. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +159 -102
  119. data/lib/active_record/dynamic_matchers.rb +140 -0
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +102 -34
  122. data/lib/active_record/explain.rb +38 -0
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +29 -0
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +318 -260
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +247 -0
  129. data/lib/active_record/integration.rb +113 -0
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +80 -52
  133. data/lib/active_record/locking/pessimistic.rb +27 -5
  134. data/lib/active_record/log_subscriber.rb +25 -18
  135. data/lib/active_record/migration/command_recorder.rb +130 -38
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +532 -201
  138. data/lib/active_record/model_schema.rb +342 -0
  139. data/lib/active_record/nested_attributes.rb +229 -139
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +304 -99
  143. data/lib/active_record/query_cache.rb +25 -43
  144. data/lib/active_record/querying.rb +68 -0
  145. data/lib/active_record/railtie.rb +86 -45
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +7 -4
  148. data/lib/active_record/railties/databases.rake +198 -377
  149. data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
  150. data/lib/active_record/readonly_attributes.rb +23 -0
  151. data/lib/active_record/reflection.rb +516 -165
  152. data/lib/active_record/relation/batches.rb +96 -45
  153. data/lib/active_record/relation/calculations.rb +221 -144
  154. data/lib/active_record/relation/delegation.rb +140 -0
  155. data/lib/active_record/relation/finder_methods.rb +362 -243
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -41
  160. data/lib/active_record/relation/query_methods.rb +982 -155
  161. data/lib/active_record/relation/spawn_methods.rb +50 -110
  162. data/lib/active_record/relation.rb +371 -180
  163. data/lib/active_record/result.rb +109 -12
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +191 -0
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +111 -61
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +135 -0
  170. data/lib/active_record/scoping/named.rb +164 -0
  171. data/lib/active_record/scoping.rb +87 -0
  172. data/lib/active_record/serialization.rb +7 -45
  173. data/lib/active_record/serializers/xml_serializer.rb +14 -65
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +205 -0
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +35 -14
  181. data/lib/active_record/transactions.rb +141 -74
  182. data/lib/active_record/translation.rb +22 -0
  183. data/lib/active_record/type/big_integer.rb +13 -0
  184. data/lib/active_record/type/binary.rb +50 -0
  185. data/lib/active_record/type/boolean.rb +31 -0
  186. data/lib/active_record/type/date.rb +50 -0
  187. data/lib/active_record/type/date_time.rb +54 -0
  188. data/lib/active_record/type/decimal.rb +64 -0
  189. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  190. data/lib/active_record/type/decorator.rb +14 -0
  191. data/lib/active_record/type/float.rb +19 -0
  192. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  193. data/lib/active_record/type/integer.rb +59 -0
  194. data/lib/active_record/type/mutable.rb +16 -0
  195. data/lib/active_record/type/numeric.rb +36 -0
  196. data/lib/active_record/type/serialized.rb +62 -0
  197. data/lib/active_record/type/string.rb +40 -0
  198. data/lib/active_record/type/text.rb +11 -0
  199. data/lib/active_record/type/time.rb +26 -0
  200. data/lib/active_record/type/time_value.rb +38 -0
  201. data/lib/active_record/type/type_map.rb +64 -0
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type/value.rb +110 -0
  204. data/lib/active_record/type.rb +23 -0
  205. data/lib/active_record/validations/associated.rb +27 -18
  206. data/lib/active_record/validations/presence.rb +67 -0
  207. data/lib/active_record/validations/uniqueness.rb +125 -66
  208. data/lib/active_record/validations.rb +37 -30
  209. data/lib/active_record/version.rb +5 -7
  210. data/lib/active_record.rb +80 -25
  211. data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
  212. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  213. data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
  214. data/lib/rails/generators/active_record/migration.rb +11 -8
  215. data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
  216. data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
  217. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  218. data/lib/rails/generators/active_record.rb +3 -11
  219. metadata +132 -53
  220. data/examples/associations.png +0 -0
  221. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
  222. data/lib/active_record/associations/join_helper.rb +0 -55
  223. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
  226. data/lib/active_record/dynamic_finder_match.rb +0 -56
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/identity_map.rb +0 -163
  229. data/lib/active_record/named_scope.rb +0 -200
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -358
  232. data/lib/active_record/test_case.rb +0 -69
  233. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
  234. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  235. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  236. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  237. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -5,31 +5,62 @@ module ActiveRecord
5
5
  # Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
6
6
  # ActiveRecord::Associations::ThroughAssociationScope
7
7
  class AliasTracker # :nodoc:
8
- attr_reader :aliases, :table_joins
8
+ attr_reader :aliases, :connection
9
9
 
10
- # table_joins is an array of arel joins which might conflict with the aliases we assign here
11
- def initialize(table_joins = [])
12
- @aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
13
- @table_joins = table_joins
10
+ def self.empty(connection)
11
+ new connection, Hash.new(0)
14
12
  end
15
13
 
16
- def aliased_table_for(table_name, aliased_name = nil)
17
- table_alias = aliased_name_for(table_name, aliased_name)
18
-
19
- if table_alias == table_name
20
- Arel::Table.new(table_name)
14
+ def self.create(connection, table_joins)
15
+ if table_joins.empty?
16
+ empty connection
21
17
  else
22
- Arel::Table.new(table_name).alias(table_alias)
18
+ aliases = Hash.new { |h,k|
19
+ h[k] = initial_count_for(connection, k, table_joins)
20
+ }
21
+ new connection, aliases
23
22
  end
24
23
  end
25
24
 
26
- def aliased_name_for(table_name, aliased_name = nil)
27
- aliased_name ||= table_name
25
+ def self.initial_count_for(connection, name, table_joins)
26
+ # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
27
+ quoted_name = connection.quote_table_name(name).downcase
28
+
29
+ counts = table_joins.map do |join|
30
+ if join.is_a?(Arel::Nodes::StringJoin)
31
+ # Table names + table aliases
32
+ join.left.downcase.scan(
33
+ /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
34
+ ).size
35
+ elsif join.respond_to? :left
36
+ join.left.table_name == name ? 1 : 0
37
+ else
38
+ # this branch is reached by two tests:
39
+ #
40
+ # activerecord/test/cases/associations/cascaded_eager_loading_test.rb:37
41
+ # with :posts
42
+ #
43
+ # activerecord/test/cases/associations/eager_test.rb:1133
44
+ # with :comments
45
+ #
46
+ 0
47
+ end
48
+ end
49
+
50
+ counts.sum
51
+ end
28
52
 
53
+ # table_joins is an array of arel joins which might conflict with the aliases we assign here
54
+ def initialize(connection, aliases)
55
+ @aliases = aliases
56
+ @connection = connection
57
+ end
58
+
59
+ def aliased_table_for(table_name, aliased_name)
29
60
  if aliases[table_name].zero?
30
61
  # If it's zero, we can have our table_name
31
62
  aliases[table_name] = 1
32
- table_name
63
+ Arel::Table.new(table_name)
33
64
  else
34
65
  # Otherwise, we need to use an alias
35
66
  aliased_name = connection.table_alias_for(aliased_name)
@@ -37,43 +68,20 @@ module ActiveRecord
37
68
  # Update the count
38
69
  aliases[aliased_name] += 1
39
70
 
40
- if aliases[aliased_name] > 1
71
+ table_alias = if aliases[aliased_name] > 1
41
72
  "#{truncate(aliased_name)}_#{aliases[aliased_name]}"
42
73
  else
43
74
  aliased_name
44
75
  end
76
+ Arel::Table.new(table_name).alias(table_alias)
45
77
  end
46
78
  end
47
79
 
48
80
  private
49
81
 
50
- def initial_count_for(name)
51
- return 0 if Arel::Table === table_joins
52
-
53
- # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
54
- quoted_name = connection.quote_table_name(name).downcase
55
-
56
- counts = table_joins.map do |join|
57
- if join.is_a?(Arel::Nodes::StringJoin)
58
- # Table names + table aliases
59
- join.left.downcase.scan(
60
- /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
61
- ).size
62
- else
63
- join.left.table_name == name ? 1 : 0
64
- end
65
- end
66
-
67
- counts.sum
68
- end
69
-
70
82
  def truncate(name)
71
83
  name.slice(0, connection.table_alias_length - 2)
72
84
  end
73
-
74
- def connection
75
- ActiveRecord::Base.connection
76
- end
77
85
  end
78
86
  end
79
87
  end
@@ -1,6 +1,4 @@
1
1
  require 'active_support/core_ext/array/wrap'
2
- require 'active_support/core_ext/object/inclusion'
3
- require 'active_support/deprecation'
4
2
 
5
3
  module ActiveRecord
6
4
  module Associations
@@ -15,38 +13,37 @@ module ActiveRecord
15
13
  # BelongsToAssociation
16
14
  # BelongsToPolymorphicAssociation
17
15
  # CollectionAssociation
18
- # HasAndBelongsToManyAssociation
19
16
  # HasManyAssociation
20
17
  # HasManyThroughAssociation + ThroughAssociation
21
18
  class Association #:nodoc:
22
19
  attr_reader :owner, :target, :reflection
20
+ attr_accessor :inversed
23
21
 
24
22
  delegate :options, :to => :reflection
25
23
 
26
24
  def initialize(owner, reflection)
27
25
  reflection.check_validity!
28
26
 
29
- @target = nil
30
27
  @owner, @reflection = owner, reflection
31
- @updated = false
32
28
 
33
29
  reset
34
30
  reset_scope
35
31
  end
36
32
 
37
- # Returns the name of the table of the related class:
33
+ # Returns the name of the table of the associated class:
38
34
  #
39
35
  # post.comments.aliased_table_name # => "comments"
40
36
  #
41
37
  def aliased_table_name
42
- reflection.klass.table_name
38
+ klass.table_name
43
39
  end
44
40
 
45
41
  # Resets the \loaded flag to +false+ and sets the \target to +nil+.
46
42
  def reset
47
43
  @loaded = false
48
- IdentityMap.remove(target) if IdentityMap.enabled? && target
49
44
  @target = nil
45
+ @stale_state = nil
46
+ @inversed = false
50
47
  end
51
48
 
52
49
  # Reloads the \target and returns +self+ on success.
@@ -64,18 +61,19 @@ module ActiveRecord
64
61
 
65
62
  # Asserts the \target has been loaded setting the \loaded flag to +true+.
66
63
  def loaded!
67
- @loaded = true
64
+ @loaded = true
68
65
  @stale_state = stale_state
66
+ @inversed = false
69
67
  end
70
68
 
71
69
  # The target is stale if the target no longer points to the record(s) that the
72
70
  # relevant foreign_key(s) refers to. If stale, the association accessor method
73
71
  # on the owner will reload the target. It's up to subclasses to implement the
74
- # state_state method if relevant.
72
+ # stale_state method if relevant.
75
73
  #
76
74
  # Note that if the target has not been loaded, it is not considered stale.
77
75
  def stale_target?
78
- loaded? && @stale_state != stale_state
76
+ !inversed && loaded? && @stale_state != stale_state
79
77
  end
80
78
 
81
79
  # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
@@ -84,19 +82,19 @@ module ActiveRecord
84
82
  loaded!
85
83
  end
86
84
 
87
- def scoped
85
+ def scope
88
86
  target_scope.merge(association_scope)
89
87
  end
90
88
 
91
89
  # The scope for this association.
92
90
  #
93
91
  # Note that the association_scope is merged into the target_scope only when the
94
- # scoped method is called. This is because at that point the call may be surrounded
92
+ # scope method is called. This is because at that point the call may be surrounded
95
93
  # by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
96
94
  # actually gets built.
97
95
  def association_scope
98
96
  if klass
99
- @association_scope ||= AssociationScope.new(self).scope
97
+ @association_scope ||= AssociationScope.scope(self, klass.connection)
100
98
  end
101
99
  end
102
100
 
@@ -106,13 +104,15 @@ module ActiveRecord
106
104
 
107
105
  # Set the inverse association, if possible
108
106
  def set_inverse_instance(record)
109
- if record && invertible_for?(record)
107
+ if invertible_for?(record)
110
108
  inverse = record.association(inverse_reflection_for(record).name)
111
109
  inverse.target = owner
110
+ inverse.inversed = true
112
111
  end
112
+ record
113
113
  end
114
114
 
115
- # This class of the target. belongs_to polymorphic overrides this to look at the
115
+ # Returns the class of the target. belongs_to polymorphic overrides this to look at the
116
116
  # polymorphic_type field on the owner.
117
117
  def klass
118
118
  reflection.klass
@@ -121,7 +121,7 @@ module ActiveRecord
121
121
  # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
122
122
  # through association's scope)
123
123
  def target_scope
124
- klass.scoped
124
+ AssociationRelation.create(klass, klass.arel_table, self).merge!(klass.all)
125
125
  end
126
126
 
127
127
  # Loads the \target if needed and returns it.
@@ -135,17 +135,8 @@ module ActiveRecord
135
135
  # ActiveRecord::RecordNotFound is rescued within the method, and it is
136
136
  # not reraised. The proxy is \reset and +nil+ is the return value.
137
137
  def load_target
138
- if find_target?
139
- begin
140
- if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
141
- @target = IdentityMap.get(association_class, owner[reflection.foreign_key])
142
- end
143
- rescue NameError
144
- nil
145
- ensure
146
- @target ||= find_target
147
- end
148
- end
138
+ @target = find_target if (@stale_state && stale_target?) || find_target?
139
+
149
140
  loaded! unless loaded?
150
141
  target
151
142
  rescue ActiveRecord::RecordNotFound
@@ -154,12 +145,31 @@ module ActiveRecord
154
145
 
155
146
  def interpolate(sql, record = nil)
156
147
  if sql.respond_to?(:to_proc)
157
- owner.send(:instance_exec, record, &sql)
148
+ owner.instance_exec(record, &sql)
158
149
  else
159
150
  sql
160
151
  end
161
152
  end
162
153
 
154
+ # We can't dump @reflection since it contains the scope proc
155
+ def marshal_dump
156
+ ivars = (instance_variables - [:@reflection]).map { |name| [name, instance_variable_get(name)] }
157
+ [@reflection.name, ivars]
158
+ end
159
+
160
+ def marshal_load(data)
161
+ reflection_name, ivars = data
162
+ ivars.each { |name, val| instance_variable_set(name, val) }
163
+ @reflection = @owner.class._reflect_on_association(reflection_name)
164
+ end
165
+
166
+ def initialize_attributes(record) #:nodoc:
167
+ skip_assign = [reflection.foreign_key, reflection.type].compact
168
+ attributes = create_scope.except(*(record.changed - skip_assign))
169
+ record.assign_attributes(attributes)
170
+ set_inverse_instance(record)
171
+ end
172
+
163
173
  private
164
174
 
165
175
  def find_target?
@@ -169,7 +179,7 @@ module ActiveRecord
169
179
  def creation_attributes
170
180
  attributes = {}
171
181
 
172
- if reflection.macro.in?([:has_one, :has_many]) && !options[:through]
182
+ if (reflection.has_one? || reflection.collection?) && !options[:through]
173
183
  attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
174
184
 
175
185
  if reflection.options[:as]
@@ -185,13 +195,14 @@ module ActiveRecord
185
195
  creation_attributes.each { |key, value| record[key] = value }
186
196
  end
187
197
 
188
- # Should be true if there is a foreign key present on the owner which
198
+ # Returns true if there is a foreign key present on the owner which
189
199
  # references the target. This is used to determine whether we can load
190
200
  # the target if the owner is currently a new record (and therefore
191
- # without a key).
201
+ # without a key). If the owner is a new record then foreign_key must
202
+ # be present in order to load target.
192
203
  #
193
204
  # Currently implemented by belongs_to (vanilla and polymorphic) and
194
- # has_one/has_many :through associations which go through a belongs_to
205
+ # has_one/has_many :through associations which go through a belongs_to.
195
206
  def foreign_key_present?
196
207
  false
197
208
  end
@@ -199,10 +210,13 @@ module ActiveRecord
199
210
  # Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
200
211
  # the kind of the class of the associated objects. Meant to be used as
201
212
  # a sanity check when you are about to assign an associated record.
202
- def raise_on_type_mismatch(record)
203
- unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
204
- message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
205
- raise ActiveRecord::AssociationTypeMismatch, message
213
+ def raise_on_type_mismatch!(record)
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
206
220
  end
207
221
  end
208
222
 
@@ -213,66 +227,38 @@ module ActiveRecord
213
227
  reflection.inverse_of
214
228
  end
215
229
 
216
- # Is this association invertible? Can be redefined by subclasses.
230
+ # Returns true if inverse association on the given record needs to be set.
231
+ # This method is redefined by subclasses.
217
232
  def invertible_for?(record)
218
- inverse_reflection_for(record)
233
+ foreign_key_for?(record) && inverse_reflection_for(record)
234
+ end
235
+
236
+ # Returns true if record contains the foreign_key
237
+ def foreign_key_for?(record)
238
+ record.has_attribute?(reflection.foreign_key)
219
239
  end
220
240
 
221
241
  # This should be implemented to return the values of the relevant key(s) on the owner,
222
- # so that when state_state is different from the value stored on the last find_target,
242
+ # so that when stale_state is different from the value stored on the last find_target,
223
243
  # the target is stale.
224
244
  #
225
245
  # This is only relevant to certain associations, which is why it returns nil by default.
226
246
  def stale_state
227
247
  end
228
248
 
229
- def association_class
230
- @reflection.klass
231
- end
232
-
233
- def build_record(attributes, options)
234
- reflection.original_build_association_called = false
235
-
236
- record = reflection.build_association(attributes, options) do |r|
237
- r.assign_attributes(
238
- create_scope.except(*r.changed),
239
- :without_protection => true
240
- )
241
- end
242
-
243
- if !reflection.original_build_association_called &&
244
- (record.changed & create_scope.keys) != create_scope.keys
245
- # We have detected that there is an overridden AssociationReflection#build_association
246
- # method, but it looks like it has not passed through the block above. So try again and
247
- # show a noisy deprecation warning.
248
-
249
- record.assign_attributes(
250
- create_scope.except(*record.changed),
251
- :without_protection => true
252
- )
253
-
254
- method = reflection.method(:build_association)
255
- if RUBY_VERSION >= '1.9.2'
256
- source = method.source_location
257
- debug_info = "It looks like the method is defined in #{source[0]} at line #{source[1]}."
258
- else
259
- debug_info = "This might help you find the method: #{method}. If you run this on Ruby 1.9.2 we can tell you exactly where the method is."
260
- end
261
-
262
- ActiveSupport::Deprecation.warn <<-WARN
263
- It looks like ActiveRecord::Reflection::AssociationReflection#build_association has been redefined, either by you or by a plugin or library that you are using. The signature of this method has changed.
264
-
265
- Before: def build_association(*options)
266
- After: def build_association(*options, &block)
267
-
268
- The block argument now needs to be passed through to ActiveRecord::Base#new when this method is overridden, or else your associations will not function correctly in Rails 3.2.
269
-
270
- #{debug_info}
271
-
272
- WARN
249
+ def build_record(attributes)
250
+ reflection.build_association(attributes) do |record|
251
+ initialize_attributes(record)
273
252
  end
253
+ end
274
254
 
275
- record
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?
276
262
  end
277
263
  end
278
264
  end