How to extend a helper using plugin?

Hello,

I'm facing this problem while trying to extend the parse_redmine_links helper method in Redmine-1.1.0 (Rails-2.3.5) from my plugin.

My idea is to use alias_method_chain so the extended version could call the original version and adjust the result to it's liking.

Anything I've tried so far exposes this behavior: the first render of a page uses the extended version, while every later renders use the original unmodified helper.

I've tried to include my patch module in the plugin's init.rb like this:

require_dependency 'application_helper' ApplicationHelper.send(:include, RedminePastebin::ApplicationHelperPatch)

Also I've tried to define my ApplicationHelper in myplugin/app/helper/ application_helper.rb with the same effect. Both approaches used self.included hook to call alias_method_chain, like this:

  def self.included(base)     base.module_eval do       alias_method_chain :parse_redmine_links, :pastes     end   end

Any ideas on how to accomplish the task are welcome!

Hello,

I'm facing this problem while trying to extend the parse_redmine_links helper method in Redmine-1.1.0 (Rails-2.3.5) from my plugin.

My idea is to use alias_method_chain so the extended version could call the original version and adjust the result to it's liking.

Anything I've tried so far exposes this behavior: the first render of a page uses the extended version, while every later renders use the original unmodified helper.

Because you're in development mode, the app's code, including application_helper is reloaded for each request, but your plugin's init.rb is only called once and never gets to do your monkey patching on the reloaded copies of application helper

You might be able to use a to_prepare callback (these get called before each request in development mode) if you're going to use this a lot in development mode - take a peek inside action_dispatch/ middleware/callbacks

Fred

Yes, but I *was* using a to_prepare block (w/o actually understanding what it does, though.)

So what I now actually have in init.rb is this:

Dispatcher.to_prepare :redmine_model_dependencies do   puts "!!! to_prepare block !!!"

  require_dependency 'application_helper'

  unless ApplicationHelper.included_modules.include? RedminePastebin::ApplicationHelperPatch     ApplicationHelper.send(:include, RedminePastebin::ApplicationHelperPatch)   end end

If I put some logging lines into redmine's application_helper.rb, I can get this:

module ApplicationHelper   def self.included(base)     puts "ApplicationHelper: INCLUDED: #{self.method_defined? (:parse_redmine_links_with_pastes)}"   end   ...   def parse_redmine_links(text, project, obj, attr, only_path, options)     logger.info "parse_redmine_links"   ...

And in my patch-module I have this log line:

    def parse_redmine_links_with_pastes(text, project, obj, attr, only_path,                                         options)       logger.info "parse_redmine_links_with_pastes"      ...

Now for the first page load I get these:

ApplicationHelper: INCLUDED: false ApplicationHelper: INCLUDED: false !!! to_prepare block !!! => Call with -d to detach => Ctrl-C to shutdown server !!! to_prepare block !!! ApplicationHelper: INCLUDED: true ... parse_redmine_links_with_pastes ...

and for the second load, these:

!!! to_prepare block !!! ApplicationHelper: INCLUDED: true ApplicationHelper: INCLUDED: true ApplicationHelper: INCLUDED: true ... parse_redmine_links

So original unmodified method is called regardless of the fact that the new one is defined...

I guess I need to move that alias_method_chain call somewhere else so it's called on every page load. How do I do so?

Now, that's interesting. The above method supposed to work, and it does work with, e.g. users_helper (see

for the supported way of overriding helper methods.)

But this does not work with application_helper. I come to think it is special in some way.

If I put this into my init.rb:

Dispatcher.to_prepare :redmine_model_dependencies do   require_dependency 'users_helper'   require_dependency 'application_helper'

  unless UsersHelper.included_modules.include?(LockUsersHelperPatch)     puts "!!! UsersHelper !!!"     UsersHelper.send(:include, LockUsersHelperPatch)   end

  unless ApplicationHelper.included_modules.include? ApplicationHelperPatch     puts "!!! ApplicationHelper !!!"     ApplicationHelper.send(:include, ApplicationHelperPatch)   end end

I can clearly see that UserHelperPatch is included on every page load, while ApplicationHelperPatch is only included for the first time. So it somehow thinks it is included, but calls the wrong (unaliased) method.

Any help on this is greatly appreciated!

I wonder if I might have better luck asking this on the -core list...

Is the problem solved? I found that the following codes works as expected.

redmine/vendor/plugins/redmine_x/init.rb:

require 'redmine' require 'dispatcher'

module XPatch   def self.included(base) # :nodoc:     if !base.method_defined?(:parse_redmine_links_without_patch)       base.send(:include, InstanceMethods)       base.class_eval do         alias_method_chain :parse_redmine_links, :patch       end     else       base.class_eval do

alias_method :parse_redmine_links, :parse_redmine_links_with_patch       end     end   end

  module InstanceMethods     def parse_redmine_links_with_patch(text, project, obj, attr, only_path, options)       parse_redmine_links_without_patch(text, project, obj, attr, only_path, options)       text.gsub!(/./, 'x')     end   end end

Dispatcher.to_prepare :x do   require_dependency 'application_helper'   ApplicationHelper.send(:include, XPatch) end

Redmine::Plugin.register :redmine_magic_links_to_notes do   name 'Redmine X plugin'   author 'ganaware'   description 'X notes'   version '0.0.1' end