英文:
Deduce controller name from namespaced string
问题
在我的Rails代码库中,我使用了以下代码:
Rails.application.routes.recognize_path(request.referer)
我的目标是获取:controller
键的类对象。到目前为止,我只能得到类似于my/namespace/posts
的东西。
然而,真正的类名是My::Namespace::PostsController
。有没有自动推断类名的方法?否则,有什么更优雅的方法可以实现这一目标?
英文:
At some point in my Rails codebase I'm using the following:
Rails.application.routes.recognize_path(request.referer)
My goal is to get the class object of the :controller
key.
So far I've only gotten to something like this: my/namespace/posts
.
The true class however is My::Namespace::PostsController
. Is there any way to automatically deduce the class name?
Otherwize, what't the most elegant way to get to that?
答案1
得分: 3
@zakariah提到了ActionDispatch::Request#controller_class
,它看起来应该可以正常工作,前提是params[:controller]
是一个String
,尽管我没有测试过:
params = {controller: '/my/namespace/posts'}
ActionDispatch::Request.new({}).controller_class_for(params[:controller])
#=> My::Namespace::PostsController
request.controller_class # 理论上
#=> My::Namespace::PostsController
然而,如果控制器不存在,这将引发错误。
话虽如此,根据您当前的实现,您已经成功获取了字符串"my/namespace/posts"
,这意味着您已经完成了90%的工作。
要将其转换为所需的控制器名称实际上非常简单:
str = "my/namespace/posts"
controller_name = str.camelize << "Controller"
#=> "::My::Namespace::PostsController"
controller_name.safe_constantize
#=> ::My::Namespace::PostsController
这实际上是AbstractController::Base#controller_path
的简单反转,它使用了underscore
。
正如文档中所提到的:
ActiveSupport::Inflector::camelize
[
camelize
] 还将'/'
转换为'::'
,这对于将路径转换为命名空间很有用...
重要说明:
...作为一个经验法则,您可以认为
camelize
是underscore
的反义词,尽管有些情况不成立:
camelize(underscore('SSLError')) # => "SslError"
因此,如果需要,您需要确保设置了正确的词形规则。
我选择使用safe_constantize
,如果常量不存在,它将返回nil
。这允许您以更符合惯例的方式处理这些问题(缺少常量),例如:
controller_name.safe_constantize || DefaultContoller
英文:
@zakariah mentioned ActionDispatch::Request#controller_class
which appears like it should work provided that params[:controller]
is a String
, although I have not tested it:
params = {controller: '/my/namespace/posts'}
ActionDispatch::Request.new({}).controller_class_for(params[:controller])
#=> My::Namespace::PostsController
request.controller_class # in theory
#=> My::Namespace::PostsController
however this will raise an error if the controller does not exist.
That being said based on your current implementation, you have managed to get to the String "my/namespace/posts"
, which means you are 90% of the way there.
To convert this to the desired controller name is actually fairly simple:
str = "my/namespace/posts"
controller_name = str.camelize << "Controller"
#=> "::My::Namespace::PostsContoller"
contoller_name.safe_constantize
#=> ::My::Namespace::PostsContoller
This is a simple inversion of AbstractController::Base#controller_path
, which uses underscore
.
As mentioned in the docs:
ActiveSupport::Inflector::camelize
> [camelize
] Also converts '/' to '::' which is useful for converting paths to namespaces....
Important Note:
> ...As a rule of thumb you can think of camelize
as the inverse of underscore
, though there are cases where that does not hold:
>
>camelize(underscore('SSLError')) # => "SslError"
So you will need to ensure that the proper inflections are setup if needed in your application.
I chose to use safe_constantize
which will return nil
if the constant does not exist. This allows you to handle these issues (missing constants) in a more idiomatic fashion e.g.
controller_name.safe_constantize || DefaultContoller
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论