activerecord 3.2.22.5 → 4.2.11.3

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 (236) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1632 -609
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +37 -41
  5. data/examples/performance.rb +31 -19
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +56 -42
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -36
  10. data/lib/active_record/associations/association.rb +73 -55
  11. data/lib/active_record/associations/association_scope.rb +143 -82
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +125 -31
  15. data/lib/active_record/associations/builder/belongs_to.rb +89 -61
  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 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +23 -17
  21. data/lib/active_record/associations/collection_association.rb +251 -177
  22. data/lib/active_record/associations/collection_proxy.rb +963 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +113 -22
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -39
  26. data/lib/active_record/associations/has_one_association.rb +43 -20
  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 -107
  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 +62 -33
  38. data/lib/active_record/associations/preloader.rb +101 -79
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +30 -16
  41. data/lib/active_record/associations.rb +463 -345
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +142 -151
  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 +137 -57
  47. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +73 -106
  50. data/lib/active_record/attribute_methods/serialization.rb +44 -94
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
  52. data/lib/active_record/attribute_methods/write.rb +57 -44
  53. data/lib/active_record/attribute_methods.rb +301 -141
  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 +246 -217
  58. data/lib/active_record/base.rb +70 -474
  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 +396 -219
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
  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 +261 -169
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
  75. data/lib/active_record/connection_adapters/column.rb +31 -245
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
  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 +430 -999
  114. data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
  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 +157 -105
  119. data/lib/active_record/dynamic_matchers.rb +119 -63
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +94 -36
  122. data/lib/active_record/explain.rb +15 -63
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +9 -5
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +302 -215
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +143 -70
  129. data/lib/active_record/integration.rb +65 -12
  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 +73 -52
  133. data/lib/active_record/locking/pessimistic.rb +5 -5
  134. data/lib/active_record/log_subscriber.rb +24 -21
  135. data/lib/active_record/migration/command_recorder.rb +124 -32
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +511 -213
  138. data/lib/active_record/model_schema.rb +91 -117
  139. data/lib/active_record/nested_attributes.rb +184 -130
  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 +276 -117
  143. data/lib/active_record/query_cache.rb +19 -37
  144. data/lib/active_record/querying.rb +28 -18
  145. data/lib/active_record/railtie.rb +73 -40
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +4 -3
  148. data/lib/active_record/railties/databases.rake +141 -416
  149. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  150. data/lib/active_record/readonly_attributes.rb +1 -4
  151. data/lib/active_record/reflection.rb +513 -154
  152. data/lib/active_record/relation/batches.rb +91 -43
  153. data/lib/active_record/relation/calculations.rb +199 -161
  154. data/lib/active_record/relation/delegation.rb +116 -25
  155. data/lib/active_record/relation/finder_methods.rb +362 -248
  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 -43
  160. data/lib/active_record/relation/query_methods.rb +928 -167
  161. data/lib/active_record/relation/spawn_methods.rb +48 -149
  162. data/lib/active_record/relation.rb +352 -207
  163. data/lib/active_record/result.rb +101 -10
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +56 -59
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +106 -63
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +50 -57
  170. data/lib/active_record/scoping/named.rb +73 -109
  171. data/lib/active_record/scoping.rb +58 -123
  172. data/lib/active_record/serialization.rb +6 -2
  173. data/lib/active_record/serializers/xml_serializer.rb +12 -22
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +168 -15
  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 +23 -16
  181. data/lib/active_record/transactions.rb +125 -79
  182. data/lib/active_record/type/big_integer.rb +13 -0
  183. data/lib/active_record/type/binary.rb +50 -0
  184. data/lib/active_record/type/boolean.rb +31 -0
  185. data/lib/active_record/type/date.rb +50 -0
  186. data/lib/active_record/type/date_time.rb +54 -0
  187. data/lib/active_record/type/decimal.rb +64 -0
  188. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  189. data/lib/active_record/type/decorator.rb +14 -0
  190. data/lib/active_record/type/float.rb +19 -0
  191. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  192. data/lib/active_record/type/integer.rb +59 -0
  193. data/lib/active_record/type/mutable.rb +16 -0
  194. data/lib/active_record/type/numeric.rb +36 -0
  195. data/lib/active_record/type/serialized.rb +62 -0
  196. data/lib/active_record/type/string.rb +40 -0
  197. data/lib/active_record/type/text.rb +11 -0
  198. data/lib/active_record/type/time.rb +26 -0
  199. data/lib/active_record/type/time_value.rb +38 -0
  200. data/lib/active_record/type/type_map.rb +64 -0
  201. data/lib/active_record/type/unsigned_integer.rb +15 -0
  202. data/lib/active_record/type/value.rb +110 -0
  203. data/lib/active_record/type.rb +23 -0
  204. data/lib/active_record/validations/associated.rb +24 -16
  205. data/lib/active_record/validations/presence.rb +67 -0
  206. data/lib/active_record/validations/uniqueness.rb +123 -64
  207. data/lib/active_record/validations.rb +36 -29
  208. data/lib/active_record/version.rb +5 -7
  209. data/lib/active_record.rb +66 -46
  210. data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
  211. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
  212. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  213. data/lib/rails/generators/active_record/migration.rb +11 -8
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
  215. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  216. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  217. data/lib/rails/generators/active_record.rb +3 -11
  218. metadata +101 -45
  219. data/examples/associations.png +0 -0
  220. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  221. data/lib/active_record/associations/join_helper.rb +0 -55
  222. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  223. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  226. data/lib/active_record/dynamic_finder_match.rb +0 -68
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/fixtures/file.rb +0 -65
  229. data/lib/active_record/identity_map.rb +0 -162
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -360
  232. data/lib/active_record/test_case.rb +0 -73
  233. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  234. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  235. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  236. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,35 +1,42 @@
