好吧,我們一致認爲,改變你的主機應用程序的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