activerecord 3.1.10 → 4.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (237) hide show
  1. checksums.yaml +6 -6
  2. data/CHANGELOG.md +1837 -338
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +39 -43
  5. data/examples/performance.rb +51 -20
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +57 -43
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -39
  10. data/lib/active_record/associations/association.rb +71 -85
  11. data/lib/active_record/associations/association_scope.rb +138 -89
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
  14. data/lib/active_record/associations/builder/association.rb +125 -29
  15. data/lib/active_record/associations/builder/belongs_to.rb +91 -60
  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 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +22 -29
  21. data/lib/active_record/associations/collection_association.rb +294 -187
  22. data/lib/active_record/associations/collection_proxy.rb +961 -94
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +118 -23
  25. data/lib/active_record/associations/has_many_through_association.rb +115 -45
  26. data/lib/active_record/associations/has_one_association.rb +57 -24
  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 -102
  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 +61 -32
  38. data/lib/active_record/associations/preloader.rb +113 -87
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +37 -19
  41. data/lib/active_record/associations.rb +505 -371
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +212 -0
  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 +141 -51
  47. data/lib/active_record/attribute_methods/primary_key.rb +87 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +74 -117
  50. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
  52. data/lib/active_record/attribute_methods/write.rb +60 -21
  53. data/lib/active_record/attribute_methods.rb +409 -48
  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 +279 -232
  58. data/lib/active_record/base.rb +84 -1969
  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 +422 -243
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
  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 +273 -170
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
  75. data/lib/active_record/connection_adapters/column.rb +33 -221
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
  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 +445 -902
  114. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
  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 +159 -102
  119. data/lib/active_record/dynamic_matchers.rb +140 -0
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +102 -34
  122. data/lib/active_record/explain.rb +38 -0
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +29 -0
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +318 -260
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +247 -0
  129. data/lib/active_record/integration.rb +113 -0
  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 +80 -52
  133. data/lib/active_record/locking/pessimistic.rb +27 -5
  134. data/lib/active_record/log_subscriber.rb +25 -18
  135. data/lib/active_record/migration/command_recorder.rb +130 -38
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +532 -201
  138. data/lib/active_record/model_schema.rb +342 -0
  139. data/lib/active_record/nested_attributes.rb +229 -139
  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 +304 -99
  143. data/lib/active_record/query_cache.rb +25 -43
  144. data/lib/active_record/querying.rb +68 -0
  145. data/lib/active_record/railtie.rb +86 -45
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +7 -4
  148. data/lib/active_record/railties/databases.rake +198 -377
  149. data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
  150. data/lib/active_record/readonly_attributes.rb +23 -0
  151. data/lib/active_record/reflection.rb +516 -165
  152. data/lib/active_record/relation/batches.rb +96 -45
  153. data/lib/active_record/relation/calculations.rb +221 -144
  154. data/lib/active_record/relation/delegation.rb +140 -0
  155. data/lib/active_record/relation/finder_methods.rb +362 -243
  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 -41
  160. data/lib/active_record/relation/query_methods.rb +982 -155
  161. data/lib/active_record/relation/spawn_methods.rb +50 -110
  162. data/lib/active_record/relation.rb +371 -180
  163. data/lib/active_record/result.rb +109 -12
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +191 -0
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +111 -61
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +135 -0
  170. data/lib/active_record/scoping/named.rb +164 -0
  171. data/lib/active_record/scoping.rb +87 -0
  172. data/lib/active_record/serialization.rb +7 -45
  173. data/lib/active_record/serializers/xml_serializer.rb +14 -65
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +205 -0
  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 +35 -14
  181. data/lib/active_record/transactions.rb +141 -74
  182. data/lib/active_record/translation.rb +22 -0
  183. data/lib/active_record/type/big_integer.rb +13 -0
  184. data/lib/active_record/type/binary.rb +50 -0
  185. data/lib/active_record/type/boolean.rb +31 -0
  186. data/lib/active_record/type/date.rb +50 -0
  187. data/lib/active_record/type/date_time.rb +54 -0
  188. data/lib/active_record/type/decimal.rb +64 -0
  189. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  190. data/lib/active_record/type/decorator.rb +14 -0
  191. data/lib/active_record/type/float.rb +19 -0
  192. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  193. data/lib/active_record/type/integer.rb +59 -0
  194. data/lib/active_record/type/mutable.rb +16 -0
  195. data/lib/active_record/type/numeric.rb +36 -0
  196. data/lib/active_record/type/serialized.rb +62 -0
  197. data/lib/active_record/type/string.rb +40 -0
  198. data/lib/active_record/type/text.rb +11 -0
  199. data/lib/active_record/type/time.rb +26 -0
  200. data/lib/active_record/type/time_value.rb +38 -0
  201. data/lib/active_record/type/type_map.rb +64 -0
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type/value.rb +110 -0
  204. data/lib/active_record/type.rb +23 -0
  205. data/lib/active_record/validations/associated.rb +27 -18
  206. data/lib/active_record/validations/presence.rb +67 -0
  207. data/lib/active_record/validations/uniqueness.rb +125 -66
  208. data/lib/active_record/validations.rb +37 -30
  209. data/lib/active_record/version.rb +5 -7
  210. data/lib/active_record.rb +80 -25
  211. data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
  212. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  213. data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
  214. data/lib/rails/generators/active_record/migration.rb +11 -8
  215. data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
  216. data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
  217. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  218. data/lib/rails/generators/active_record.rb +3 -11
  219. metadata +132 -53
  220. data/examples/associations.png +0 -0
  221. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
  222. data/lib/active_record/associations/join_helper.rb +0 -55
  223. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
  226. data/lib/active_record/dynamic_finder_match.rb +0 -56
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/identity_map.rb +0 -163
  229. data/lib/active_record/named_scope.rb +0 -200
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -358
  232. data/lib/active_record/test_case.rb +0 -69
  233. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
  234. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  235. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  236. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  237. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -1,19 +1,100 @@