1
1
  # -*- coding: utf-8 -*-
2
- require 'active_support/core_ext/object/blank'
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, :reordering, :reverse_order, :uniq]
7
+ MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
8
+ :order, :joins, :where, :having, :bind, :references,
9
+ :extending, :unscope]
10
+
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]
14
+
15
+ VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
11
16
 
12
17
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
13
18
 
14
19
  attr_reader :table, :klass, :loaded
15
- attr_accessor :extensions, :default_scoped
20
+ alias :model :klass
16
21
  alias :loaded? :loaded
17
- alias :default_scoped? :default_scoped
18
-
19
- def initialize(klass, table)
20
- @klass, @table = klass, table
21
22
 
22
- @implicit_readonly = nil
23
- @loaded = false
24
- @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
25
30
 
26
- SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
27
- (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
28
- @extensions = []
29
- @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
30
37
  end
31
38
 
32
- def insert(values)
39
+ def insert(values) # :nodoc:
33
40
  primary_key_value = nil
34
41
 
35
42
  if primary_key && Hash === values
@@ -46,16 +53,7 @@ module ActiveRecord
46
53
  im = arel.create_insert
47
54
  im.into @table
48
55
 
49
- conn = @klass.connection
50
-
51
- substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
52
- binds = substitutes.map do |arel_attr, value|
53
- [@klass.columns_hash[arel_attr.name], value]
54
- end
55
-
56
- substitutes.each_with_index do |tuple, i|
57
- tuple[1] = conn.substitute_at(binds[i][0], i)
58
- end
56
+ substitutes, binds = substitute_values values
59
57
 
60
58
  if values.empty? # empty insert
61
59
  im.values = Arel.sql(connection.empty_insert_statement_value)
@@ -63,7 +61,7 @@ module ActiveRecord
63
61
  im.insert substitutes
64
62
  end
65
63
 
66
- conn.insert(
64
+ @klass.connection.insert(
67
65
  im,
68
66
  'SQL',
69
67
  primary_key,
@@ -72,65 +70,158 @@ module ActiveRecord
72
70
  binds)
73
71
  end
74
72
 
75
- def new(*args, &block)
76
- 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
+ )
77
93
  end
78
94
 
79
- def initialize_copy(other)
80
- @bind_values = @bind_values.dup
81
- 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) }
82
121
  end
83
122
 
84
123
  alias build new
85
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, ...>
86
142
  def create(*args, &block)
