activerecord 3.2.19 → 5.0.0

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

Potentially problematic release.


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

Files changed (264) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1715 -604
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +40 -45
  5. data/examples/performance.rb +33 -22
  6. data/examples/simple.rb +3 -4
  7. data/lib/active_record/aggregations.rb +76 -51
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +54 -40
  10. data/lib/active_record/associations/association.rb +76 -56
  11. data/lib/active_record/associations/association_scope.rb +125 -93
  12. data/lib/active_record/associations/belongs_to_association.rb +57 -28
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +120 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +115 -62
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -53
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
  18. data/lib/active_record/associations/builder/has_many.rb +9 -65
  19. data/lib/active_record/associations/builder/has_one.rb +18 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +18 -19
  21. data/lib/active_record/associations/collection_association.rb +268 -186
  22. data/lib/active_record/associations/collection_proxy.rb +1003 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +81 -41
  25. data/lib/active_record/associations/has_many_through_association.rb +76 -55
  26. data/lib/active_record/associations/has_one_association.rb +51 -21
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
  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 +239 -155
  32. data/lib/active_record/associations/preloader/association.rb +97 -62
  33. data/lib/active_record/associations/preloader/collection_association.rb +2 -8
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +75 -33
  38. data/lib/active_record/associations/preloader.rb +111 -79
  39. data/lib/active_record/associations/singular_association.rb +35 -13
  40. data/lib/active_record/associations/through_association.rb +41 -19
  41. data/lib/active_record/associations.rb +727 -501
  42. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  43. data/lib/active_record/attribute.rb +213 -0
  44. data/lib/active_record/attribute_assignment.rb +32 -162
  45. data/lib/active_record/attribute_decorators.rb +67 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  47. data/lib/active_record/attribute_methods/dirty.rb +101 -61
  48. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  49. data/lib/active_record/attribute_methods/query.rb +7 -6
  50. data/lib/active_record/attribute_methods/read.rb +56 -117
  51. data/lib/active_record/attribute_methods/serialization.rb +43 -96
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
  53. data/lib/active_record/attribute_methods/write.rb +34 -45
  54. data/lib/active_record/attribute_methods.rb +333 -144
  55. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  56. data/lib/active_record/attribute_set/builder.rb +108 -0
  57. data/lib/active_record/attribute_set.rb +108 -0
  58. data/lib/active_record/attributes.rb +265 -0
  59. data/lib/active_record/autosave_association.rb +285 -223
  60. data/lib/active_record/base.rb +95 -490
  61. data/lib/active_record/callbacks.rb +95 -61
  62. data/lib/active_record/coders/json.rb +13 -0
  63. data/lib/active_record/coders/yaml_column.rb +28 -19
  64. data/lib/active_record/collection_cache_key.rb +40 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
  78. data/lib/active_record/connection_adapters/column.rb +30 -259
  79. data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
  80. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  81. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  82. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  83. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  84. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  89. data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
  90. data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
  91. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
  92. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
  115. data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
  116. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
  117. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
  118. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  119. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
  120. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  121. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  122. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
  123. data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
  124. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  125. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
  129. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  130. data/lib/active_record/connection_handling.rb +155 -0
  131. data/lib/active_record/core.rb +561 -0
  132. data/lib/active_record/counter_cache.rb +146 -105
  133. data/lib/active_record/dynamic_matchers.rb +101 -64
  134. data/lib/active_record/enum.rb +234 -0
  135. data/lib/active_record/errors.rb +153 -56
  136. data/lib/active_record/explain.rb +15 -63
  137. data/lib/active_record/explain_registry.rb +30 -0
  138. data/lib/active_record/explain_subscriber.rb +10 -6
  139. data/lib/active_record/fixture_set/file.rb +77 -0
  140. data/lib/active_record/fixtures.rb +355 -232
  141. data/lib/active_record/gem_version.rb +15 -0
  142. data/lib/active_record/inheritance.rb +144 -79
  143. data/lib/active_record/integration.rb +66 -13
  144. data/lib/active_record/internal_metadata.rb +56 -0
  145. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  146. data/lib/active_record/locale/en.yml +9 -1
  147. data/lib/active_record/locking/optimistic.rb +77 -56
  148. data/lib/active_record/locking/pessimistic.rb +6 -6
  149. data/lib/active_record/log_subscriber.rb +53 -28
  150. data/lib/active_record/migration/command_recorder.rb +166 -33
  151. data/lib/active_record/migration/compatibility.rb +126 -0
  152. data/lib/active_record/migration/join_table.rb +15 -0
  153. data/lib/active_record/migration.rb +792 -264
  154. data/lib/active_record/model_schema.rb +192 -130
  155. data/lib/active_record/nested_attributes.rb +238 -145
  156. data/lib/active_record/no_touching.rb +52 -0
  157. data/lib/active_record/null_relation.rb +89 -0
  158. data/lib/active_record/persistence.rb +357 -157
  159. data/lib/active_record/query_cache.rb +22 -43
  160. data/lib/active_record/querying.rb +34 -23
  161. data/lib/active_record/railtie.rb +88 -48
  162. data/lib/active_record/railties/console_sandbox.rb +3 -4
  163. data/lib/active_record/railties/controller_runtime.rb +5 -4
  164. data/lib/active_record/railties/databases.rake +170 -422
  165. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  166. data/lib/active_record/readonly_attributes.rb +2 -5
  167. data/lib/active_record/reflection.rb +715 -189
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  169. data/lib/active_record/relation/batches.rb +203 -50
  170. data/lib/active_record/relation/calculations.rb +203 -194
  171. data/lib/active_record/relation/delegation.rb +103 -25
  172. data/lib/active_record/relation/finder_methods.rb +457 -261
  173. data/lib/active_record/relation/from_clause.rb +32 -0
  174. data/lib/active_record/relation/merger.rb +167 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  179. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  180. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  181. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  182. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  183. data/lib/active_record/relation/predicate_builder.rb +153 -48
  184. data/lib/active_record/relation/query_attribute.rb +19 -0
  185. data/lib/active_record/relation/query_methods.rb +1019 -194
  186. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  187. data/lib/active_record/relation/spawn_methods.rb +46 -150
  188. data/lib/active_record/relation/where_clause.rb +174 -0
  189. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  190. data/lib/active_record/relation.rb +450 -245
  191. data/lib/active_record/result.rb +104 -12
  192. data/lib/active_record/runtime_registry.rb +22 -0
  193. data/lib/active_record/sanitization.rb +120 -94
  194. data/lib/active_record/schema.rb +28 -18
  195. data/lib/active_record/schema_dumper.rb +141 -74
  196. data/lib/active_record/schema_migration.rb +50 -0
  197. data/lib/active_record/scoping/default.rb +64 -57
  198. data/lib/active_record/scoping/named.rb +93 -108
  199. data/lib/active_record/scoping.rb +73 -121
  200. data/lib/active_record/secure_token.rb +38 -0
  201. data/lib/active_record/serialization.rb +7 -5
  202. data/lib/active_record/statement_cache.rb +113 -0
  203. data/lib/active_record/store.rb +173 -15
  204. data/lib/active_record/suppressor.rb +58 -0
  205. data/lib/active_record/table_metadata.rb +68 -0
  206. data/lib/active_record/tasks/database_tasks.rb +313 -0
  207. data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
  208. data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
  209. data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
  210. data/lib/active_record/timestamp.rb +42 -24
  211. data/lib/active_record/touch_later.rb +58 -0
  212. data/lib/active_record/transactions.rb +233 -105
  213. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  214. data/lib/active_record/type/date.rb +7 -0
  215. data/lib/active_record/type/date_time.rb +7 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  217. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  218. data/lib/active_record/type/internal/timezone.rb +15 -0
  219. data/lib/active_record/type/serialized.rb +63 -0
  220. data/lib/active_record/type/time.rb +20 -0
  221. data/lib/active_record/type/type_map.rb +64 -0
  222. data/lib/active_record/type.rb +72 -0
  223. data/lib/active_record/type_caster/connection.rb +29 -0
  224. data/lib/active_record/type_caster/map.rb +19 -0
  225. data/lib/active_record/type_caster.rb +7 -0
  226. data/lib/active_record/validations/absence.rb +23 -0
  227. data/lib/active_record/validations/associated.rb +33 -18
  228. data/lib/active_record/validations/length.rb +24 -0
  229. data/lib/active_record/validations/presence.rb +66 -0
  230. data/lib/active_record/validations/uniqueness.rb +128 -68
  231. data/lib/active_record/validations.rb +48 -40
  232. data/lib/active_record/version.rb +5 -7
  233. data/lib/active_record.rb +71 -47
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
  235. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
  236. data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
  237. data/lib/rails/generators/active_record/migration.rb +18 -8
  238. data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
  239. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  240. data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
  241. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  242. data/lib/rails/generators/active_record.rb +3 -11
  243. metadata +188 -134
  244. data/examples/associations.png +0 -0
  245. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  246. data/lib/active_record/associations/join_helper.rb +0 -55
  247. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  248. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  249. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  250. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  251. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  252. data/lib/active_record/dynamic_finder_match.rb +0 -68
  253. data/lib/active_record/dynamic_scope_match.rb +0 -23
  254. data/lib/active_record/fixtures/file.rb +0 -65
  255. data/lib/active_record/identity_map.rb +0 -162
  256. data/lib/active_record/observer.rb +0 -121
  257. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  258. data/lib/active_record/session_store.rb +0 -360
  259. data/lib/active_record/test_case.rb +0 -73
  260. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  261. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  262. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  263. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  264. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,35 +1,43 @@
