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
@@ -1,39 +1,42 @@
1
- require 'active_support/core_ext/object/blank'
2
- require 'active_support/core_ext/module/delegation'
1
+ # -*- coding: utf-8 -*-
2
+ require 'arel/collectors/bind'
3
3
 
4
4
  module ActiveRecord
5
5
  # = Active Record Relation
6
6
  class Relation
7
- JoinOperation = Struct.new(:relation, :join_class, :on)
8
- ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
9
- MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind]
10
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reorder, :reverse_order]
7
+ MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
8
+ :order, :joins, :where, :having, :bind, :references,
9
+ :extending, :unscope]
11
10
 
12
- include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
11
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
12
+ :reverse_order, :distinct, :create_with, :uniq]
13
+ INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
13
14
 
14
- # These are explicitly delegated to improve performance (avoids method_missing)
15
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
16
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :to => :klass
15
+ VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
16
+
17
+ include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
17
18
 
18
19
  attr_reader :table, :klass, :loaded
19
- attr_accessor :extensions, :default_scoped
20
+ alias :model :klass
20
21
  alias :loaded? :loaded
21
- alias :default_scoped? :default_scoped
22
-
23
- def initialize(klass, table)
24
- @klass, @table = klass, table
25
22
 
26
- @implicit_readonly = nil
27
- @loaded = false
28
- @default_scoped = false
23
+ def initialize(klass, table, values = {})
24
+ @klass = klass
25
+ @table = table
26
+ @values = values
27
+ @offsets = {}
28
+ @loaded = false
29
+ end
29
30
 
