activerecord 4.2.0 → 5.2.8.1

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