activerecord 5.0.7.2 → 5.1.0.beta1

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 (216) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +389 -2252
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record.rb +20 -20
  8. data/lib/active_record/aggregations.rb +244 -244
  9. data/lib/active_record/association_relation.rb +5 -5
  10. data/lib/active_record/associations.rb +1579 -1569
  11. data/lib/active_record/associations/alias_tracker.rb +1 -1
  12. data/lib/active_record/associations/association.rb +23 -15
  13. data/lib/active_record/associations/association_scope.rb +83 -81
  14. data/lib/active_record/associations/belongs_to_association.rb +0 -1
  15. data/lib/active_record/associations/builder/belongs_to.rb +16 -14
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  18. data/lib/active_record/associations/collection_association.rb +74 -241
  19. data/lib/active_record/associations/collection_proxy.rb +144 -70
  20. data/lib/active_record/associations/has_many_association.rb +15 -19
  21. data/lib/active_record/associations/has_many_through_association.rb +12 -5
  22. data/lib/active_record/associations/has_one_association.rb +22 -28
  23. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  24. data/lib/active_record/associations/join_dependency.rb +117 -115
  25. data/lib/active_record/associations/join_dependency/join_association.rb +16 -13
  26. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  28. data/lib/active_record/associations/preloader.rb +94 -94
  29. data/lib/active_record/associations/preloader/association.rb +87 -64
  30. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  31. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  32. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  33. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +34 -41
  35. data/lib/active_record/associations/singular_association.rb +8 -25
  36. data/lib/active_record/associations/through_association.rb +3 -6
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  39. data/lib/active_record/attribute_assignment.rb +61 -61
  40. data/lib/active_record/attribute_decorators.rb +35 -13
  41. data/lib/active_record/attribute_methods.rb +56 -65
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  43. data/lib/active_record/attribute_methods/dirty.rb +216 -34
  44. data/lib/active_record/attribute_methods/primary_key.rb +78 -73
  45. data/lib/active_record/attribute_methods/read.rb +39 -35
  46. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  48. data/lib/active_record/attribute_methods/write.rb +36 -30
  49. data/lib/active_record/attribute_mutation_tracker.rb +53 -10
  50. data/lib/active_record/attribute_set.rb +9 -6
  51. data/lib/active_record/attribute_set/builder.rb +41 -49
  52. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  53. data/lib/active_record/attributes.rb +21 -21
  54. data/lib/active_record/autosave_association.rb +13 -13
  55. data/lib/active_record/base.rb +24 -22
  56. data/lib/active_record/callbacks.rb +52 -14
  57. data/lib/active_record/coders/yaml_column.rb +9 -11
  58. data/lib/active_record/collection_cache_key.rb +6 -17
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +320 -278
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -34
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -27
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -57
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +9 -19
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +78 -79
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +99 -93
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -5
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +156 -128
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +424 -382
  71. data/lib/active_record/connection_adapters/column.rb +27 -5
  72. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  73. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -43
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +49 -31
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +5 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -26
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -28
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -35
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +9 -9
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  94. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  95. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  97. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +28 -30
  98. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +38 -36
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +161 -170
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +4 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -7
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +179 -152
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -20
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +187 -130
  116. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  117. data/lib/active_record/connection_handling.rb +14 -26
  118. data/lib/active_record/core.rb +110 -93
  119. data/lib/active_record/counter_cache.rb +62 -13
  120. data/lib/active_record/define_callbacks.rb +20 -0
  121. data/lib/active_record/dynamic_matchers.rb +80 -79
  122. data/lib/active_record/enum.rb +8 -6
  123. data/lib/active_record/errors.rb +58 -15
  124. data/lib/active_record/explain.rb +1 -2
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +7 -4
  127. data/lib/active_record/fixture_set/file.rb +11 -8
  128. data/lib/active_record/fixtures.rb +66 -53
  129. data/lib/active_record/gem_version.rb +3 -3
  130. data/lib/active_record/inheritance.rb +93 -79
  131. data/lib/active_record/integration.rb +7 -7
  132. data/lib/active_record/internal_metadata.rb +3 -16
  133. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  134. data/lib/active_record/locking/optimistic.rb +64 -56
  135. data/lib/active_record/locking/pessimistic.rb +10 -1
  136. data/lib/active_record/log_subscriber.rb +29 -29
  137. data/lib/active_record/migration.rb +155 -172
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +76 -37
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/model_schema.rb +85 -119
  142. data/lib/active_record/nested_attributes.rb +200 -199
  143. data/lib/active_record/null_relation.rb +10 -33
  144. data/lib/active_record/persistence.rb +45 -38
  145. data/lib/active_record/query_cache.rb +4 -8
  146. data/lib/active_record/querying.rb +2 -3
  147. data/lib/active_record/railtie.rb +16 -17
  148. data/lib/active_record/railties/controller_runtime.rb +6 -2
  149. data/lib/active_record/railties/databases.rake +125 -140
  150. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  151. data/lib/active_record/readonly_attributes.rb +2 -2
  152. data/lib/active_record/reflection.rb +79 -96
  153. data/lib/active_record/relation.rb +72 -115
  154. data/lib/active_record/relation/batches.rb +87 -58
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  156. data/lib/active_record/relation/calculations.rb +154 -160
  157. data/lib/active_record/relation/delegation.rb +30 -29
  158. data/lib/active_record/relation/finder_methods.rb +195 -226
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder.rb +92 -89
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  165. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  166. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +247 -295
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +79 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/result.rb +29 -31
  174. data/lib/active_record/runtime_registry.rb +3 -3
  175. data/lib/active_record/sanitization.rb +182 -197
  176. data/lib/active_record/schema.rb +3 -3
  177. data/lib/active_record/schema_dumper.rb +14 -37
  178. data/lib/active_record/schema_migration.rb +3 -3
  179. data/lib/active_record/scoping.rb +9 -10
  180. data/lib/active_record/scoping/default.rb +87 -91
  181. data/lib/active_record/scoping/named.rb +16 -28
  182. data/lib/active_record/secure_token.rb +2 -2
  183. data/lib/active_record/statement_cache.rb +13 -15
  184. data/lib/active_record/store.rb +31 -32
  185. data/lib/active_record/suppressor.rb +2 -1
  186. data/lib/active_record/table_metadata.rb +9 -5
  187. data/lib/active_record/tasks/database_tasks.rb +72 -65
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +75 -72
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +53 -48
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  191. data/lib/active_record/timestamp.rb +39 -25
  192. data/lib/active_record/touch_later.rb +1 -2
  193. data/lib/active_record/transactions.rb +98 -110
  194. data/lib/active_record/type.rb +17 -13
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +9 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/serialized.rb +8 -8
  199. data/lib/active_record/type/text.rb +9 -0
  200. data/lib/active_record/type/time.rb +0 -1
  201. data/lib/active_record/type/type_map.rb +11 -15
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type_caster.rb +2 -2
  204. data/lib/active_record/type_caster/connection.rb +8 -6
  205. data/lib/active_record/type_caster/map.rb +3 -1
  206. data/lib/active_record/validations.rb +4 -4
  207. data/lib/active_record/validations/associated.rb +1 -1
  208. data/lib/active_record/validations/presence.rb +2 -2
  209. data/lib/active_record/validations/uniqueness.rb +8 -39
  210. data/lib/active_record/version.rb +1 -1
  211. data/lib/rails/generators/active_record.rb +4 -4
  212. data/lib/rails/generators/active_record/migration.rb +2 -2
  213. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  215. metadata +22 -13
  216. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,5 +1,3 @@
