Railsvalidation以确保用户名不会与现有路由冲突?

我想确保用户无法创建与我现有路由冲突的用户名。 我也希望能够拒绝我可能定义的未来路线。 我想要完成这样的事情:

在模型中:

class User  @@invalid_usernames } end 

在一些初始化程序中:

 User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq 

这是“Rails方式”吗? 有没有更好的办法?

这是我自己的答案,测试和使用Rails 3.1.3和Ruby 1.9.3

应用程序/模型/ user.rb

 class User < ActiveRecord::Base class_attribute :invalid_usernames self.invalid_usernames = Set.new %w() validates :username, presence: true, uniqueness: { case_sensitive: false }, exclusion: { in: lambda { self.invalid_usernames }} end 

配置/ application.rb中

 [:after_initialize, :to_prepare].each do |hook| config.send(hook) do User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq end end 

笔记

我最初尝试在User.invalid_usernames期间设置User.invalid_usernames ,但发现它需要在to_prepare期间(即在开发模式中的每个请求之前,以及生产模式中的第一个请求之前)设置,因为模型在每个请求和原始设置之前在开发中重新加载迷路了。

但是,我也在 User.invalid_usernames期间设置User.invalid_usernames ,因为在测试环境中运行时, to_prepare期间似乎没有可用的路由。 我为此尝试的另一个解决方法是,在to_prepare期间强制加载路由:

 config.to_prepare do Rails.application.reload_routes! User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq end 

我喜欢这个,因为它很干并且易于阅读。 但我担心在每个请求上重新加载路由,即使它只是在开发模式下。 如果这意味着我完全理解其影响,我宁愿使用更难读的东西。 开放批评!

当我发现前者适用于整个类层次结构时(即更改其在子类上的值会影响超类)时,我也为cattr_accessor抛弃了cattr_accessor

我还选择使用Set for User.invalid_usernames而不是数组,因为不需要存储和比较dupes,这是一个透明的变化。

我也改为Ruby 1.9哈希语法(: