Ok, we're agreed that altering your host app's application_controller.rb isn't the way to go. Let's look at different ways of adding methods to the ApplicationController class (actually ActionController::Base) via a gem.
I've created a very simple gem. I want it to add one function, rot13, that means any controller will be able to call rot13('something!') to get back 'fbzrguvat!'.
(In real life you'd add this to String...)
You could extend ActionController::Base, like this:
class ActionController::Base
def rot13 str
a = 'a'.ord
z = 'z'.ord
str.unpack('c*').map { |x| (a..z).cover?(x) ? (((x - a) + 13) % 26) + a : x }.pack('c*')
end
end
And now in my application I can call rot13('ibvyn!') inside any controller and voila!
It's safer to add a module and include it in ActionController::Base via a Railtie hook. So let's add a Railtie.
I add lib/rot13/railtie.rb as follows:
module Rot13
class Rot13Railtie < Rails::Railtie
initializer "rot_13_railtie.extend_action_controller" do
ActiveSupport.on_load :action_controller do
# At this point, self == ActionController::Base
include Rot13::ControllerMethods
end
end
end
end
Now lib/rot13.rb looks like this:
require "rot13/version"
require "rot13/railtie.rb" if defined? Rails
module Rot13
module ControllerMethods
def rot13 str
a = 'a'.ord
z = 'z'.ord
str.unpack('c*').map { |x| (a..z).cover?(x) ? (((x - a) + 13) % 26) + a : x }.pack('c*')
end
end
end
This is fine for most purposes.
Let's say you didn't want your rot13 method to be defined in ActionController::Base and available to all controllers--let's say you wanted users of your gem to 'opt in' on a controller-by-controller basis, e.g.
class ApplicationController < ActionController::Base
with_rot_13
# and so on...
end
Instead of include Rot13::ControllerMethods you might call extend Rot13::ControllerOptIn within that on_load block to add a with_rot_13 method at the class level of ActionController::Base, then define a ControllerOptIn module as follows:
module Rot13
module ControllerMethods
# ...
end
module ControllerOptIn
def with_rot_13
include ControllerMethods
end
end
end
That's it!
Edit: just to address the additional question of 'why isn't the method visible in my view?'--your method is not defined as a helper, so it's not automatically visible in your views without suffixing it with controller, e.g. %p= controller.rot13 'blah'. Luckily you can define it as a helper with a call to helper_method, e.g.
module ControllerOptIn
def with_rot_13
include ControllerMethods
helper_method :rot13
end
end
Now you can do this (note: HAML):
%p= controller.rot13 'hello there!'
%p= rot13 'well how are ya?'
But it's not great to have to specify helper_method :rot13 here. Can you dig the necessary symbols straight out of Rot13::ControllerMethods? Sure, as long as you're sure it's what you want:
module ControllerOptIn
def with_rot_13
include ControllerMethods
helper_method *ControllerMethods.instance_methods
end
end