1
1
  module ActiveRecord
2
2
  # = Active Record Persistence
3
3
  module Persistence
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
8
+ # The resulting object is returned whether the object was saved successfully to the database or not.
9
+ #
10
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
11
+ # attributes on the objects that are to be created.
12
+ #
13
+ # ==== Examples
14
+ # # Create a single new object
15
+ # User.create(first_name: 'Jamie')
16
+ #
17
+ # # Create an Array of new objects
18
+ # User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
19
+ #
20
+ # # Create a single object and pass it into a block to set other attributes.
21
+ # User.create(first_name: 'Jamie') do |u|
22
+ # u.is_admin = false
23
+ # end
24
+ #
25
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
26
+ # User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
27
+ # u.is_admin = false
28
+ # end
29
+ def create(attributes = nil, &block)
30
+ if attributes.is_a?(Array)
31
+ attributes.collect { |attr| create(attr, &block) }
32
+ else
33
+ object = new(attributes, &block)
34
+ object.save
35
+ object
36
+ end
37
+ end
38
+
39
+ # Creates an object (or multiple objects) and saves it to the database,
40
+ # if validations pass. Raises a RecordInvalid error if validations fail,
41
+ # unlike Base#create.
42
+ #
43
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes.
44
+ # These describe which attributes to be created on the object, or
45
+ # multiple objects when given an Array of Hashes.
46
+ def create!(attributes = nil, &block)
47
+ if attributes.is_a?(Array)
48
+ attributes.collect { |attr| create!(attr, &block) }
49
+ else
50
+ object = new(attributes, &block)
51
+ object.save!
52
+ object
53
+ end
54
+ end
55
+
56
+ # Given an attributes hash, +instantiate+ returns a new instance of
57
+ # the appropriate class. Accepts only keys as strings.
58
+ #
59
+ # For example, +Post.all+ may return Comments, Messages, and Emails
60
+ # by storing the record's subclass in a +type+ attribute. By calling
61
+ # +instantiate+ instead of +new+, finder methods ensure they get new
62
+ # instances of the appropriate class for each record.
63
+ #
64
+ # See +ActiveRecord::Inheritance#discriminate_class_for_record+ to see
65
+ # how this "single-table" inheritance mapping is implemented.
66
+ def instantiate(attributes, column_types = {})
67
+ klass = discriminate_class_for_record(attributes)
68
+ attributes = klass.attributes_builder.build_from_database(attributes, column_types)
69
+ klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
70
+ end
71
+
72
+ private
73
+ # Called by +instantiate+ to decide which class to use for a new
74
+ # record instance.
75
+ #
76
+ # See +ActiveRecord::Inheritance#discriminate_class_for_record+ for
77
+ # the single-table inheritance discriminator.
78
+ def discriminate_class_for_record(record)
79
+ self
80
+ end
81
+ end
82
+
4
83
  # Returns true if this object hasn't been saved yet -- that is, a record
