Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Allow ActiveSupport::Deprecation features to be used by rails applications and library authors #6348

Merged
merged 2 commits into from

7 participants

@LTe

Updated version of #2310

@jeremy
Owner
  1. If we're exposing this for reuse, we need docs! How should a library author use this?
  2. Feels like libraries using separate deprecators should be working with instances of a class rather than extending with a module that injects a bunch of behavior, including its internal state. Could make ActiveSupport::Deprecation a class (without breaking API) and delegate the existing toplevel methods to a singleton ActiveSupport::Deprecation.instance
  3. The #deprecator instance method may be unnecessary. Looks like the only reason we need it is so the deprecated method wrapper can refer to it. It'd be cleaner to pass a deprecator instance and reference it directly from the method wrapper (using define_method instead of class_eval)
@LTe

@jeremy done :)

...vesupport/lib/active_support/deprecation/behaviors.rb
((13 lines not shown))
- # Default warning behaviors per Rails.env.
- DEFAULT_BEHAVIORS = {
@jeremy Owner
jeremy added a note

Should probably leave these as a constant since it acts as public API now. People add behaviors to this hash directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jeremy
Owner

@LTe Very nice! Looking good.

...port/lib/active_support/deprecation/proxy_wrappers.rb
((6 lines not shown))
end
end
- class DeprecatedConstantProxy < DeprecationProxy #:nodoc:all
- def initialize(old_const, new_const)
+ # This DeprecatedConstantProxy transforms constant to depracated constant.
+ #
+ # Example
+ # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST')
+ # Example with custom deprecator
+ # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST'. deprecator_instance)

Typo: . instead of , after NEW_CONST.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...vesupport/lib/active_support/deprecation/reporting.rb
@@ -22,6 +23,13 @@ def silence
@silenced = old_silenced
end
+ # Outputs a deprecation warning message
+ # ActiveSupport::Deprecation.deprecated_method_warning(:method_name)
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
+ # ActiveSupport::Deprecation.deprecated_method_warning(:method_name, :another_method)
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"

Indent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
activesupport/test/deprecation_test.rb
((116 lines not shown))
+ def message
+ ActiveSupport::Deprecation.warn 'warning in error message'
+ super
+ end
+ end
+
+ raise error_class.new('hmm')
+
+ rescue => e
+ error = Test::Unit::Error.new('testing ur doodz', e)
+ assert_not_deprecated { error.message }
+ assert_nil @last_message
+ end
+ end
+
+

Two lines :scissors:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ort/lib/active_support/core_ext/module/deprecation.rb
((17 lines not shown))
+ # "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
+ # end
+ #
+ # def warn(message, backtrace)
+ # puts(message)
+ # end
+ # end
+ # end
+ #
+ # When we deprecate method
+ # deprecate :foo => "this is very old method", :deprecator => CustomClass::Deprecator.instance
+ #
+ # It will execute
+ # CustomClass::Deprecator.instance.deprecated_method_warning(:foo, "this is very old method").tap |message|
+ # CustomClass::Deprecator.instance.warn(message, backtrace)
+ # end

The docs are pretty good, but there's no mention of instance in CustomClass::Deprecator, this may confuse people. Perhaps you can just use new instead of instance, or add the method or singleton there to show how it'd look like?

@jeremy Owner
jeremy added a note

Agree -- no need to introduce a singleton for the custom case

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@LTe

@carlosantoniodasilva @jeremy thanks for feedback!

@paneq

What do you think about it:

module ActiveSupport
  class Deprecation
    def initialize(horizon = '3.2', gem_name = 'rails')
      self.deprecation_horizon = horizon
      self.gem_name = gem_name
    end
  end
end

For custom gems authors should create new instances of ActiveSupport::Deprecation to generate proper warning messages. If they use ActiveSupport::Deprecation.instance the message is going to say that something will be removed from rails which is in many cases not what the authors of gems would like to announce to the users.

These:

deprecator = ActiveSupport::Deprecation.new
deprecator.deprecation_horizon = "2.0.0"
ActiveSupport::Deprecation.new.tap{|d| d.deprecation_horizon = "2.0.0" }

seem to be unnecessary verbose to me.

ActiveSupport::Deprecation.new("2.0.0", "formtastic")

looks much better imho.

@paneq

Science Fiction:

module ActiveSupport
  class Deprecation
    def initialize(horizon = default_horizon, gem_name = 'rails')
      self.deprecation_horizon = horizon
      self.gem_name = gem_name
    end

    def default_horizon
      "#{ActiveSupport::VERSION::MAJOR}.#{ActiveSupport::VERSION::MINOR + 1}"
    end
  end
end

because sometimes it should be major + 1. Or maybe it should be always major +1 and never minor + 1 ?

@LTe

Now you can create instance of deprecator and create new object via new.
I like @paneq approach -- now this is simple to reuse.

About science fiction I think better solution is pass directly version to initialize method.

@paneq

"About science fiction I think better solution is pass directly version to initialize method." - probably. Except for the fact that the core team needs to change this one line every time there is new rails version. I wanted to spare them this trouble in this science fiction solution :)

@LTe

According to http://semver.org/ this always should be major +1

@LTe

@jeremy what do you think? We can merge current implementation?

...vesupport/lib/active_support/deprecation/behaviors.rb
((6 lines not shown))
# [+log+] Log all deprecation warnings to +Rails.logger+.
- # [+notify+] Use <tt>ActiveSupport::Notifications</tt> to notify +deprecation.rails+.
+ # [+notify] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
@jeremy Owner
jeremy added a note

[+notify+] formatting changed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ort/lib/active_support/deprecation/method_wrappers.rb
@@ -2,25 +2,26 @@
require 'active_support/core_ext/array/extract_options'
module ActiveSupport
- module Deprecation
- # Declare that a method has been deprecated.
- def self.deprecate_methods(target_module, *method_names)
- options = method_names.extract_options!
- method_names += options.keys
+ class Deprecation
+ module MethodWrapper
+ # Declare that a method has been deprecated.
+ def deprecate_methods(target_module, *method_names)
+ options = method_names.extract_options!
+ deprecator = options.delete(:deprecator) || ActiveSupport::Deprecation
@jeremy Owner
jeremy added a note

ActiveSupport::Deprecation.instance here rather than rely on the instance delegator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...port/lib/active_support/deprecation/proxy_wrappers.rb
@@ -25,10 +25,21 @@ def method_missing(called, *args, &block)
end
end
- class DeprecatedObjectProxy < DeprecationProxy #:nodoc:
- def initialize(object, message)
+ # This DeprecatedObjectProxy transforms object to depracated object.
+ #
+ # Example
+ # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!")
+ # Example with custom deprecator
+ # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance)
+ #
+ # When someone execute any method expect +inspect+ on proxy object this will trigger +warn+ method on +deprecator_instance+
+ #
+ # Default deprecator is ActiveSupport::Deprecation
+ class DeprecatedObjectProxy < DeprecationProxy
+ def initialize(object, message, deprecator = ActiveSupport::Deprecation)
@jeremy Owner
jeremy added a note

.instance here too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@LTe

@jeremy updated and rebased

...vesupport/lib/active_support/deprecation/behaviors.rb
@@ -1,8 +1,32 @@
require "active_support/notifications"
module ActiveSupport
- module Deprecation
- class << self
+ class Deprecation
+ # Default warning behaviors per Rails.env.
+ DEFAULT_BEHAVIORS = {
+ :stderr => Proc.new { |message, callstack|
+ $stderr.puts(message)
@jeremy Owner
jeremy added a note

Extra indentation here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jeremy jeremy commented on the diff
...ort/lib/active_support/core_ext/module/deprecation.rb
@@ -5,6 +5,36 @@ class Module
# deprecate :foo
# deprecate :bar => 'message'
# deprecate :foo, :bar, :baz => 'warning!', :qux => 'gone!'
+ #
+ # You can use custom deprecator instance
@jeremy Owner
jeremy added a note

You can use a custom deprecator object that responds to +warn+ and +deprecated_method_warning+. This is useful when you use +ActiveSupport::Deprecation+ in your own libraries and want to customize deprecation behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ort/lib/active_support/core_ext/module/deprecation.rb
((9 lines not shown))
# deprecate :foo
# deprecate :bar => 'message'
# deprecate :foo, :bar, :baz => 'warning!', :qux => 'gone!'
+ #
+ # You can use custom deprecator instance
+ # deprecate :foo, :deprecator => CustomClass::Deprecator.new
+ # deprecate :foo, :bar => "warning!", :deprecator => CustomClass::Deprecator.new
+ #
+ # Custom deprecator instance need respond to two methods
+ # [deprecated_method_warning(method_name, message)] Method will be executed with *deprecated* method name
+ # [warn(message, backtrace)] Method will accept message from *deprecated_method_warning* method and backtrace
+ #
+ # Example
+ # class CustomClass
+ # class Deprecator
+ # def deprecated_method_warning(method_name, message)
@jeremy Owner
jeremy added a note
class MyLib::Deprecator
  def warn(message, backtrace)
    Kernel.warn message
  end

  def deprecated_method_warning(method_name, message)
    "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
  end
end
@jeremy Owner
jeremy added a note
module MyLib
  mattr_accessor :deprecator
  self.deprecator = Deprecator.new
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ort/lib/active_support/core_ext/module/deprecation.rb
((12 lines not shown))
+ #
+ # You can use custom deprecator instance
+ # deprecate :foo, :deprecator => CustomClass::Deprecator.new
+ # deprecate :foo, :bar => "warning!", :deprecator => CustomClass::Deprecator.new
+ #
+ # Custom deprecator instance need respond to two methods
+ # [deprecated_method_warning(method_name, message)] Method will be executed with *deprecated* method name
+ # [warn(message, backtrace)] Method will accept message from *deprecated_method_warning* method and backtrace
+ #
+ # Example
+ # class CustomClass
+ # class Deprecator
+ # def deprecated_method_warning(method_name, message)
+ # "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
+ # end
+ #
@jeremy Owner
jeremy added a note
class MyLib::Bar
  deprecate :foo => "this is very old method", :deprecator => MyLib.deprecator
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ort/lib/active_support/core_ext/module/deprecation.rb
((18 lines not shown))
+ # [deprecated_method_warning(method_name, message)] Method will be executed with *deprecated* method name
+ # [warn(message, backtrace)] Method will accept message from *deprecated_method_warning* method and backtrace
+ #
+ # Example
+ # class CustomClass
+ # class Deprecator
+ # def deprecated_method_warning(method_name, message)
+ # "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
+ # end
+ #
+ # def warn(message, backtrace)
+ # puts(message)
+ # end
+ # end
+ # end
+ #
@jeremy Owner
jeremy added a note

It will build a deprecation warning message by calling MyLib.deprecator.deprecated_method_warning(:foo, "this is a very old method") then invoke the deprecator warning with the message and the caller's backtrace: MyLib.deprecator.warn(warning_message, caller)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ort/lib/active_support/core_ext/module/deprecation.rb
@@ -1,10 +1,43 @@
require 'active_support/deprecation/method_wrappers'
class Module
- # Declare that a method has been deprecated.
+ # You can use a custom deprecator object that responds to +warn+ and +deprecated_method_warning+.
+ # This is useful when you use +ActiveSupport::Deprecation+ in your own libraries and want to customize
+ # deprecation behavior.
@jeremy Owner
jeremy added a note

Can leave these out, they're better demonstrated below

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ort/lib/active_support/core_ext/module/deprecation.rb
@@ -1,10 +1,43 @@
require 'active_support/deprecation/method_wrappers'
class Module
- # Declare that a method has been deprecated.
+ # You can use a custom deprecator object that responds to +warn+ and +deprecated_method_warning+.
+ # This is useful when you use +ActiveSupport::Deprecation+ in your own libraries and want to customize
+ # deprecation behavior.
+ #
# deprecate :foo
@jeremy Owner
jeremy added a note

Custom deprecators must respond to two methods:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ort/lib/active_support/core_ext/module/deprecation.rb
@@ -1,10 +1,43 @@
require 'active_support/deprecation/method_wrappers'
class Module
- # Declare that a method has been deprecated.
+ # You can use a custom deprecator object that responds to +warn+ and +deprecated_method_warning+.
+ # This is useful when you use +ActiveSupport::Deprecation+ in your own libraries and want to customize
+ # deprecation behavior.
+ #
# deprecate :foo
# deprecate :bar => 'message'
@jeremy Owner
jeremy added a note

[deprecated_method_warning(method_name, message)] will be called with the deprecated method name and the message it was declared with. Return a warning message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ort/lib/active_support/core_ext/module/deprecation.rb
@@ -1,10 +1,43 @@
require 'active_support/deprecation/method_wrappers'
class Module
- # Declare that a method has been deprecated.
+ # You can use a custom deprecator object that responds to +warn+ and +deprecated_method_warning+.
+ # This is useful when you use +ActiveSupport::Deprecation+ in your own libraries and want to customize
+ # deprecation behavior.
+ #
# deprecate :foo
# deprecate :bar => 'message'
# deprecate :foo, :bar, :baz => 'warning!', :qux => 'gone!'
@jeremy Owner
jeremy added a note

[warn(message, backtrace)] will be called with the message from your deprecated_method_warning method and the caller's backtrace. Implement whatever warning behavior you like here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jeremy
Owner

@LTe some comments on the documentation :)

It covers how to give a custom deprecator object, but doesn't show how you can use ActiveSupport::Deprecation.new(version, libname) yourself.

When I read through the duck type for a deprecator, having to implement two methods seems wrong, too. We call deprecator.deprecated_method_warning then pass its return value to #warn -- we could just call one method and let the deprecator handle that internally.

@paneq

@jeremy - I think that's because #warn has the logic for outputing to logger, stderr, output or whatever is configured in current development environment and #deprecated_method_warning is just responsible for generting the warning. One might want to generate different warnings for his own gem but keep the logic coherent with Rails as to where they are displayed. What do you think ?

@jeremy
Owner

@paneq definitely -- and a higher-level method like deprecation_warning(deprecated_method_name, message, caller_backtrace) could wrap that up. Then custom classes can implement that one method whereas subclasses of ActiveSupport::Deprecation can override just deprecated_method_warning if they like.

@LTe
LTe commented

Updated

@LTe

@jeremy what do you think about merge?

@steveklabnik
Collaborator

@LTe it needs rebased at least, it can't be merged cleanly anymore.

@LTe
LTe commented

@steveklabnik rebased ;-)

@steveklabnik
Collaborator

Thanks. Let's see what @jeremy says.

@paneq

What do you think @jeremy ?

@jeremy
Owner

:+1:

@carlosantoniodasilva carlosantoniodasilva commented on the diff
...ort/lib/active_support/core_ext/module/deprecation.rb
((12 lines not shown))
+ #
+ # \Custom deprecators must respond to one method
+ # [deprecation_warning(deprecated_method_name, message, caller_backtrace)] will be called with the deprecated
+ # method name, the message it was declared
+ # with and caller_backtrace. Implement
+ # whatever warning behavior you like here.
+ #
+ # Example
+ # class MyLib::Deprecator
+ #
+ # def deprecation_warning(deprecated_method_name, message, caller_backtrace)
+ # message = "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
+ # Kernel.warn message
+ # end
+ #
+ # end

Please remove whitespaces between the method and class declaration, there's no need for them :scissors:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
activesupport/test/deprecation_test.rb
((117 lines not shown))
+ deprecator = ActiveSupport::Deprecation.new
+
+ deprecator.send(:deprecated_method_warning, :deprecated_method, "You are calling deprecated method").tap do |message|
+ assert_match(/is deprecated and will be removed from Rails/, message)
+ end
+ end
+
+ def test_custom_gem_name
+ deprecator = ActiveSupport::Deprecation.new('2.0', 'Custom')
+
+ deprecator.send(:deprecated_method_warning, :deprecated_method, "You are calling deprecated method").tap do |message|
+ assert_match(/is deprecated and will be removed from Custom/, message)
+ end
+ end
+
+ unless defined?(::MiniTest)

I think this will be always defined, why do you need to test using `Test::Unit::Error in this particular case?

@steveklabnik Collaborator

This will be, except on some distros like Fedora which (imho) give you a broken Ruby.

Especially on master, where we only support 1.9, this should basically always be true.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@carlosantoniodasilva

@LTe hey, could you please add a changelog entry for this, and check the minor comments I made, so that we can get this in master? Thanks!

@LTe

@carlosantoniodasilva updated, rebased.

@carlosantoniodasilva

@LTe, great thanks. I'll have to ask you one more thing though, to review your commit message and improve the changelog a bit with an example of how to use this new feature. This will help others to understand the reasoning and how to use the feature at the same time when reading the commits or the changelog.

Here's an explanation about how to go with the commit message, and here's another about changelogs.

I'll merge it afterwards. Thanks!

paneq and others added some commits
@paneq paneq extend ActiveSupport::Deprecation with self, allow other objects to e…
…xtend/include it also.

test local deprecation

deprecator object

Test ActiveSupport::Deprecation when included
2c690a0
@LTe LTe Change ActiveSupport::Deprecation to class.
ActiveSupport::Deprecation is now a class rather than a module. You can
get instance of ActiveSupport::Deprecation calling #instance method.

  ActiveSupport::Deprecation.instance

But when you need to get new object od ActiveSupport::Deprecation you
need to just call #new.

  @instance = ActiveSupport::Deprecation.new

Since you can create a new object, you can change the version and the
name of the library where the deprecator concerned.

  ActiveSupport::Deprecation.new('2.0', 'MyGem')

If you need use another deprecator instance you can select it in the
options of deprecate method.

  deprecate :method, :deprecator => deprecator_instance

Documentation has been updated.
71993c6
@carlosantoniodasilva

@LTe great, thank you!

@carlosantoniodasilva carlosantoniodasilva merged commit 01ef633 into rails:master
@splattael

The definition of self.deprecator should go before this deprecate call.

Fixed @ 8692db5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 13, 2012
  1. @paneq @LTe

    extend ActiveSupport::Deprecation with self, allow other objects to e…

    paneq authored LTe committed
    …xtend/include it also.
    
    test local deprecation
    
    deprecator object
    
    Test ActiveSupport::Deprecation when included
  2. @LTe

    Change ActiveSupport::Deprecation to class.

    LTe authored
    ActiveSupport::Deprecation is now a class rather than a module. You can
    get instance of ActiveSupport::Deprecation calling #instance method.
    
      ActiveSupport::Deprecation.instance
    
    But when you need to get new object od ActiveSupport::Deprecation you
    need to just call #new.
    
      @instance = ActiveSupport::Deprecation.new
    
    Since you can create a new object, you can change the version and the
    name of the library where the deprecator concerned.
    
      ActiveSupport::Deprecation.new('2.0', 'MyGem')
    
    If you need use another deprecator instance you can select it in the
    options of deprecate method.
    
      deprecate :method, :deprecator => deprecator_instance
    
    Documentation has been updated.
This page is out of date. Refresh to see the latest.
View
32 activesupport/CHANGELOG.md
@@ -1,5 +1,37 @@
## Rails 4.0.0 (unreleased) ##
+* ActiveSupport::Deprecation is now a class. It is possible to create an instance
+ of deprecator. Backwards compatibility has been preserved.
+
+ You can choose which instance of the deprecator will be used.
+
+ deprecate :method_name, :deprecator => deprecator_instance
+
+ You can use ActiveSupport::Deprecation in your gem.
+
+ require 'active_support/deprecation'
+ require 'active_support/core_ext/module/deprecation'
+
+ class MyGem
+ def old_method
+ end
+ deprecate :old_method => :new_method, :deprecator => deprecator
+
+ def new_method
+ end
+
+ def self.deprecator
+ ActiveSupport::Deprecation.new('2.0', 'MyGem')
+ end
+ end
+
+ MyGem.new.old_method
+
+ DEPRECATION WARNING: old_method is deprecated and will be removed from MyGem 2.0
+ (use new_method instead). (called from <main> at file.rb:18)
+
+ *Piotr Niełacny & Robert Pankowecki*
+
* `ERB::Util.html_escape` encodes single quote as `#39`. Decimal form has better support in old browsers. *Kalys Osmonov*
* `ActiveSupport::Callbacks`: deprecate monkey patch of object callbacks.
View
34 activesupport/lib/active_support/core_ext/module/deprecation.rb
@@ -1,10 +1,42 @@
require 'active_support/deprecation/method_wrappers'
class Module
- # Declare that a method has been deprecated.
# deprecate :foo
# deprecate :bar => 'message'
# deprecate :foo, :bar, :baz => 'warning!', :qux => 'gone!'
+ #
+ # You can use custom deprecator instance
@jeremy Owner
jeremy added a note

