activerecord 3.2.22.5 → 4.2.11.3

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 (236) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1632 -609
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +37 -41
  5. data/examples/performance.rb +31 -19
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +56 -42
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -36
  10. data/lib/active_record/associations/association.rb +73 -55
  11. data/lib/active_record/associations/association_scope.rb +143 -82
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +125 -31
  15. data/lib/active_record/associations/builder/belongs_to.rb +89 -61
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +23 -17
  21. data/lib/active_record/associations/collection_association.rb +251 -177
  22. data/lib/active_record/associations/collection_proxy.rb +963 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +113 -22
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -39
  26. data/lib/active_record/associations/has_one_association.rb +43 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +62 -33
  38. data/lib/active_record/associations/preloader.rb +101 -79
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +30 -16
  41. data/lib/active_record/associations.rb +463 -345
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +142 -151
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +137 -57
  47. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +73 -106
  50. data/lib/active_record/attribute_methods/serialization.rb +44 -94
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
  52. data/lib/active_record/attribute_methods/write.rb +57 -44
  53. data/lib/active_record/attribute_methods.rb +301 -141
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +246 -217
  58. data/lib/active_record/base.rb +70 -474
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +396 -219
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +261 -169
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
  75. data/lib/active_record/connection_adapters/column.rb +31 -245
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +430 -999
  114. data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +157 -105
  119. data/lib/active_record/dynamic_matchers.rb +119 -63
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +94 -36
  122. data/lib/active_record/explain.rb +15 -63
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +9 -5
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +302 -215
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +143 -70
  129. data/lib/active_record/integration.rb +65 -12
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +73 -52
  133. data/lib/active_record/locking/pessimistic.rb +5 -5
  134. data/lib/active_record/log_subscriber.rb +24 -21
  135. data/lib/active_record/migration/command_recorder.rb +124 -32
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +511 -213
  138. data/lib/active_record/model_schema.rb +91 -117
  139. data/lib/active_record/nested_attributes.rb +184 -130
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +276 -117
  143. data/lib/active_record/query_cache.rb +19 -37
  144. data/lib/active_record/querying.rb +28 -18
  145. data/lib/active_record/railtie.rb +73 -40
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +4 -3
  148. data/lib/active_record/railties/databases.rake +141 -416
  149. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  150. data/lib/active_record/readonly_attributes.rb +1 -4
  151. data/lib/active_record/reflection.rb +513 -154
  152. data/lib/active_record/relation/batches.rb +91 -43
  153. data/lib/active_record/relation/calculations.rb +199 -161
  154. data/lib/active_record/relation/delegation.rb +116 -25
  155. data/lib/active_record/relation/finder_methods.rb +362 -248
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -43
  160. data/lib/active_record/relation/query_methods.rb +928 -167
  161. data/lib/active_record/relation/spawn_methods.rb +48 -149
  162. data/lib/active_record/relation.rb +352 -207
  163. data/lib/active_record/result.rb +101 -10
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +56 -59
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +106 -63
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +50 -57
  170. data/lib/active_record/scoping/named.rb +73 -109
  171. data/lib/active_record/scoping.rb +58 -123
  172. data/lib/active_record/serialization.rb +6 -2
  173. data/lib/active_record/serializers/xml_serializer.rb +12 -22
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +168 -15
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +23 -16
  181. data/lib/active_record/transactions.rb +125 -79
  182. data/lib/active_record/type/big_integer.rb +13 -0
  183. data/lib/active_record/type/binary.rb +50 -0
  184. data/lib/active_record/type/boolean.rb +31 -0
  185. data/lib/active_record/type/date.rb +50 -0
  186. data/lib/active_record/type/date_time.rb +54 -0
  187. data/lib/active_record/type/decimal.rb +64 -0
  188. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  189. data/lib/active_record/type/decorator.rb +14 -0
  190. data/lib/active_record/type/float.rb +19 -0
  191. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  192. data/lib/active_record/type/integer.rb +59 -0
  193. data/lib/active_record/type/mutable.rb +16 -0
  194. data/lib/active_record/type/numeric.rb +36 -0
  195. data/lib/active_record/type/serialized.rb +62 -0
  196. data/lib/active_record/type/string.rb +40 -0
  197. data/lib/active_record/type/text.rb +11 -0
  198. data/lib/active_record/type/time.rb +26 -0
  199. data/lib/active_record/type/time_value.rb +38 -0
  200. data/lib/active_record/type/type_map.rb +64 -0
  201. data/lib/active_record/type/unsigned_integer.rb +15 -0
  202. data/lib/active_record/type/value.rb +110 -0
  203. data/lib/active_record/type.rb +23 -0
  204. data/lib/active_record/validations/associated.rb +24 -16
  205. data/lib/active_record/validations/presence.rb +67 -0
  206. data/lib/active_record/validations/uniqueness.rb +123 -64
  207. data/lib/active_record/validations.rb +36 -29
  208. data/lib/active_record/version.rb +5 -7
  209. data/lib/active_record.rb +66 -46
  210. data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
  211. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
  212. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  213. data/lib/rails/generators/active_record/migration.rb +11 -8
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
  215. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  216. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  217. data/lib/rails/generators/active_record.rb +3 -11
  218. metadata +101 -45
  219. data/examples/associations.png +0 -0
  220. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  221. data/lib/active_record/associations/join_helper.rb +0 -55
  222. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  223. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  226. data/lib/active_record/dynamic_finder_match.rb +0 -68
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/fixtures/file.rb +0 -65
  229. data/lib/active_record/identity_map.rb +0 -162
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -360
  232. data/lib/active_record/test_case.rb +0 -73
  233. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  234. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  235. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  236. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  # end
23
23
  #
24
24
  # # Comments are not patches, this assignment raises AssociationTypeMismatch.
25
- # @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
25
+ # @ticket.patches << Comment.new(content: "Please attach tests to your patch.")
26
26
  class AssociationTypeMismatch < ActiveRecordError
27
27
  end
28
28
 
@@ -30,17 +30,18 @@ module ActiveRecord
30
30
  class SerializationTypeMismatch < ActiveRecordError
31
31
  end
32
32
 
33
- # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
34
- # misses adapter field).
33
+ # Raised when adapter not specified on connection (or configuration file
34
+ # +config/database.yml+ misses adapter field).
35
35
  class AdapterNotSpecified < ActiveRecordError
36
36
  end
37
37
 
38
- # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
38
+ # Raised when Active Record cannot find database adapter specified in
39
+ # +config/database.yml+ or programmatically.
39
40
  class AdapterNotFound < ActiveRecordError
40
41
  end
41
42
 
42
- # Raised when connection to the database could not been established (for example when <tt>connection=</tt>
43
- # is given a nil object).
43
+ # Raised when connection to the database could not been established (for
44
+ # example when +connection=+ is given a nil object).
44
45
  class ConnectionNotEstablished < ActiveRecordError
45
46
  end
46
47
 
@@ -51,28 +52,48 @@ module ActiveRecord
51
52
  # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
52
53
  # saved because record is invalid.
53
54
  class RecordNotSaved < ActiveRecordError
54
- end
55
+ attr_reader :record
55
56
 
56
- # Raised when SQL statement cannot be executed by the database (for example, it's often the case for
57
- # MySQL when Ruby driver used is too old).
58
- class StatementInvalid < ActiveRecordError
57
+ def initialize(message, record = nil)
58
+ @record = record
59
+ super(message)
60
+ end
59
61
  end
60
62
 
61
- # Raised when SQL statement is invalid and the application gets a blank result.
62
- class ThrowResult < ActiveRecordError
63
+ # Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
64
+ #
65
+ # begin
66
+ # complex_operation_that_internally_calls_destroy!
67
+ # rescue ActiveRecord::RecordNotDestroyed => invalid
68
+ # puts invalid.record.errors
69
+ # end
70
+ #
71
+ class RecordNotDestroyed < ActiveRecordError
72
+ attr_reader :record
73
+
74
+ def initialize(message, record = nil)
75
+ @record = record
76
+ super(message)
77
+ end
63
78
  end
64
79
 
65
- # Parent class for all specific exceptions which wrap database driver exceptions
66
- # provides access to the original exception also.
67
- class WrappedDatabaseException < StatementInvalid
80
+ # Superclass for all database execution errors.
81
+ #
82
+ # Wraps the underlying database error as +original_exception+.
83
+ class StatementInvalid < ActiveRecordError
68
84
  attr_reader :original_exception
69
85
 
70
- def initialize(message, original_exception)
86
+ def initialize(message, original_exception = nil)
71
87
  super(message)
72
88
  @original_exception = original_exception
73
89
  end
74
90
  end
75
91
 
92
+ # Defunct wrapper class kept for compatibility.
93
+ # +StatementInvalid+ wraps the original exception now.
94
+ class WrappedDatabaseException < StatementInvalid
95
+ end
96
+
76
97
  # Raised when a record cannot be inserted because it would violate a uniqueness constraint.
77
98
  class RecordNotUnique < WrappedDatabaseException
78
99
  end
@@ -81,38 +102,40 @@ module ActiveRecord
81
102
  class InvalidForeignKey < WrappedDatabaseException
82
103
  end
83
104
 
84
- # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example,
85
- # when using +find+ method)
86
- # does not match number of expected variables.
105
+ # Raised when number of bind variables in statement given to +:condition+ key
106
+ # (for example, when using +find+ method) does not match number of expected
107
+ # values supplied.
87
108
  #
88
- # For example, in
109
+ # For example, when there are two placeholders with only one value supplied:
89
110
  #
90
111
  # Location.where("lat = ? AND lng = ?", 53.7362)
91
- #
92
- # two placeholders are given but only one variable to fill them.
93
112
  class PreparedStatementInvalid < ActiveRecordError
94
113
  end
95
114
 
115
+ # Raised when a given database does not exist.
116
+ class NoDatabaseError < StatementInvalid
117
+ end
118
+
96
119
  # Raised on attempt to save stale record. Record is stale when it's being saved in another query after
97
120
  # instantiation, for example, when two users edit the same wiki page and one starts editing and saves
98
121
  # the page before the other.
99
122
  #
100
- # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
123
+ # Read more about optimistic locking in ActiveRecord::Locking module
124
+ # documentation.
101
125
  class StaleObjectError < ActiveRecordError
102
126
  attr_reader :record, :attempted_action
103
127
 
104
128
  def initialize(record, attempted_action)
129
+ super("Attempted to #{attempted_action} a stale object: #{record.class.name}")
105
130
  @record = record
106
131
  @attempted_action = attempted_action
107
132
  end
108
133
 
109
- def message
110
- "Attempted to #{attempted_action} a stale object: #{record.class.name}"
111
- end
112
134
  end
113
135
 
114
- # Raised when association is being configured improperly or
115
- # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
136
+ # Raised when association is being configured improperly or user tries to use
137
+ # offset and limit together with +has_many+ or +has_and_belongs_to_many+
138
+ # associations.
116
139
  class ConfigurationError < ActiveRecordError
117
140
  end
118
141
 
@@ -150,23 +173,34 @@ module ActiveRecord
150
173
  class Rollback < ActiveRecordError
151
174
  end
152
175
 
153
- # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
176
+ # Raised when attribute has a name reserved by Active Record (when attribute
177
+ # has name of one of Active Record instance methods).
154
178
  class DangerousAttributeError < ActiveRecordError
155
179
  end
156
180
 
157
181
  # Raised when unknown attributes are supplied via mass assignment.
158
182
  class UnknownAttributeError < NoMethodError
183
+
184
+ attr_reader :record, :attribute
185
+
186
+ def initialize(record, attribute)
187
+ @record = record
188
+ @attribute = attribute.to_s
189
+ super("unknown attribute '#{attribute}' for #{@record.class}.")
190
+ end
191
+
159
192
  end
160
193
 
161
194
  # Raised when an error occurred while doing a mass assignment to an attribute through the
162
- # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
195
+ # +attributes=+ method. The exception has an +attribute+ property that is the name of the
163
196
  # offending attribute.
164
197
  class AttributeAssignmentError < ActiveRecordError
165
198
  attr_reader :exception, :attribute
199
+
166
200
  def initialize(message, exception, attribute)
201
+ super(message)
167
202
  @exception = exception
168
203
  @attribute = attribute
169
- @message = message
170
204
  end
171
205
  end
172
206
 
@@ -175,21 +209,45 @@ module ActiveRecord
175
209
  # objects, each corresponding to the error while assigning to an attribute.
176
210
  class MultiparameterAssignmentErrors < ActiveRecordError
177
211
  attr_reader :errors
212
+
178
213
  def initialize(errors)
179
214
  @errors = errors
180
215
  end
181
216
  end
182
217
 
183
- # Raised when a primary key is needed, but there is not one specified in the schema or model.
218
+ # Raised when a primary key is needed, but not specified in the schema or model.
184
219
  class UnknownPrimaryKey < ActiveRecordError
