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!

Alex

On Apr 8, 2:18pm, Alex S. [email protected] wrote:

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

On Apr 8, 5:06pm, Frederick C. [email protected]
wrote:

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

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?


Thanks,
Alex

On Apr 8, 7:42pm, Alex S. [email protected] wrote:

You might be able to use a to_prepare callback (these get called
puts “!!! to_prepare block !!!”

require_dependency ‘application_helper’

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

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!


Alex

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

On Apr 9, 1:43pm, Alex S. [email protected] wrote:

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.

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