Let’s assume we discovered a limitation or even a bug in a Gem. After a little investigation, we identified the problematic code, and we want to fix it. Since we are charitable developers, we fork the Gem’s repository, fix the method and prepare a merge request.
So far so good, but the bug still remains in our application. We need to wait until the Gem’s maintainer releases a new version.
We could change the application’s Gemfile to use our forked repository and let bundler install the Gem from git.
However, this change may be forgotten the next time we run
bundle update to upgrade our application’s dependencies.
Alternatively, we can get the fix into our Rails application by leveraging Ruby’s dynamic nature. While we wait for the new release, we monkey patch the bad method within an initializer:
# config/initializers/dsl_factory_fix.rb if Gem.loaded_specs['dsl_factory'].version.to_s == '0.1.0' module DslFactory def define_dsl # fix the issue end end else Rails.logger.warn("The Gem version of 'dsl_factory' has changed.") Rails.logger.warn("Please check if we still need above workaround!") # or even raise an error end
In the code above, we check the version of the dsl_factory Gem. If the version matches, we overwrite the problematic method. If the version has changed, we print out a warning. We can leave all sorts of instructions in the warning (e.g. a link to the pending merge request). We can even raise an exception to throw it in the developer’s face. Problem solved. Now, we can continue to work on our application and forget the fix.
When, a few days later, we update the dependencies, the maintainer may have updated the Gem. As a consequence of the new version, a warning will be shown to us, and we are reminded to remove the temporary fix.