Add ActiveRecord::Base.suppress
This commit is contained in:
parent
83be86933d
commit
b9a1e9a4b2
@ -1,3 +1,32 @@
|
||||
* Add ActiveRecord::Base.suppress to prevent the receiver from being saved
|
||||
during the given block.
|
||||
|
||||
For example, here's a pattern of creating notifications when new comments
|
||||
are posted. (The notification may in turn trigger an email, a push
|
||||
notification, or just appear in the UI somewhere):
|
||||
|
||||
class Comment < ActiveRecord::Base
|
||||
belongs_to :commentable, polymorphic: true
|
||||
after_create -> { Notification.create! comment: self,
|
||||
recipients: commentable.recipients }
|
||||
end
|
||||
|
||||
That's what you want the bulk of the time. New comment creates a new
|
||||
Notification. But there may well be off cases, like copying a commentable
|
||||
and its comments, where you don't want that. So you'd have a concern
|
||||
something like this:
|
||||
|
||||
module Copyable
|
||||
def copy_to(destination)
|
||||
Notification.suppress do
|
||||
# Copy logic that creates new comments that we do not want triggering
|
||||
# notifications.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
*Michael Ryan*
|
||||
|
||||
* `:time` option added for `#touch`
|
||||
Fixes #18905.
|
||||
|
||||
|
@ -62,6 +62,7 @@ module ActiveRecord
|
||||
autoload :Serialization
|
||||
autoload :StatementCache
|
||||
autoload :Store
|
||||
autoload :Suppressor
|
||||
autoload :TableMetadata
|
||||
autoload :Timestamp
|
||||
autoload :Transactions
|
||||
|
@ -313,6 +313,7 @@ class Base
|
||||
include Serialization
|
||||
include Store
|
||||
include SecureToken
|
||||
include Suppressor
|
||||
end
|
||||
|
||||
ActiveSupport.run_load_hooks(:active_record, Base)
|
||||
|
55
activerecord/lib/active_record/suppressor.rb
Normal file
55
activerecord/lib/active_record/suppressor.rb
Normal file
@ -0,0 +1,55 @@
|
||||
module ActiveRecord
|
||||
# ActiveRecord::Suppressor prevents the receiver from being saved during
|
||||
# a given block.
|
||||
#
|
||||
# For example, here's a pattern of creating notifications when new comments
|
||||
# are posted. (The notification may in turn trigger an email, a push
|
||||
# notification, or just appear in the UI somewhere):
|
||||
#
|
||||
# class Comment < ActiveRecord::Base
|
||||
# belongs_to :commentable, polymorphic: true
|
||||
# after_create -> { Notification.create! comment: self,
|
||||
# recipients: commentable.recipients }
|
||||
# end
|
||||
#
|
||||
# That's what you want the bulk of the time. New comment creates a new
|
||||
# Notification. But there may well be off cases, like copying a commentable
|
||||
# and its comments, where you don't want that. So you'd have a concern
|
||||
# something like this:
|
||||
#
|
||||
# module Copyable
|
||||
# def copy_to(destination)
|
||||
# Notification.suppress do
|
||||
# # Copy logic that creates new comments that we do not want
|
||||
# # triggering notifications.
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
module Suppressor
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
def suppress(&block)
|
||||
SuppressorRegistry.suppressed[name] = true
|
||||
yield
|
||||
ensure
|
||||
SuppressorRegistry.suppressed[name] = false
|
||||
end
|
||||
end
|
||||
|
||||
# Ignore saving events if we're in suppression mode.
|
||||
def save!(*args)
|
||||
SuppressorRegistry.suppressed[self.class.name] ? self : super
|
||||
end
|
||||
end
|
||||
|
||||
class SuppressorRegistry # :nodoc:
|
||||
extend ActiveSupport::PerThreadRegistry
|
||||
|
||||
attr_reader :suppressed
|
||||
|
||||
def initialize
|
||||
@suppressed = {}
|
||||
end
|
||||
end
|
||||
end
|
22
activerecord/test/cases/suppressor_test.rb
Normal file
22
activerecord/test/cases/suppressor_test.rb
Normal file
@ -0,0 +1,22 @@
|
||||
require 'cases/helper'
|
||||
require 'models/notification'
|
||||
require 'models/user'
|
||||
|
||||
class SuppressorTest < ActiveRecord::TestCase
|
||||
|
||||
def test_suppresses_creation_of_record_generated_by_callback
|
||||
assert_difference -> { User.count } do
|
||||
assert_no_difference -> { Notification.count } do
|
||||
Notification.suppress { UserWithNotification.create! }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_resumes_saving_after_suppression_complete
|
||||
Notification.suppress { UserWithNotification.create! }
|
||||
|
||||
assert_difference -> { Notification.count } do
|
||||
Notification.create!
|
||||
end
|
||||
end
|
||||
end
|
2
activerecord/test/models/notification.rb
Normal file
2
activerecord/test/models/notification.rb
Normal file
@ -0,0 +1,2 @@
|
||||
class Notification < ActiveRecord::Base
|
||||
end
|
@ -2,3 +2,7 @@ class User < ActiveRecord::Base
|
||||
has_secure_token
|
||||
has_secure_token :auth_token
|
||||
end
|
||||
|
||||
class UserWithNotification < User
|
||||
after_create -> { Notification.create! message: "A new user has been created." }
|
||||
end
|
||||
|
@ -468,6 +468,10 @@ def except(adapter_names_to_exclude)
|
||||
t.string :name
|
||||
end
|
||||
|
||||
create_table :notifications, force: true do |t|
|
||||
t.string :message
|
||||
end
|
||||
|
||||
create_table :numeric_data, force: true do |t|
|
||||
t.decimal :bank_balance, precision: 10, scale: 2
|
||||
t.decimal :big_bank_balance, precision: 15, scale: 2
|
||||
|
Loading…
Reference in New Issue
Block a user