5
- # for the object doesn't exist in the data store yet; otherwise, returns false.
84
+ # for the object doesn't exist in the database yet; otherwise, returns false.
6
85
  def new_record?
86
+ sync_with_transaction_state
7
87
  @new_record
8
88
  end
9
89
 
10
90
  # Returns true if this object has been destroyed, otherwise returns false.
11
91
  def destroyed?
92
+ sync_with_transaction_state
12
93
  @destroyed
13
94
  end
14
95
 
15
- # Returns if the record is persisted, i.e. it's not a new record and it was
16
- # not destroyed.
96
+ # Returns true if the record is persisted, i.e. it's not a new record and it was
97
+ # not destroyed, otherwise returns false.
17
98
  def persisted?
18
99
  !(new_record? || destroyed?)
19
100
  end
@@ -25,19 +106,20 @@ module ActiveRecord
25
106
  #
26
107
  # By default, save always run validations. If any of them fail the action
27
108
  # is cancelled and +save+ returns +false+. However, if you supply
28
- # :validate => false, validations are bypassed altogether. See
109
+ # validate: false, validations are bypassed altogether. See
29
110
  # ActiveRecord::Validations for more information.
30
111
  #
31
112
  # There's a series of callbacks associated with +save+. If any of the
32
113
  # <tt>before_*</tt> callbacks return +false+ the action is cancelled and
33
114
  # +save+ returns +false+. See ActiveRecord::Callbacks for further
34
115
  # details.
116
+ #
117
+ # Attributes marked as readonly are silently ignored if the record is
118
+ # being updated.
35
119
  def save(*)
36
- begin
37
- create_or_update
38
- rescue ActiveRecord::RecordInvalid
39
- false
40
- end
120
+ create_or_update
121
+ rescue ActiveRecord::RecordInvalid
122
+ false
41
123
  end
42
124
 
43
125
  # Saves the model.
@@ -53,8 +135,11 @@ module ActiveRecord
53
135
  # the <tt>before_*</tt> callbacks return +false+ the action is cancelled
54
136
  # and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
55
137
  # ActiveRecord::Callbacks for further details.
138
+ #
139
+ # Attributes marked as readonly are silently ignored if the record is
140
+ # being updated.
56
141
  def save!(*)
57
- create_or_update || raise(RecordNotSaved)
142
+ create_or_update || raise(RecordNotSaved.new("Failed to save the record", self))
58
143
  end
59
144
 
60
145
  # Deletes the record in the database and freezes this instance to
@@ -65,45 +150,47 @@ module ActiveRecord
65
150
  # record's primary key, and no callbacks are executed.
66
151
  #
67
152
  # To enforce the object's +before_destroy+ and +after_destroy+
68
- # callbacks, Observer methods, or any <tt>:dependent</tt> association
153
+ # callbacks or any <tt>:dependent</tt> association
69
154
  # options, use <tt>#destroy</tt>.
70
155
  def delete
71
- if persisted?
72
- self.class.delete(id)
73
- IdentityMap.remove(self) if IdentityMap.enabled?
74
- end
156
+ self.class.delete(id) if persisted?
75
157
  @destroyed = true
76
158
  freeze
77
159
  end
78
160
 
79
161
  # Deletes the record in the database and freezes this instance to reflect
80
162
  # that no changes should be made (since they can't be persisted).
163
+ #
164
+ # There's a series of callbacks associated with <tt>destroy</tt>. If
165
+ # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
166
+ # and <tt>destroy</tt> returns +false+. See
167
+ # ActiveRecord::Callbacks for further details.
81
168
  def destroy
169
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
82
170
  destroy_associations
83
-
84
- if persisted?
85
- IdentityMap.remove(self) if IdentityMap.enabled?
86
- pk = self.class.primary_key
87
- column = self.class.columns_hash[pk]
88
- substitute = connection.substitute_at(column, 0)
89
-
90
- relation = self.class.unscoped.where(
91
- self.class.arel_table[pk].eq(substitute))
92
-
93
- relation.bind_values = [[column, id]]
94
- relation.delete_all
95
- end
96
-
171
+ self.class.connection.add_transaction_record(self)
172
+ destroy_row if persisted?
97
173
  @destroyed = true
98
174
  freeze
99
175
  end
100
176
 
177
+ # Deletes the record in the database and freezes this instance to reflect
178
+ # that no changes should be made (since they can't be persisted).
179
+ #
180
+ # There's a series of callbacks associated with <tt>destroy!</tt>. If
181
+ # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
182
+ # and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
183
+ # ActiveRecord::Callbacks for further details.
184
+ def destroy!
185
+ destroy || raise(RecordNotDestroyed.new("Failed to destroy the record", self))
186
+ end
187
+
101
188
  # Returns an instance of the specified +klass+ with the attributes of the
102
189
  # current record. This is mostly useful in relation to single-table
103
190
  # inheritance structures where you want a subclass to appear as the
104
191
  # superclass. This can be used along with record identification in
105
192
  # Action Pack to allow, say, <tt>Client < Company</tt> to do something
106
- # like render <tt>:partial => @client.becomes(Company)</tt> to render that
193
+ # like render <tt>partial: @client.becomes(Company)</tt> to render that
107
194
  # instance using the companies/company partial instead of clients/client.
108
195
  #
109
196
  # Note: The new instance will share a link to the same attributes as the original class.
@@ -111,10 +198,27 @@ module ActiveRecord
111
198
  def becomes(klass)
112
199
  became = klass.new
113
200
  became.instance_variable_set("@attributes", @attributes)
114
- became.instance_variable_set("@attributes_cache", @attributes_cache)
201
+ changed_attributes = @changed_attributes if defined?(@changed_attributes)
202
+ became.instance_variable_set("@changed_attributes", changed_attributes || {})
115
203
  became.instance_variable_set("@new_record", new_record?)
116
204
  became.instance_variable_set("@destroyed", destroyed?)
117
- became.type = klass.name unless self.class.descends_from_active_record?
205
+ became.instance_variable_set("@errors", errors)
206
+ became
207
+ end
208
+
209
+ # Wrapper around +becomes+ that also changes the instance's sti column value.
210
+ # This is especially useful if you want to persist the changed class in your
211
+ # database.
212
+ #
213
+ # Note: The old instance's sti column value will be changed too, as both objects
214
+ # share the same set of attributes.
215
+ def becomes!(klass)
216
+ became = becomes(klass)
217
+ sti_type = nil
218
+ if !klass.descends_from_active_record?
219
+ sti_type = klass.sti_name
220
+ end
221
+ became.public_send("#{klass.inheritance_column}=", sti_type)
118
222
  became
119
223
  end
120
224
 
@@ -126,55 +230,81 @@ module ActiveRecord
126
230
  # * updated_at/updated_on column is updated if that column is available.
127
231
  # * Updates all the attributes that are dirty in this object.
128
232
  #
233
+ # This method raises an +ActiveRecord::ActiveRecordError+ if the
234
+ # attribute is marked as readonly.
235
+ #
236
+ # See also +update_column+.
129
237
  def update_attribute(name, value)
130
238
  name = name.to_s
131
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
239
+ verify_readonly_attribute(name)
132
240
  send("#{name}=", value)
133
- save(:validate => false)
134
- end
135
-
136
- # Updates a single attribute of an object, without calling save.
137
- #
138
- # * Validation is skipped.
139
- # * Callbacks are skipped.
140
- # * updated_at/updated_on column is not updated if that column is available.
141
- #
142
- def update_column(name, value)
143
- name = name.to_s
144
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
145
- raise ActiveRecordError, "can not update on a new record object" unless persisted?
146
- raw_write_attribute(name, value)
147
- self.class.update_all({ name => value }, self.class.primary_key => id) == 1
241
+ save(validate: false)
148
242
  end
149
243
 
150
244
  # Updates the attributes of the model from the passed-in hash and saves the
151
245
  # record, all wrapped in a transaction. If the object is invalid, the saving
152
246
  # will fail and false will be returned.
153
- #
154
- # When updating model attributes, mass-assignment security protection is respected.
155
- # If no +:as+ option is supplied then the +:default+ role will be used.
156
- # If you want to bypass the protection given by +attr_protected+ and
157
- # +attr_accessible+ then you can do so using the +:without_protection+ option.
158
- def update_attributes(attributes, options = {})
247
+ def update(attributes)
159
248
  # The following transaction covers any possible database side-effects of the
160
249
  # attributes assignment. For example, setting the IDs of a child collection.
161
250
  with_transaction_returning_status do
162
- self.assign_attributes(attributes, options)
251
+ assign_attributes(attributes)
163
252
  save
164
253
  end
165
254
  end
166
255
 
167
- # Updates its receiver just like +update_attributes+ but calls <tt>save!</tt> instead
256
+ alias update_attributes update
257
+
258
+ # Updates its receiver just like +update+ but calls <tt>save!</tt> instead
168
259
  # of +save+, so an exception is raised if the record is invalid.
169
- def update_attributes!(attributes, options = {})
260
+ def update!(attributes)
170
261
  # The following transaction covers any possible database side-effects of the
171
262
  # attributes assignment. For example, setting the IDs of a child collection.
172
263
  with_transaction_returning_status do
173
- self.assign_attributes(attributes, options)
264
+ assign_attributes(attributes)
174
265
  save!
175
266
  end
176
267
  end
177
268
 
269
+ alias update_attributes! update!
270
+
271
+ # Equivalent to <code>update_columns(name => value)</code>.
272
+ def update_column(name, value)
273
+ update_columns(name => value)
274
+ end
275
+
276
+ # Updates the attributes directly in the database issuing an UPDATE SQL
277
+ # statement and sets them in the receiver:
278
+ #
279
+ # user.update_columns(last_request_at: Time.current)
280
+ #
281
+ # This is the fastest way to update attributes because it goes straight to
282
+ # the database, but take into account that in consequence the regular update
283
+ # procedures are totally bypassed. In particular:
284
+ #
285
+ # * Validations are skipped.
286
+ # * Callbacks are skipped.
287
+ # * +updated_at+/+updated_on+ are not updated.
288
+ #
289
+ # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
290
+ # objects, or when at least one of the attributes is marked as readonly.
291
+ def update_columns(attributes)
292
+ raise ActiveRecordError, "cannot update a new record" if new_record?
293
+ raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
294
+
295
+ attributes.each_key do |key|
296
+ verify_readonly_attribute(key.to_s)
297
+ end
298
+
299
+ updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
300
+
301
+ attributes.each do |k, v|
302
+ raw_write_attribute(k, v)
303
+ end
304
+
305
+ updated_count == 1
306
+ end
307
+
178
308
  # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
179
309
  # The increment is performed directly on the underlying attribute, no setter is invoked.
