I'm looking for a part-time remote job.

Hire me


I'm the author of:

Mastering Redmine is a comprehensive guide with tips, tricks and best practices, and an easy-to-learn structure.

Check the book's project or

Buy the book

Social pages of the book:

By buying this book you also donate to Redmine (see this page).


Follow me:

Porting plugins to Redmine 4

No alias_method_chain

The proper way to fix this is to use prepend instead of include and super instead of foo_without_bar (in this way foo_with_bar becomes just foo).

Example:

module ModelPatch
  def self.included(base)
    base.send(:include, InstanceMethods)
    base.class_eval do
      unloadable
      alias_method_chain :method, :changes
    end
  end
  module InstanceMethods
    def method_with_changes
      method_without_changes
      ...
    end
  end
end
Model.send(:include, ModelPatch)

Should be:

module ModelPatch
  def method
    super
    ...
  end
end
Model.send(:prepend, ModelPatch)

However, such fix is not always possible (e.g., for helpers). Moreover, it can even cause infinite loop (if another plugin uses alias_method_chain). If this is the case, the following not very good alternative can be used (actually, this is what alias_method_chain did):

alias_method :foo_without_bar, :foo
alias_method :foo, :foo_with_bar

Generally, if your plugin can be used in different environments with other plugin, it’s safer to use the latter fix.

No more attr_protected

If you ported plugins to Redmine 3.x, you, probably, spent some time adding attr_protected to your models. Now, you need to remove it, cause this method no longer exists. Or, use the following code for backwards compatibility:

attr_protected :id if Rails::VERSION::MAJOR < 5

No more before_filter and so on

If you haven’t renamed before_filter into before_action in your controllers yet, it’s the time to do this, as before_filter no longer exists.

The same applies to prepend_before_filter, after_filter and so on.

ActionController::Parameters is no longer a Hash

So, you may need to convert it to Hash before using. The correct way to do this is:

params.permit(:foo, :bar).to_h

But, sometimes you may need to do this as follows:

params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params

Migrations should extend ActiveRecord::Migration[<version>]

When you run a database migration in Rails 5, it makes some assumptions (e.g., which indexes should be added), that it did not do before. Therefore, you are now forced to specify the version of Rails, for which the migration is written. All pre-Rails 5 migrations should use the version 4.2.

For backwards compatibility (i.e., to keep them working for Redmine 3), you may declare migrations as follow:

class YourMigration < Rails::VERSION::MAJOR < 5 ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
    ...
end

New methods to access changes in after callbacks

Before, in after callbacks (such as after_save) you could use common methods to check, which changes had been made to the object’s attributes. Now, such methods no longer return the changes – they behave as if the object has been reloaded.

To access the changes the new special methods were introduced:

  • Instead of <attribute>_changed? you should use saved_change_to_<attribute>?.
  • Instead of changes you should use previous_changes.
  • Instead of changed? you should use saved_changes?.
  • Instead of <attribute>_was you should use <attribute>_before_last_save.

Thus, to preserve backwards compatibility you can write the code as follows:

Rails::VERSION::MAJOR < 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR < 1) ? attribute_changed? : saved_change_to_attribute?

Mailer methods must have user as the first argument

If a User object is not the first argument, the Mailer will raise an exception. This change was made for proper localization of email notifications (a special method checks the first argument of all methods before invoking them).

So, if you are monkey-patching a Mailer method, you’ll need to have different patched methods for Redmine 4 and previous versions, e.g., as follows:

if Redmine::VERSION::MAJOR > 3
  base.send(:include, Redmine4InstanceMethods)
else
  base.send(:include, Redmine3InstanceMethods)
end

User.current rewritten in Mailer

If you have something like this in your Mailer method:

@author = User.current

You are in trouble, because User.current is now rewritten for each method, before invoking it (the first argument is used – see the previous section).

The best solution is to pass the user object as a additional argument to your Mailer method, e.g., as follows:

def object_updated(user, object, author)
  @author = author

deliver_later should be used to deliver mail

Now, Redmine can use a queue for delivering emails, what should speed up its work. To use this feature you need to call deliver_later instead of deliver. However...

To put an email into the queue Redmine needs to serialize its data, what sometimes crashes (not all data can be serialized). Therefore, I find it safer to still use deliver, for now (but, this should be fixed in some time).

Terms of use | Privacy policy