poo*_*nza 7 validation activerecord ruby-on-rails activemodel ruby-on-rails-3
我想确保用户无法创建与我现有路由冲突的用户名.我也希望能够拒绝我可能定义的未来路线.我想要完成这样的事情:
在模型中:
class User < ActiveRecord::Base
@@invalid_usernames = %w()
cattr_accessor :invalid_usernames
validates :username, :exclusion { :in => @@invalid_usernames }
end
Run Code Online (Sandbox Code Playgroud)
在一些初始化程序中:
User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq
Run Code Online (Sandbox Code Playgroud)
这是"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
Run Code Online (Sandbox Code Playgroud)
配置/ 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
Run Code Online (Sandbox Code Playgroud)
我最初尝试设置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
Run Code Online (Sandbox Code Playgroud)
我喜欢这个,因为它很干并且易于阅读.但我担心在每个请求上重新加载路由,即使它只是在开发模式下.我宁愿使用的东西有点困难,如果这意味着我完全理解阅读的冲击.开放批评!
当我发现前者适用于整个类层次结构时(也就是更改它在子类上的值会影响超类)时我也抛弃cattr_accessor了class_attribute
我还选择使用Setfor User.invalid_usernames而不是数组,因为不需要存储和比较dupes,这是一个透明的变化.
我还改为Ruby 1.9哈希语法(: