我希望确保用户无法创建与我现有路由冲突的用户名。我也希望能够拒绝我可能定义的未来路线。我想像这样完成这个:
在模型中:
class User < ActiveRecord::Base
@@invalid_usernames = %w()
cattr_accessor :invalid_usernames
validates :username, :exclusion { :in => @@invalid_usernames }
end
在某些初始值设定项中:
User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq
这是&#34; Rails方式&#34;?还有更好的方法吗?
答案 0 :(得分:6)
这是我自己的答案,测试并使用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
期间设置after_initialize
,但发现需要在to_prepare
期间设置(即在开发模式下每个请求之前,之前生产模式中的第一个请求)因为在每个请求之前模型都在开发中重新加载并且原始设置丢失。
但是,User.invalid_usernames
期间我还设置after_initialize
,因为在测试环境中运行时,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
我喜欢这个,因为它很干并且易于阅读。但我担心在每个请求上重新加载路由,即使它只是在开发模式下。如果这意味着我完全理解其影响,我宁愿使用 little 更难读的东西。接受批评!
当我发现前者适用于整个类层次结构时(即更改其在子类上的值会影响超类)时,我也放弃cattr_accessor
class_attribute
我还选择使用Set
代替User.invalid_usernames
代替数组,因为不需要存储和比较欺骗,这是一个透明的变化。
我也改为Ruby 1.9哈希语法(: