activerecord 4.2.0

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

Potentially problematic release.


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

Files changed (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,52 @@
1
+ module ActiveRecord
2
+ # = Active Record No Touching
3
+ module NoTouching
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ # Lets you selectively disable calls to `touch` for the
8
+ # duration of a block.
9
+ #
10
+ # ==== Examples
11
+ # ActiveRecord::Base.no_touching do
12
+ # Project.first.touch # does nothing
13
+ # Message.first.touch # does nothing
14
+ # end
15
+ #
16
+ # Project.no_touching do
17
+ # Project.first.touch # does nothing
18
+ # Message.first.touch # works, but does not touch the associated project
19
+ # end
20
+ #
21
+ def no_touching(&block)
22
+ NoTouching.apply_to(self, &block)
23
+ end
24
+ end
25
+
26
+ class << self
27
+ def apply_to(klass) #:nodoc:
28
+ klasses.push(klass)
29
+ yield
30
+ ensure
31
+ klasses.pop
32
+ end
33
+
34
+ def applied_to?(klass) #:nodoc:
35
+ klasses.any? { |k| k >= klass }
36
+ end
37
+
38
+ private
39
+ def klasses
40
+ Thread.current[:no_touching_classes] ||= []
41
+ end
42
+ end
43
+
44
+ def no_touching?
45
+ NoTouching.applied_to?(self.class)
46
+ end
47
+
48
+ def touch(*)
49
+ super unless no_touching?
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,81 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module ActiveRecord
4
+ module NullRelation # :nodoc:
5
+ def exec_queries
6
+ @records = []
7
+ end
8
+
9
+ def pluck(*column_names)
10
+ []
11
+ end
12
+
13
+ def delete_all(_conditions = nil)
14
+ 0
15
+ end
16
+
17
+ def update_all(_updates, _conditions = nil, _options = {})
18
+ 0
19
+ end
20
+
21
+ def delete(_id_or_array)
22
+ 0
23
+ end
24
+
25
+ def size
26
+ calculate :size, nil
27
+ end
28
+
29
+ def empty?
30
+ true
31
+ end
32
+
33
+ def any?
34
+ false
35
+ end
36
+
37
+ def many?
38
+ false
39
+ end
40
+
41
+ def to_sql
42
+ ""
43
+ end
44
+
45
+ def count(*)
46
+ calculate :count, nil
47
+ end
48
+
49
+ def sum(*)
50
+ calculate :sum, nil
51
+ end
52
+
53
+ def average(*)
54
+ calculate :average, nil
55
+ end
56
+
57
+ def minimum(*)
58
+ calculate :minimum, nil
59
+ end
60
+
61
+ def maximum(*)
62
+ calculate :maximum, nil
63
+ end
64
+
65
+ def calculate(operation, _column_name, _options = {})
66
+ # TODO: Remove _options argument as soon we remove support to
67
+ # activerecord-deprecated_finders.
68
+ if [:count, :sum, :size].include? operation
69
+ group_values.any? ? Hash.new : 0
70
+ elsif [:average, :minimum, :maximum].include?(operation) && group_values.any?
71
+ Hash.new
72
+ else
73
+ nil
74
+ end
75
+ end
76
+
77
+ def exists?(_id = false)
78
+ false
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,532 @@
1
+ module ActiveRecord
2
+ # = Active Record Persistence
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
+
83
+ # Returns true if this object hasn't been saved yet -- that is, a record
84
+ # for the object doesn't exist in the database yet; otherwise, returns false.
85
+ def new_record?
86
+ sync_with_transaction_state
87
+ @new_record
88
+ end
89
+
90
+ # Returns true if this object has been destroyed, otherwise returns false.
91
+ def destroyed?
92
+ sync_with_transaction_state
93
+ @destroyed
94
+ end
95
+
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.
98
+ def persisted?
99
+ !(new_record? || destroyed?)
100
+ end
101
+
102
+ # Saves the model.
103
+ #
104
+ # If the model is new a record gets created in the database, otherwise
105
+ # the existing record gets updated.
106
+ #
107
+ # By default, save always run validations. If any of them fail the action
108
+ # is cancelled and +save+ returns +false+. However, if you supply
109
+ # validate: false, validations are bypassed altogether. See
110
+ # ActiveRecord::Validations for more information.
111
+ #
112
+ # There's a series of callbacks associated with +save+. If any of the
113
+ # <tt>before_*</tt> callbacks return +false+ the action is cancelled and
114
+ # +save+ returns +false+. See ActiveRecord::Callbacks for further
115
+ # details.
116
+ #
117
+ # Attributes marked as readonly are silently ignored if the record is
118
+ # being updated.
119
+ def save(*)
120
+ create_or_update
121
+ rescue ActiveRecord::RecordInvalid
122
+ false
123
+ end
124
+
125
+ # Saves the model.
126
+ #
127
+ # If the model is new a record gets created in the database, otherwise
128
+ # the existing record gets updated.
129
+ #
130
+ # With <tt>save!</tt> validations always run. If any of them fail
131
+ # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
132
+ # for more information.
133
+ #
134
+ # There's a series of callbacks associated with <tt>save!</tt>. If any of
135
+ # the <tt>before_*</tt> callbacks return +false+ the action is cancelled
136
+ # and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
137
+ # ActiveRecord::Callbacks for further details.
138
+ #
139
+ # Attributes marked as readonly are silently ignored if the record is
140
+ # being updated.
141
+ def save!(*)
142
+ create_or_update || raise(RecordNotSaved.new(nil, self))
143
+ end
144
+
145
+ # Deletes the record in the database and freezes this instance to
146
+ # reflect that no changes should be made (since they can't be
147
+ # persisted). Returns the frozen instance.
148
+ #
149
+ # The row is simply removed with an SQL +DELETE+ statement on the
150
+ # record's primary key, and no callbacks are executed.
151
+ #
152
+ # To enforce the object's +before_destroy+ and +after_destroy+
153
+ # callbacks or any <tt>:dependent</tt> association
154
+ # options, use <tt>#destroy</tt>.
155
+ def delete
156
+ self.class.delete(id) if persisted?
157
+ @destroyed = true
158
+ freeze
159
+ end
160
+
161
+ # Deletes the record in the database and freezes this instance to reflect
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.
168
+ def destroy
169
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
170
+ destroy_associations
171
+ destroy_row if persisted?
172
+ @destroyed = true
173
+ freeze
174
+ end
175
+
176
+ # Deletes the record in the database and freezes this instance to reflect
177
+ # that no changes should be made (since they can't be persisted).
178
+ #
179
+ # There's a series of callbacks associated with <tt>destroy!</tt>. If
180
+ # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
181
+ # and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
182
+ # ActiveRecord::Callbacks for further details.
183
+ def destroy!
184
+ destroy || raise(ActiveRecord::RecordNotDestroyed, self)
185
+ end
186
+
187
+ # Returns an instance of the specified +klass+ with the attributes of the
188
+ # current record. This is mostly useful in relation to single-table
189
+ # inheritance structures where you want a subclass to appear as the
190
+ # superclass. This can be used along with record identification in
191
+ # Action Pack to allow, say, <tt>Client < Company</tt> to do something
192
+ # like render <tt>partial: @client.becomes(Company)</tt> to render that
193
+ # instance using the companies/company partial instead of clients/client.
194
+ #
195
+ # Note: The new instance will share a link to the same attributes as the original class.
196
+ # So any change to the attributes in either instance will affect the other.
197
+ def becomes(klass)
198
+ became = klass.new
199
+ became.instance_variable_set("@attributes", @attributes)
200
+ became.instance_variable_set("@changed_attributes", @changed_attributes) if defined?(@changed_attributes)
201
+ became.instance_variable_set("@new_record", new_record?)
202
+ became.instance_variable_set("@destroyed", destroyed?)
203
+ became.instance_variable_set("@errors", errors)
204
+ became
205
+ end
206
+
207
+ # Wrapper around +becomes+ that also changes the instance's sti column value.
208
+ # This is especially useful if you want to persist the changed class in your
209
+ # database.
210
+ #
211
+ # Note: The old instance's sti column value will be changed too, as both objects
212
+ # share the same set of attributes.
213
+ def becomes!(klass)
214
+ became = becomes(klass)
215
+ sti_type = nil
216
+ if !klass.descends_from_active_record?
217
+ sti_type = klass.sti_name
218
+ end
219
+ became.public_send("#{klass.inheritance_column}=", sti_type)
220
+ became
221
+ end
222
+
223
+ # Updates a single attribute and saves the record.
224
+ # This is especially useful for boolean flags on existing records. Also note that
225
+ #
226
+ # * Validation is skipped.
227
+ # * Callbacks are invoked.
228
+ # * updated_at/updated_on column is updated if that column is available.
229
+ # * Updates all the attributes that are dirty in this object.
230
+ #
231
+ # This method raises an +ActiveRecord::ActiveRecordError+ if the
232
+ # attribute is marked as readonly.
233
+ #
234
+ # See also +update_column+.
235
+ def update_attribute(name, value)
236
+ name = name.to_s
237
+ verify_readonly_attribute(name)
238
+ send("#{name}=", value)
239
+ save(validate: false)
240
+ end
241
+
242
+ # Updates the attributes of the model from the passed-in hash and saves the
243
+ # record, all wrapped in a transaction. If the object is invalid, the saving
244
+ # will fail and false will be returned.
245
+ def update(attributes)
246
+ # The following transaction covers any possible database side-effects of the
247
+ # attributes assignment. For example, setting the IDs of a child collection.
248
+ with_transaction_returning_status do
249
+ assign_attributes(attributes)
250
+ save
251
+ end
252
+ end
253
+
254
+ alias update_attributes update
255
+
256
+ # Updates its receiver just like +update+ but calls <tt>save!</tt> instead
257
+ # of +save+, so an exception is raised if the record is invalid.
258
+ def update!(attributes)
259
+ # The following transaction covers any possible database side-effects of the
260
+ # attributes assignment. For example, setting the IDs of a child collection.
261
+ with_transaction_returning_status do
262
+ assign_attributes(attributes)
263
+ save!
264
+ end
265
+ end
266
+
267
+ alias update_attributes! update!
268
+
269
+ # Equivalent to <code>update_columns(name => value)</code>.
270
+ def update_column(name, value)
271
+ update_columns(name => value)
272
+ end
273
+
274
+ # Updates the attributes directly in the database issuing an UPDATE SQL
275
+ # statement and sets them in the receiver:
276
+ #
277
+ # user.update_columns(last_request_at: Time.current)
278
+ #
279
+ # This is the fastest way to update attributes because it goes straight to
280
+ # the database, but take into account that in consequence the regular update
281
+ # procedures are totally bypassed. In particular:
282
+ #
283
+ # * Validations are skipped.
284
+ # * Callbacks are skipped.
285
+ # * +updated_at+/+updated_on+ are not updated.
286
+ #
287
+ # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
288
+ # objects, or when at least one of the attributes is marked as readonly.
289
+ def update_columns(attributes)
290
+ raise ActiveRecordError, "cannot update a new record" if new_record?
291
+ raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
292
+
293
+ attributes.each_key do |key|
294
+ verify_readonly_attribute(key.to_s)
295
+ end
296
+
297
+ updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
298
+
299
+ attributes.each do |k, v|
300
+ raw_write_attribute(k, v)
301
+ end
302
+
303
+ updated_count == 1
304
+ end
305
+
306
+ # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
307
+ # The increment is performed directly on the underlying attribute, no setter is invoked.
308
+ # Only makes sense for number-based attributes. Returns +self+.
309
+ def increment(attribute, by = 1)
310
+ self[attribute] ||= 0
311
+ self[attribute] += by
312
+ self
313
+ end
314
+
315
+ # Wrapper around +increment+ that saves the record. This method differs from
316
+ # its non-bang version in that it passes through the attribute setter.
317
+ # Saving is not subjected to validation checks. Returns +true+ if the
318
+ # record could be saved.
319
+ def increment!(attribute, by = 1)
320
+ increment(attribute, by).update_attribute(attribute, self[attribute])
321
+ end
322
+
323
+ # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
324
+ # The decrement is performed directly on the underlying attribute, no setter is invoked.
325
+ # Only makes sense for number-based attributes. Returns +self+.
326
+ def decrement(attribute, by = 1)
327
+ self[attribute] ||= 0
328
+ self[attribute] -= by
329
+ self
330
+ end
331
+
332
+ # Wrapper around +decrement+ that saves the record. This method differs from
333
+ # its non-bang version in that it passes through the attribute setter.
334
+ # Saving is not subjected to validation checks. Returns +true+ if the
335
+ # record could be saved.
336
+ def decrement!(attribute, by = 1)
337
+ decrement(attribute, by).update_attribute(attribute, self[attribute])
338
+ end
339
+
340
+ # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
341
+ # if the predicate returns +true+ the attribute will become +false+. This
342
+ # method toggles directly the underlying value without calling any setter.
343
+ # Returns +self+.
344
+ def toggle(attribute)
345
+ self[attribute] = !send("#{attribute}?")
346
+ self
347
+ end
348
+
349
+ # Wrapper around +toggle+ that saves the record. This method differs from
350
+ # its non-bang version in that it passes through the attribute setter.
351
+ # Saving is not subjected to validation checks. Returns +true+ if the
352
+ # record could be saved.
353
+ def toggle!(attribute)
354
+ toggle(attribute).update_attribute(attribute, self[attribute])
355
+ end
356
+
357
+ # Reloads the record from the database.
358
+ #
359
+ # This method finds record by its primary key (which could be assigned manually) and
360
+ # modifies the receiver in-place:
361
+ #
362
+ # account = Account.new
363
+ # # => #<Account id: nil, email: nil>
364
+ # account.id = 1
365
+ # account.reload
366
+ # # Account Load (1.2ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1 [["id", 1]]
367
+ # # => #<Account id: 1, email: 'account@example.com'>
368
+ #
369
+ # Attributes are reloaded from the database, and caches busted, in
370
+ # particular the associations cache.
371
+ #
372
+ # If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
373
+ # is raised. Otherwise, in addition to the in-place modification the method
374
+ # returns +self+ for convenience.
375
+ #
376
+ # The optional <tt>:lock</tt> flag option allows you to lock the reloaded record:
377
+ #
378
+ # reload(lock: true) # reload with pessimistic locking
379
+ #
380
+ # Reloading is commonly used in test suites to test something is actually
381
+ # written to the database, or when some action modifies the corresponding
382
+ # row in the database but not the object in memory:
383
+ #
384
+ # assert account.deposit!(25)
385
+ # assert_equal 25, account.credit # check it is updated in memory
386
+ # assert_equal 25, account.reload.credit # check it is also persisted
387
+ #
388
+ # Another common use case is optimistic locking handling:
389
+ #
390
+ # def with_optimistic_retry
391
+ # begin
392
+ # yield
393
+ # rescue ActiveRecord::StaleObjectError
394
+ # begin
395
+ # # Reload lock_version in particular.
396
+ # reload
397
+ # rescue ActiveRecord::RecordNotFound
398
+ # # If the record is gone there is nothing to do.
399
+ # else
400
+ # retry
401
+ # end
402
+ # end
403
+ # end
404
+ #
405
+ def reload(options = nil)
406
+ clear_aggregation_cache
407
+ clear_association_cache
408
+
409
+ fresh_object =
410
+ if options && options[:lock]
411
+ self.class.unscoped { self.class.lock(options[:lock]).find(id) }
412
+ else
413
+ self.class.unscoped { self.class.find(id) }
414
+ end
415
+
416
+ @attributes = fresh_object.instance_variable_get('@attributes')
417
+ @new_record = false
418
+ self
419
+ end
420
+
421
+ # Saves the record with the updated_at/on attributes set to the current time.
422
+ # Please note that no validation is performed and only the +after_touch+,
423
+ # +after_commit+ and +after_rollback+ callbacks are executed.
424
+ #
425
+ # If attribute names are passed, they are updated along with updated_at/on
426
+ # attributes.
427
+ #
428
+ # product.touch # updates updated_at/on
429
+ # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
430
+ # product.touch(:started_at, :ended_at) # updates started_at, ended_at and updated_at/on attributes
431
+ #
432
+ # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on
433
+ # associated object.
434
+ #
435
+ # class Brake < ActiveRecord::Base
436
+ # belongs_to :car, touch: true
437
+ # end
438
+ #
439
+ # class Car < ActiveRecord::Base
440
+ # belongs_to :corporation, touch: true
441
+ # end
442
+ #
443
+ # # triggers @brake.car.touch and @brake.car.corporation.touch
444
+ # @brake.touch
445
+ #
446
+ # Note that +touch+ must be used on a persisted object, or else an
447
+ # ActiveRecordError will be thrown. For example:
448
+ #
449
+ # ball = Ball.new
450
+ # ball.touch(:updated_at) # => raises ActiveRecordError
451
+ #
452
+ def touch(*names)
453
+ raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
454
+
455
+ attributes = timestamp_attributes_for_update_in_model
456
+ attributes.concat(names)
457
+
458
+ unless attributes.empty?
459
+ current_time = current_time_from_proper_timezone
460
+ changes = {}
461
+
462
+ attributes.each do |column|
463
+ column = column.to_s
464
+ changes[column] = write_attribute(column, current_time)
465
+ end
466
+
467
+ changes[self.class.locking_column] = increment_lock if locking_enabled?
468
+
469
+ clear_attribute_changes(changes.keys)
470
+ primary_key = self.class.primary_key
471
+ self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
472
+ else
473
+ true
474
+ end
475
+ end
476
+
477
+ private
478
+
479
+ # A hook to be overridden by association modules.
480
+ def destroy_associations
481
+ end
482
+
483
+ def destroy_row
484
+ relation_for_destroy.delete_all
485
+ end
486
+
487
+ def relation_for_destroy
488
+ pk = self.class.primary_key
489
+ column = self.class.columns_hash[pk]
490
+ substitute = self.class.connection.substitute_at(column)
491
+
492
+ relation = self.class.unscoped.where(
493
+ self.class.arel_table[pk].eq(substitute))
494
+
495
+ relation.bind_values = [[column, id]]
496
+ relation
497
+ end
498
+
499
+ def create_or_update
500
+ raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
501
+ result = new_record? ? _create_record : _update_record
502
+ result != false
503
+ end
504
+
505
+ # Updates the associated record with values matching those of the instance attributes.
506
+ # Returns the number of affected rows.
507
+ def _update_record(attribute_names = self.attribute_names)
508
+ attributes_values = arel_attributes_with_values_for_update(attribute_names)
509
+ if attributes_values.empty?
510
+ 0
511
+ else
512
+ self.class.unscoped._update_record attributes_values, id, id_was
513
+ end
514
+ end
515
+
516
+ # Creates a record with values matching those of the instance attributes
517
+ # and returns its id.
518
+ def _create_record(attribute_names = self.attribute_names)
519
+ attributes_values = arel_attributes_with_values_for_create(attribute_names)
520
+
521
+ new_id = self.class.unscoped.insert attributes_values
522
+ self.id ||= new_id if self.class.primary_key
523
+
524
+ @new_record = false
525
+ id
526
+ end
527
+
528
+ def verify_readonly_attribute(name)
529
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
530
+ end
531
+ end
532
+ end