87
143
  scoping { @klass.create(*args, &block) }
88
144
  end
89
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>.
90
150
  def create!(*args, &block)
91
151
  scoping { @klass.create!(*args, &block) }
92
152
  end
93
153
 
94
- # Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
95
- #
96
- # Expects arguments in the same format as <tt>Base.create</tt>.
154
+ def first_or_create(attributes = nil, &block) # :nodoc:
155
+ first || create(attributes, &block)
156
+ end
157
+
158
+ def first_or_create!(attributes = nil, &block) # :nodoc:
159
+ first || create!(attributes, &block)
160
+ end
161
+
162
+ def first_or_initialize(attributes = nil, &block) # :nodoc:
163
+ first || new(attributes, &block)
164
+ end
165
+
166
+ # Finds the first record with the given attributes, or creates a record
167
+ # with the attributes if one is not found:
97
168
  #
98
- # ==== Examples
99
- # # Find the first user named Penélope or create a new one.
100
- # User.where(:first_name => 'Penélope').first_or_create
101
- # # => <User id: 1, first_name: 'Penélope', last_name: nil>
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>
102
172
  #
103
- # # Find the first user named Penélope or create a new one.
173
+ # # Find the first user named "Penélope" or create a new one.
104
174
  # # We already have one so the existing record will be returned.
105
- # User.where(:first_name => 'Penélope').first_or_create
106
- # # => <User id: 1, first_name: 'Penélope', last_name: nil>
175
+ # User.find_or_create_by(first_name: 'Penélope')
176
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
107
177
  #
108
- # # Find the first user named Scarlett or create a new one with a particular last name.
109
- # User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
110
- # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
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">
111
182
  #
112
- # # Find the first user named Scarlett or create a new one with a different last name.
113
- # # We already have one so the existing record will be returned.
114
- # User.where(:first_name => 'Scarlett').first_or_create do |user|
115
- # user.last_name = "O'Hara"
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'
116
190
  # end
117
- # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
118
- def first_or_create(attributes = nil, options = {}, &block)
119
- first || create(attributes, options, &block)
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)
120
214
  end
121
215
 
122
- # Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
123
- #
124
- # Expects arguments in the same format as <tt>Base.create!</tt>.
125
- def first_or_create!(attributes = nil, options = {}, &block)
126
- first || create!(attributes, options, &block)
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)
127
220
  end
128
221
 
129
- # Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
130
- #
131
- # Expects arguments in the same format as <tt>Base.new</tt>.
132
- def first_or_initialize(attributes = nil, options = {}, &block)
133
- first || new(attributes, options, &block)
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)
134
225
  end
135
226
 
136
227
  # Runs EXPLAIN on the query or queries triggered by this relation and
@@ -141,58 +232,22 @@ module ActiveRecord
141
232
  # are needed by the next ones when eager loading is going on.
142
233
  #
143
234
  # Please see further details in the
144
- # {Active Record Query Interface guide}[http://edgeguides.rubyonrails.org/active_record_querying.html#running-explain].
235
+ # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
145
236
  def explain
146
- _, queries = collecting_queries_for_explain { exec_queries }
147
- exec_explain(queries)
237
+ #TODO: Fix for binds.
238
+ exec_explain(collecting_queries_for_explain { exec_queries })
148
239
  end
149
240
 
241
+ # Converts relation objects to Array.
150
242
  def to_a
151
- # We monitor here the entire execution rather than individual SELECTs
152
- # because from the point of view of the user fetching the records of a
153
- # relation is a single unit of work. You want to know if this call takes
154
- # too long, not if the individual queries take too long.
155
- #
156
- # It could be the case that none of the queries involved surpass the
157
- # threshold, and at the same time the sum of them all does. The user
158
- # should get a query plan logged in that case.
159
- logging_query_plan do
160
- exec_queries
161
- end
243
+ load
244
+ @records
162
245
  end
163
246
 
164
- def exec_queries
165
- return @records if loaded?
166
-
167
- default_scoped = with_default_scope
168
-
169
- if default_scoped.equal?(self)
170
- @records = if @readonly_value.nil? && !@klass.locking_enabled?
171
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
172
- else
173
- IdentityMap.without do
174
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
175
- end
176
- end
177
-
178
- preload = @preload_values
179
- preload += @includes_values unless eager_loading?
180
- preload.each do |associations|
181
- ActiveRecord::Associations::Preloader.new(@records, associations).run
182
- end
183
-
184
- # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
185
- # are JOINS and no explicit SELECT.
186
- readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
187
- @records.each { |record| record.readonly! } if readonly
188
- else
189
- @records = default_scoped.to_a
190
- end
191
-
192
- @loaded = true
193
- @records
247
+ # Serializes the relation objects Array.
248
+ def encode_with(coder)
249
+ coder.represent_seq(nil, to_a)
194
250
  end
195
- private :exec_queries
196
251
 
197
252
  def as_json(options = nil) #:nodoc:
198
253
  to_a.as_json(options)
@@ -200,17 +255,22 @@ module ActiveRecord
200
255
 
201
256
  # Returns size of the records.
202
257
  def size
203
- loaded? ? @records.length : count
258
+ loaded? ? @records.length : count(:all)
204
259
  end
205
260
 
206
261
  # Returns true if there are no records.
207
262
  def empty?
208
263
  return @records.empty? if loaded?
209
264
 
210
- c = count
211
- 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
212
271
  end
213
272
 
273
+ # Returns true if there are any records.
214
274
  def any?
215
275
  if block_given?
216
276
  to_a.any? { |*block_args| yield(*block_args) }
@@ -219,80 +279,70 @@ module ActiveRecord
219
279
  end
220
280
  end
221
281
 
282
+ # Returns true if there is more than one record.
222
283
  def many?
223
284
  if block_given?
224
285
  to_a.many? { |*block_args| yield(*block_args) }
225
286
  else
226
- @limit_value ? to_a.many? : size > 1
287
+ limit_value ? to_a.many? : size > 1
227
288
  end
228
289
  end
229
290
 
230
291
  # Scope all queries to the current scope.
231
292
  #
232
- # ==== Example
233
- #
234
- # Comment.where(:post_id => 1).scoping do
235
- # Comment.first # SELECT * FROM comments WHERE post_id = 1
293
+ # Comment.where(post_id: 1).scoping do
294
+ # Comment.first
236
295
  # end
296
+ # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
237
297
  #
238
298
  # Please check unscoped if you want to remove all previous scopes (including
239
299
  # the default_scope) during the execution of a block.
240
300
  def scoping
241
- @klass.with_scope(self, :overwrite) { yield }
301
+ previous, klass.current_scope = klass.current_scope, self
302
+ yield
303
+ ensure
304
+ klass.current_scope = previous
242
305
  end
243
306
 
244
- # Updates all records with details given if they match a set of conditions supplied, limits and order can
245
- # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
246
- # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
247
- # 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.
248
312
  #
249
313
  # ==== Parameters
250
314
  #
251
315
  # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
252
- # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement.
253
- # See conditions in the intro.
254
- # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
255
316
  #
256
317
  # ==== Examples
257
318
  #
258
319
  # # Update all customers with the given attributes
259
- # Customer.update_all :wants_email => true
320
+ # Customer.update_all wants_email: true
260
321
  #
261
322
  # # Update all books with 'Rails' in their title
262
- # Book.update_all "author = 'David'", "title LIKE '%Rails%'"
263
- #
264
- # # Update all avatars migrated more than a week ago
265
- # Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago]
323
+ # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
266
324
  #
267
325
  # # Update all books that match conditions, but limit it to 5 ordered by date
268
- # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5
269
- #
270
- # # Conditions from the current relation also works
271
- # Book.where('title LIKE ?', '%Rails%').update_all(:author => 'David')
272
- #
273
- # # The same idea applies to limit and order
274
- # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(:author => 'David')
275
- def update_all(updates, conditions = nil, options = {})
276
- IdentityMap.repository[symbolized_base_class].clear if IdentityMap.enabled?
277
- if conditions || options.present?
278
- where(conditions).apply_finder_options(options.slice(:limit, :order)).update_all(updates)
279
- else
280
- 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?
281
329
 