185
220
  attr_reader :model
186
221
 
187
- def initialize(model)
222
+ def initialize(model, description = nil)
223
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
224
+ message += "\n#{description}" if description
225
+ super(message)
188
226
  @model = model
189
227
  end
228
+ end
190
229
 
191
- def message
192
- "Unknown primary key for table #{model.table_name} in model #{model}."
193
- end
230
+ # Raised when a relation cannot be mutated because it's already loaded.
231
+ #
232
+ # class Task < ActiveRecord::Base
233
+ # end
234
+ #
235
+ # relation = Task.all
236
+ # relation.loaded? # => true
237
+ #
238
+ # # Methods which try to mutate a loaded relation fail.
239
+ # relation.where!(title: 'TODO') # => ActiveRecord::ImmutableRelation
240
+ # relation.limit!(5) # => ActiveRecord::ImmutableRelation
241
+ class ImmutableRelation < ActiveRecordError
242
+ end
243
+
244
+ # TransactionIsolationError will be raised under the following conditions:
245
+ #
246
+ # * The adapter does not support setting the isolation level
247
+ # * You are joining an existing open transaction
248
+ # * You are creating a nested (savepoint) transaction
249
+ #
250
+ # The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
251
+ class TransactionIsolationError < ActiveRecordError
194
252
  end
195
253
  end
@@ -1,62 +1,22 @@
1
- require 'active_support/core_ext/class/attribute'
1
+ require 'active_support/lazy_load_hooks'
2
+ require 'active_record/explain_registry'
2
3
 
3
4
  module ActiveRecord
4
5
  module Explain
5
- def self.extended(base)
6
- base.class_eval do
7
- # If a query takes longer than these many seconds we log its query plan
8
- # automatically. nil disables this feature.
9
- class_attribute :auto_explain_threshold_in_seconds, :instance_writer => false
10
- self.auto_explain_threshold_in_seconds = nil
11
- end
12
- end
13
-
14
- # If the database adapter supports explain and auto explain is enabled,
15
- # this method triggers EXPLAIN logging for the queries triggered by the
16
- # block if it takes more than the threshold as a whole. That is, the
17
- # threshold is not checked against each individual query, but against the
18
- # duration of the entire block. This approach is convenient for relations.
19
-
20
- #
21
- # The available_queries_for_explain thread variable collects the queries
22
- # to be explained. If the value is nil, it means queries are not being
23
- # currently collected. A false value indicates collecting is turned
24
- # off. Otherwise it is an array of queries.
25
- def logging_query_plan # :nodoc:
26
- return yield unless logger
27
-
28
- threshold = auto_explain_threshold_in_seconds
29
- current = Thread.current
30
- if connection.supports_explain? && threshold && current[:available_queries_for_explain].nil?
31
- begin
32
- queries = current[:available_queries_for_explain] = []
33
- start = Time.now
34
- result = yield
35
- logger.warn(exec_explain(queries)) if Time.now - start > threshold
36
- result
37
- ensure
38
- current[:available_queries_for_explain] = nil
39
- end
40
- else
41
- yield
42
- end
43
- end
44
-
45
- # Relation#explain needs to be able to collect the queries regardless of
46
- # whether auto explain is enabled. This method serves that purpose.
6
+ # Executes the block with the collect flag enabled. Queries are collected
7
+ # asynchronously by the subscriber and returned.
47
8
  def collecting_queries_for_explain # :nodoc:
48
- current = Thread.current
49
- original, current[:available_queries_for_explain] = current[:available_queries_for_explain], []
50
- return yield, current[:available_queries_for_explain]
9
+ ExplainRegistry.collect = true
10
+ yield
11
+ ExplainRegistry.queries
51
12
  ensure
52
- # Note that the return value above does not depend on this assigment.
53
- current[:available_queries_for_explain] = original
13
+ ExplainRegistry.reset
54
14
  end
55
15
 
56
16
  # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
57
17
  # Returns a formatted string ready to be logged.
58
18
  def exec_explain(queries) # :nodoc:
59
- queries && queries.map do |sql, bind|
19
+ str = queries.map do |sql, bind|
60
20
  [].tap do |msg|
61
21
  msg << "EXPLAIN for: #{sql}"
62
22
  unless bind.empty?
@@ -66,21 +26,13 @@ module ActiveRecord
66
26
  msg << connection.explain(sql, bind)
