Deprewriter
Deprewriter is an experimental gem that helps you rewrite deprecated code automatically.
When library authors deprecate methods, they typically notify users through documentation and deprecation warnings at runtime. Users then need to manually update their code to stop using deprecated methods and switch to recommended alternatives. Deprewriter automates this process.
This project is inspired by Deprewriter in the Pharo programming language. For more details, see "Deprewriter: On the fly rewriting method deprecations." (2022).
For more details about this gem and its implementation, see the presentation at RubyKaigi 2025: On-the-fly Suggestions of Rewriting Method Deprecations.
Installation
Install the gem and add to the application's Gemfile by executing:
bundle add deprewriter
If bundler is not being used to manage dependencies, install the gem by executing:
gem install deprewriter
Usage
Let's say a library code has a class Legacy with a deprecated method old_method and a new method new_method. The code owner can declare deprewrite with transformation rules.
# Library code
require "deprewriter"
class Legacy
def old_method(arg)
# ...
end
def new_method(arg)
# ...
end
extend Deprewriter
deprewrite :old_method, to: "new_method({{arguments}})"
end
And then, when a user program calls old_method with the following code:
# User code
legacy = Legacy.new
legacy.old_method(arg) # This line will trigger Deprewriter
If a user program runs with DEPREWRITER=log environment variable...
DEPREWRITER=1 ruby your_script.rb
...it will log a warning with the suggested changes to rewrite the code:
legacy = Legacy.new
-legacy.old_method(arg)
+legacy.new_method(arg)
For code owners
You can also specify a pattern to match for transformation using the from option:
deprewrite :old_method,
from: '.call_node[name=old_method]', # Rewrite calls only when matched
to: 'new_method({{arguments}})'
The syntax of transformation rules is taken from Synvert and it uses Prism to parse and rewrite the source code. Variables in a transformation rule string like {{arguments}} and {{block}} etc. correspond to methods provided by Prism node.
For more details, see:
For client users
Deprewriter can be configured either through environment variables or programmatically. There are three modes of operation:
Environment Variable Configuration
Set the DEPREWRITER environment variable to one of these values:
log- For logging mode. Logs deprecation warnings and shows diffs (default)diff- For diff mode. Creates patch files for each deprecationdangerously_rewrite- For rewrite mode. Automatically rewrites files (use with caution!)- If not set, Deprewriter will be disabled
Example:
# Deprewriter is disabled
ruby your_script.rb
# Deprewriter is enabled as log mode.
DEPREWRITER=1 ruby your_script.rb
DEPREWRITER=log ruby your_script.rb
# Deprewriter is enabled as diff mode.
DEPREWRITER=diff ruby your_script.rb
# Deprewriter is enabled as rewrite mode.
DEPREWRITER=dangerously_rewrite ruby your_script.rb
Programmatic Configuration
You can configure Deprewriter programmatically using the configuration block:
Deprewriter.configure do |config|
# Set the mode (:log, :diff, :rewrite, or :disabled)
config.mode = :log
# Configure custom logger (defaults to STDOUT)
config.logger = Logger.new('deprewriter.log')
end
Mechanism
Deprewriter determines caller code at runtime and attempts to rewrite it according to predefined transformation rules, though the actual rewriting depends on the configured mode and file permissions.
- Library authors define deprecation rules for methods they want to deprecate, along with transformation rules specifying how the code should be rewritten.
- When the program loads, Deprewriter renames the deprecated method to
_deprecated_fooand dynamically defines a new methodfoo. This is similar to howGem::Deprecate.deprecateworks. - When the deprecated method
foois called by user code, Deprewriter analyzes the caller's code and generates a transformation according to the rules. Depending on the configured mode. In all cases, it calls the original_deprecated_footo preserve the original behavior. - If the file was successfully rewritten, subsequent executions will use the transformed code. Otherwise, the deprecated method will continue to be called and generate warnings/diffs on each invocation.
TODO
- [x] Complete major deprecation scenarios
- Rename method
- Remove argument(-s)
- Add argument(-s)
- [x] Decide DSL for transformation rules
- [x] Use Synvert for now
- [x] Testable code structure and write tests
- [x] Skip redundant rewrite, when the same code is rewritten multiple times
- [ ] Reduce other gem dependencies to be a standalone gem
- [ ] Handle more complex cases, e.g. metaprogramming, DSLs, etc.
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/ohbarye/deprewriter. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the Deprewriter project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.