1
- require 'active_support/concern'
2
-
3
1
  module ActiveRecord
4
2
  module Delegation # :nodoc:
5
3
  module DelegateCache # :nodoc:
@@ -17,7 +15,10 @@ module ActiveRecord
17
15
  delegate = Class.new(klass) {
18
16
  include ClassSpecificRelation
19
17
  }
20
- const_set klass.name.gsub('::'.freeze, '_'.freeze), delegate
18
+ mangled_name = klass.name.gsub("::".freeze, "_".freeze)
19
+ const_set mangled_name, delegate
20
+ private_constant mangled_name
21
+
21
22
  cache[klass] = delegate
22
23
  end
23
24
  end
@@ -41,7 +42,7 @@ module ActiveRecord
41
42
  :shuffle, :split, :index, to: :records
42
43
 
43
44
  delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
44
- :connection, :columns_hash, :to => :klass
45
+ :connection, :columns_hash, to: :klass
45
46
 
46
47
  module ClassSpecificRelation # :nodoc:
47
48
  extend ActiveSupport::Concern
@@ -59,7 +60,7 @@ module ActiveRecord
59
60
  @delegation_mutex.synchronize do
60
61
  return if method_defined?(method)
61
62
 
62
- if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
63
+ if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
63
64
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
64
65
  def #{method}(*args, &block)
