-
Notifications
You must be signed in to change notification settings - Fork 21.9k
Include gem name and deprecation horizon when calling the deprecation behaviors #28800
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
Include gem name and deprecation horizon when calling the deprecation behaviors #28800
Conversation
fa0d17a
to
e55f7ce
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this changes the number of arguments that can be passed to a custom installed behavior, we'd need to inspect the arity and coerce those that don't support the more elaborate version. Perhaps something like this:
def behavior=(behavior)
@behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
end
private
def arity_coerce(behavior)
if behavior.arity >= 2
behavior
else
-> message, callstack, _, _ { behavior.call(message, callstack) }
end
end
@@ -119,7 +119,7 @@ def test_raise_behaviour | |||
callstack = caller_locations | |||
|
|||
e = assert_raise ActiveSupport::DeprecationException do | |||
ActiveSupport::Deprecation.behavior.first.call(message, callstack) | |||
ActiveSupport::Deprecation.behavior.first.call(message, callstack, "horizon", "gem") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alludes to backwards incompatibility: custom installed behaviors can raise ArgumentError's now.
behavior = ActiveSupport::Deprecation.behavior.first | ||
|
||
begin | ||
events = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why define this as an array when we expect there to only be one? If it wasn't the right message, we'd eventually find out in the latter tests.
E.g. we could opt for _, event = ActiveSupport::Notifications.subscribe("deprecation.my_gem.custom", &:itself)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't work because the itself
method doesn't have the correct arity.
I also kind of like the explicitness of the array approach. Less obscure on how it works, and makes sure we're not actually overriding another earlier deprecation notification without noticing.
@@ -5,8 +5,6 @@ class Deprecation | |||
module Reporting | |||
# Whether to print a message (silent mode) | |||
attr_accessor :silenced | |||
# Name of gem where method is deprecated | |||
attr_accessor :gem_name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This module looks like it's public API so we can't just yank this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardly it will break any behavior unless someone is including Reporting
in their own class, what I'd not recommend anyway since this method depends on the others in Deprecation. I think this change is fine since the public API (ActiveSupport::Deprecation
) did't changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll just revert this change. I noticed two other accessors defined on other modules as well. I feel they should be part of the main module given that it directly uses them in the initializer, but it's kind of out of scope for this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with what @kaspth said -- we have to be careful with backwards compatibility here. Can you add a CHANGELOG.md entry, since we are modifying public API?
@@ -29,6 +29,9 @@ class Deprecation | |||
# The version number in which the deprecated behavior will be removed, by default. | |||
attr_accessor :deprecation_horizon | |||
|
|||
# Name of gem where method is deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing .
in the end of the text (in the method below too)
ActiveSupport::Notifications.instrument("deprecation.rails", | ||
message: message, callstack: callstack) | ||
notify: ->(message, callstack, deprecation_horizon, gem_name) { | ||
notification_name = "deprecation.#{gem_name.underscore.tr('/', '.')}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe _
instead of .
? We don't want to mix the gem namespace with the notification namespace. deprecation.action_controller.test_helper
seems more like a test_helper
notification namespace instead of the gem namespace.
@@ -5,8 +5,6 @@ class Deprecation | |||
module Reporting | |||
# Whether to print a message (silent mode) | |||
attr_accessor :silenced | |||
# Name of gem where method is deprecated | |||
attr_accessor :gem_name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardly it will break any behavior unless someone is including Reporting
in their own class, what I'd not recommend anyway since this method depends on the others in Deprecation. I think this change is fine since the public API (ActiveSupport::Deprecation
) did't changed.
e55f7ce
to
448d44d
Compare
Thanks for the fast reviews! Pushed some updates. |
448d44d
to
14eeee3
Compare
…vier handler, and make sure they are used for the ActiveSupport::Notifications message.
14eeee3
to
0bdf6d7
Compare
❤️ 💚 💙 💛 💜 |
The callback parameters need to reflect changes after rails#28800
ActiveSupport::Notifications.instrument("deprecation.rails", | ||
message: message, callstack: callstack) | ||
notify: ->(message, callstack, deprecation_horizon, gem_name) { | ||
notification_name = "deprecation.#{gem_name.underscore.tr('/', '_')}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for digging this up after one year, adding the gem_name and the deprecation_horizon to the event is a great add however generating dynamically the notification name has one major disadvantage because that means we need to know in advance which gems have deprecations in order to keep track of them.
class MySubscriber < ActiveSupport::Subscriber
attach_to :rails
# Prior to this PR, any gems using AS deprecations could be tracked thanks to this subscriber.
# Now we can't because we'd need to attach the subscriber to other namespace which we don't know, thus the concept of "notifying" when a deprecation occur (no matter inside which gem) no longer exists
def deprecation(event)
...
end
end
My suggestion would be to use a static namespace like before (maybe change the rails
namespace because it's not super accurate since any gems can trigger deprecation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was not a big fan of this, but this was required for backwards compatibility. I would have preferred to simply include the gem name as a parameter rather than in the event name.
I think you can use regular expressions for subscriptions, at least with the lower level API ActiveSupport::Notifications::Fanout
API. That may not be propagated to ActiveSupport::Subscriber
though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok I see thanks for the explanation, and TIL indeed the lower level API Fanout can be passed a regex. I'm going to open a PR to make it possible for AS::Subscriber
as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to know in advance which gems have deprecations in order to keep track of them.
That was exactly the reason why we did this. We should not thread deprecation in Rails in the same way we treat deprecation in other gems. At the time of a Rails upgrade, we don't care if the gem X also has deprecations, only if Rails has deprecation. Because of this we need to have different subscriber to each gem so we can chose the behavior we want based on the gem.
This was updated in rails/rails#28800
This was updated in rails/rails#28800
Summary
We are planning to use
ActiveSupport::Deprecation
in our own code base to mark deprecated behavior. We have some tooling subscribing to theActiveSupport::Notifications
that are generated by the deprecations.However, for our tooling we would like to be able to make sure we can differentiate between the source of the deprecation. ActiveSupport::Deprecation already has useful context for this: gem name, and deprecation horizon. Unfortunately, these pieces of metadata are not made available to the behavior lambdas.
This PR exposes them as extra arguments, and makes sure those arguments are passed on to the
ActiveSupport::Notification
we create. As part of this, we also dynamically generate the notification name based on the gem name. It was hardcoded todeprecations.rails
before. All the other predefined behaviors are unaffected by this.cc @rafaelfranca @casperisfine