1
- # -*- coding: utf-8 -*-
2
- require 'active_support/core_ext/object/blank'
1
+ require "arel/collectors/bind"
3
2
 
4
3
  module ActiveRecord
5
- # = Active Record Relation
4
+ # = Active Record \Relation
6
5
  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]
6
+ MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
7
+ :order, :joins, :left_joins, :left_outer_joins, :references,
8
+ :extending, :unscope]
11
9
 
10
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
11
+ :reverse_order, :distinct, :create_with]
12
+ CLAUSE_METHODS = [:where, :having, :from]
13
+ INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
14
+
15
+ VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
16
+
17
+ include Enumerable
12
18
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
13
19
 
14
- attr_reader :table, :klass, :loaded
15
- attr_accessor :extensions, :default_scoped
20
+ attr_reader :table, :klass, :loaded, :predicate_builder
21
+ alias :model :klass
16
22
  alias :loaded? :loaded
17
- alias :default_scoped? :default_scoped
18
-
19
- def initialize(klass, table)
20
- @klass, @table = klass, table
21
23
 
22
- @implicit_readonly = nil
23
- @loaded = false
24
- @default_scoped = false
24
+ def initialize(klass, table, predicate_builder, values = {})
25
+ @klass = klass
26
+ @table = table
27
+ @values = values
28
+ @offsets = {}
29
+ @loaded = false
30
+ @predicate_builder = predicate_builder
31
+ end
25
32
 
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 = {}
33
+ def initialize_copy(other)
34
+ # This method is a hot spot, so for now, use Hash[] to dup the hash.
35
+ # https://bugs.ruby-lang.org/issues/7166
36
+ @values = Hash[@values]
37
+ reset
30
38
  end