30
- SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
31
- (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
32
- @extensions = []
33
- @create_with_value = {}
31
+ def initialize_copy(other)
32
+ # This method is a hot spot, so for now, use Hash[] to dup the hash.
33
+ # https://bugs.ruby-lang.org/issues/7166
34
+ @values = Hash[@values]
35
+ @values[:bind] = @values[:bind].dup if @values.key? :bind
36
+ reset
34
37
  end
35
38
 
36
- def insert(values)
39
+ def insert(values) # :nodoc:
37
40
  primary_key_value = nil
38
41
 
39
42
  if primary_key && Hash === values
@@ -50,16 +53,7 @@ module ActiveRecord
50
53
  im = arel.create_insert
51
54
  im.into @table
52
55
 
53
- conn = @klass.connection
54
-
55
- substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
56
- binds = substitutes.map do |arel_attr, value|
57
- [@klass.columns_hash[arel_attr.name], value]
58
- end
59
-
60
- substitutes.each_with_index do |tuple, i|
61
- tuple[1] = conn.substitute_at(binds[i][0], i)
62
- end
56
+ substitutes, binds = substitute_values values
63
57
 
64
58
  if values.empty? # empty insert
65
59
  im.values = Arel.sql(connection.empty_insert_statement_value)
@@ -67,7 +61,7 @@ module ActiveRecord
67
61
  im.insert substitutes
68
62
  end
69
63
 
70
- conn.insert(
64
+ @klass.connection.insert(
71
65
  im,
72
66
  'SQL',
73
67
  primary_key,
@@ -76,81 +70,207 @@ module ActiveRecord
76
70
  binds)
77
71
  end
78
72
 
79
- def new(*args, &block)
80
- scoping { @klass.new(*args, &block) }
73
+ def _update_record(values, id, id_was) # :nodoc:
74
+ substitutes, binds = substitute_values values
75
+
76
+ scope = @klass.unscoped
77
+
78
+ if @klass.finder_needs_type_condition?
79
+ scope.unscope!(where: @klass.inheritance_column)
80
+ end
81
+
82
+ relation = scope.where(@klass.primary_key => (id_was || id))
83
+ bvs = binds + relation.bind_values
84
+ um = relation
85
+ .arel
86
+ .compile_update(substitutes, @klass.primary_key)
87
+
88
+ @klass.connection.update(
89
+ um,
90
+ 'SQL',
91
+ bvs,
92
+ )
81
93
  end
82
94
 
83
- def initialize_copy(other)
84
- @bind_values = @bind_values.dup
85
- reset
95
+ def substitute_values(values) # :nodoc:
96
+ binds = values.map do |arel_attr, value|
97
+ [@klass.columns_hash[arel_attr.name], value]
98
+ end
99
+
100
+ substitutes = values.each_with_index.map do |(arel_attr, _), i|
101
+ [arel_attr, @klass.connection.substitute_at(binds[i][0])]
102
+ end
103
+
104
+ [substitutes, binds]
105
+ end
106
+
107
+ # Initializes new record from relation while maintaining the current
108
+ # scope.
109
+ #
110
+ # Expects arguments in the same format as +Base.new+.
111
+ #
112
+ # users = User.where(name: 'DHH')
113
+ # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
114
+ #
115
+ # You can also pass a block to new with the new record as argument:
116
+ #
117
+ # user = users.new { |user| user.name = 'Oscar' }
118
+ # user.name # => Oscar
119
+ def new(*args, &block)
120
+ scoping { @klass.new(*args, &block) }
86
121
  end
87
122
 
88
123
  alias build new
89
124
 
125
+ # Tries to create a new record with the same scoped attributes
126
+ # defined in the relation. Returns the initialized object if validation fails.
127
+ #
128
+ # Expects arguments in the same format as +Base.create+.
129
+ #
130
+ # ==== Examples
131
+ # users = User.where(name: 'Oscar')
132
+ # users.create # #<User id: 3, name: "oscar", ...>
133
+ #
134
+ # users.create(name: 'fxn')
135
+ # users.create # #<User id: 4, name: "fxn", ...>
136
+ #
137
+ # users.create { |user| user.name = 'tenderlove' }
138
+ # # #<User id: 5, name: "tenderlove", ...>
139
+ #
140
+ # users.create(name: nil) # validation on name
141
+ # # #<User id: nil, name: nil, ...>
90
142
  def create(*args, &block)
91
143
  scoping { @klass.create(*args, &block) }
92
144
  end
93
145
 
146
+ # Similar to #create, but calls +create!+ on the base class. Raises
147
+ # an exception if a validation error occurs.
148
+ #
149
+ # Expects arguments in the same format as <tt>Base.create!</tt>.
94
150
  def create!(*args, &block)
95
151
  scoping { @klass.create!(*args, &block) }
96
152
  end
97
153
 
98
- def respond_to?(method, include_private = false)
99
- arel.respond_to?(method, include_private) ||
100
- Array.method_defined?(method) ||
101
- @klass.respond_to?(method, include_private) ||
102
- super
154
+ def first_or_create(attributes = nil, &block) # :nodoc:
155
+ first || create(attributes, &block)
103
156
  end
104
157
 
105
- def to_a
106
- return @records if loaded?
158
+ def first_or_create!(attributes = nil, &block) # :nodoc:
159
+ first || create!(attributes, &block)
160
+ end
107
161
 
108
- default_scoped = with_default_scope
162
+ def first_or_initialize(attributes = nil, &block) # :nodoc:
163
+ first || new(attributes, &block)
164
+ end
109
165
 
110
- if default_scoped.equal?(self)
111
- @records = if @readonly_value.nil? && !@klass.locking_enabled?
112
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
113
- else
114
- IdentityMap.without do
115
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
116
- end
117
- end
166
+ # Finds the first record with the given attributes, or creates a record
167
+ # with the attributes if one is not found:
168
+ #
169
+ # # Find the first user named "Penélope" or create a new one.
170
+ # User.find_or_create_by(first_name: 'Penélope')
171
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
172
+ #
173
+ # # Find the first user named "Penélope" or create a new one.
174
+ # # We already have one so the existing record will be returned.
175
+ # User.find_or_create_by(first_name: 'Penélope')
176
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
177
+ #
178
+ # # Find the first user named "Scarlett" or create a new one with
179
+ # # a particular last name.
180
+ # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
181
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
182
+ #
183
+ # This method accepts a block, which is passed down to +create+. The last example
184
+ # above can be alternatively written this way:
185
+ #
186
+ # # Find the first user named "Scarlett" or create a new one with a
187
+ # # different last name.
188
+ # User.find_or_create_by(first_name: 'Scarlett') do |user|
189
+ # user.last_name = 'Johansson'
190
+ # end
191
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
192
+ #
193
+ # This method always returns a record, but if creation was attempted and
194
+ # failed due to validation errors it won't be persisted, you get what
195
+ # +create+ returns in such situation.
196
+ #
197
+ # Please note *this method is not atomic*, it runs first a SELECT, and if
198
+ # there are no results an INSERT is attempted. If there are other threads
199
+ # or processes there is a race condition between both calls and it could
200
+ # be the case that you end up with two similar records.
201
+ #
202
+ # Whether that is a problem or not depends on the logic of the
203
+ # application, but in the particular case in which rows have a UNIQUE
204
+ # constraint an exception may be raised, just retry:
205
+ #
206
+ # begin
207
+ # CreditAccount.find_or_create_by(user_id: user.id)
208
+ # rescue ActiveRecord::RecordNotUnique
209
+ # retry
210
+ # end
211
+ #
212
+ def find_or_create_by(attributes, &block)
213
+ find_by(attributes) || create(attributes, &block)
214
+ end
118
215
 
119
- preload = @preload_values
120
- preload += @includes_values unless eager_loading?
121
- preload.each do |associations|
122
- ActiveRecord::Associations::Preloader.new(@records, associations).run
123
- end
216
+ # Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception
217
+ # is raised if the created record is invalid.
218
+ def find_or_create_by!(attributes, &block)
219
+ find_by(attributes) || create!(attributes, &block)
220
+ end
124
221
 
125
- # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
126
- # are JOINS and no explicit SELECT.
127
- readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
128
- @records.each { |record| record.readonly! } if readonly
129
- else
130
- @records = default_scoped.to_a
131
- end
222
+ # Like <tt>find_or_create_by</tt>, but calls <tt>new</tt> instead of <tt>create</tt>.
223
+ def find_or_initialize_by(attributes, &block)
224
+ find_by(attributes) || new(attributes, &block)
225
+ end
132
226
 
133
- @loaded = true
227
+ # Runs EXPLAIN on the query or queries triggered by this relation and
228
+ # returns the result as a string. The string is formatted imitating the
229
+ # ones printed by the database shell.
230
+ #
231
+ # Note that this method actually runs the queries, since the results of some
232
+ # are needed by the next ones when eager loading is going on.
233
+ #
234
+ # Please see further details in the
235
+ # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
236
+ def explain
237
+ #TODO: Fix for binds.
238
+ exec_explain(collecting_queries_for_explain { exec_queries })
239
+ end
240
+
241
+ # Converts relation objects to Array.
242
+ def to_a
243
+ load
134
244
  @records
135
245
  end
136
246
 
247
+ # Serializes the relation objects Array.
248
+ def encode_with(coder)
249
+ coder.represent_seq(nil, to_a)
250
+ end
251
+
137
252
  def as_json(options = nil) #:nodoc:
138
253
  to_a.as_json(options)
139
254
  end
140
255
 
141
256
  # Returns size of the records.
142
257
  def size
143
- loaded? ? @records.length : count
258
+ loaded? ? @records.length : count(:all)
144
259
  end
145
260
 
146
261
  # Returns true if there are no records.
147
262
  def empty?
148
263
  return @records.empty? if loaded?
149
264
 
150
- c = count
151
- c.respond_to?(:zero?) ? c.zero? : c.empty?
265
+ if limit_value == 0
266
+ true
267
+ else
268
+ c = count(:all)
269
+ c.respond_to?(:zero?) ? c.zero? : c.empty?
270
+ end
152
271
  end
153
272
 
273
+ # Returns true if there are any records.
154
274
  def any?
155
275
  if block_given?
156
276
  to_a.any? { |*block_args| yield(*block_args) }
@@ -159,80 +279,70 @@ module ActiveRecord
159
279
  end
160
280
  end
161
281
 
282
+ # Returns true if there is more than one record.
162
283
  def many?
163
284
  if block_given?
164
285
  to_a.many? { |*block_args| yield(*block_args) }
165
286
  else
166
- @limit_value ? to_a.many? : size > 1
287
+ limit_value ? to_a.many? : size > 1
167
288
  end
168
289
  end
169
290
 
170
291
  # Scope all queries to the current scope.
171
292
  #
172
- # ==== Example
173
- #
174
- # Comment.where(:post_id => 1).scoping do
175
- # Comment.first # SELECT * FROM comments WHERE post_id = 1
293
+ # Comment.where(post_id: 1).scoping do
294
+ # Comment.first
176
295
  # end
296
+ # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
177
297
  #
178
298
  # Please check unscoped if you want to remove all previous scopes (including
179
299
  # the default_scope) during the execution of a block.
180
300
  def scoping
181
- @klass.send(:with_scope, self, :overwrite) { yield }
301
+ previous, klass.current_scope = klass.current_scope, self
302
+ yield
303
+ ensure
304
+ klass.current_scope = previous
182
305
  end
183
306
 
184
- # Updates all records with details given if they match a set of conditions supplied, limits and order can
185
- # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
186
- # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
187
- # or validations.
307
+ # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
308
+ # statement and sends it straight to the database. It does not instantiate the involved models and it does not
309
+ # trigger Active Record callbacks or validations. Values passed to `update_all` will not go through
310
+ # ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL
311
+ # database.
188
312
  #
189
313
  # ==== Parameters
190
314
  #
191
315
  # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
192
- # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement.
193
- # See conditions in the intro.
194
- # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
195
316
  #
196
317
  # ==== Examples
197
318
  #
198
319
  # # Update all customers with the given attributes
199
- # Customer.update_all :wants_email => true
320
+ # Customer.update_all wants_email: true
200
321
  #
201
322
  # # Update all books with 'Rails' in their title
202
- # Book.update_all "author = 'David'", "title LIKE '%Rails%'"
203
- #
204
- # # Update all avatars migrated more than a week ago
205
- # Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago]
323
+ # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
206
324
  #
207
325
  # # Update all books that match conditions, but limit it to 5 ordered by date
208
- # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5
209
- #
210
- # # Conditions from the current relation also works
211
- # Book.where('title LIKE ?', '%Rails%').update_all(:author => 'David')
212
- #
213
- # # The same idea applies to limit and order
214
- # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(:author => 'David')
215
- def update_all(updates, conditions = nil, options = {})
216
- IdentityMap.repository[symbolized_base_class].clear if IdentityMap.enabled?
217
- if conditions || options.present?
218
- where(conditions).apply_finder_options(options.slice(:limit, :order)).update_all(updates)
219
- else
220
- stmt = Arel::UpdateManager.new(arel.engine)
326
+ # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
327
+ def update_all(updates)
328
+ raise ArgumentError, "Empty list of attributes to change" if updates.blank?
221
329
 
222
- stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
223
- stmt.table(table)
224
- stmt.key = table[primary_key]
330
+ stmt = Arel::UpdateManager.new(arel.engine)
225
331
 
226
- if joins_values.any?
227
- @klass.connection.join_to_update(stmt, arel)
228
- else
229
- stmt.take(arel.limit)
230
- stmt.order(*arel.orders)
231
- stmt.wheres = arel.constraints
232
- end
332
+ stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
333
+ stmt.table(table)
334
+ stmt.key = table[primary_key]
233
335
 
234
- @klass.connection.update stmt, 'SQL', bind_values
336
+ if joins_values.any?
337
+ @klass.connection.join_to_update(stmt, arel)
338
+ else
339
+ stmt.take(arel.limit)
340
+ stmt.order(*arel.orders)
341
+ stmt.wheres = arel.constraints
235
342
  end
343
+
344
+ bvs = arel.bind_values + bind_values
345
+ @klass.connection.update stmt, 'SQL', bvs
236
346
  end
237
347
 
238
348
  # Updates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -246,29 +356,26 @@ module ActiveRecord
246
356
  # ==== Examples
247
357
  #
248
358
  # # Updates one record
249
- # Person.update(15, :user_name => 'Samuel', :group => 'expert')
359
+ # Person.update(15, user_name: 'Samuel', group: 'expert')
250
360
  #
251
361
  # # Updates multiple records
252
362
  # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
253
363
  # Person.update(people.keys, people.values)
254
364
  def update(id, attributes)
255
365
  if id.is_a?(Array)
256
- idx = -1
257
- id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
366
+ id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
258
367
  else
259
368
  object = find(id)
260
- object.update_attributes(attributes)
369
+ object.update(attributes)
261
370
  object
262
371
  end
263
372
  end
264
373
 
265
374
  # Destroys the records matching +conditions+ by instantiating each
266
375
  # record and calling its +destroy+ method. Each object's callbacks are
267
- # executed (including <tt>:dependent</tt> association options and
268
- # +before_destroy+/+after_destroy+ Observer methods). Returns the
376
+ # executed (including <tt>:dependent</tt> association options). Returns the
269
377
  # collection of objects that were destroyed; each will be frozen, to
270
- # reflect that no changes should be made (since they can't be
271
- # persisted).
378
+ # reflect that no changes should be made (since they can't be persisted).
272
379
  #
273
380
  # Note: Instantiation, callback execution, and deletion of each
274
381
  # record can be time consuming when you're removing many records at
@@ -287,8 +394,8 @@ module ActiveRecord
287
394
  # ==== Examples
288
395
  #
289
396
  # Person.destroy_all("last_login < '2004-04-04'")
290
- # Person.destroy_all(:status => "inactive")
291
- # Person.where(:age => 0..18).destroy_all
397
+ # Person.destroy_all(status: "inactive")
398
+ # Person.where(age: 0..18).destroy_all
292
399
  def destroy_all(conditions = nil)
293
400
  if conditions
294
401
  where(conditions).destroy_all
@@ -297,8 +404,8 @@ module ActiveRecord
297
404
  end
298
405
  end
299
406
 
300
- # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
301
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
407
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
408
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
302
409
  # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
303
410
  #
304
411
  # This essentially finds the object (or multiple objects) with the given id, creates a new object
@@ -324,32 +431,51 @@ module ActiveRecord
324
431
  end
325
432
  end
326
433
 
327
- # Deletes the records matching +conditions+ without instantiating the records first, and hence not
328
- # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
329
- # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
330
- # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
331
- # the number of rows affected.
332
- #
333
- # ==== Parameters
334
- #
335
- # * +conditions+ - Conditions are specified the same way as with +find+ method.
336
- #
337
- # ==== Example
434
+ # Deletes the records matching +conditions+ without instantiating the records
435
+ # first, and hence not calling the +destroy+ method nor invoking callbacks. This
436
+ # is a single SQL DELETE statement that goes straight to the database, much more
437
+ # efficient than +destroy_all+. Be careful with relations though, in particular
438
+ # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
439
+ # number of rows affected.
338
440
  #
339
441
  # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
340
442
  # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
341
- # Post.where(:person_id => 5).where(:category => ['Something', 'Else']).delete_all
443
+ # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
342
444
  #
343
445
  # Both calls delete the affected posts all at once with a single DELETE statement.
344
446
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
345
447
  # +after_destroy+ callbacks, use the +destroy_all+ method instead.
448
+ #
449
+ # If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
450
+ #
451
+ # Post.limit(100).delete_all
452
+ # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
346
453
  def delete_all(conditions = nil)
347
- IdentityMap.repository[symbolized_base_class] = {} if IdentityMap.enabled?
454
+ invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
455
+ if MULTI_VALUE_METHODS.include?(method)
456
+ send("#{method}_values").any?
457
+ else
458
+ send("#{method}_value")
459
+ end
460
+ }
461
+ if invalid_methods.any?
462
+ raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
463
+ end
464
+
348
465
  if conditions
349
466
  where(conditions).delete_all
350
467
  else
351
- statement = arel.compile_delete
352
- affected = @klass.connection.delete(statement, 'SQL', bind_values)
468
+ stmt = Arel::DeleteManager.new(arel.engine)
469
+ stmt.from(table)
470
+
471
+ if joins_values.any?
472
+ @klass.connection.join_to_delete(stmt, arel, table[primary_key])
473
+ else
474
+ stmt.wheres = arel.constraints
475
+ end
476
+
477
+ bvs = arel.bind_values + bind_values
478
+ affected = @klass.connection.delete(stmt, 'SQL', bvs)
353
479
 
354
480
  reset
355
481
  affected
@@ -359,8 +485,7 @@ module ActiveRecord
359
485
  # Deletes the row with a primary key matching the +id+ argument, using a
360
486
  # SQL +DELETE+ statement, and returns the number of rows deleted. Active
361
487
  # Record objects are not instantiated, so the object's callbacks are not
362
- # executed, including any <tt>:dependent</tt> association options or
363
- # Observer methods.
488
+ # executed, including any <tt>:dependent</tt> association options.
364
489
  #
365
490
  # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
366
491
  #
@@ -377,55 +502,110 @@ module ActiveRecord
377
502
  # # Delete multiple rows
378
503
  # Todo.delete([2,3,4])
379
504
  def delete(id_or_array)
380
- IdentityMap.remove_by_id(self.symbolized_base_class, id_or_array) if IdentityMap.enabled?
381
505
  where(primary_key => id_or_array).delete_all
382
506
  end
383
507
 
508
+ # Causes the records to be loaded from the database if they have not
509
+ # been loaded already. You can use this if for some reason you need
510
+ # to explicitly load some records before actually using them. The
511
+ # return value is the relation itself, not the records.
512
+ #
513
+ # Post.where(published: true).load # => #<ActiveRecord::Relation>
514
+ def load
515
+ exec_queries unless loaded?
516
+
517
+ self
518
+ end
519
+
520
+ # Forces reloading of relation.
384
521
  def reload
385
522
  reset
386
- to_a # force reload
387
- self
523
+ load
388
524
  end
389
525
 
390
526
  def reset
391
- @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
527
+ @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
392
528
  @should_eager_load = @join_dependency = nil
393
529
  @records = []
530
+ @offsets = {}
394
531
  self
395
532
  end
396
533
 
534
+ # Returns sql statement for the relation.
535
+ #
536
+ # User.where(name: 'Oscar').to_sql
537
+ # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
397
538
  def to_sql
398
- @to_sql ||= klass.connection.to_sql(arel, @bind_values.dup)
399
- end
400
-
401
- def where_values_hash
402
- equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
403
- node.left.relation.name == table_name
539
+ @to_sql ||= begin
540
+ relation = self
541
+ connection = klass.connection
542
+ visitor = connection.visitor
543
+
544
+ if eager_loading?
545
+ find_with_associations { |rel| relation = rel }
546
+ end
547
+
548
+ arel = relation.arel
549
+ binds = (arel.bind_values + relation.bind_values).dup
550
+ binds.map! { |bv| connection.quote(*bv.reverse) }
551
+ collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
552
+ collect.substitute_binds(binds).join
553
+ end
554
+ end
555
+
556
+ # Returns a hash of where conditions.
557
+ #
558
+ # User.where(name: 'Oscar').where_values_hash
559
+ # # => {name: "Oscar"}
560
+ def where_values_hash(relation_table_name = table_name)
561
+ equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
562
+ node.left.relation.name == relation_table_name
404
563
  }
