activerecord 3.2.22.5 → 4.0.0.beta1

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

Potentially problematic release.


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

Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/class/attribute'
2
1
 
3
2
  module ActiveRecord
4
3
  # = Active Record Timestamp
@@ -37,14 +36,14 @@ module ActiveRecord
37
36
  self.record_timestamps = true
38
37
  end
39
38
 
40
- def initialize_dup(other)
39
+ def initialize_dup(other) # :nodoc:
41
40
  clear_timestamp_attributes
42
41
  super
43
42
  end
44
43
 
45
44
  private
46
45
 
47
- def create #:nodoc:
46
+ def create_record
48
47
  if self.record_timestamps
49
48
  current_time = current_time_from_proper_timezone
50
49
 
@@ -58,7 +57,7 @@ module ActiveRecord
58
57
  super
59
58
  end
60
59
 
61
- def update(*args) #:nodoc:
60
+ def update_record(*args)
62
61
  if should_record_timestamps?
63
62
  current_time = current_time_from_proper_timezone
64
63
 
@@ -72,7 +71,7 @@ module ActiveRecord
72
71
  end
73
72
 
74
73
  def should_record_timestamps?
75
- self.record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
74
+ self.record_timestamps && (!partial_writes? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
76
75
  end
77
76
 
78
77
  def timestamp_attributes_for_create_in_model
@@ -87,19 +86,19 @@ module ActiveRecord
87
86
  timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
88
87
  end
89
88
 
90
- def timestamp_attributes_for_update #:nodoc:
89
+ def timestamp_attributes_for_update
91
90
  [:updated_at, :updated_on]
92
91
  end
93
92
 
94
- def timestamp_attributes_for_create #:nodoc:
93
+ def timestamp_attributes_for_create
95
94
  [:created_at, :created_on]
96
95
  end
97
96
 
98
- def all_timestamp_attributes #:nodoc:
97
+ def all_timestamp_attributes
99
98
  timestamp_attributes_for_create + timestamp_attributes_for_update
100
99
  end
101
100
 
102
- def current_time_from_proper_timezone #:nodoc:
101
+ def current_time_from_proper_timezone
103
102
  self.class.default_timezone == :utc ? Time.now.utc : Time.now
104
103
  end
105
104
 
@@ -4,6 +4,7 @@ module ActiveRecord
4
4
  # See ActiveRecord::Transactions::ClassMethods for documentation.
5
5
  module Transactions
6
6
  extend ActiveSupport::Concern
7
+ ACTIONS = [:create, :destroy, :update]
7
8
 
8
9
  class TransactionError < ActiveRecordError # :nodoc:
9
10
  end
@@ -108,10 +109,10 @@ module ActiveRecord
108
109
  #
109
110
  # # Suppose that we have a Number model with a unique column called 'i'.
110
111
  # Number.transaction do
111
- # Number.create(:i => 0)
112
+ # Number.create(i: 0)
112
113
  # begin
113
114
  # # This will raise a unique constraint error...
114
- # Number.create(:i => 0)
115
+ # Number.create(i: 0)
115
116
  # rescue ActiveRecord::StatementInvalid
116
117
  # # ...which we ignore.
117
118
  # end
@@ -119,7 +120,7 @@ module ActiveRecord
119
120
  # # On PostgreSQL, the transaction is now unusable. The following
120
121
  # # statement will cause a PostgreSQL error, even though the unique
121
122
  # # constraint is no longer violated:
122
- # Number.create(:i => 1)
123
+ # Number.create(i: 1)
123
124
  # # => "PGError: ERROR: current transaction is aborted, commands
124
125
  # # ignored until end of transaction block"
125
126
  # end
@@ -134,9 +135,9 @@ module ActiveRecord
134
135
  # transaction. For example, the following behavior may be surprising:
135
136
  #
136
137
  # User.transaction do
137
- # User.create(:username => 'Kotori')
138
+ # User.create(username: 'Kotori')
138
139
  # User.transaction do
139
- # User.create(:username => 'Nemu')
140
+ # User.create(username: 'Nemu')
140
141
  # raise ActiveRecord::Rollback
141
142
  # end
142
143
  # end
@@ -147,14 +148,14 @@ module ActiveRecord
147
148
  # real transaction is committed.
148
149
  #
149
150
  # In order to get a ROLLBACK for the nested transaction you may ask for a real
150
- # sub-transaction by passing <tt>:requires_new => true</tt>. If anything goes wrong,
151
+ # sub-transaction by passing <tt>requires_new: true</tt>. If anything goes wrong,
151
152
  # the database rolls back to the beginning of the sub-transaction without rolling
152
153
  # back the parent transaction. If we add it to the previous example:
153
154
  #
154
155
  # User.transaction do
155
- # User.create(:username => 'Kotori')
156
- # User.transaction(:requires_new => true) do
157
- # User.create(:username => 'Nemu')
156
+ # User.create(username: 'Kotori')
157
+ # User.transaction(requires_new: true) do
158
+ # User.create(username: 'Nemu')
158
159
  # raise ActiveRecord::Rollback
159
160
  # end
160
161
  # end
@@ -165,7 +166,7 @@ module ActiveRecord
165
166
  # writing, the only database that we're aware of that supports true nested
166
167
  # transactions, is MS-SQL. Because of this, Active Record emulates nested
167
168
  # transactions by using savepoints on MySQL and PostgreSQL. See
168
- # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
169
+ # http://dev.mysql.com/doc/refman/5.6/en/savepoint.html
169
170
  # for more information about savepoints.
170
171
  #
171
172
  # === Callbacks
@@ -194,7 +195,7 @@ module ActiveRecord
194
195
  # automatically released. The following example demonstrates the problem:
195
196
  #
196
197
  # Model.connection.transaction do # BEGIN
197
- # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
198
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
198
199
  # Model.connection.create_table(...) # active_record_1 now automatically released
199
200
  # end # RELEASE savepoint active_record_1
200
201
  # # ^^^^ BOOM! database error!
@@ -213,22 +214,17 @@ module ActiveRecord
213
214
  # You can specify that the callback should only be fired by a certain action with
214
215
  # the +:on+ option:
215
216
  #
216
- # after_commit :do_foo, :on => :create
217
- # after_commit :do_bar, :on => :update
218
- # after_commit :do_baz, :on => :destroy
217
+ # after_commit :do_foo, on: :create
218
+ # after_commit :do_bar, on: :update
219
+ # after_commit :do_baz, on: :destroy
219
220
  #
220
- # Also, to have the callback fired on create and update, but not on destroy:
221
- #
222
- # after_commit :do_zoo, :if => :persisted?
221
+ # after_commit :do_foo_bar, :on [:create, :update]
222
+ # after_commit :do_bar_baz, :on [:update, :destroy]
223
223
  #
224
224
  # Note that transactional fixtures do not play well with this feature. Please
225
225
  # use the +test_after_commit+ gem to have these hooks fired in tests.
226
226
  def after_commit(*args, &block)
227
- options = args.last
228
- if options.is_a?(Hash) && options[:on]
229
- options[:if] = Array.wrap(options[:if])
230
- options[:if] << "transaction_include_action?(:#{options[:on]})"
231
- end
227
+ set_options_for_callbacks!(args)
232
228
  set_callback(:commit, :after, *args, &block)
233
229
  end
234
230
 
@@ -236,12 +232,27 @@ module ActiveRecord
236
232
  #
237
233
  # Please check the documentation of +after_commit+ for options.
238
234
  def after_rollback(*args, &block)
235
+ set_options_for_callbacks!(args)
236
+ set_callback(:rollback, :after, *args, &block)
237
+ end
238
+
239
+ private
240
+
241
+ def set_options_for_callbacks!(args)
239
242
  options = args.last
240
243
  if options.is_a?(Hash) && options[:on]
241
- options[:if] = Array.wrap(options[:if])
242
- options[:if] << "transaction_include_action?(:#{options[:on]})"
244
+ assert_valid_transaction_action(options[:on])
245
+ options[:if] = Array(options[:if])
246
+ fire_on = Array(options[:on]).map(&:to_sym)
247
+ options[:if] << "transaction_include_any_action?(#{fire_on})"
248
+ end
249
+ end
250
+
251
+ def assert_valid_transaction_action(actions)
252
+ actions = Array(actions)
253
+ if (actions - ACTIONS).any?
254
+ raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS.join(",")}"
243
255
  end
244
- set_callback(:rollback, :after, *args, &block)
245
256
  end
246
257
  end
247
258
 
@@ -269,7 +280,6 @@ module ActiveRecord
269
280
  remember_transaction_record_state
270
281
  yield
271
282
  rescue Exception
272
- IdentityMap.remove(self) if IdentityMap.enabled?
273
283
  restore_transaction_record_state
274
284
  raise
275
285
  ensure
@@ -277,8 +287,11 @@ module ActiveRecord
277
287
  end
278
288
 
279
289
  # Call the after_commit callbacks
290
+ #
291
+ # Ensure that it is not called if the object was never persisted (failed create),
292
+ # but call it after the commit of a destroyed object
280
293
  def committed! #:nodoc:
281
- run_callbacks :commit
294
+ run_callbacks :commit if destroyed? || persisted?
282
295
  ensure
283
296
  clear_transaction_record_state
284
297
  end
@@ -288,7 +301,6 @@ module ActiveRecord
288
301
  def rolledback!(force_restore_state = false) #:nodoc:
289
302
  run_callbacks :rollback
290
303
  ensure
291
- IdentityMap.remove(self) if IdentityMap.enabled?
292
304
  restore_transaction_record_state(force_restore_state)
293
305
  end
294
306
 
@@ -310,7 +322,13 @@ module ActiveRecord
310
322
  status = nil
