Skip to content

Patches and Cross-plugin Extensions

When my_plugin extends behavior owned by core, an engine, or another plugin, keep that extension in my_plugin.

The preferred solution is to define a patch in the extending component instead of adding if my_plugin_active? or if Redmine::Plugin.installed?(:my_plugin) branches to the component being extended.

Why this is preferred

  • The feature is loaded only when the extending plugin is installed and active.
  • Ownership stays clear. Code for my_plugin remains in my_plugin.
  • The extended component stays isolated from optional dependencies.
  • Removing or disabling the plugin removes the behavior cleanly.

Preferred approach

If my_plugin needs to extend Issue, ApplicationController, a service object, or another plugin's class, create a patch under my_plugin and register or include it from my_plugin during boot.

Typical locations are under patches/ or another plugin-local namespace dedicated to patches.

plugins/my_plugin/patches/redmine/models/issue_patch.rb
module PluginA
  module IssuePatch
    def some_method
      super

      # my_plugin-specific extension
      do_something_for_my_plugin
    end
  end
end

Avoid this pattern

Do not keep my_plugin behavior inside the extended component behind a condition.

bad example
class Issue < ActiveRecord::Base
  def some_method
    result = super

    if Redmine::Plugin.installed?(:my_plugin)
      do_something_for_my_plugin
    end

    result
  end
end

This couples the base component to an optional plugin and spreads my_plugin logic outside of my_plugin.

What belongs in the original component

Keep code in the original component only when the behavior is genuinely generic and should exist independently of the extending plugin.

If you need a safer extension point for multiple consumers, prefer adding a hook or another explicit extension point to the original component, then implement the plugin-specific behavior in the plugin.

For views, follow the existing rule from code style:

  • for small additions, prefer hooks
  • for large rewrites, a template or partial override can be acceptable

Summary rule

If a feature exists only because my_plugin is present, the implementation should live in my_plugin, usually as a patch.