405
564
 
406
- Hash[equalities.map { |where| [where.left.name, where.right] }]
565
+ binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
566
+
567
+ Hash[equalities.map { |where|
568
+ name = where.left.name
569
+ [name, binds.fetch(name.to_s) {
570
+ case where.right
571
+ when Array then where.right.map(&:val)
572
+ when Arel::Nodes::Casted
573
+ where.right.val
574
+ end
575
+ }]
576
+ }]
407
577
  end
408
578
 
409
579
  def scope_for_create
410
580
  @scope_for_create ||= where_values_hash.merge(create_with_value)
411
581
  end
412
582
 
583
+ # Returns true if relation needs eager loading.
413
584
  def eager_loading?
414
585
  @should_eager_load ||=
415
- @eager_load_values.any? ||
416
- @includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
586
+ eager_load_values.any? ||
587
+ includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
417
588
  end
418
589
 
419
590
  # Joins that are also marked for preloading. In which case we should just eager load them.
420
591
  # Note that this is a naive implementation because we could have strings and symbols which
421
592
  # represent the same association, but that aren't matched by this. Also, we could have
422
- # nested hashes which partially match, e.g. { :a => :b } & { :a => [:b, :c] }
593
+ # nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
423
594
  def joined_includes_values
424
- @includes_values & @joins_values
595
+ includes_values & joins_values
596
+ end
597
+
598
+ # +uniq+ and +uniq!+ are silently deprecated. +uniq_value+ delegates to +distinct_value+
599
+ # to maintain backwards compatibility. Use +distinct_value+ instead.
600
+ def uniq_value
601
+ distinct_value
425
602
  end