You can use a custom deprecator object that responds to +warn+ and +deprecated_method_warning+. This is useful when you use +ActiveSupport::Deprecation+ in your own libraries and want to customize deprecation behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ # deprecate :foo, :deprecator => MyLib::Deprecator.new
+ # deprecate :foo, :bar => "warning!", :deprecator => MyLib::Deprecator.new
+ #
+ # \Custom deprecators must respond to one method
+ # [deprecation_warning(deprecated_method_name, message, caller_backtrace)] will be called with the deprecated
+ # method name, the message it was declared
+ # with and caller_backtrace. Implement
+ # whatever warning behavior you like here.
+ #
+ # Example
+ # class MyLib::Deprecator
+ #
+ # def deprecation_warning(deprecated_method_name, message, caller_backtrace)
+ # message = "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
+ # Kernel.warn message
+ # end
+ #
+ # end

Please remove whitespaces between the method and class declaration, there's no need for them :scissors:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ #
+ # module MyLib
+ # mattr_accessor :deprecator
+ # self.deprecator = Deprecator.new
+ # end
+ #
+ # When we deprecate method
+ # class MyLib::Bar
+ # deprecate :foo => "this is very old method", :deprecator => MyLib.deprecator
+ # end
+ #
+ # It will build deprecation message and invoke deprecator warning by calling
+ # MyLib.deprecator.deprecation_warning(:foo, "this is a very old method", caller)
def deprecate(*method_names)
ActiveSupport::Deprecation.deprecate_methods(self, *method_names)
end
View
41 activesupport/lib/active_support/deprecation.rb
@@ -1,19 +1,40 @@
require 'active_support/core_ext/module/deprecation'
+require 'active_support/deprecation/instance_delegator'
require 'active_support/deprecation/behaviors'
require 'active_support/deprecation/reporting'
require 'active_support/deprecation/method_wrappers'
require 'active_support/deprecation/proxy_wrappers'
+require 'singleton'
module ActiveSupport
- module Deprecation
- class << self
- # The version the deprecated behavior will be removed, by default.
- attr_accessor :deprecation_horizon
- end
- self.deprecation_horizon = '4.1'
+ # \Deprecation specifies the API used by Rails to deprecate
+ # methods, instance variables, objects and constants.
+ # The API depends on four methods:
+ #
+ # * +initialize+ which expects two parameters
+ # described below;
+ class Deprecation
+ include Singleton
+ include InstanceDelegator
+ include Behavior
+ include Reporting
+ include MethodWrapper
+
+ # The version the deprecated behavior will be removed, by default.
+ attr_accessor :deprecation_horizon
- # By default, warnings are not silenced and debugging is off.
- self.silenced = false
- self.debug = false
+ # It accepts two parameters on initialization. The first is an version of library
+ # and the second is an library name
+ #
+ # == Example
+ #
+ # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
+ def initialize(deprecation_horizon = '4.1', gem_name = 'Rails')
+ self.gem_name = gem_name
+ self.deprecation_horizon = deprecation_horizon
+ # By default, warnings are not silenced and debugging is off.
+ self.silenced = false
+ self.debug = false
+ end
end
-end
+end
View
58 activesupport/lib/active_support/deprecation/behaviors.rb
@@ -1,8 +1,32 @@
require "active_support/notifications"
module ActiveSupport
- module Deprecation
- class << self
+ class Deprecation
+ # Default warning behaviors per Rails.env.
+ DEFAULT_BEHAVIORS = {
+ :stderr => Proc.new { |message, callstack|
+ $stderr.puts(message)
+ $stderr.puts callstack.join("\n ") if debug
+ },
+ :log => Proc.new { |message, callstack|
+ logger =
+ if defined?(Rails) && Rails.logger
+ Rails.logger
+ else
+ require 'active_support/logger'
+ ActiveSupport::Logger.new($stderr)
+ end
+ logger.warn message
+ logger.debug callstack.join("\n ") if debug
+ },
+ :notify => Proc.new { |message, callstack|
+ ActiveSupport::Notifications.instrument("deprecation.rails",
+ :message => message, :callstack => callstack)
+ },
+ :silence => Proc.new { |message, callstack| }
+ }
+
+ module Behavior
# Whether to print a backtrace along with the warning.
attr_accessor :debug
@@ -16,9 +40,9 @@ def behavior
#
# Available behaviors:
#
- # [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
+ # [+stderr+] Log all deprecation warnings to +$stderr+.
# [+log+] Log all deprecation warnings to +Rails.logger+.
- # [+notify+] Use <tt>ActiveSupport::Notifications</tt> to notify +deprecation.rails+.
+ # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
# [+silence+] Do nothing.
#
# Setting behaviors only affects deprecations that happen after boot time.
@@ -28,36 +52,12 @@ def behavior
# ActiveSupport::Deprecation.behavior = :stderr
# ActiveSupport::Deprecation.behavior = [:stderr, :log]
# ActiveSupport::Deprecation.behavior = MyCustomHandler
- # ActiveSupport::Deprecation.behavior = proc { |message, callstack|
+ # ActiveSupport::Deprecation.behavior = proc { |message, callstack|
# # custom stuff
# }
def behavior=(behavior)
@behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
end
end
-
- # Default warning behaviors per Rails.env.
- DEFAULT_BEHAVIORS = {
- :stderr => Proc.new { |message, callstack|
- $stderr.puts(message)
- $stderr.puts callstack.join("\n ") if debug
- },
- :log => Proc.new { |message, callstack|
- logger =
- if defined?(Rails) && Rails.logger
- Rails.logger
- else
- require 'active_support/logger'
- ActiveSupport::Logger.new($stderr)
- end
- logger.warn message
- logger.debug callstack.join("\n ") if debug
- },
- :notify => Proc.new { |message, callstack|
- ActiveSupport::Notifications.instrument("deprecation.rails",
- :message => message, :callstack => callstack)
- },
- :silence => Proc.new { |message, callstack| }
- }
end
end
View
24 activesupport/lib/active_support/deprecation/instance_delegator.rb
@@ -0,0 +1,24 @@
+require 'active_support/core_ext/kernel/singleton_class'
+require 'active_support/core_ext/module/delegation'
+
+module ActiveSupport
+ class Deprecation
+ module InstanceDelegator
+ def self.included(base)
+ base.extend(ClassMethods)
+ base.public_class_method :new
+ end
+
+ module ClassMethods
+ def include(included_module)
+ included_module.instance_methods.each { |m| method_added(m) }
+ super
+ end
+
+ def method_added(method_name)
+ singleton_class.delegate(method_name, to: :instance)
+ end
+ end
+ end
+ end
+end
View
70 activesupport/lib/active_support/deprecation/method_wrappers.rb
@@ -2,45 +2,41 @@
require 'active_support/core_ext/array/extract_options'
module ActiveSupport
- module Deprecation
- # Declare that a method has been deprecated.
- #
- # module Fred
- # extend self
- #
- # def foo; end
- # def bar; end
- # def baz; end
- # end
- #
- # ActiveSupport::Deprecation.deprecate_methods(Fred, :foo, bar: :qux, baz: 'use Bar#baz instead')
- # # => [:foo, :bar, :baz]
- #
- # Fred.foo
- # # => "DEPRECATION WARNING: foo is deprecated and will be removed from Rails 4.1."
- #
- # Fred.bar
- # # => "DEPRECATION WARNING: bar is deprecated and will be removed from Rails 4.1 (use qux instead)."
- #
- # Fred.baz
- # # => "DEPRECATION WARNING: baz is deprecated and will be removed from Rails 4.1 (use Bar#baz instead)."
- def self.deprecate_methods(target_module, *method_names)
- options = method_names.extract_options!
- method_names += options.keys
+ class Deprecation
+ module MethodWrapper
+ # Declare that a method has been deprecated.
+ #
+ # module Fred
+ # extend self
+ #
+ # def foo; end
+ # def bar; end
+ # def baz; end
+ # end
+ #
+ # ActiveSupport::Deprecation.deprecate_methods(Fred, :foo, bar: :qux, baz: 'use Bar#baz instead')
+ # # => [:foo, :bar, :baz]
+ #
+ # Fred.foo
+ # # => "DEPRECATION WARNING: foo is deprecated and will be removed from Rails 4.1."
+ #
+ # Fred.bar
+ # # => "DEPRECATION WARNING: bar is deprecated and will be removed from Rails 4.1 (use qux instead)."
+ #
+ # Fred.baz
+ # # => "DEPRECATION WARNING: baz is deprecated and will be removed from Rails 4.1 (use Bar#baz instead)."
+ def deprecate_methods(target_module, *method_names)
+ options = method_names.extract_options!
+ deprecator = options.delete(:deprecator) || ActiveSupport::Deprecation.instance
+ method_names += options.keys
- method_names.each do |method_name|
- target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation|
- target_module.module_eval(<<-end_eval, __FILE__, __LINE__ + 1)
- def #{target}_with_deprecation#{punctuation}(*args, &block)
- ::ActiveSupport::Deprecation.warn(
- ::ActiveSupport::Deprecation.deprecated_method_warning(
- :#{method_name},
- #{options[method_name].inspect}),
- caller
- )
- send(:#{target}_without_deprecation#{punctuation}, *args, &block)
+ method_names.each do |method_name|
+ target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation|
+ target_module.send(:define_method, "#{target}_with_deprecation#{punctuation}") do |*args, &block|
+ deprecator.deprecation_warning(method_name, options[method_name], caller)
+ send(:"#{target}_without_deprecation#{punctuation}", *args, &block)
end
- end_eval
+ end
end
end
end
View
70 activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -1,7 +1,7 @@
require 'active_support/inflector/methods'
module ActiveSupport
- module Deprecation
+ class Deprecation
class DeprecationProxy #:nodoc:
def self.new(*args, &block)
object = args.first
@@ -25,10 +25,21 @@ def method_missing(called, *args, &block)
end
end
- class DeprecatedObjectProxy < DeprecationProxy #:nodoc:
- def initialize(object, message)
+ # This DeprecatedObjectProxy transforms object to depracated object.
+ #
+ # Example
+ # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!")
+ # Example with custom deprecator
+ # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance)
+ #
+ # When someone execute any method expect +inspect+ on proxy object this will trigger +warn+ method on +deprecator_instance+
+ #
+ # Default deprecator is ActiveSupport::Deprecation
+ class DeprecatedObjectProxy < DeprecationProxy
+ def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance)
@object = object
@message = message
+ @deprecator = deprecator
end
private
@@ -37,15 +48,38 @@ def target
end
def warn(callstack, called, args)
- ActiveSupport::Deprecation.warn(@message, callstack)
+ @deprecator.warn(@message, callstack)
end
end
- # Stand-in for <tt>@request</tt>, <tt>@attributes</tt>, <tt>@params</tt>, etc.
- # which emits deprecation warnings on any method call (except +inspect+).
- class DeprecatedInstanceVariableProxy < DeprecationProxy #:nodoc:
- def initialize(instance, method, var = "@#{method}")
- @instance, @method, @var = instance, method, var
+ # This DeprecatedInstanceVariableProxy transforms instance variable to depracated instance variable.
+ #
+ # Example
+ # class Example
+ # def initialize(deprecator)
+ # @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
+ # @_request = :a_request
+ # end
+ #
+ # def request
+ # @_request
+ # end
+ #
+ # def old_request
+ # @request
+ # end
+ # end
+ #
+ # When someone execute any method on @request variable this will trigger +warn+ method on +deprecator_instance+
+ # and will fetch @_request variable via +request+ method and execute the same method on non-proxy instance variable.
+ #
+ # Default deprecator is ActiveSupport::Deprecation
+ class DeprecatedInstanceVariableProxy < DeprecationProxy
+ def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance)
+ @instance = instance
+ @method = method
+ @var = var
+ @deprecator = deprecator
end
private
@@ -54,14 +88,24 @@ def target
end
def warn(callstack, called, args)
- ActiveSupport::Deprecation.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
+ @deprecator.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
end
end
- class DeprecatedConstantProxy < DeprecationProxy #:nodoc:all
- def initialize(old_const, new_const)
+ # This DeprecatedConstantProxy transforms constant to depracated constant.
+ #
+ # Example
+ # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST')
+ # Example with custom deprecator
+ # OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance)
+ # When someone use old constant this will trigger +warn+ method on +deprecator_instance+
+ #
+ # Default deprecator is ActiveSupport::Deprecation
+ class DeprecatedConstantProxy < DeprecationProxy
+ def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance)
@old_const = old_const
@new_const = new_const
+ @deprecator = deprecator
end
def class
@@ -74,7 +118,7 @@ def target
end
def warn(callstack, called, args)
- ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack)
+ @deprecator.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack)
end
end
end
View
32 activesupport/lib/active_support/deprecation/reporting.rb
@@ -1,7 +1,10 @@
module ActiveSupport
- module Deprecation
- class << self
+ 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
# Outputs a deprecation warning to the output configured by
# <tt>ActiveSupport::Deprecation.behavior</tt>.
@@ -31,16 +34,29 @@ def silence
@silenced = old_silenced
end
- def deprecated_method_warning(method_name, message = nil)
- warning = "#{method_name} is deprecated and will be removed from Rails #{deprecation_horizon}"
- case message
- when Symbol then "#{warning} (use #{message} instead)"
- when String then "#{warning} (#{message})"
- else warning
+ def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = caller)
+ deprecated_method_warning(deprecated_method_name, message).tap do |message|
+ warn(message, caller_backtrace)
end
end
private
+ # Outputs a deprecation warning message
+ # ActiveSupport::Deprecation.deprecated_method_warning(:method_name)
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
+ # ActiveSupport::Deprecation.deprecated_method_warning(:method_name, :another_method)
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
+ # ActiveSupport::Deprecation.deprecated_method_warning(:method_name, "Optional message")
+ # # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
+ def deprecated_method_warning(method_name, message = nil)
+ warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
+ case message
+ when Symbol then "#{warning} (use #{message} instead)"
+ when String then "#{warning} (#{message})"
+ else warning
+ end
+ end
+
def deprecation_message(callstack, message = nil)
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
message += '.' unless message =~ /\.$/
View
149 activesupport/test/deprecation_test.rb
@@ -104,6 +104,17 @@ def test_default_stderr_behavior
assert_match(/call stack!/, content)
end
+ def test_default_stderr_behavior_with_warn_method
+ ActiveSupport::Deprecation.behavior = :stderr
+
+ content = capture(:stderr) {
+ ActiveSupport::Deprecation.warn('Instance error!', ['instance call stack!'])
+ }
+
+ assert_match(/Instance error!/, content)
+ assert_match(/instance call stack!/, content)
+ end
+
def test_default_silence_behavior
ActiveSupport::Deprecation.behavior = :silence
behavior = ActiveSupport::Deprecation.behavior.first
@@ -186,4 +197,142 @@ def test_deprecation_with_alternate_method
def test_deprecation_with_explicit_message
assert_deprecated(/you now need to do something extra for this one/) { @dtc.d }
end
+
+ def test_deprecation_in_other_object
+ messages = []
+
+ klass = Class.new do
+ delegate :warn, :behavior=, to: ActiveSupport::Deprecation
+ end
+
+ o = klass.new
+ o.behavior = Proc.new { |message, callstack| messages << message }
+ assert_difference("messages.size") do
+ o.warn("warning")
+ end
+ end
+
+ def test_deprecated_method_with_custom_method_warning
+ deprecator = deprecator_with_messages
+
+ class << deprecator
+ private
+ def deprecated_method_warning(method, message)
+ "deprecator.deprecated_method_warning.#{method}"
+ end
+ end
+
+ deprecatee = Class.new do
+ def method
+ end
+ deprecate :method, deprecator: deprecator
+ end
+
+ deprecatee.new.method
+ assert deprecator.messages.first.match("DEPRECATION WARNING: deprecator.deprecated_method_warning.method")
+ end
+
+ def test_deprecate_with_custom_deprecator
+ custom_deprecator = mock('Deprecator') do
+ expects(:deprecation_warning)
+ end
+
+ klass = Class.new do
+ def method
+ end
+ deprecate :method, deprecator: custom_deprecator
+ end
+
+ klass.new.method
+ end
+
+ def test_deprecated_constant_with_deprecator_given
+ deprecator = deprecator_with_messages
+ klass = Class.new
+ klass.const_set(:OLD, ActiveSupport::Deprecation::DeprecatedConstantProxy.new('klass::OLD', 'Object', deprecator) )
+ assert_difference("deprecator.messages.size") do
+ klass::OLD.to_s
+ end
+ end
+
+ def test_deprecated_instance_variable_with_instance_deprecator
+ deprecator = deprecator_with_messages
+
+ klass = Class.new() do
+ def initialize(deprecator)
+ @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
+ @_request = :a_request
+ end
+ def request; @_request end
+ def old_request; @request end
+ end
+
+ assert_difference("deprecator.messages.size") { klass.new(deprecator).old_request.to_s }
+ end
+
+ def test_deprecated_instance_variable_with_given_deprecator
+ deprecator = deprecator_with_messages
+
+ klass = Class.new do
+ define_method(:initialize) do
+ @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
+ @_request = :a_request
+ end
+ def request; @_request end
+ def old_request; @request end
+ end
+
+ assert_difference("deprecator.messages.size") { klass.new.old_request.to_s }
+ end
+
+ def test_delegate_deprecator_instance
+ klass = Class.new do
+ attr_reader :last_message
+ delegate :warn, :behavior=, to: ActiveSupport::Deprecation
+
+ def initialize
+ self.behavior = [Proc.new { |message| @last_message = message }]
+ end
+
+ def deprecated_method
+ warn(deprecated_method_warning(:deprecated_method, "You are calling deprecated method"))
+ end
+
+ private
+ def deprecated_method_warning(method_name, message = nil)
+ message || "#{method_name} is deprecated and will be removed from This Library"
+ end
+ end
+
+ object = klass.new
+ object.deprecated_method
+ assert_match(/You are calling deprecated method/, object.last_message)
+ end
+
+ def test_default_gem_name
+ deprecator = ActiveSupport::Deprecation.new
+
+ deprecator.send(:deprecated_method_warning, :deprecated_method, "You are calling deprecated method").tap do |message|
+ assert_match(/is deprecated and will be removed from Rails/, message)
+ end
+ end
+
+ def test_custom_gem_name
+ deprecator = ActiveSupport::Deprecation.new('2.0', 'Custom')
+
+ deprecator.send(:deprecated_method_warning, :deprecated_method, "You are calling deprecated method").tap do |message|
+ assert_match(/is deprecated and will be removed from Custom/, message)
+ end
+ end
+
+ private
+ def deprecator_with_messages
+ klass = Class.new(ActiveSupport::Deprecation)
+ deprecator = klass.new
+ deprecator.behavior = Proc.new{|message, callstack| deprecator.messages << message}
+ def deprecator.messages
+ @messages ||= []
+ end
+ deprecator
+ end
end
Something went wrong with that request. Please try again.