views:

261

answers:

2

I have this code in vendor/{engine}/app/helpers/application_helper.rb:

module ApplicationHelper
  def application_title()
    "Foo"
  end

  def other_method()
    # ...
  end
end

And this in app/helpers/application_helper.rb:

module ApplicationHelper
  def application_title()
    "Bar"
  end
end

I get "Bar" as expected when calling application_title() from my app, but my application can't see other_method(). How do I modify ApplicationHelper to fix this?

A: 

I can't say for sure that this is the problem in Rails, but my guess is that it's not far off the mark. The "fix" at the end may not be easily applied in the case of a Rails app.

First, a directory listing:

> ls -R
.:
bar_then_foo.rb  bar_then_foo2.rb  foo_then_bar.rb  lib_foo  lib_fubar

./lib_foo:
helper.rb

./lib_fubar:
helper.rb

The helper modules:

> cat lib_foo/helper.rb 
module Helper
  def foo
    puts "foo"
  end

  def bar
    puts "bar"
  end
end

> cat lib_fubar/helper.rb 
module Helper
  def foo
    puts "foo-bar"
  end
end

The Ruby code:

> cat bar_then_foo.rb 
$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib_fubar'
$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib_foo'

require 'helper'

class FoobieDoo
  include Helper
end

f = FoobieDoo.new
f.foo
f.bar

> cat foo_then_bar.rb 
$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib_foo'
$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib_fubar'

require 'helper'

class FoobieDoo
  include Helper
end

f = FoobieDoo.new
f.foo
f.bar

And some output:

> ruby bar_then_foo.rb 
foo
bar

> ruby foo_then_bar.rb 
foo-bar
foo_then_bar.rb:12: undefined method 'bar' for #<FoobieDoo:0x1042e93c> (NoMethodError)

So Ruby is searching $LOAD_PATH from the end and stops as soon as it finds a match.

Let's try something else:

> cat bar_then_foo2.rb 
$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib_fubar'
$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib_foo'

require 'helper'
load "lib_fubar/helper.rb"

class FoobieDoo
  include Helper
end

f = FoobieDoo.new
f.foo
f.bar

> ruby bar_then_foo2.rb 
foo-bar
bar

Desired result?

Maybe you can modify app/helpers/application_helper.rb to search for other application_helper.rb files in the LOAD_PATH and load them?

mcl
A: 

I think the problem is that you are expecting the helper in your engine to overide the one in the main app. I believe this worked in the Engines plugin but in Rails 2.3 this feature was not brought in.

From http://rails-engines.org/news/2009/02/02/engines-in-rails-2-3/

Code mixing

Rails 2.3 doesn’t attempt to do any of the ‘code mixing’ that the engines plugin does. For those of you not in the know, this is the functionality that lets your application override a single method in a controller or helper, without having to copy the whole file from the plugin into your application.

I cannot say for sure, but I doubt this feature will ever make it into Rails itself, and to be honest, I think that’s probably a good thing. While the code mixing mechanism is quite neat, there are other ways of overriding the implementation of methods which are more typically Rubyish, and involve less magic. I’ll try and post some examples here soon.

Hope this helps.

Lee Irving
Also see http://agilewebdevelopment.com/plugins/datebocksengine for example of how the engines work.
Lee Irving
specifically step 5 shows including the helper from the engine into the projects helper.
Lee Irving
D'oh. Well, that's a bummer. I was really hoping I could use this feature. Is there a smarter way of doing things?
Kyle Kaitan