使用 CanCan 和 Role gems,仍然需要一种方法来检查 Route 并查看“current_user”是否有权根据他们的角色访问该 Route - 然后根据该角色显示/隐藏。
这可以节省用户点击事物并被告知他们看不到它 - 或者我们必须编写每个项目的“如果”逻辑,指定哪些角色可以看到哪些列表项目(客户将定期更改,因为角色被更改/改进)围绕一个菜单中的每个链接(考虑一个引导菜单,其中包含 50 多个嵌套在具有 html 格式的组中的项目等),这太疯狂了。
如果我们必须在每个菜单项周围放置 if 逻辑,让我们通过检查我们已经在能力文件中定义的角色/权限来对每个项目使用完全相同的逻辑。
但是在我们的菜单列表中,我们有路由助手——而不是“控制器/方法”信息,那么如何测试用户点击为每个链接中的“路径”指定的控制器操作的能力?
获取路径的控制器和方法(操作)(我的示例使用 'users_path' 路由助手)...
Rails.application.routes.recognize_path(app.users_path)
=> {:controller=>"users", :action=>"index"}
只获取控制器名称
Rails.application.routes.recognize_path(app.users_path)[:controller]
=> "users"
能力使用模型进行分解,因此从控制器名称转换为其模型(假设使用默认命名)......
Rails.application.routes.recognize_path(app.users_path)[:controller].classify
=> "User"
只获取动作名称
Rails.application.routes.recognize_path(app.users_path)[:action]
=> "index"
并且由于“可以吗?” 方法需要一个符号来表示动作,而常量则需要模型,对于每个菜单项,我们得到这个:
path_hash = Rails.application.routes.recognize_path(app.users_path)
model = path_hash[:controller].classify.constantize
action = path_hash[:action].to_sym
然后使用我们现有的 Abilty 系统检查 current_user 是否可以访问它,我们必须将操作作为符号传递,将模型作为常量传递,所以...
<% if can? action model %>
<%= link_to "Users List", users_path %>
<% end %>
现在我们可以更改谁可以从技能文件中查看此资源和链接,而无需再次弄乱菜单。但为了使这更清晰,我在应用程序控制器中提取了每个菜单项的查找:
def get_path_parts(path)
path_hash = Rails.application.routes.recognize_path(path)
model_name = path_hash[:controller].classify.constantize
action_name = path_hash[:action].to_sym
return [model_name, action_name]
end
helper_method :get_path_parts
...所以我可以在视图中执行此操作(为简单起见,我从链接中取出了所有 html 格式):
<% path_parts = get_path_parts(users_path); if can?(path_parts[1], path_parts[0]) %>
<%= link_to "Users Listing", users_path %>
<% end %>
...并且为了避免整天键入这些每个菜单项的 if-wraps,我使用正则表达式查找/替换与捕获和通配符将其包装在一次通过的菜单项列表中的每个列表项周围。
这远非理想,我可以做更多的事情来让它变得更好,但我没有空闲时间来编写 Role/CanCan 系统的其余部分。我希望这部分可以帮助某人。