67
27
  end.join("\n")
68
28
  end.join("\n")
69
- end
70
29
 
71
- # Silences automatic EXPLAIN logging for the duration of the block.
72
- #
73
- # This has high priority, no EXPLAINs will be run even if downwards
74
- # the threshold is set to 0.
75
- #
76
- # As the name of the method suggests this only applies to automatic
77
- # EXPLAINs, manual calls to +ActiveRecord::Relation#explain+ run.
78
- def silence_auto_explain
79
- current = Thread.current
80
- original, current[:available_queries_for_explain] = current[:available_queries_for_explain], false
81
- yield
82
- ensure
83
- current[:available_queries_for_explain] = original
30
+ # Overriding inspect to be more human readable, especially in the console.
31
+ def str.inspect
32
+ self
33
+ end
34
+
35
+ str
84
36
  end
85
37
  end
86
38
  end
@@ -0,0 +1,30 @@
1
+ require 'active_support/per_thread_registry'
2
+
3
+ module ActiveRecord
4
+ # This is a thread locals registry for EXPLAIN. For example
5
+ #
6
+ # ActiveRecord::ExplainRegistry.queries
7
+ #
8
+ # returns the collected queries local to the current thread.
9
+ #
10
+ # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
11
+ # for further details.
12
+ class ExplainRegistry # :nodoc:
13
+ extend ActiveSupport::PerThreadRegistry
14
+
15
+ attr_accessor :queries, :collect
16
+
17
+ def initialize
18
+ reset
19
+ end
20
+
21
+ def collect?
22
+ @collect
23
+ end
24
+
25
+ def reset
26
+ @collect = false
27
+ @queries = []
28
+ end
29
+ end
30
+ end
@@ -1,11 +1,15 @@
1
1
  require 'active_support/notifications'
2
+ require 'active_record/explain_registry'
2
3
 
3
4
  module ActiveRecord
4
5
  class ExplainSubscriber # :nodoc:
5
- def call(*args)
6
- if queries = Thread.current[:available_queries_for_explain]
7
- payload = args.last
8
- queries << payload.values_at(:sql, :binds) unless ignore_payload?(payload)
6
+ def start(name, id, payload)
7
+ # unused
8
+ end
9
+
10
+ def finish(name, id, payload)
11
+ if ExplainRegistry.collect? && !ignore_payload?(payload)
12
+ ExplainRegistry.queries << payload.values_at(:sql, :binds)
9
13
  end
10
14
  end
11
15
 
@@ -15,7 +19,7 @@ module ActiveRecord
15
19
  # On the other hand, we want to monitor the performance of our real database
16
20
  # queries, not the performance of the access to the query cache.
17
21
  IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
18
- EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
22
+ EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
19
23
  def ignore_payload?(payload)
20
24
  payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
21
25
  end
@@ -0,0 +1,56 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+
4
+ module ActiveRecord
5
+ class FixtureSet
6
+ class File # :nodoc:
7
+ include Enumerable
8
+
9
+ ##
10
+ # Open a fixture file named +file+. When called with a block, the block
11
+ # is called with the filehandle and the filehandle is automatically closed
12
+ # when the block finishes.
13
+ def self.open(file)
14
+ x = new file
15
+ block_given? ? yield(x) : x
16
+ end
17
+
18
+ def initialize(file)
19
+ @file = file
20
+ @rows = nil
21
+ end
22
+
23
+ def each(&block)
24
+ rows.each(&block)
25
+ end
26
+
27
+
28
+ private
29
+ def rows
30
+ return @rows if @rows
31
+
32
+ begin
33
+ data = YAML.load(render(IO.read(@file)))
34
+ rescue ArgumentError, Psych::SyntaxError => error
35
+ raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
36
+ end
37
+ @rows = data ? validate(data).to_a : []
38
+ end
39
+
40
+ def render(content)
41
+ context = ActiveRecord::FixtureSet::RenderContext.create_subclass.new
42
+ ERB.new(content).result(context.get_binding)
43
+ end
44
+
45
+ # Validate our unmarshalled data.
46
+ def validate(data)
47
+ unless Hash === data || YAML::Omap === data
48
+ raise Fixture::FormatError, 'fixture is not a hash'
49
+ end
50
+
51
+ raise Fixture::FormatError unless data.all? { |name, row| Hash === row }
52
+ data
53
+ end
54
+ end
55
+ end
56
+ end