282
- stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
283
- stmt.table(table)
284
- stmt.key = table[primary_key]
330
+ stmt = Arel::UpdateManager.new(arel.engine)
285
331
 
286
- if joins_values.any?
287
- @klass.connection.join_to_update(stmt, arel)
288
- else
289
- stmt.take(arel.limit)
290
- stmt.order(*arel.orders)
291
- stmt.wheres = arel.constraints
292
- end
332
+ stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
333
+ stmt.table(table)
334
+ stmt.key = table[primary_key]
293
335
 
294
- @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
295
342
  end
343
+
344
+ bvs = arel.bind_values + bind_values
345
+ @klass.connection.update stmt, 'SQL', bvs
296
346
  end
297
347
 
298
348
  # Updates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -306,28 +356,26 @@ module ActiveRecord
306
356
  # ==== Examples
307
357
  #
308
358
  # # Updates one record
309
- # Person.update(15, :user_name => 'Samuel', :group => 'expert')
359
+ # Person.update(15, user_name: 'Samuel', group: 'expert')
310
360
  #
311
361
  # # Updates multiple records
312
362
  # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
313
363
  # Person.update(people.keys, people.values)
314
364
  def update(id, attributes)
315
365
  if id.is_a?(Array)
316
- id.each.with_index.map {|one_id, idx| update(one_id, attributes[idx])}
366
+ id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
317
367
  else
318
368
  object = find(id)
319
- object.update_attributes(attributes)
369
+ object.update(attributes)
320
370
  object
321
371
  end
322
372
  end
323
373
 
324
374
  # Destroys the records matching +conditions+ by instantiating each
325
375
  # record and calling its +destroy+ method. Each object's callbacks are
326
- # executed (including <tt>:dependent</tt> association options and
327
- # +before_destroy+/+after_destroy+ Observer methods). Returns the
376
+ # executed (including <tt>:dependent</tt> association options). Returns the
328
377
  # collection of objects that were destroyed; each will be frozen, to
329
- # reflect that no changes should be made (since they can't be
330
- # persisted).
378
+ # reflect that no changes should be made (since they can't be persisted).
331
379
  #
332
380
  # Note: Instantiation, callback execution, and deletion of each
333
381
  # record can be time consuming when you're removing many records at
@@ -346,8 +394,8 @@ module ActiveRecord
346
394
  # ==== Examples
347
395
  #
348
396
  # Person.destroy_all("last_login < '2004-04-04'")
349
- # Person.destroy_all(:status => "inactive")
350
- # Person.where(:age => 0..18).destroy_all
397
+ # Person.destroy_all(status: "inactive")
398
+ # Person.where(age: 0..18).destroy_all
351
399
  def destroy_all(conditions = nil)
352
400
  if conditions
353
401
  where(conditions).destroy_all
@@ -356,7 +404,7 @@ module ActiveRecord
356
404
  end
357
405
  end
358
406
 
359
- # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
407
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
360
408
  # therefore all callbacks and filters are fired off before the object is deleted. This method is
361
409
  # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
362
410
  #
@@ -383,34 +431,51 @@ module ActiveRecord
383
431
  end
384
432
  end
385
433
 
386
- # Deletes the records matching +conditions+ without instantiating the records first, and hence not
387
- # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
388
- # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
389
- # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
390
- # the number of rows affected.
391
- #
392
- # ==== Parameters
393
- #
394
- # * +conditions+ - Conditions are specified the same way as with +find+ method.
395
- #
396
- # ==== 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.
397
440
  #
398
441
  # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
399
442
  # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
400
- # Post.where(:person_id => 5).where(:category => ['Something', 'Else']).delete_all
443
+ # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
401
444
  #
402
445
  # Both calls delete the affected posts all at once with a single DELETE statement.
403
446
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
404
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
405
453
  def delete_all(conditions = nil)
406
- raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
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
407
464
 
408
- IdentityMap.repository[symbolized_base_class] = {} if IdentityMap.enabled?
409
465
  if conditions
410
466
  where(conditions).delete_all
411
467
  else
412
- statement = arel.compile_delete
413
- 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)
414
479
 
415
480
  reset