31
39
 
32
- def insert(values)
40
+ def insert(values) # :nodoc:
33
41
  primary_key_value = nil
34
42
 
35
43
  if primary_key && Hash === values
@@ -37,25 +45,16 @@ module ActiveRecord
37
45
  k.name == primary_key
38
46
  }]
39
47
 
40
- if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
41
- primary_key_value = connection.next_sequence_value(klass.sequence_name)
42
- values[klass.arel_table[klass.primary_key]] = primary_key_value
48
+ if !primary_key_value && klass.prefetch_primary_key?
49
+ primary_key_value = klass.next_sequence_value
50
+ values[arel_attribute(klass.primary_key)] = primary_key_value
43
51
  end
44
52
  end
45
53
 
46
54
  im = arel.create_insert
47
55
  im.into @table
48
56
 
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
57
+ substitutes, binds = substitute_values values
59
58
 
60
59
  if values.empty? # empty insert
61
60
  im.values = Arel.sql(connection.empty_insert_statement_value)
@@ -63,7 +62,7 @@ module ActiveRecord
63
62
  im.insert substitutes
64
63
  end
65
64
 
66
- conn.insert(
65
+ @klass.connection.insert(
67
66
  im,
68
67
  'SQL',
69
68
  primary_key,
@@ -72,65 +71,170 @@ module ActiveRecord
72
71
  binds)
73
72
  end
74
73
 
75
- def new(*args, &block)
76
- scoping { @klass.new(*args, &block) }
74
+ def _update_record(values, id, id_was) # :nodoc:
75
+ substitutes, binds = substitute_values values
76
+
77
+ scope = @klass.unscoped
78
+
79
+ if @klass.finder_needs_type_condition?
80
+ scope.unscope!(where: @klass.inheritance_column)
81
+ end
82
+
83
+ relation = scope.where(@klass.primary_key => (id_was || id))
84
+ bvs = binds + relation.bound_attributes
85
+ um = relation
86
+ .arel
87
+ .compile_update(substitutes, @klass.primary_key)
88
+
89
+ @klass.connection.update(
90
+ um,
91
+ 'SQL',
92
+ bvs,
93
+ )
77
94
  end
78
95
 
79
- def initialize_copy(other)
80
- @bind_values = @bind_values.dup
81
- reset
96
+ def substitute_values(values) # :nodoc:
97
+ binds = []
98
+ substitutes = []
99
+
100
+ values.each do |arel_attr, value|
101
+ binds.push QueryAttribute.new(arel_attr.name, value, klass.type_for_attribute(arel_attr.name))
102
+ substitutes.push [arel_attr, Arel::Nodes::BindParam.new]
103
+ end
104
+
105
+ [substitutes, binds]
106
+ end
107
+
108
+ def arel_attribute(name) # :nodoc:
109
+ klass.arel_attribute(name, table)
110
+ end
111
+
112
+ # Initializes new record from relation while maintaining the current
113
+ # scope.
114
+ #
115
+ # Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new].
116
+ #
117
+ # users = User.where(name: 'DHH')
118
+ # user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
119
+ #
120
+ # You can also pass a block to new with the new record as argument:
121
+ #
122
+ # user = users.new { |user| user.name = 'Oscar' }
123
+ # user.name # => Oscar
124
+ def new(*args, &block)
125
+ scoping { @klass.new(*args, &block) }
82
126
  end
83
127
 
84
128
  alias build new
85
129
 
130
+ # Tries to create a new record with the same scoped attributes
131
+ # defined in the relation. Returns the initialized object if validation fails.
132
+ #
133
+ # Expects arguments in the same format as
134
+ # {ActiveRecord::Base.create}[rdoc-ref:Persistence::ClassMethods#create].
135
+ #
136
+ # ==== Examples
137
+ #
138
+ # users = User.where(name: 'Oscar')
139
+ # users.create # => #<User id: 3, name: "Oscar", ...>
140
+ #
141
+ # users.create(name: 'fxn')
142
+ # users.create # => #<User id: 4, name: "fxn", ...>
143
+ #
144
+ # users.create { |user| user.name = 'tenderlove' }
145
+ # # => #<User id: 5, name: "tenderlove", ...>
146
+ #
147
+ # users.create(name: nil) # validation on name
148
+ # # => #<User id: nil, name: nil, ...>
86
149
  def create(*args, &block)
87
150
  scoping { @klass.create(*args, &block) }
88
151
  end
89
152
 
153
+ # Similar to #create, but calls
154
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!]
155
+ # on the base class. Raises an exception if a validation error occurs.
156
+ #
157
+ # Expects arguments in the same format as
158
+ # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
90
159
  def create!(*args, &block)