180
310
  # Only makes sense for number-based attributes. Returns +self+.
@@ -226,60 +356,124 @@ module ActiveRecord
226
356
  toggle(attribute).update_attribute(attribute, self[attribute])
227
357
  end
228
358
 
229
- # Reloads the attributes of this object from the database.
230
- # The optional options argument is passed to find when reloading so you
231
- # may do e.g. record.reload(:lock => true) to reload the same record with
232
- # an exclusive row lock.
359
+ # Reloads the record from the database.
360
+ #
361
+ # This method finds record by its primary key (which could be assigned manually) and
362
+ # modifies the receiver in-place:
363
+ #
364
+ # account = Account.new
365
+ # # => #<Account id: nil, email: nil>
366
+ # account.id = 1
367
+ # account.reload
368
+ # # Account Load (1.2ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1 [["id", 1]]
369
+ # # => #<Account id: 1, email: 'account@example.com'>
370
+ #
371
+ # Attributes are reloaded from the database, and caches busted, in
372
+ # particular the associations cache and the QueryCache.
373
+ #
374
+ # If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
375
+ # is raised. Otherwise, in addition to the in-place modification the method
376
+ # returns +self+ for convenience.
377
+ #
378
+ # The optional <tt>:lock</tt> flag option allows you to lock the reloaded record:
379
+ #
380
+ # reload(lock: true) # reload with pessimistic locking
381
+ #
382
+ # Reloading is commonly used in test suites to test something is actually
383
+ # written to the database, or when some action modifies the corresponding
384
+ # row in the database but not the object in memory:
385
+ #
386
+ # assert account.deposit!(25)
387
+ # assert_equal 25, account.credit # check it is updated in memory
388
+ # assert_equal 25, account.reload.credit # check it is also persisted
389
+ #
390
+ # Another common use case is optimistic locking handling:
391
+ #
392
+ # def with_optimistic_retry
393
+ # begin
394
+ # yield
395
+ # rescue ActiveRecord::StaleObjectError
396
+ # begin
397
+ # # Reload lock_version in particular.
398
+ # reload
399
+ # rescue ActiveRecord::RecordNotFound
400
+ # # If the record is gone there is nothing to do.
401
+ # else
402
+ # retry
403
+ # end
404
+ # end
405
+ # end
406
+ #
233
407
  def reload(options = nil)
234
408
  clear_aggregation_cache
235
409
  clear_association_cache
410
+ self.class.connection.clear_query_cache
236
411
 
237
- IdentityMap.without do
238
- fresh_object = self.class.unscoped { self.class.find(self.id, options) }
239
- @attributes.update(fresh_object.instance_variable_get('@attributes'))
240
- end
412
+ fresh_object =
413
+ if options && options[:lock]
414
+ self.class.unscoped { self.class.lock(options[:lock]).find(id) }
415
+ else
416
+ self.class.unscoped { self.class.find(id) }
417
+ end
241
418
 
242
- @attributes_cache = {}
419
+ @attributes = fresh_object.instance_variable_get('@attributes')
420
+ @new_record = false
243
421
  self
244
422
  end
245
423
 
246
424
  # Saves the record with the updated_at/on attributes set to the current time.
247
- # Please note that no validation is performed and no callbacks are executed.
248
- # If an attribute name is passed, that attribute is updated along with
249
- # updated_at/on attributes.
425
+ # Please note that no validation is performed and only the +after_touch+,
426
+ # +after_commit+ and +after_rollback+ callbacks are executed.
427
+ #
428
+ # If attribute names are passed, they are updated along with updated_at/on
429
+ # attributes.
250
430
  #
251
- # product.touch # updates updated_at/on
252
- # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
431
+ # product.touch # updates updated_at/on
432
+ # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
433
+ # product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
253
434
  #
254
- # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on associated object.
435
+ # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on
436
+ # associated object.
255
437
  #
256
438
  # class Brake < ActiveRecord::Base