65
66
  scoping { @klass.#{method}(*args, &block) }
@@ -81,19 +82,19 @@ module ActiveRecord
81
82
  end
82
83
  end
83
84
 
84
- protected
85
+ private
85
86
 
86
- def method_missing(method, *args, &block)
87
- if @klass.respond_to?(method)
88
- self.class.delegate_to_scoped_klass(method)
89
- scoping { @klass.public_send(method, *args, &block) }
90
- elsif arel.respond_to?(method)
91
- self.class.delegate method, :to => :arel
92
- arel.public_send(method, *args, &block)
93
- else
94
- super
87
+ def method_missing(method, *args, &block)
88
+ if @klass.respond_to?(method)
89
+ self.class.delegate_to_scoped_klass(method)
90
+ scoping { @klass.public_send(method, *args, &block) }
91
+ elsif arel.respond_to?(method)
92
+ self.class.delegate method, to: :arel
93
+ arel.public_send(method, *args, &block)
94
+ else
95
+ super
96
+ end
95
97
  end
96
- end
97
98
  end
98
99
 
99
100
  module ClassMethods # :nodoc:
@@ -103,26 +104,26 @@ module ActiveRecord
103
104
 
104
105
  private
105
106
 
106
- def relation_class_for(klass)
107
- klass.relation_delegate_class(self)
108
- end
107
+ def relation_class_for(klass)
108
+ klass.relation_delegate_class(self)
109
+ end
109
110
  end
110
111
 
111
- def respond_to?(method, include_private = false)
112
+ def respond_to_missing?(method, include_private = false)
112
113
  super || @klass.respond_to?(method, include_private) ||
113
114
  arel.respond_to?(method, include_private)
114
115
  end
115
116
 
116
- protected
117
+ private
117
118
 
118
- def method_missing(method, *args, &block)
119
- if @klass.respond_to?(method)
120
- scoping { @klass.public_send(method, *args, &block) }
121
- elsif arel.respond_to?(method)
122
- arel.public_send(method, *args, &block)
123
- else
124
- super
119
+ def method_missing(method, *args, &block)
120
+ if @klass.respond_to?(method)
121
+ scoping { @klass.public_send(method, *args, &block) }
122
+ elsif arel.respond_to?(method)
123
+ arel.public_send(method, *args, &block)
124
+ else
125
+ super
126
+ end
125
127
  end
126
- end
127
128
  end
128
129
  end
@@ -1,8 +1,8 @@
1
- require 'active_support/core_ext/string/filters'
1
+ require "active_support/core_ext/string/filters"
2
2
 
3
3
  module ActiveRecord
4
4
  module FinderMethods
5
- ONE_AS_ONE = '1 AS one'
5
+ ONE_AS_ONE = "1 AS one"
6
6
 
7
7
  # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
8
8
  # If one or more records can not be found for the requested ids, then RecordNotFound will be raised. If the primary key
@@ -76,7 +76,7 @@ module ActiveRecord
76
76
  # Post.find_by "published_at < ?", 2.weeks.ago
77
77
  def find_by(arg, *args)
78
78
  where(arg, *args).take
79
- rescue RangeError
79
+ rescue ::RangeError
80
80
  nil
81
81
  end
82
82
 
@@ -84,7 +84,7 @@ module ActiveRecord
84
84
  # an ActiveRecord::RecordNotFound error.
85
85
  def find_by!(arg, *args)
86
86
  where(arg, *args).take!
87
- rescue RangeError
87
+ rescue ::RangeError
88
88
  raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
89
89
  @klass.name)
90
90
  end
@@ -97,13 +97,13 @@ module ActiveRecord
97
97
  # Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
98
98
  # Person.where(["name LIKE '%?'", name]).take
99
99
  def take(limit = nil)
100
- limit ? limit(limit).to_a : find_take
100
+ limit ? find_take_with_limit(limit) : find_take
101
101
  end
102
102
 
103
103
  # Same as #take but raises ActiveRecord::RecordNotFound if no record
104
104
  # is found. Note that #take! accepts no arguments.
105
105
  def take!
106
- take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
106
+ take || raise_record_not_found_exception!
107
107
  end
108
108
 
109
109
  # Find the first record (or first N records if a parameter is supplied).
@@ -117,7 +117,7 @@ module ActiveRecord
117
117
  #
118
118
  def first(limit = nil)
119
119
  if limit
120
- find_nth_with_limit_and_offset(0, limit, offset: offset_index)
120
+ find_nth_with_limit(0, limit)
121
121
  else
122
122
  find_nth 0
123
123
  end
@@ -126,7 +126,7 @@ module ActiveRecord
126
126
  # Same as #first but raises ActiveRecord::RecordNotFound if no record
127
127
  # is found. Note that #first! accepts no arguments.
128
128
  def first!
129
- find_nth! 0
129
+ first || raise_record_not_found_exception!
130
130
  end
131
131
 
132
132
  # Find the last record (or last N records if a parameter is supplied).
@@ -152,20 +152,12 @@ module ActiveRecord
152
152
  result = result.reverse_order!
153
153
 
154
154
  limit ? result.reverse : result.first
155
- rescue ActiveRecord::IrreversibleOrderError
156
- ActiveSupport::Deprecation.warn(<<-WARNING.squish)
157
- Finding a last element by loading the relation when SQL ORDER
158
- can not be reversed is deprecated.
159
- Rails 5.1 will raise ActiveRecord::IrreversibleOrderError in this case.
160
- Please call `to_a.last` if you still want to load the relation.
161
- WARNING
162
- find_last(limit)
163
155
  end
164
156
 
165
157
  # Same as #last but raises ActiveRecord::RecordNotFound if no record
166
158
  # is found. Note that #last! accepts no arguments.
167
159
  def last!
168
- last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
160
+ last || raise_record_not_found_exception!
169
161
  end
170
162
 
171
163
  # Find the second record.
@@ -181,7 +173,7 @@ module ActiveRecord
181
173
  # Same as #second but raises ActiveRecord::RecordNotFound if no record
182
174
  # is found.
183
175
  def second!
184
- find_nth! 1
176
+ second || raise_record_not_found_exception!
185
177
  end
186
178
 
187
179
  # Find the third record.
@@ -197,7 +189,7 @@ module ActiveRecord
197
189
  # Same as #third but raises ActiveRecord::RecordNotFound if no record
198
190
  # is found.
199
191
  def third!
200
- find_nth! 2
192
+ third || raise_record_not_found_exception!
201
193
  end
202
194
 
203
195
  # Find the fourth record.
@@ -213,7 +205,7 @@ module ActiveRecord
213
205
  # Same as #fourth but raises ActiveRecord::RecordNotFound if no record
214
206
  # is found.
215
207
  def fourth!
216
- find_nth! 3
208
+ fourth || raise_record_not_found_exception!
217
209
  end
218
210
 
219
211
  # Find the fifth record.
@@ -229,7 +221,7 @@ module ActiveRecord
229
221
  # Same as #fifth but raises ActiveRecord::RecordNotFound if no record
230
222
  # is found.
231
223
  def fifth!
232
- find_nth! 4
224
+ fifth || raise_record_not_found_exception!
233
225
  end
234
226
 
235
227
  # Find the forty-second record. Also known as accessing "the reddit".
@@ -245,7 +237,7 @@ module ActiveRecord
245
237
  # Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
246
238
  # is found.
247
239
  def forty_two!
248
- find_nth! 41
240
+ forty_two || raise_record_not_found_exception!
249
241
  end
250
242
 
251
243
  # Find the third-to-last record.
@@ -261,7 +253,7 @@ module ActiveRecord
261
253
  # Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
262
254
  # is found.
263
255
  def third_to_last!
264
- find_nth_from_last 3 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
256
+ third_to_last || raise_record_not_found_exception!
265
257
  end
266
258
 
267
259
  # Find the second-to-last record.
@@ -277,7 +269,7 @@ module ActiveRecord
277
269
  # Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
278
270
  # is found.
279
271
  def second_to_last!
280
- find_nth_from_last 2 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
272
+ second_to_last || raise_record_not_found_exception!
281
273
  end
282
274
 
283
275
  # Returns true if a record exists in the table that matches the +id+ or
@@ -309,8 +301,7 @@ module ActiveRecord
309
301
  # Person.exists?
310
302
  def exists?(conditions = :none)
311
303
  if Base === conditions
312
- conditions = conditions.id
313
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
304
+ raise ArgumentError, <<-MSG.squish
314
305
  You are passing an instance of ActiveRecord::Base to `exists?`.
315
306
  Please pass the id of the object by calling `.id`.
316
307
  MSG
@@ -318,10 +309,10 @@ module ActiveRecord
318
309
 
319
310
  return false if !conditions
320
311
 
321
- relation = apply_join_dependency(self, construct_join_dependency)
312
+ relation = apply_join_dependency(self, construct_join_dependency(eager_loading: false))
322
313
  return false if ActiveRecord::NullRelation === relation
323
314
 
324
- relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1)
315
+ relation = relation.except(:select, :distinct).select(ONE_AS_ONE).limit(1)
325
316
 
326
317
  case conditions
327
318
  when Array, Hash
@@ -333,6 +324,8 @@ module ActiveRecord
333
324
  end
334
325
 
335
326
  connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
327
+ rescue ::RangeError
328
+ false
336
329
  end
337
330
 
338
331
  # This method is called whenever no records are found with either a single
@@ -343,258 +336,234 @@ module ActiveRecord
343
336
  # of results obtained should be provided in the +result_size+ argument and
344
337
  # the expected number of results should be provided in the +expected_size+
345
338
  # argument.
346
- def raise_record_not_found_exception!(ids, result_size, expected_size, key = primary_key) #:nodoc:
339
+ def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
347
340
  conditions = arel.where_sql(@klass.arel_engine)
348
341
  conditions = " [#{conditions}]" if conditions
349
342
  name = @klass.name
350
343
 
351
- if Array(ids).size == 1
344
+ if ids.nil?
345
+ error = "Couldn't find #{name}"
346
+ error << " with#{conditions}" if conditions
347
+ raise RecordNotFound.new(error, name)
348
+ elsif Array(ids).size == 1
352
349
  error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
353
350
  raise RecordNotFound.new(error, name, key, ids)
354
351
  else
355
352
  error = "Couldn't find all #{name.pluralize} with '#{key}': "
356
353
  error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
357
354
 
358
- raise RecordNotFound, error
355
+ raise RecordNotFound.new(error, name, primary_key, ids)
359
356
  end
360
357
  end
361
358
 
362
359
  private
363
360
 
364
- def offset_index
365
- offset_value || 0
366
- end
361
+ def offset_index
362
+ offset_value || 0
363
+ end
367
364
 