91
160
  scoping { @klass.create!(*args, &block) }
92
161
  end
93
162
 
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>.
163
+ def first_or_create(attributes = nil, &block) # :nodoc:
164
+ first || create(attributes, &block)
165
+ end
166
+
167
+ def first_or_create!(attributes = nil, &block) # :nodoc:
168
+ first || create!(attributes, &block)
169
+ end
170
+
171
+ def first_or_initialize(attributes = nil, &block) # :nodoc:
172
+ first || new(attributes, &block)
173
+ end
174
+
175
+ # Finds the first record with the given attributes, or creates a record
176
+ # with the attributes if one is not found:
97
177
  #
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>
178
+ # # Find the first user named "Penélope" or create a new one.
179
+ # User.find_or_create_by(first_name: 'Penélope')
180
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
102
181
  #
103
- # # Find the first user named Penélope or create a new one.
182
+ # # Find the first user named "Penélope" or create a new one.
104
183
  # # 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>
184
+ # User.find_or_create_by(first_name: 'Penélope')
185
+ # # => #<User id: 1, first_name: "Penélope", last_name: nil>
107
186
  #
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'>
187
+ # # Find the first user named "Scarlett" or create a new one with
188
+ # # a particular last name.
189
+ # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
190
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
111
191
  #
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"
192
+ # This method accepts a block, which is passed down to #create. The last example
193
+ # above can be alternatively written this way:
194
+ #
195
+ # # Find the first user named "Scarlett" or create a new one with a
196
+ # # different last name.
197
+ # User.find_or_create_by(first_name: 'Scarlett') do |user|
198
+ # user.last_name = 'Johansson'
116
199
  # 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)
200
+ # # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
201
+ #
202
+ # This method always returns a record, but if creation was attempted and
203
+ # failed due to validation errors it won't be persisted, you get what
204
+ # #create returns in such situation.
205
+ #
206
+ # Please note *this method is not atomic*, it runs first a SELECT, and if
207
+ # there are no results an INSERT is attempted. If there are other threads
208
+ # or processes there is a race condition between both calls and it could
209
+ # be the case that you end up with two similar records.
210
+ #
211
+ # Whether that is a problem or not depends on the logic of the
212
+ # application, but in the particular case in which rows have a UNIQUE
213
+ # constraint an exception may be raised, just retry:
214
+ #
215
+ # begin
216
+ # CreditAccount.transaction(requires_new: true) do
217
+ # CreditAccount.find_or_create_by(user_id: user.id)
218
+ # end
219
+ # rescue ActiveRecord::RecordNotUnique
220
+ # retry
221
+ # end
222
+ #
223
+ def find_or_create_by(attributes, &block)
224
+ find_by(attributes) || create(attributes, &block)
120
225
  end
121
226
 
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)
227
+ # Like #find_or_create_by, but calls
228
+ # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
229
+ # is raised if the created record is invalid.
230
+ def find_or_create_by!(attributes, &block)
231
+ find_by(attributes) || create!(attributes, &block)
127
232
  end
128
233
 
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)
234
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
235
+ # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
236
+ def find_or_initialize_by(attributes, &block)
237
+ find_by(attributes) || new(attributes, &block)
134
238
  end