257
- # belongs_to :car, :touch => true
439
+ # belongs_to :car, touch: true
258
440
  # end
259
441
  #
260
442
  # class Car < ActiveRecord::Base
261
- # belongs_to :corporation, :touch => true
443
+ # belongs_to :corporation, touch: true
262
444
  # end
263
445
  #
264
446
  # # triggers @brake.car.touch and @brake.car.corporation.touch
265
447
  # @brake.touch
266
- def touch(name = nil)
448
+ #
449
+ # Note that +touch+ must be used on a persisted object, or else an
450
+ # ActiveRecordError will be thrown. For example:
451
+ #
452
+ # ball = Ball.new
453
+ # ball.touch(:updated_at) # => raises ActiveRecordError
454
+ #
455
+ def touch(*names)
456
+ raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
457
+
267
458
  attributes = timestamp_attributes_for_update_in_model
268
- attributes << name if name
459
+ attributes.concat(names)
269
460
 
270
461
  unless attributes.empty?
271
462
  current_time = current_time_from_proper_timezone
272
463
  changes = {}
273
464
 
274
465
  attributes.each do |column|
275
- changes[column.to_s] = write_attribute(column.to_s, current_time)
466
+ column = column.to_s
467
+ changes[column] = write_attribute(column, current_time)
276
468
  end
277
469
 
278
470
  changes[self.class.locking_column] = increment_lock if locking_enabled?
279
471
 
280
- @changed_attributes.except!(*changes.keys)
472
+ clear_attribute_changes(changes.keys)
281
473
  primary_key = self.class.primary_key
282
- self.class.unscoped.update_all(changes, { primary_key => self[primary_key] }) == 1
474
+ self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
475
+ else
476
+ true
283
477
  end
284
478
  end
285
479
 
@@ -289,42 +483,53 @@ module ActiveRecord
289
483
  def destroy_associations
290
484
  end
291
485
 
486
+ def destroy_row
487
+ relation_for_destroy.delete_all
488
+ end
489
+
490
+ def relation_for_destroy
491
+ pk = self.class.primary_key
492
+ column = self.class.columns_hash[pk]
493
+ substitute = self.class.connection.substitute_at(column)
494
+
495
+ relation = self.class.unscoped.where(
496
+ self.class.arel_table[pk].eq(substitute))
497
+
498
+ relation.bind_values = [[column, id]]
499
+ relation
500
+ end
501
+
292
502
  def create_or_update
293
- raise ReadOnlyRecord if readonly?
294
- result = new_record? ? create : update
503
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
504
+ result = new_record? ? _create_record : _update_record
295
505
  result != false
296
506
  end
297
507
 
298
508
  # Updates the associated record with values matching those of the instance attributes.
299
509
  # Returns the number of affected rows.
300
- def update(attribute_names = @attributes.keys)
301
- attributes_with_values = arel_attributes_values(false, false, attribute_names)
302
- return 0 if attributes_with_values.empty?
303
- klass = self.class
304
- stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
305
- klass.connection.update stmt
510
+ def _update_record(attribute_names = self.attribute_names)
511
+ attributes_values = arel_attributes_with_values_for_update(attribute_names)
512
+ if attributes_values.empty?
513
+ 0
514
+ else
515
+ self.class.unscoped._update_record attributes_values, id, id_was
516
+ end
306
517
  end
307
518
 
308
519
  # Creates a record with values matching those of the instance attributes
309
520
  # and returns its id.
310
- def create
311
- attributes_values = arel_attributes_values(!id.nil?)
521
+ def _create_record(attribute_names = self.attribute_names)
522
+ attributes_values = arel_attributes_with_values_for_create(attribute_names)
312
523
 
313
524
  new_id = self.class.unscoped.insert attributes_values
525
+ self.id ||= new_id if self.class.primary_key
314
526
 
315
- self.id ||= new_id
316
-
317
- IdentityMap.add(self) if IdentityMap.enabled?
318
527
  @new_record = false