426
603
 
604
+ # Compares two relations for equality.
427
605
  def ==(other)
428
606
  case other
607
+ when Associations::CollectionProxy, AssociationRelation
608
+ self == other.to_a
429
609
  when Relation
430
610
  other.to_sql == to_sql
431
611
  when Array
@@ -433,36 +613,48 @@ module ActiveRecord
433
613
  end
434
614
  end
435
615
 
436
- def inspect
437
- to_a.inspect
616
+ def pretty_print(q)
617
+ q.pp(self.to_a)
438
618
  end
439
619
 
440
- def with_default_scope #:nodoc:
441
- if default_scoped? && default_scope = klass.send(:build_default_scope)
442
- default_scope = default_scope.merge(self)
443
- default_scope.default_scoped = false
444
- default_scope
445
- else
446
- self
447
- end
620
+ # Returns true if relation is blank.
621
+ def blank?
622
+ to_a.blank?
448
623
  end
449
624
 
450
- protected
625
+ def values
626
+ Hash[@values]
627
+ end
451
628
 
452
- def method_missing(method, *args, &block)
453
- if Array.method_defined?(method)
454
- to_a.send(method, *args, &block)
455
- elsif @klass.respond_to?(method)
456
- scoping { @klass.send(method, *args, &block) }
457
- elsif arel.respond_to?(method)
458
- arel.send(method, *args, &block)
459
- else
460
- super
461
- end
629
+ def inspect
630
+ entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
631
+ entries[10] = '...' if entries.size == 11
632
+
633
+ "#<#{self.class.name} [#{entries.join(', ')}]>"
462
634
  end
463
635
 
464
636
  private
465
637
 
638
+ def exec_queries
639
+ @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, arel.bind_values + bind_values)
640
+
641
+ preload = preload_values
642
+ preload += includes_values unless eager_loading?
643
+ preloader = build_preloader
644
+ preload.each do |associations|
645
+ preloader.preload @records, associations
646
+ end
647
+
648
+ @records.each { |record| record.readonly! } if readonly_value
649
+
650
+ @loaded = true
651
+ @records
652
+ end
653
+
654
+ def build_preloader
655
+ ActiveRecord::Associations::Preloader.new
656
+ end
657
+
466
658
  def references_eager_loaded_tables?
467
659
  joined_tables = arel.join_sources.map do |join|
468
660
  if join.is_a?(Arel::Nodes::StringJoin)
@@ -477,7 +669,7 @@ module ActiveRecord
477
669
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
478
670
  joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
479
671
 
480
- (tables_in_string(to_sql) - joined_tables).any?
672
+ (references_values - joined_tables).any?
481
673
  end
482
674
 
483
675
  def tables_in_string(string)
@@ -486,6 +678,5 @@ module ActiveRecord
486
678
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
487
679
  string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
488
680
  end
489
-
490
681
  end
491
682
  end