368
- def find_with_associations
369
- # NOTE: the JoinDependency constructed here needs to know about
370
- # any joins already present in `self`, so pass them in
371
- #
372
- # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
373
- # incorrect SQL is generated. In that case, the join dependency for
374
- # SpecialCategorizations is constructed without knowledge of the
375
- # preexisting join in joins_values to categorizations (by way of
376
- # the `has_many :through` for categories).
377
- #
378
- join_dependency = construct_join_dependency(joins_values)
379
-
380
- aliases = join_dependency.aliases
381
- relation = select aliases.columns
382
- relation = apply_join_dependency(relation, join_dependency)
383
-
384
- if block_given?
385
- yield relation
386
- else
387
- if ActiveRecord::NullRelation === relation
388
- []
365
+ def find_with_associations
366
+ # NOTE: the JoinDependency constructed here needs to know about
367
+ # any joins already present in `self`, so pass them in
368
+ #
369
+ # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
370
+ # incorrect SQL is generated. In that case, the join dependency for
371
+ # SpecialCategorizations is constructed without knowledge of the
372
+ # preexisting join in joins_values to categorizations (by way of
373
+ # the `has_many :through` for categories).
374
+ #
375
+ join_dependency = construct_join_dependency(joins_values)
376
+
377
+ aliases = join_dependency.aliases
378
+ relation = select aliases.columns
379
+ relation = apply_join_dependency(relation, join_dependency)
380
+
381
+ if block_given?
382
+ yield relation
389
383
  else
390
- arel = relation.arel
391
- rows = connection.select_all(arel, 'SQL', relation.bound_attributes)
392
- join_dependency.instantiate(rows, aliases)
384
+ if ActiveRecord::NullRelation === relation
385
+ []
386
+ else
387
+ arel = relation.arel
388
+ rows = connection.select_all(arel, "SQL", relation.bound_attributes)
389
+ join_dependency.instantiate(rows, aliases)
390
+ end
393
391
  end
394
392
  end
395
- end
396
393
 
397
- def construct_join_dependency(joins = [])
398
- including = eager_load_values + includes_values
399
- ActiveRecord::Associations::JoinDependency.new(@klass, including, joins)
400
- end
394
+ def construct_join_dependency(joins = [], eager_loading: true)
395
+ including = eager_load_values + includes_values
396
+ ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
397
+ end
401
398
 
402
- def construct_relation_for_association_calculations
403
- from = arel.froms.first
404
- if Arel::Table === from
399
+ def construct_relation_for_association_calculations
405
400
  apply_join_dependency(self, construct_join_dependency(joins_values))
406
- else
407
- # FIXME: as far as I can tell, `from` will always be an Arel::Table.
408
- # There are no tests that test this branch, but presumably it's
409
- # possible for `from` to be a list?
410
- apply_join_dependency(self, construct_join_dependency(from))
411
401
  end
412
- end
413
402
 
414
- def apply_join_dependency(relation, join_dependency)
415
- relation = relation.except(:includes, :eager_load, :preload)
416
- relation = relation.joins join_dependency
403
+ def apply_join_dependency(relation, join_dependency)
404
+ relation = relation.except(:includes, :eager_load, :preload)
405
+ relation = relation.joins join_dependency
417
406
 
418
- if using_limitable_reflections?(join_dependency.reflections)
419
- relation
420
- else
421
- if relation.limit_value
422
- limited_ids = limited_ids_for(relation)
423
- limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
407
+ if using_limitable_reflections?(join_dependency.reflections)
408
+ relation
409
+ else
410
+ if relation.limit_value
411
+ limited_ids = limited_ids_for(relation)
412
+ limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
413
+ end
414
+ relation.except(:limit, :offset)
424
415
  end
425
- relation.except(:limit, :offset)
426
416
  end
427
- end
428
417
 