135
239
 
136
240
  # Runs EXPLAIN on the query or queries triggered by this relation and
@@ -141,158 +245,151 @@ module ActiveRecord
141
245
  # are needed by the next ones when eager loading is going on.
142
246
  #
143
247
  # Please see further details in the
144
- # {Active Record Query Interface guide}[http://edgeguides.rubyonrails.org/active_record_querying.html#running-explain].
248
+ # {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
145
249
  def explain
146
- _, queries = collecting_queries_for_explain { exec_queries }
147
- exec_explain(queries)
250
+ #TODO: Fix for binds.
251
+ exec_explain(collecting_queries_for_explain { exec_queries })
148
252
  end
149
253
 
254
+ # Converts relation objects to Array.
150
255
  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
256
+ records.dup
162
257
  end
163
258
 
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
259
+ def records # :nodoc:
260
+ load
193
261
  @records
194
262
  end
195
- private :exec_queries
263
+
264
+ # Serializes the relation objects Array.
265
+ def encode_with(coder)
266
+ coder.represent_seq(nil, records)
267
+ end
196
268
 
197
269
  def as_json(options = nil) #:nodoc:
198
- to_a.as_json(options)
270
+ records.as_json(options)
199
271
  end
200
272
 
201
273
  # Returns size of the records.
202
274
  def size
203
- loaded? ? @records.length : count
275
+ loaded? ? @records.length : count(:all)
204
276
  end
205
277
 
206
278
  # Returns true if there are no records.
207
279
  def empty?
208
280
  return @records.empty? if loaded?
209
281
 
210
- c = count
211
- c.respond_to?(:zero?) ? c.zero? : c.empty?
282
+ if limit_value == 0
283
+ true
284
+ else
285
+ c = count(:all)
286
+ c.respond_to?(:zero?) ? c.zero? : c.empty?
287
+ end
288
+ end
289
+
290
+ # Returns true if there are no records.
291
+ def none?
292
+ return super if block_given?
293
+ empty?
212
294
  end
213
295
 
296
+ # Returns true if there are any records.
214
297
  def any?
215
- if block_given?
216
- to_a.any? { |*block_args| yield(*block_args) }
217
- else
218
- !empty?
219
- end
298
+ return super if block_given?
299
+ !empty?
220
300
  end
221
301
 
302
+ # Returns true if there is exactly one record.
303
+ def one?
304
+ return super if block_given?
305
+ limit_value ? records.one? : size == 1
306
+ end
307
+
308
+ # Returns true if there is more than one record.
222
309
  def many?
223
- if block_given?
224
- to_a.many? { |*block_args| yield(*block_args) }
225
- else
226
- @limit_value ? to_a.many? : size > 1
227
- end
310
+ return super if block_given?
311
+ limit_value ? records.many? : size > 1
228
312
  end
229
313
 
230
- # Scope all queries to the current scope.
314
+ # Returns a cache key that can be used to identify the records fetched by
315
+ # this query. The cache key is built with a fingerprint of the sql query,
316
+ # the number of records matched by the query and a timestamp of the last
317
+ # updated record. When a new record comes to match the query, or any of
318
+ # the existing records is updated or deleted, the cache key changes.
319
+ #
320
+ # Product.where("name like ?", "%Cosmic Encounter%").cache_key
321
+ # # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
322
+ #
323
+ # If the collection is loaded, the method will iterate through the records
324
+ # to generate the timestamp, otherwise it will trigger one SQL query like:
325
+ #
326
+ # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
327
+ #
328
+ # You can also pass a custom timestamp column to fetch the timestamp of the
329
+ # last updated record.
330
+ #
331
+ # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
231
332
  #
232
- # ==== Example
333
+ # You can customize the strategy to generate the key on a per model basis
334
+ # overriding ActiveRecord::Base#collection_cache_key.
335
+ def cache_key(timestamp_column = :updated_at)
336
+ @cache_keys ||= {}
337
+ @cache_keys[timestamp_column] ||= @klass.collection_cache_key(self, timestamp_column)
338
+ end
339
+
340
+ # Scope all queries to the current scope.
233
341
  #
234
- # Comment.where(:post_id => 1).scoping do
235
- # Comment.first # SELECT * FROM comments WHERE post_id = 1
342
+ # Comment.where(post_id: 1).scoping do
343
+ # Comment.first
236
344
  # end
345
+ # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
237
346
  #
238
347
  # Please check unscoped if you want to remove all previous scopes (including
239
348
  # the default_scope) during the execution of a block.
240
349
  def scoping
241
- @klass.with_scope(self, :overwrite) { yield }
350
+ previous, klass.current_scope = klass.current_scope, self
351
+ yield
352
+ ensure
353
+ klass.current_scope = previous
242
354
  end
