从带命名空间的字符串中推断控制器名称

huangapple go评论62阅读模式
英文:

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] 还将'/'转换为'::',这对于将路径转换为命名空间很有用...

重要说明:

...作为一个经验法则,您可以认为camelizeunderscore的反义词,尽管有些情况不成立:

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: &#39;/my/namespace/posts&#39;}
ActionDispatch::Request.new({}).controller_class_for(params[:controller])
#=&gt; My::Namespace::PostsController
request.controller_class # in theory 
#=&gt; 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 &quot;my/namespace/posts&quot;, which means you are 90% of the way there.

To convert this to the desired controller name is actually fairly simple:

str = &quot;my/namespace/posts&quot; 
controller_name = str.camelize &lt;&lt; &quot;Controller&quot; 
#=&gt; &quot;::My::Namespace::PostsContoller&quot;
contoller_name.safe_constantize
#=&gt; ::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(&#39;SSLError&#39;)) # =&gt; &quot;SslError&quot;

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

huangapple
  • 本文由 发表于 2023年3月7日 03:58:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/75655295.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定