New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add `ActiveRecord::Base.suppress` to allow save events to be suppressed within a specified block. #18910

Merged
merged 1 commit into from Feb 19, 2015

Conversation

Projects
None yet
7 participants
@perceptec
Contributor

perceptec commented Feb 12, 2015

Fixes #18847.

@georgeclaghorn

View changes

Show outdated Hide outdated activerecord/test/cases/suppressor_test.rb
@georgeclaghorn

View changes

Show outdated Hide outdated activerecord/CHANGELOG.md
@georgeclaghorn

View changes

Show outdated Hide outdated activerecord/lib/active_record/suppressor.rb
@kaspth

View changes

Show outdated Hide outdated activerecord/lib/active_record/suppressor.rb

@kaspth kaspth added the activerecord label Feb 12, 2015

@kaspth kaspth added this to the 5.0.0 milestone Feb 12, 2015

@perceptec

This comment has been minimized.

Show comment
Hide comment
@perceptec

perceptec Feb 12, 2015

Contributor

Thanks for the feedback, @georgeclaghorn and @kaspth.

Contributor

perceptec commented Feb 12, 2015

Thanks for the feedback, @georgeclaghorn and @kaspth.

@kaspth

View changes

Show outdated Hide outdated activerecord/lib/active_record/suppressor.rb
@kaspth

View changes

Show outdated Hide outdated activerecord/lib/active_record/suppressor.rb
@kaspth

View changes

Show outdated Hide outdated activerecord/lib/active_record/suppressor.rb
@kaspth

View changes

Show outdated Hide outdated activerecord/lib/active_record/suppressor.rb
@kaspth

View changes

Show outdated Hide outdated activerecord/lib/active_record/suppressor.rb
@kaspth

View changes

Show outdated Hide outdated activerecord/test/cases/suppressor_test.rb
@kaspth

This comment has been minimized.

Show comment
Hide comment
@kaspth

kaspth Feb 12, 2015

Member

Left some notes, otherwise this looks good. Thanks for working on it 😄

Just so you know, it doesn't seem like GitHub supports "closes #x" in the Pull Request title: https://github.com/blog/1506-closing-issues-via-pull-requests. But the issue will close when this is merged, because you've put the issue number in your commit message.

Member

kaspth commented Feb 12, 2015

Left some notes, otherwise this looks good. Thanks for working on it 😄

Just so you know, it doesn't seem like GitHub supports "closes #x" in the Pull Request title: https://github.com/blog/1506-closing-issues-via-pull-requests. But the issue will close when this is merged, because you've put the issue number in your commit message.

@perceptec perceptec changed the title from Add `ActiveRecord::Base.suppress` to allow save events to be suppressed within a specified block. Closes #18847. to Add `ActiveRecord::Base.suppress` to allow save events to be suppressed within a specified block. Feb 12, 2015

@mcmire

This comment has been minimized.

Show comment
Hide comment
@mcmire

mcmire Feb 13, 2015

Contributor

Does .suppress cancel all callbacks, or only certain callbacks?

Contributor

mcmire commented Feb 13, 2015

Does .suppress cancel all callbacks, or only certain callbacks?

@georgeclaghorn

View changes

Show outdated Hide outdated activerecord/CHANGELOG.md
@mcmire

This comment has been minimized.

Show comment
Hide comment
@mcmire

mcmire Feb 15, 2015

Contributor

I don't really agree with this change. First, I don't think that there's a legitimate use case for this, and second, I don't think this is the correct solution to this problem.

I have rarely ever seen a case where I've added a callback that involves saving another record and then for some reason I don't want that save to occur. I can see this happening in tests, but that is easily solved by stubbing the method that performs the save (Notification.create!, in the example). Now, if I did have a need to prevent this from happening in production code, it would actually tell me that these two components are too tightly coupled, and it would encourage me to break them apart into a higher level object (CreateCommentWithNotification, for instance) so that I had the freedom to only create the comment if I wanted to. In other words, I think this change encourages bad design.

Even if that were not the case, I think that the usage is weird. As a reader of this code, it seems like the context around the use of suppress would not be immediately clear. In other words, if I saw the Notification.suppress line, I'd have to know that it's tied back to the after_create... inside of Comment. I would immediately ask myself, if we are modifying the behavior of Comment, why should we have to tell Notification to do something? We're reaching inside of Notification from a completely different class. On top of this, this seems like a shotgun solution. What if there are other things happening in the block that also save notifications? Wouldn't it be better to be able to ask Comment to disable the callback in question instead of having to know what that callback does and disable its contents?

Anyway... that's a bit of a moot point, because honestly I think both problems can be solved by making a separate class that encapsulates creating a comment and also creating a notification. Is it one more class? Sure, but you don't have to disable anything, and the intention is perfectly clear to other people reading the code.

Contributor

mcmire commented Feb 15, 2015

I don't really agree with this change. First, I don't think that there's a legitimate use case for this, and second, I don't think this is the correct solution to this problem.