243
355
 
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.
356
+ # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
357
+ # statement and sends it straight to the database. It does not instantiate the involved models and it does not
358
+ # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
359
+ # Active Record's normal type casting and serialization.
248
360
  #
249
361
  # ==== Parameters
250
362
  #
251
363
  # * +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
364
  #
256
365
  # ==== Examples
257
366
  #
258
367
  # # Update all customers with the given attributes
259
- # Customer.update_all :wants_email => true
368
+ # Customer.update_all wants_email: true
260
369
  #
261
370
  # # 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]
371
+ # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
266
372
  #
267
373
  # # 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)
374
+ # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
375
+ def update_all(updates)
376
+ raise ArgumentError, "Empty list of attributes to change" if updates.blank?
281
377
 
282
- stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
283
- stmt.table(table)
284
- stmt.key = table[primary_key]
378
+ stmt = Arel::UpdateManager.new
285
379
 
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
380
+ stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
381
+ stmt.table(table)
293
382
 
294
- @klass.connection.update stmt, 'SQL', bind_values
383
+ if joins_values.any?
384
+ @klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
385
+ else
386
+ stmt.key = arel_attribute(primary_key)
387
+ stmt.take(arel.limit)
388
+ stmt.order(*arel.orders)
389
+ stmt.wheres = arel.constraints
295
390
  end
391
+
392
+ @klass.connection.update stmt, 'SQL', bound_attributes
296
393
  end
297
394
 
298
395
  # Updates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -306,59 +403,71 @@ module ActiveRecord
306
403
  # ==== Examples
307
404
  #
308
405
  # # Updates one record
309
- # Person.update(15, :user_name => 'Samuel', :group => 'expert')
406
+ # Person.update(15, user_name: 'Samuel', group: 'expert')
310
407
  #
311
408
  # # Updates multiple records
312
409
  # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
313
410
  # Person.update(people.keys, people.values)
314
- def update(id, attributes)
411
+ #
412
+ # # Updates multiple records from the result of a relation
413
+ # people = Person.where(group: 'expert')
414
+ # people.update(group: 'masters')
415
+ #
416
+ # Note: Updating a large number of records will run an
417
+ # UPDATE query for each record, which may cause a performance
418
+ # issue. So if it is not needed to run callbacks for each update, it is
419
+ # preferred to use #update_all for updating all records using
420
+ # a single query.
421
+ def update(id = :all, attributes)
315
422
  if id.is_a?(Array)
316
- id.each.with_index.map {|one_id, idx| update(one_id, attributes[idx])}
423
+ id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
424
+ elsif id == :all
425
+ records.each { |record| record.update(attributes) }
317
426
  else
427
+ if ActiveRecord::Base === id
428
+ id = id.id
429
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
430
+ You are passing an instance of ActiveRecord::Base to `update`.
431
+ Please pass the id of the object by calling `.id`.
432
+ MSG
433
+ end
318
434
  object = find(id)
319
- object.update_attributes(attributes)
435
+ object.update(attributes)
320
436
  object
321
437
  end
322
438
  end
323
439
 
324
- # Destroys the records matching +conditions+ by instantiating each
325
- # 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
328
- # 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).
440
+ # Destroys the records by instantiating each
441
+ # record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
442
+ # Each object's callbacks are executed (including <tt>:dependent</tt> association options).
443
+ # Returns the collection of objects that were destroyed; each will be frozen, to
444
+ # reflect that no changes should be made (since they can't be persisted).
331
445
  #
332
446
  # Note: Instantiation, callback execution, and deletion of each
333
447
  # record can be time consuming when you're removing many records at
334
448
  # once. It generates at least one SQL +DELETE+ query per record (or
335
449
  # possibly more, to enforce your callbacks). If you want to delete many
336
450
  # rows quickly, without concern for their associations or callbacks, use
337
- # +delete_all+ instead.
338
- #
339
- # ==== Parameters
340
- #
341
- # * +conditions+ - A string, array, or hash that specifies which records
342
- # to destroy. If omitted, all records are destroyed. See the
343
- # Conditions section in the introduction to ActiveRecord::Base for
344
- # more information.
451
+ # #delete_all instead.
345
452
  #
346
453
  # ==== Examples
347
454
  #
348
- # Person.destroy_all("last_login < '2004-04-04'")
349
- # Person.destroy_all(:status => "inactive")
350
- # Person.where(:age => 0..18).destroy_all
455
+ # Person.where(age: 0..18).destroy_all
351
456
  def destroy_all(conditions = nil)
352
457
  if conditions
458
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
459
+ Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1.
460
+ To achieve the same use where(conditions).destroy_all.
461
+ MESSAGE
353
462
  where(conditions).destroy_all
354
463
  else
355
- to_a.each {|object| object.destroy }.tap { reset }
464
+ records.each(&:destroy).tap { reset }
356
465
  end
357
466
  end
358
467
 
359
- # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
468
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
360
469
  # therefore all callbacks and filters are fired off before the object is deleted. This method is
361
- # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
470
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
362
471
  #
363
472
  # This essentially finds the object (or multiple objects) with the given id, creates a new object
364
473
  # from the attributes, and then calls destroy on it.
@@ -383,34 +492,55 @@ module ActiveRecord
383
492
  end
384
493
  end
385
494
 
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
495
+ # Deletes the records without instantiating the records
496
+ # first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
497
+ # method nor invoking callbacks.
498
+ # This is a single SQL DELETE statement that goes straight to the database, much more
499
+ # efficient than #destroy_all. Be careful with relations though, in particular
500
+ # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
501
+ # number of rows affected.
397
502
  #
398
- # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
399
- # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
400
- # Post.where(:person_id => 5).where(:category => ['Something', 'Else']).delete_all
503
+ # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
401
504
  #
402
505
  # Both calls delete the affected posts all at once with a single DELETE statement.
403
506
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
404
- # +after_destroy+ callbacks, use the +destroy_all+ method instead.
507
+ # +after_destroy+ callbacks, use the #destroy_all method instead.
508
+ #
509
+ # If an invalid method is supplied, #delete_all raises an ActiveRecordError:
510
+ #
511
+ # Post.limit(100).delete_all
512
+ # # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
405
513
  def delete_all(conditions = nil)
406
- raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
514
+ invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
515
+ if MULTI_VALUE_METHODS.include?(method)
516
+ send("#{method}_values").any?
517
+ elsif SINGLE_VALUE_METHODS.include?(method)
518
+ send("#{method}_value")
519
+ elsif CLAUSE_METHODS.include?(method)
520
+ send("#{method}_clause").any?
521
+ end
522
+ }
523
+ if invalid_methods.any?
524
+ raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
525
+ end
407
526
 
408
- IdentityMap.repository[symbolized_base_class] = {} if IdentityMap.enabled?
409
527
  if conditions
528
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
529
+ Passing conditions to delete_all is deprecated and will be removed in Rails 5.1.
530
+ To achieve the same use where(conditions).delete_all.
531
+ MESSAGE
410
532
  where(conditions).delete_all
411
533
  else
412
- statement = arel.compile_delete
413
- affected = @klass.connection.delete(statement, 'SQL', bind_values)
534
+ stmt = Arel::DeleteManager.new
535
+ stmt.from(table)
536
+
537
+ if joins_values.any?
538
+ @klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
539
+ else
540
+ stmt.wheres = arel.constraints
541
+ end
542
+
543
+ affected = @klass.connection.delete(stmt, 'SQL', bound_attributes)
414
544
 
415
545
  reset
416
546
  affected
@@ -420,13 +550,12 @@ module ActiveRecord
420
550
  # Deletes the row with a primary key matching the +id+ argument, using a
421
551
  # SQL +DELETE+ statement, and returns the number of rows deleted. Active
422
552
  # 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.
553
+ # executed, including any <tt>:dependent</tt> association options.
425
554
  #
426
555
  # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
427
556
  #
428
557
  # Note: Although it is often much faster than the alternative,
429
- # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
558
+ # #destroy, skipping callbacks might bypass business logic in
430
559
  # your application that ensures referential integrity or performs other
431
560
  # essential jobs.
432
561
  #
@@ -438,78 +567,154 @@ module ActiveRecord
438
567
  # # Delete multiple rows
439
568
  # Todo.delete([2,3,4])
440
569
  def delete(id_or_array)
441
- IdentityMap.remove_by_id(self.symbolized_base_class, id_or_array) if IdentityMap.enabled?
442
570
  where(primary_key => id_or_array).delete_all
443
571
  end
444
572
 
573
+ # Causes the records to be loaded from the database if they have not
574
+ # been loaded already. You can use this if for some reason you need
575
+ # to explicitly load some records before actually using them. The
576
+ # return value is the relation itself, not the records.
577
+ #
578
+ # Post.where(published: true).load # => #<ActiveRecord::Relation>
579
+ def load
580
+ exec_queries unless loaded?
581
+
582
+ self
583
+ end
584
+
585
+ # Forces reloading of relation.
445
586
  def reload
446
587
  reset
447
- to_a # force reload
448
- self
588
+ load
449
589
  end
450
590
 
451
591
  def reset
452
- @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
592
+ @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
453
593
  @should_eager_load = @join_dependency = nil
454
- @records = []
594
+ @records = [].freeze
595
+ @offsets = {}
455
596
  self
456
597
  end
457
598
 
599
+ # Returns sql statement for the relation.
600
+ #
601
+ # User.where(name: 'Oscar').to_sql
602
+ # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
458
603
  def to_sql
