How can I override a Ruby on Rails module method via metaprogramming?
As much I would like to I still don't know how to implement metaprogramming.
For views rendered from my Admin::Base controller I would like override the Rails compute_publ开发者_如何学Goic_path
located under ActionView::Helpers::AssetTagHelper, so that all my admin's layout files will be under public/admin.
There is probably a better way to this, but at this point I want to learn how to do it this way.
I put this in Admin::Base controller:
module ActionView::Helpers::AssetTagHelper
def compute_public_path(source, dir, ext = nil, include_host = true)
super(source, "admin/#{dir}", ext = nil, include_host = true)
end
end
but it gives me:
super: no superclass method `compute_public_path' for #<ActionView::Base:0x1032240a8>
Which doesn't surprise me.
If I try this in my admin helper:
def compute_public_path_with_admin(source, dir, ext = nil, include_host = true)
compute_public_path_without_admin(source, "admin/#{dir}", ext, include_host)
end
alias_method_chain :compute_public_path, :admin
I get "undefined method 'compute_public_path' for module 'AdminHelper'", which I am guessing happens because compute_public_path
is a private method.
I found this works:
ActionView::Helpers::AssetTagHelper.class_eval do
def compute_public_path_with_admin(source, dir, ext = nil, include_host = true)
compute_public_path_without_admin(source, "admin/#{dir}", ext, include_host)
end
alias_method_chain :compute_public_path, :admin
end
as long as I set config.cache_classes
to false
, otherwise I get a stack level too deep error.
Thanks to Squeegy for pointing me in the right direction.
How can I make this work with disabling class caching?
def compute_public_path_with_admin(source, dir, ext = nil, include_host = true)
compute_public_path_without_admin(source, "admin/#{dir}", ext, include_host)
end
alias_method_chain :compute_public_path, :admin
compute_public_path
will now call your implementation, which then calls the original implementation.
See: Is alias_method_chain synonymous with alias_method?
Please don't do this.
The accepted answer will get what you wanted, but what you want might have dangerous security implications.
Why would you want your admin layouts under public
? There's a reason why Rails by default puts them under app/views/layouts
: No one will be able to read their source there.
If you put them in public
, they are basically readable for any visitor.
If you put them where they belong, you're not exposing your layout code. And on top of that, you can structure app/views/layouts
any way you want without any metaprogramming, i.e. without having to hack framework internals.
精彩评论