319
528
  id
320
529
  end
321
530
 
322
- # Initializes the attributes array with keys matching the columns from the linked table and
323
- # the values matching the corresponding default value of that column, so
324
- # that a new instance, or one populated from a passed-in Hash, still has all the attributes
325
- # that instances loaded from the database would.
326
- def attributes_from_column_definition
327
- self.class.column_defaults.dup
531
+ def verify_readonly_attribute(name)
532
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
328
533
  end
329
534
  end
330
535
  end
@@ -1,24 +1,24 @@
1
- require 'active_support/core_ext/object/blank'
2
-
3
1
  module ActiveRecord
4
2
  # = Active Record Query Cache
5
3
  class QueryCache
6
4
  module ClassMethods
7
5
  # Enable the query cache within the block if Active Record is configured.
6
+ # If it's not, it will execute the given block.
8
7
  def cache(&block)
9
- if ActiveRecord::Base.configurations.blank?
10
- yield
11
- else
8
+ if ActiveRecord::Base.connected?
12
9
  connection.cache(&block)
10
+ else
11
+ yield
13
12
  end
14
13
  end
15
14
 
16
15
  # Disable the query cache within the block if Active Record is configured.
16
+ # If it's not, it will execute the given block.
17
17
  def uncached(&block)
18
- if ActiveRecord::Base.configurations.blank?
19
- yield
20
- else
18
+ if ActiveRecord::Base.connected?
21
19
  connection.uncached(&block)
20
+ else
21
+ yield
22
22
  end
23
23
  end
24
24
  end
@@ -27,48 +27,30 @@ module ActiveRecord
27
27
  @app = app
28
28
  end
29
29
 
30
- class BodyProxy # :nodoc:
31
- def initialize(original_cache_value, target, connection_id)
32
- @original_cache_value = original_cache_value
33
- @target = target
34
- @connection_id = connection_id
35
- end
36
-
37
- def method_missing(method_sym, *arguments, &block)
38
- @target.send(method_sym, *arguments, &block)
39
- end
40
-
41
- def respond_to?(method_sym, include_private = false)
42
- super || @target.respond_to?(method_sym)
43
- end
30
+ def call(env)
31
+ connection = ActiveRecord::Base.connection
32
+ enabled = connection.query_cache_enabled
33
+ connection_id = ActiveRecord::Base.connection_id
34
+ connection.enable_query_cache!
44
35
 
45
- def each(&block)
46
- @target.each(&block)
36
+ response = @app.call(env)
37
+ response[2] = Rack::BodyProxy.new(response[2]) do
38
+ restore_query_cache_settings(connection_id, enabled)
47
39
  end
48
40
 
49
- def close
50
- @target.close if @target.respond_to?(:close)
51
- ensure
52
- ActiveRecord::Base.connection_id = @connection_id
53
- ActiveRecord::Base.connection.clear_query_cache
54
- unless @original_cache_value
55
- ActiveRecord::Base.connection.disable_query_cache!
56
- end
57
- end
41
+ response
42
+ rescue Exception => e
43
+ restore_query_cache_settings(connection_id, enabled)
44
+ raise e
58
45
  end
59
46
 
60
- def call(env)
61
- old = ActiveRecord::Base.connection.query_cache_enabled
62
- ActiveRecord::Base.connection.enable_query_cache!
47
+ private
63
48
 
64
- status, headers, body = @app.call(env)
65
- [status, headers, BodyProxy.new(old, body, ActiveRecord::Base.connection_id)]
66
- rescue Exception => e
49
+ def restore_query_cache_settings(connection_id, enabled)
50
+ ActiveRecord::Base.connection_id = connection_id
67
51
  ActiveRecord::Base.connection.clear_query_cache
68
- unless old
69
- ActiveRecord::Base.connection.disable_query_cache!
70
- end
71
- raise e
52
+ ActiveRecord::Base.connection.disable_query_cache! unless enabled
72
53
  end
54
+
73
55
  end
74
56
  end