在rails中,我可以通过controller_name获得当前控制器的名称,通过调用action_name获得当前操作。我正在寻找类似的运行时反射获取以下内容:
- 应用程序中所有控制器的列表。
- 给定控制器中所有动作的列表。
例如,我有一个Product控制器,它有"添加"one_answers"编辑"动作。我能否以编程方式拉出这些操作的名称,以向用户显示支持哪些操作?
我研究了一个调用ActionController::Routing::Routes.named_routes.routes.each
的方法但我不能让它工作。当我使用它时,我得到了未初始化的常量ActionDispatch::Routing::Routes
错误。
如果有任何好的教程或文档,可以帮助我了解如何rails反射的能力。我搜索了一下,但我得到的大多是活跃的记录反映相关的博客。我正在寻找一些东西,让我在运行时获得控制器和动作/方法的信息。
谢谢你,
Tabrez
获取控制器类列表的最简单方法是:
ApplicationController.descendants
然而,由于类是惰性加载的,因此您需要在执行此操作之前立即加载所有类。请记住,此方法将花费时间,并且会减慢引导速度:
Rails.application.eager_load!
获取控制器中的所有动作,使用action_methods
PostsController.action_methods
这将返回一个Set
,其中包含控制器中所有方法的列表,这些方法是"动作"(使用相同的逻辑Rails用于决定方法是否为路由的有效动作)。
PostsController.action_methods
将返回PostsController
的所有动作,包括继承,这不是我想要的,我发现PostsController.instance_methods(false)
,这将返回PostsController
的所有实例方法,而不包括继承,正是我想要的。
我有一个类似的问题;在我的例子中,它是用于编写控制器测试。我希望对某些控制器的每个操作运行某些基本测试(例如,如果用户未登录,则返回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
我们正在清理未使用的控制器和操作。所以我想创建一个详尽的列表来标记删除。这是我最后做的
ApplicationController.descendants.each do |controller|
puts controller.name
controller.action_methods.each do |action|
puts ' ' + action
end
end
这提供了一个很好的列表,在控制器名称下缩进操作。
TLDR
你在你的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
在rails的第7个版本中根本不起作用。
或者你可以使用use my class on my public github gist ControllerActionList link