416
481
  affected
@@ -420,8 +485,7 @@ module ActiveRecord
420
485
  # Deletes the row with a primary key matching the +id+ argument, using a
421
486
  # SQL +DELETE+ statement, and returns the number of rows deleted. Active
422
487
  # Record objects are not instantiated, so the object's callbacks are not
423
- # executed, including any <tt>:dependent</tt> association options or
424
- # Observer methods.
488
+ # executed, including any <tt>:dependent</tt> association options.
425
489
  #
426
490
  # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
427
491
  #
@@ -438,55 +502,110 @@ module ActiveRecord
438
502
  # # Delete multiple rows
439
503
  # Todo.delete([2,3,4])
440
504
  def delete(id_or_array)
441
- IdentityMap.remove_by_id(self.symbolized_base_class, id_or_array) if IdentityMap.enabled?
442
505
  where(primary_key => id_or_array).delete_all
443
506
  end
444
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.
445
521
  def reload
446
522
  reset
447
- to_a # force reload
448
- self
523
+ load
449
524
  end
450
525
 
451
526
  def reset
452
- @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
527
+ @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
453
528
  @should_eager_load = @join_dependency = nil
454
529
  @records = []
530
+ @offsets = {}
455
531
  self
456
532
  end
457
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'
458
538
  def to_sql
459
- @to_sql ||= klass.connection.to_sql(arel, @bind_values.dup)
460
- end
461
-
462
- def where_values_hash
463
- equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
464
- 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
465
563
  }
466
564
 
467
- Hash[equalities.map { |where| [where.left.name, where.right] }].with_indifferent_access
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
+ }]
468
577
  end
469
578
 
470
579
  def scope_for_create
471
580
  @scope_for_create ||= where_values_hash.merge(create_with_value)
472
581
  end
473
582
 
583
+ # Returns true if relation needs eager loading.
474
584
  def eager_loading?
475
585
  @should_eager_load ||=
476
- @eager_load_values.any? ||
477
- @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?)
478
588
  end
479
589
 
480
590
  # Joins that are also marked for preloading. In which case we should just eager load them.
481
591
  # Note that this is a naive implementation because we could have strings and symbols which
482
592
  # represent the same association, but that aren't matched by this. Also, we could have
483
- # 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] }
484
594
  def joined_includes_values
485
- @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
486
602
  end
487
603
 
604
+ # Compares two relations for equality.
488
605
  def ==(other)
489
606
  case other
607
+ when Associations::CollectionProxy, AssociationRelation
608
+ self == other.to_a
490
609
  when Relation
491
610
  other.to_sql == to_sql
492
611
  when Array
@@ -494,21 +613,47 @@ module ActiveRecord
494
613
  end
495
614
  end
496
615
 
616
+ def pretty_print(q)
617
+ q.pp(self.to_a)
618
+ end
619
+
620
+ # Returns true if relation is blank.
621
+ def blank?
622
+ to_a.blank?
623
+ end
624
+
625
+ def values
626
+ Hash[@values]
627
+ end
628
+
497
629
  def inspect
498
- to_a.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(', ')}]>"
499
634
  end
500
635
 
501
- def with_default_scope #:nodoc:
502
- if default_scoped? && default_scope = klass.send(:build_default_scope)
503
- default_scope = default_scope.merge(self)
504
- default_scope.default_scoped = false
505
- default_scope
506
- else
507
- self
636
+ private
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
508
646
  end
647
+
648
+ @records.each { |record| record.readonly! } if readonly_value
649
+
650
+ @loaded = true
651
+ @records
509
652
  end
510
653
 
511
- private
654
+ def build_preloader
655
+ ActiveRecord::Associations::Preloader.new
656
+ end
512
657
 
513
658
  def references_eager_loaded_tables?
514
659
  joined_tables = arel.join_sources.map do |join|
@@ -524,7 +669,7 @@ module ActiveRecord
524
669
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
525
670
  joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
526
671
 
527
- (tables_in_string(to_sql) - joined_tables).any?
672
+ (references_values - joined_tables).any?
528
673
  end
529
674
 
530
675
  def tables_in_string(string)