好吧,我们一致认为,改变你的主机应用程序的application_controller.rb不要走的路。让我们看看通过gem将方法添加到ApplicationController类(实际上是ActionController :: Base)的不同方法。
我创建了一个非常简单的宝石。我想要它添加一个功能,rot13,这意味着任何控制器将能够拨打rot13('something!')
找回'fbzrguvat!'
。 (在现实生活中,你要补充这String
...)
你可以扩展ActionController::Base
,像这样:
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
现在在我的应用程序可以调用任何rot13('ibvyn!')
控制器和voila!
内
添加模块并通过Railtie钩子将其包含在ActionController :: Base中更安全。所以让我们添加一个Railtie。
我添加lib/rot13/railtie.rb
如下:
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
现在lib/rot13.rb
看起来是这样的:
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
这是适合大多数的目的。
假设您不希望您的rot13
方法在ActionController::Base
中定义并可供所有控制器使用 - 比方说,您希望您的gem用户在逐控制器的基础上“加入”。
class ApplicationController < ActionController::Base
with_rot_13
# and so on...
end
相反的include Rot13::ControllerMethods
你可能称之为on_load
块内extend Rot13::ControllerOptIn
到的ActionController::Base
类级别添加with_rot_13
方法,然后定义一个ControllerOptIn
模块如下:
module Rot13
module ControllerMethods
# ...
end
module ControllerOptIn
def with_rot_13
include ControllerMethods
end
end
end
这就是它!
编辑:刚刚解决的另一个问题“为什么不是我的视图中可见的方法是什么?” - 你的方法没有被定义为一个帮手,所以它在你的看法是不会自动无可见它后面添加与controller
,例如%p= controller.rot13 'blah'
。幸运的是,您可以通过拨打helper_method
来定义它为助手,例如
module ControllerOptIn
def with_rot_13
include ControllerMethods
helper_method :rot13
end
end
现在你可以做到这一点(注:HAML):
%p= controller.rot13 'hello there!'
%p= rot13 'well how are ya?'
但它不是伟大的,有在这里指定helper_method :rot13
。你能直接从Rot13::ControllerMethods
挖掘必要的符号吗?当然,只要你确定它是你想要的:
module ControllerOptIn
def with_rot_13
include ControllerMethods
helper_method *ControllerMethods.instance_methods
end
end
你想让你的宝石字面上编辑application_controller.rb?如果在运行时重新开放类(或者这不适用于你的gem做什么?),在运行时扩展ApplicationController不是更好吗?你可以尝试从文件底部向上扫描,寻找一个'end' 'class ApplicationController'匹配 - 但我确信有可能被破坏的方法。 (你会尝试你自己的解析。) – struthersneil
是的,我正在寻找直接编辑它。我可能会误解你'在运行时扩展'的意思,但是如果应用程序中存在一个具有相同名称的控制器,那么Rails会覆盖这些控制器,所以我不能只将这些方法写在我的引擎控制器中。 –
考虑在一行中存在的类定义的可能性。或者一些尴尬但有效的情况,其中ApplicationController是通过调用Class.new()来定义的 – struthersneil