459
- @to_sql ||= klass.connection.to_sql(arel, @bind_values.dup)
460
- end
604
+ @to_sql ||= begin
605
+ relation = self
606
+ connection = klass.connection
607
+ visitor = connection.visitor
461
608
 
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
465
- }
609
+ if eager_loading?
610
+ find_with_associations { |rel| relation = rel }
611
+ end
612
+
613
+ binds = relation.bound_attributes
614
+ binds = connection.prepare_binds_for_database(binds)
615
+ binds.map! { |value| connection.quote(value) }
616
+ collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new)
617
+ collect.substitute_binds(binds).join
618
+ end
619
+ end
466
620
 
467
- Hash[equalities.map { |where| [where.left.name, where.right] }].with_indifferent_access
621
+ # Returns a hash of where conditions.
622
+ #
623
+ # User.where(name: 'Oscar').where_values_hash
624
+ # # => {name: "Oscar"}
625
+ def where_values_hash(relation_table_name = table_name)
626
+ where_clause.to_h(relation_table_name)
468
627
  end
469
628
 
470
629
  def scope_for_create
471
630
  @scope_for_create ||= where_values_hash.merge(create_with_value)
472
631
  end
473
632
 
633
+ # Returns true if relation needs eager loading.
474
634
  def eager_loading?
475
635
  @should_eager_load ||=
476
- @eager_load_values.any? ||
477
- @includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
636
+ eager_load_values.any? ||
637
+ includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
478
638
  end
479
639
 
480
640
  # Joins that are also marked for preloading. In which case we should just eager load them.
481
641
  # Note that this is a naive implementation because we could have strings and symbols which
482
642
  # 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] }
643
+ # nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
484
644
  def joined_includes_values
485
- @includes_values & @joins_values
645
+ includes_values & joins_values
646
+ end
647
+
648
+ # {#uniq}[rdoc-ref:QueryMethods#uniq] and
649
+ # {#uniq!}[rdoc-ref:QueryMethods#uniq!] are silently deprecated.
650
+ # #uniq_value delegates to #distinct_value to maintain backwards compatibility.
651
+ # Use #distinct_value instead.
652
+ def uniq_value
653
+ distinct_value
486
654
  end
655
+ deprecate uniq_value: :distinct_value
487
656
 
657
+ # Compares two relations for equality.
488
658
  def ==(other)
489
659
  case other
660
+ when Associations::CollectionProxy, AssociationRelation
661
+ self == other.records
490
662
  when Relation
491
663
  other.to_sql == to_sql
492
664
  when Array
493
- to_a == other
665
+ records == other
494
666
  end
495
667
  end
496
668
 
669
+ def pretty_print(q)
670
+ q.pp(self.records)
671
+ end
672
+
673
+ # Returns true if relation is blank.
674
+ def blank?
675
+ records.blank?
676
+ end
677
+
678
+ def values
679
+ Hash[@values]
680
+ end
681
+
497
682
  def inspect
498
- to_a.inspect
683
+ entries = records.take([limit_value, 11].compact.min).map!(&:inspect)
684
+ entries[10] = '...' if entries.size == 11
685
+
686
+ "#<#{self.class.name} [#{entries.join(', ')}]>"
499
687
  end
500
688
 
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
689
+ protected
690
+
691
+ def load_records(records)
692
+ @records = records.freeze
693
+ @loaded = true
508
694
  end
509
- end
510
695
 
511
696
  private
512
697
 
698
+ def exec_queries
699
+ @records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes).freeze
700
+
701
+ preload = preload_values
702
+ preload += includes_values unless eager_loading?
703
+ preloader = build_preloader
704
+ preload.each do |associations|
705
+ preloader.preload @records, associations
706
+ end
707
+
708
+ @records.each(&:readonly!) if readonly_value
709
+
710
+ @loaded = true
711
+ @records
712
+ end
713
+
714
+ def build_preloader
715
+ ActiveRecord::Associations::Preloader.new
716
+ end
717
+
513
718
  def references_eager_loaded_tables?
514
719
  joined_tables = arel.join_sources.map do |join|
515
720
  if join.is_a?(Arel::Nodes::StringJoin)
@@ -522,16 +727,16 @@ module ActiveRecord
522
727
  joined_tables += [table.name, table.table_alias]
523
728
 
524
729
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
525
- joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
730
+ joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
526
731
 
527
- (tables_in_string(to_sql) - joined_tables).any?
732
+ (references_values - joined_tables).any?
528
733
  end
529
734
 
530
735
  def tables_in_string(string)
531
736
  return [] if string.blank?
532
737
  # always convert table names to downcase as in Oracle quoted table names are in uppercase
533
738
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
534
- string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
739
+ string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_']
535
740
  end
536
741
  end
537
742
  end