429
- def limited_ids_for(relation)
430
- values = @klass.connection.columns_for_distinct(
431
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
418
+ def limited_ids_for(relation)
419
+ values = @klass.connection.columns_for_distinct(
420
+ "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
432
421
 
433
- relation = relation.except(:select).select(values).distinct!
434
- arel = relation.arel
422
+ relation = relation.except(:select).select(values).distinct!
423
+ arel = relation.arel
435
424
 
436
- id_rows = @klass.connection.select_all(arel, 'SQL', relation.bound_attributes)
437
- id_rows.map {|row| row[primary_key]}
438
- end
439
-
440
- def using_limitable_reflections?(reflections)
441
- reflections.none?(&:collection?)
442
- end
425
+ id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
426
+ id_rows.map { |row| row[primary_key] }
427
+ end
443
428
 
444
- protected
429
+ def using_limitable_reflections?(reflections)
430
+ reflections.none?(&:collection?)
431
+ end
445
432
 
446
- def find_with_ids(*ids)
447
- raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
433
+ private
448
434
 
449
- expects_array = ids.first.kind_of?(Array)
450
- return ids.first if expects_array && ids.first.empty?
435
+ def find_with_ids(*ids)
436
+ raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
451
437
 
452
- ids = ids.flatten.compact.uniq
438
+ expects_array = ids.first.kind_of?(Array)
439
+ return ids.first if expects_array && ids.first.empty?
453
440
 
454
- case ids.size
455
- when 0
456
- raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
457
- when 1
458
- result = find_one(ids.first)
459
- expects_array ? [ result ] : result
460
- else
461
- find_some(ids)
462
- end
463
- rescue RangeError
464
- raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
465
- end
441
+ ids = ids.flatten.compact.uniq
466
442
 
467
- def find_one(id)
468
- if ActiveRecord::Base === id
469
- id = id.id
470
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
471
- You are passing an instance of ActiveRecord::Base to `find`.
472
- Please pass the id of the object by calling `.id`.
473
- MSG
474
- end
443
+ case ids.size
444
+ when 0
445
+ raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
446
+ when 1
447
+ result = find_one(ids.first)
448
+ expects_array ? [ result ] : result
449
+ else
450
+ find_some(ids)
451
+ end
452
+ rescue ::RangeError
453
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
454
+ end
475
455
 
476
- relation = where(primary_key => id)
477
- record = relation.take
456
+ def find_one(id)
457
+ if ActiveRecord::Base === id
458
+ raise ArgumentError, <<-MSG.squish
459
+ You are passing an instance of ActiveRecord::Base to `find`.
460
+ Please pass the id of the object by calling `.id`.
461
+ MSG
462
+ end
478
463
 
479
- raise_record_not_found_exception!(id, 0, 1) unless record
464
+ relation = where(primary_key => id)
465
+ record = relation.take
480
466
 
481
- record
482
- end
467
+ raise_record_not_found_exception!(id, 0, 1) unless record
483
468
 
484
- def find_some(ids)
485
- return find_some_ordered(ids) unless order_values.present?
469
+ record
470
+ end
486
471
 
487
- result = where(primary_key => ids).to_a
472
+ def find_some(ids)
473
+ return find_some_ordered(ids) unless order_values.present?
488
474
 
489
- expected_size =
490
- if limit_value && ids.size > limit_value
491
- limit_value
492
- else
493
- ids.size
494
- end
475
+ result = where(primary_key => ids).to_a
495
476
 
496
- # 11 ids with limit 3, offset 9 should give 2 results.
497
- if offset_value && (ids.size - offset_value < expected_size)
498
- expected_size = ids.size - offset_value
499
- end
477
+ expected_size =
478
+ if limit_value && ids.size > limit_value
479
+ limit_value
480
+ else
481
+ ids.size
482
+ end
500
483
 
501
- if result.size == expected_size
502
- result
503
- else
504
- raise_record_not_found_exception!(ids, result.size, expected_size)
505
- end
506
- end
484
+ # 11 ids with limit 3, offset 9 should give 2 results.
485
+ if offset_value && (ids.size - offset_value < expected_size)
486
+ expected_size = ids.size - offset_value
487
+ end
507
488
 
508
- def find_some_ordered(ids)
509
- ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
489
+ if result.size == expected_size
490
+ result
491
+ else
492
+ raise_record_not_found_exception!(ids, result.size, expected_size)
493
+ end
494
+ end
510
495
 
511
- result = except(:limit, :offset).where(primary_key => ids).records
496
+ def find_some_ordered(ids)
497
+ ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
512
498
 
513
- if result.size == ids.size
514
- pk_type = @klass.type_for_attribute(primary_key)
499
+ result = except(:limit, :offset).where(primary_key => ids).records
515
500
 
516
- records_by_id = result.index_by(&:id)
517
- ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
518
- else
519
- raise_record_not_found_exception!(ids, result.size, ids.size)
520
- end
521
- end
501
+ if result.size == ids.size
502
+ pk_type = @klass.type_for_attribute(primary_key)
522
503
 
523
- def find_take
524
- if loaded?
525
- records.first
526
- else
527
- @take ||= limit(1).records.first
528
- end
529
- end
504
+ records_by_id = result.index_by(&:id)
505
+ ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
506
+ else
507
+ raise_record_not_found_exception!(ids, result.size, ids.size)
508
+ end
509
+ end
530
510
 
531
- def find_nth(index, offset = nil)
532
- # TODO: once the offset argument is removed we rely on offset_index
533
- # within find_nth_with_limit, rather than pass it in via
534
- # find_nth_with_limit_and_offset
535
- if offset
536
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
537
- Passing an offset argument to find_nth is deprecated,
538
- please use Relation#offset instead.
539
- MSG
540
- end
541
- if loaded?
542
- records[index]
543
- else
544
- offset ||= offset_index
545
- @offsets[offset + index] ||= find_nth_with_limit_and_offset(index, 1, offset: offset).first
546
- end
547
- end
511
+ def find_take
512
+ if loaded?
513
+ records.first
514
+ else
515
+ @take ||= limit(1).records.first
516
+ end
517
+ end
548
518
 
549
- def find_nth!(index)
550
- find_nth(index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
551
- end
519
+ def find_take_with_limit(limit)
520
+ if loaded?
521
+ records.take(limit)
522
+ else
523
+ limit(limit).to_a
524
+ end
525
+ end
552
526
 
553
- def find_nth_with_limit(index, limit)
554
- # TODO: once the offset argument is removed from find_nth,
555
- # find_nth_with_limit_and_offset can be merged into this method
556
- relation = if order_values.empty? && primary_key
557
- order(arel_attribute(primary_key).asc)
558
- else
559
- self
560
- end
561
-
562
- relation = relation.offset(index) unless index.zero?
563
- relation.limit(limit).to_a
564
- end
527
+ def find_nth(index)
528
+ @offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
529
+ end
565
530
 
566
- def find_nth_from_last(index)
567
- if loaded?
568
- records[-index]
569
- else
570
- relation = if order_values.empty? && primary_key
571
- order(arel_attribute(primary_key).asc)
572
- else
573
- self
574
- end
575
-
576
- relation.to_a[-index]
577
- # TODO: can be made more performant on large result sets by
578
- # for instance, last(index)[-index] (which would require
579
- # refactoring the last(n) finder method to make test suite pass),
580
- # or by using a combination of reverse_order, limit, and offset,
581
- # e.g., reverse_order.offset(index-1).first
582
- end
583
- end
584
-
585
- private
531
+ def find_nth_with_limit(index, limit)
532
+ if loaded?
533
+ records[index, limit] || []
534
+ else
535
+ relation = if order_values.empty? && primary_key
536
+ order(arel_attribute(primary_key).asc)
537
+ else
538
+ self
539
+ end
540
+
541
+ relation = relation.offset(offset_index + index) unless index.zero?
542
+ relation.limit(limit).to_a
543
+ end
544
+ end
586
545
 
587
- def find_nth_with_limit_and_offset(index, limit, offset:) # :nodoc:
588
- if loaded?
589
- records[index, limit]
590
- else
591
- index += offset
592
- find_nth_with_limit(index, limit)
593
- end
594
- end
546
+ def find_nth_from_last(index)
547
+ if loaded?
548
+ records[-index]
549
+ else
550
+ relation = if order_values.empty? && primary_key
551
+ order(arel_attribute(primary_key).asc)
552
+ else
553
+ self
554
+ end
555
+
556
+ relation.to_a[-index]
557
+ # TODO: can be made more performant on large result sets by
558
+ # for instance, last(index)[-index] (which would require
559
+ # refactoring the last(n) finder method to make test suite pass),
560
+ # or by using a combination of reverse_order, limit, and offset,
561
+ # e.g., reverse_order.offset(index-1).first
562
+ end
563
+ end
595
564
 
596
- def find_last(limit)
597
- limit ? records.last(limit) : records.last
598
- end
565
+ def find_last(limit)
566
+ limit ? records.last(limit) : records.last
567
+ end
599
568
  end
600
569
  end