How to extend a helper using plugin?

292 views
Skip to first unread message

Alex Shulgin

unread,
Apr 8, 2011, 9:18:29 AM4/8/11
to Ruby on Rails: Talk
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

Frederick Cheung

unread,
Apr 8, 2011, 10:06:29 AM4/8/11
to Ruby on Rails: Talk


On Apr 8, 2:18 pm, Alex Shulgin <alex.shul...@gmail.com> wrote:
> 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

Alex Shulgin

unread,
Apr 8, 2011, 12:42:15 PM4/8/11
to Ruby on Rails: Talk
On Apr 8, 5:06 pm, Frederick Cheung <frederick.che...@gmail.com>
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

Alex Shulgin

unread,
Apr 9, 2011, 6:43:47 AM4/9/11
to Ruby on Rails: Talk
On Apr 8, 7:42 pm, Alex Shulgin <alex.shul...@gmail.com> wrote:
> On Apr 8, 5:06 pm, Frederick Cheung <frederick.che...@gmail.com>
> 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

Now, that's interesting. The above method supposed to work, and it
does work with, e.g. users_helper (see
http://www.redmine.org/projects/redmine/wiki/Plugin_Internals#Wrapping-an-existing-method
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

Alex Shulgin

unread,
Apr 30, 2011, 7:54:03 AM4/30/11
to Ruby on Rails: Talk
On Apr 9, 1:43 pm, Alex Shulgin <alex.shul...@gmail.com> 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...

ganaware

unread,
Oct 6, 2011, 9:19:31 PM10/6/11
to Alex Shulgin, gana...@gmail.com, Ruby on Rails: Talk
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

Reply all
Reply to author
Forward
0 new messages