311
323
  self.class.transaction do
312
324
  add_to_transaction
313
- status = yield
325
+ begin
326
+ status = yield
327
+ rescue ActiveRecord::Rollback
328
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
329
+ status = nil
330
+ end
331
+
314
332
  raise ActiveRecord::Rollback unless status
315
333
  end
316
334
  status
@@ -320,32 +338,25 @@ module ActiveRecord
320
338
 
321
339
  # Save the new record state and id of a record so it can be restored later if a transaction fails.
322
340
  def remember_transaction_record_state #:nodoc:
323
- @_start_transaction_state ||= {}
324
341
  @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
325
- unless @_start_transaction_state.include?(:new_record)
326
- @_start_transaction_state[:new_record] = @new_record
327
- end
328
- unless @_start_transaction_state.include?(:destroyed)
329
- @_start_transaction_state[:destroyed] = @destroyed
330
- end
342
+ @_start_transaction_state[:new_record] = @new_record
343
+ @_start_transaction_state[:destroyed] = @destroyed
331
344
  @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
332
345
  @_start_transaction_state[:frozen?] = @attributes.frozen?
333
346
  end
334
347
 
335
348
  # Clear the new record state and id of a record.
336
349
  def clear_transaction_record_state #:nodoc:
337
- if defined?(@_start_transaction_state)
338
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
339
- remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1
340
- end
350
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
351
+ @_start_transaction_state.clear if @_start_transaction_state[:level] < 1
341
352
  end
342
353
 
343
354
  # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
344
355
  def restore_transaction_record_state(force = false) #:nodoc:
345
- if defined?(@_start_transaction_state)
356
+ unless @_start_transaction_state.empty?
346
357
  @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
347
358
  if @_start_transaction_state[:level] < 1 || force
348
- restore_state = remove_instance_variable(:@_start_transaction_state)
359
+ restore_state = @_start_transaction_state
349
360
  was_frozen = restore_state[:frozen?]
350
361
  @attributes = @attributes.dup if @attributes.frozen?
351
362
  @new_record = restore_state[:new_record]
@@ -357,24 +368,27 @@ module ActiveRecord
357
368
  @attributes_cache.delete(self.class.primary_key)
358
369
  end
359
370
  @attributes.freeze if was_frozen
371
+ @_start_transaction_state.clear
360
372
  end
361
373
  end
362
374
  end
363
375
 
364
376
  # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
365
377
  def transaction_record_state(state) #:nodoc:
366
- @_start_transaction_state[state] if defined?(@_start_transaction_state)
378
+ @_start_transaction_state[state]
367
379
  end
368
380
 
369
381
  # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
370
- def transaction_include_action?(action) #:nodoc:
371
- case action
372
- when :create
373
- transaction_record_state(:new_record)
374
- when :destroy
375
- destroyed?
376
- when :update
377
- !(transaction_record_state(:new_record) || destroyed?)
382
+ def transaction_include_any_action?(actions) #:nodoc:
383
+ actions.any? do |action|
384
+ case action
385
+ when :create
386
+ transaction_record_state(:new_record)
387
+ when :destroy
388
+ destroyed?
389
+ when :update
390
+ !(transaction_record_state(:new_record) || destroyed?)
391
+ end
378
392
  end
