如何在Ruby on Rails中获取控制器和操作列表?

39
在Rails中,可以通过调用controller_name获取当前控制器的名称,并通过action_name获取当前操作。 我正在寻找类似的运行时反射来获取以下内容:
  1. 应用程序中所有控制器的列表。
  2. 给定控制器中所有操作的列表。
例如,我有一个名为Product的控制器,它有“add”和“edit”操作。 我能否以编程方式获取这些操作的名称,以向用户显示支持的操作?
我查看了一个调用ActionController :: Routing :: Routes.named_routes.routes.each方法的方法,但是我无法使其工作。 当我使用它时,出现未初始化常数ActionDispatch :: Routing :: Routes的错误。
如果有任何好的教程或文档可以帮助我理解rails反射功能。 我已经搜索过了,但我大多数都得到了与活动记录反射相关的博客。 我正在寻找一些让我在运行时获取有关控制器和操作/方法信息的东西。
谢谢,Tabrez

你是在运行时还是在Rake作业中执行此操作?在运行时进行可能不是一个好主意。使用Rake作业可以更加轻松地完成此操作。 - drhenner
你是不是想说“rake routes”?我在运行时正在寻找某些东西。我不需要rake routes输出的详细信息,我只需要控制器名称,我认为它们必须作为一个集合可用。作为Rails的新手,我不知道在运行时可以访问哪些上下文对象。 - Tabrez
在这里找到了类似的问题,https://dev59.com/IHI_5IYBdhLWcg3wBuNs - nkm
@nkm:我已经查看了那个问题。在某种程度上它是相似的,但被接受的答案并不符合我所寻找的信息。 - Tabrez
5个回答

119

获取控制器类列表的最简单方法是:

ApplicationController.descendants

然而,由于类是延迟加载的,您需要在执行此操作之前急切地加载所有类。请记住,这种方法需要时间,并且会减慢启动速度:

Rails.application.eager_load!

要获取控制器中的所有操作,请使用action_methods

PostsController.action_methods

这将返回一个Set,其中包含您控制器中所有“操作”方法的列表(使用Rails使用的相同逻辑来决定方法是否是有效的操作以路由到)。


这正是我一直在寻找的。非常感谢!有没有特定的Rails资源/指南/书籍可以帮助理解这些问题,更具体地说是页面生命周期和运行时对象,Rails会经历哪些过程?任何指针都将是巨大的帮助,因为我是新手。 - Tabrez
3
值得一提的是,调用.action_methods方法只会获取类中定义的公共方法(=操作)。由于Rails可以在控制器中没有实际方法的情况下呈现模板,这可能会导致应用程序中的安全漏洞。 - Artur INTECH

12

PostsController.action_methods将返回PostsController中的所有动作,包括继承的动作,这不是我想要的。我找到了PostsController.instance_methods(false),它将返回PostsController的所有实例方法,不包括继承的方法,正是我想要的。


.instance_methods(false),这正是我正在寻找的。 - Amir El-Bashary

6
我曾遇到类似的问题,我的情况是写控制器测试。对于某些控制器的每个操作,我想运行某些基本测试(例如:如果用户未登录则返回401)。action_methods的问题在于它获取了控制器类上的所有方法,即使该方法在config/routes.rb中没有相应路由。
这是我最终采取的解决方案:
def actions_for_controller(controller_path)
  route_defaults = Rails.application.routes.routes.map(&:defaults)
  route_defaults = route_defaults.select { |x| x[:controller] == controller_path }
  route_defaults.map { |x| x[:action] }.uniq
end

如果你想要一个关于PostsController的动作列表,你需要这样说:

actions_for_controller('posts')

您将获得:

['show', 'create', 'edit', 'destroy'] (or whatever)

如果你想获取所有控制器的列表,可以使用以下方式:
def controllers_list
  route_defaults = Rails.application.routes.routes.map(&:defaults)
  files = route_defaults.map { |x| "#{x[:controller]}_controller" }.uniq
  files -= ['_controller']
  files.map { |x| x.split('/').map(&:camelize).join('::').constantize }
end

由于某些原因,我也需要这个,因为.intance_methods(false)仅在应用程序内部工作,但是当我尝试运行rake swagger:docs时,在我的情况下,我得到了一个空数组,而您提到的方法完美地解决了这个问题。 - Amir El-Bashary

5

我们正在清理未使用的控制器和操作。因此,我想创建一个详尽的列表以标记删除。以下是我最终完成的内容:

ApplicationController.descendants.each do |controller|
  puts controller.name
  controller.action_methods.each do |action|
    puts '  ' + action
  end
end

这将提供一个漂亮的列表,其中包含缩进在控制器名称下面的操作。


1
这非常好! - Sarah Marie

0

简而言之

你可以在Rails控制台上检查这个

all_controllers = Rails.application.routes.routes.map do |route|
  route.defaults[:controller]
end.uniq.compact

controllers = all_controllers.reject { |x| x =~ /active_storage|rails|action_mailbox|devise|service_worker/ }
controller_with_actions = controllers.map do |name|
  controller = if name.match?('/') 
      splits = name.split('/')
      prefix = splits[0..-2].map(&:capitalize)
      suffix = [splits[-1]].map(&:classify)
      (prefix + suffix).join('::').pluralize
    else 
      name.classify.pluralize
    end
  verified_controller = controller.concat('Controller').constantize rescue controller.singularize.concat('Controller').constantize rescue next
  [verified_controller.to_s, verified_controller&.public_instance_methods(false)&.to_a] rescue next
end.compact 

# [
#   ["DashboardController", ["index"]],
#   ["UsersController", ["index", "new", "create", "show", "update", "destroy"]]
#   ["Api::V1::StaticController", ["index", "new", "create", "show", "update", "destroy"]]
# ] 

这已经在Rails 7.0和其他低版本中进行了测试。

因为ApplicationController.descendants在第7个版本的Rails中根本不起作用。

或者,您可以使用我在公共GitHub Gist ControllerActionList 链接上的类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接