I have rarely ever seen a case where I've added a callback that involves saving another record and then for some reason I don't want that save to occur. I can see this happening in tests, but that is easily solved by stubbing the method that performs the save (Notification.create!, in the example). Now, if I did have a need to prevent this from happening in production code, it would actually tell me that these two components are too tightly coupled, and it would encourage me to break them apart into a higher level object (CreateCommentWithNotification, for instance) so that I had the freedom to only create the comment if I wanted to. In other words, I think this change encourages bad design.

Even if that were not the case, I think that the usage is weird. As a reader of this code, it seems like the context around the use of suppress would not be immediately clear. In other words, if I saw the Notification.suppress line, I'd have to know that it's tied back to the after_create... inside of Comment. I would immediately ask myself, if we are modifying the behavior of Comment, why should we have to tell Notification to do something? We're reaching inside of Notification from a completely different class. On top of this, this seems like a shotgun solution. What if there are other things happening in the block that also save notifications? Wouldn't it be better to be able to ask Comment to disable the callback in question instead of having to know what that callback does and disable its contents?

Anyway... that's a bit of a moot point, because honestly I think both problems can be solved by making a separate class that encapsulates creating a comment and also creating a notification. Is it one more class? Sure, but you don't have to disable anything, and the intention is perfectly clear to other people reading the code.

@kaspth

This comment has been minimized.

Show comment
Hide comment
@kaspth

kaspth Feb 15, 2015

Member

@mcmire please see the original issue #18847 for a use case and justification.

@perceptec can you add "Fixes #18847" to the description, so others will have an easier time evaluating this? 😄

Member

kaspth commented Feb 15, 2015

@mcmire please see the original issue #18847 for a use case and justification.

@perceptec can you add "Fixes #18847" to the description, so others will have an easier time evaluating this? 😄

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Feb 18, 2015

Member

Looking good @perceptec! Did @kaspth or @georgeclaghorn have any other comments, or are we good to merge?

@mcmire What @kaspth said. Use case is explained in the original issue.

Member

dhh commented Feb 18, 2015

Looking good @perceptec! Did @kaspth or @georgeclaghorn have any other comments, or are we good to merge?

@mcmire What @kaspth said. Use case is explained in the original issue.

@rafaelfranca

View changes

Show outdated Hide outdated activerecord/CHANGELOG.md
@rafaelfranca

View changes

Show outdated Hide outdated activerecord/lib/active_record/suppressor.rb
@georgeclaghorn

This comment has been minimized.

Show comment
Hide comment
@georgeclaghorn

georgeclaghorn Feb 18, 2015

Member

Looks good to me!

Member

georgeclaghorn commented Feb 18, 2015

Looks good to me!

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Feb 18, 2015

Member

@mcmire The general principle I apply for this design is "the exception must carry the weight of its own work". If you create a new CreateCommentWithNotifications object, you're pushing the weight of the work onto the default case. That's not proportional in my book.

Member

dhh commented Feb 18, 2015

@mcmire The general principle I apply for this design is "the exception must carry the weight of its own work". If you create a new CreateCommentWithNotifications object, you're pushing the weight of the work onto the default case. That's not proportional in my book.

@kaspth

This comment has been minimized.

Show comment
Hide comment
@kaspth

kaspth Feb 18, 2015

Member

Once @georgeclaghorn's and @rafaelfranca's concerns are addressed then 👍

Oh, and we'll need a rebase @perceptec 😄

Member

kaspth commented Feb 18, 2015

Once @georgeclaghorn's and @rafaelfranca's concerns are addressed then 👍

Oh, and we'll need a rebase @perceptec 😄

@perceptec

This comment has been minimized.

Show comment
Hide comment
@perceptec

perceptec Feb 18, 2015

Contributor

This one's been slippery to define. "save callbacks" was intended more in a compound sense (if a bit of a cop-out).

Will this work? (Since there's already been discussion/confusion around the intended functionality.)

Add ActiveRecord::Base.suppress to prevent the receiver from being saved during callbacks generated by a given block.

No matter what we come up with for a definition, I think the code itself will be more illustrative...

Contributor

perceptec commented Feb 18, 2015

This one's been slippery to define. "save callbacks" was intended more in a compound sense (if a bit of a cop-out).

Will this work? (Since there's already been discussion/confusion around the intended functionality.)

Add ActiveRecord::Base.suppress to prevent the receiver from being saved during callbacks generated by a given block.

No matter what we come up with for a definition, I think the code itself will be more illustrative...

@georgeclaghorn

This comment has been minimized.

Show comment
Hide comment
@georgeclaghorn

georgeclaghorn Feb 18, 2015

Member

Add ActiveRecord::Base.suppress to prevent the receiver from being saved during callbacks generated by a given block.

It's not necessary to mention callbacks at all. ActiveRecord::Base.suppress prevents saves for objects of the receiving class inside the block, regardless of where the saves occur. This still suggests that it only suppresses saves in callbacks.

Member

georgeclaghorn commented Feb 18, 2015

Add ActiveRecord::Base.suppress to prevent the receiver from being saved during callbacks generated by a given block.

It's not necessary to mention callbacks at all. ActiveRecord::Base.suppress prevents saves for objects of the receiving class inside the block, regardless of where the saves occur. This still suggests that it only suppresses saves in callbacks.

@rafaelfranca

This comment has been minimized.

Show comment
Hide comment
@rafaelfranca

rafaelfranca Feb 18, 2015

Member

Maybe Add ActiveRecord::Base.suppress to prevent the receiver from being saved during the given block.

Member

rafaelfranca commented Feb 18, 2015

Maybe Add ActiveRecord::Base.suppress to prevent the receiver from being saved during the given block.

@perceptec

This comment has been minimized.

Show comment
Hide comment
@perceptec

perceptec Feb 18, 2015

Contributor

@georgeclaghorn True, is everybody okay with @rafaelfranca's edit?

Contributor

perceptec commented Feb 18, 2015

@georgeclaghorn True, is everybody okay with @rafaelfranca's edit?

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Feb 18, 2015

Member

What George said. Let’s just focus on suppress = no saving of records.

On Feb 18, 2015, at 13:26, George Claghorn notifications@github.com wrote:

Add ActiveRecord::Base.suppress to prevent the receiver from being saved during callbacks generated by a given block.

It's not necessary to mention callbacks at all. ActiveRecord::Base.suppress prevents saves for objects of the receiving class inside the block, regardless of where the saves occur. This still suggests that it only suppresses saves in callbacks.


Reply to this email directly or view it on GitHub.

Member

dhh commented Feb 18, 2015

What George said. Let’s just focus on suppress = no saving of records.

On Feb 18, 2015, at 13:26, George Claghorn notifications@github.com wrote:

Add ActiveRecord::Base.suppress to prevent the receiver from being saved during callbacks generated by a given block.

It's not necessary to mention callbacks at all. ActiveRecord::Base.suppress prevents saves for objects of the receiving class inside the block, regardless of where the saves occur. This still suggests that it only suppresses saves in callbacks.


Reply to this email directly or view it on GitHub.

@georgeclaghorn

This comment has been minimized.

Show comment
Hide comment
@georgeclaghorn
Member

georgeclaghorn commented Feb 18, 2015

👍

belongs_to :commentable, polymorphic: true
after_create -> { Notification.create! comment: self,
recipients: commentable.recipients }
end

This comment has been minimized.

@georgeclaghorn

georgeclaghorn Feb 19, 2015

Member

Indent code examples in this file by an extra four spaces.

@georgeclaghorn

georgeclaghorn Feb 19, 2015

Member

Indent code examples in this file by an extra four spaces.

# belongs_to :commentable, polymorphic: true
# after_create -> { Notification.create! comment: self,
# recipients: commentable.recipients }
# end

This comment has been minimized.

@georgeclaghorn

georgeclaghorn Feb 19, 2015

Member

Indent code examples here by an extra two spaces.

@georgeclaghorn

georgeclaghorn Feb 19, 2015

Member

Indent code examples here by an extra two spaces.

require 'models/user'
class SuppressorTest < ActiveRecord::TestCase

This comment has been minimized.

@georgeclaghorn

georgeclaghorn Feb 19, 2015

Member

Delete this empty line.

@georgeclaghorn

georgeclaghorn Feb 19, 2015

Member

Delete this empty line.

@rafaelfranca rafaelfranca merged commit b9a1e9a into rails:master Feb 19, 2015

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details

rafaelfranca pushed a commit that referenced this pull request Feb 19, 2015

Rafael Mendonça França
Merge pull request #18910 from perceptec/add_suppressor
Add `ActiveRecord::Base.suppress` to allow save events to be suppressed within a specified block.
@kaspth

This comment has been minimized.

Show comment
Hide comment
@kaspth

kaspth Feb 19, 2015

Member

Boom! Well done @perceptec 😄

Kasper

Den 19/02/2015 kl. 12.08 skrev Rafael Mendonça França notifications@github.com:

Merged #18910.


Reply to this email directly or view it on GitHub.

Member

kaspth commented Feb 19, 2015

Boom! Well done @perceptec 😄

Kasper

Den 19/02/2015 kl. 12.08 skrev Rafael Mendonça França notifications@github.com:

Merged #18910.


Reply to this email directly or view it on GitHub.

@perceptec perceptec deleted the perceptec:add_suppressor branch Feb 19, 2015

@dhh

This comment has been minimized.

Show comment
Hide comment
@dhh

dhh Feb 19, 2015

Member

Great job 👏

Member

dhh commented Feb 19, 2015

Great job 👏

@rafaelfranca rafaelfranca modified the milestones: 5.0.0 [temp], 5.0.0 Dec 30, 2015

@rails rails locked and limited conversation to collaborators May 23, 2016

@jeremy

This comment has been minimized.

Show comment
Hide comment
@jeremy

jeremy May 23, 2016

Member

(Removed a disparaging comment about the feature that was thoughtlessly posted on the implementation work done here. Please contribute to discussion on the original issue #18847.)

Member

jeremy commented May 23, 2016

(Removed a disparaging comment about the feature that was thoughtlessly posted on the implementation work done here. Please contribute to discussion on the original issue #18847.)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.