379
393
  end
380
394
  end
@@ -10,11 +10,11 @@ module ActiveRecord
10
10
  # puts invalid.record.errors
11
11
  # end
12
12
  class RecordInvalid < ActiveRecordError
13
- attr_reader :record
14
- def initialize(record)
13
+ attr_reader :record # :nodoc:
14
+ def initialize(record) # :nodoc:
15
15
  @record = record
16
16
  errors = @record.errors.full_messages.join(", ")
17
- super(I18n.t("activerecord.errors.messages.record_invalid", :errors => errors))
17
+ super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
18
18
  end
19
19
  end
20
20
 
@@ -32,11 +32,11 @@ module ActiveRecord
32
32
  module ClassMethods
33
33
  # Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+
34
34
  # so an exception is raised if the record is invalid.
35
- def create!(attributes = nil, options = {}, &block)
35
+ def create!(attributes = nil, &block)
36
36
  if attributes.is_a?(Array)
37
- attributes.collect { |attr| create!(attr, options, &block) }
37
+ attributes.collect { |attr| create!(attr, &block) }
38
38
  else
39
- object = new(attributes, options)
39
+ object = new(attributes)
40
40
  yield(object) if block_given?
41
41
  object.save!
42
42
  object
@@ -44,23 +44,24 @@ module ActiveRecord
44
44
  end
45
45
  end
46
46
 
47
- # The validation process on save can be skipped by passing <tt>:validate => false</tt>. The regular Base#save method is
48
- # replaced with this when the validations module is mixed in, which it is by default.
47
+ # The validation process on save can be skipped by passing <tt>validate: false</tt>.
48
+ # The regular Base#save method is replaced with this when the validations
49
+ # module is mixed in, which it is by default.
49
50
  def save(options={})
50
51
  perform_validations(options) ? super : false
51
52
  end
52
53
 
53
- # Attempts to save the record just like Base#save but will raise a +RecordInvalid+ exception instead of returning false
54
- # if the record is not valid.
54
+ # Attempts to save the record just like Base#save but will raise a +RecordInvalid+
55
+ # exception instead of returning +false+ if the record is not valid.
55
56
  def save!(options={})
56
57
  perform_validations(options) ? super : raise(RecordInvalid.new(self))
57
58
  end
58
59
 
59
- # Runs all the validations within the specified context. Returns true if no errors are found,
60
- # false otherwise.
60
+ # Runs all the validations within the specified context. Returns +true+ if
61
+ # no errors are found, +false+ otherwise.
61
62
  #
62
- # If the argument is false (default is +nil+), the context is set to <tt>:create</tt> if
63
- # <tt>new_record?</tt> is true, and to <tt>:update</tt> if it is not.
63
+ # If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
64
+ # <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
64
65
  #
65
66
  # Validations with no <tt>:on</tt> option will run no matter the context. Validations with
66
67
  # some <tt>:on</tt> option will only run in the specified context.
@@ -72,7 +73,7 @@ module ActiveRecord
72
73
 
73
74
  protected
74
75
 
75
- def perform_validations(options={})
76
+ def perform_validations(options={}) # :nodoc:
76
77
  perform_validation = options[:validate] != false
77
78
  perform_validation ? valid?(options[:context]) : true
78
79
  end
@@ -81,3 +82,4 @@ end
81
82
 
82
83
  require "active_record/validations/associated"
83
84
  require "active_record/validations/uniqueness"
85
+ require "active_record/validations/presence"
@@ -1,15 +1,16 @@
1
1
  module ActiveRecord
2
2
  module Validations
3
- class AssociatedValidator < ActiveModel::EachValidator
3
+ class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
4
4
  def validate_each(record, attribute, value)
5
- if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?}.any?
5
+ if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?(record.validation_context) }.any?
6
6
  record.errors.add(attribute, :invalid, options.merge(:value => value))
7
7
  end
8
8
  end
9
9
  end
10
10
 
11
11
  module ClassMethods
12
- # Validates whether the associated object or objects are all valid themselves. Works with any kind of association.
12
+ # Validates whether the associated object or objects are all valid
13
+ # themselves. Works with any kind of association.
13
14
  #
14
15
  # class Book < ActiveRecord::Base
15
16
  # has_many :pages
@@ -18,23 +19,28 @@ module ActiveRecord
18
19
  # validates_associated :pages, :library
19
20
  # end
20
21
  #
21
- # WARNING: This validation must not be used on both ends of an association. Doing so will lead to a circular dependency and cause infinite recursion.
22
+ # WARNING: This validation must not be used on both ends of an association.
23
+ # Doing so will lead to a circular dependency and cause infinite recursion.
22
24
  #
23
- # NOTE: This validation will not fail if the association hasn't been assigned. If you want to
24
- # ensure that the association is both present and guaranteed to be valid, you also need to
25
- # use +validates_presence_of+.
25
+ # NOTE: This validation will not fail if the association hasn't been
26
+ # assigned. If you want to ensure that the association is both present and
27
+ # guaranteed to be valid, you also need to use +validates_presence_of+.
26
28
  #
27
29
  # Configuration options:
28
- # * <tt>:message</tt> - A custom error message (default is: "is invalid")
30
+ #
31
+ # * <tt>:message</tt> - A custom error message (default is: "is invalid").
29
32
  # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
30
33
  # validation contexts by default (+nil+), other options are <tt>:create</tt>
31
34
  # and <tt>:update</tt>.
32
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
33
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
34
- # method, proc or string should return or evaluate to a true or false value.
35
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
36
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
37
- # method, proc or string should return or evaluate to a true or false value.
35
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
36
+ # if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
37
+ # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
38
+ # proc or string should return or evaluate to a +true+ or +false+ value.
39
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to
40
+ # determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
41
+ # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
42
+ # method, proc or string should return or evaluate to a +true+ or +false+
43
+ # value.
38
44
  def validates_associated(*attr_names)
39
45
  validates_with AssociatedValidator, _merge_attributes(attr_names)
40
46
  end
@@ -0,0 +1,65 @@
1
+ module ActiveRecord
2
+ module Validations
3
+ class PresenceValidator < ActiveModel::Validations::PresenceValidator # :nodoc:
4
+ def validate(record)
5
+ super
6
+ attributes.each do |attribute|
7
+ next unless record.class.reflect_on_association(attribute)
8
+ associated_records = Array(record.send(attribute))
9
+
10
+ # Superclass validates presence. Ensure present records aren't about to be destroyed.
11
+ if associated_records.present? && associated_records.all? { |r| r.marked_for_destruction? }
12
+ record.errors.add(attribute, :blank, options)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ # Validates that the specified attributes are not blank (as defined by
20
+ # Object#blank?), and, if the attribute is an association, that the
21
+ # associated object is not marked for destruction. Happens by default
22
+ # on save.
23
+ #
24
+ # class Person < ActiveRecord::Base
25
+ # has_one :face
26
+ # validates_presence_of :face
27
+ # end
28
+ #
29
+ # The face attribute must be in the object and it cannot be blank or marked
30
+ # for destruction.
31
+ #
32
+ # If you want to validate the presence of a boolean field (where the real values
33
+ # are true and false), you will want to use
34
+ # <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
35
+ #
36
+ # This is due to the way Object#blank? handles boolean values:
37
+ # <tt>false.blank? # => true</tt>.
38
+ #
39
+ # This validator defers to the ActiveModel validation for presence, adding the
40
+ # check to see that an associated object is not marked for destruction. This
41
+ # prevents the parent object from validating successfully and saving, which then
42
+ # deletes the associated object, thus putting the parent object into an invalid
43
+ # state.
44
+ #
45
+ # Configuration options:
46
+ # * <tt>:message</tt> - A custom error message (default is: "can't be blank").
47
+ # * <tt>:on</tt> - Specifies when this validation is active. Runs in all
48
+ # validation contexts by default (+nil+), other options are <tt>:create</tt>
49
+ # and <tt>:update</tt>.
50
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
51
+ # the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
52
+ # <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
53
+ # or string should return or evaluate to a +true+ or +false+ value.
54
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
55
+ # if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
56
+ # or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
57
+ # proc or string should return or evaluate to a +true+ or +false+ value.
58
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
59
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
60
+ def validates_presence_of(*attr_names)
61
+ validates_with PresenceValidator, _merge_attributes(attr_names)
62
+ end
63
+ end
64
+ end
65
+ end