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

时间:2012-01-02 23:20:36

标签: ruby-on-rails ruby-on-rails-3 validation activerecord activemodel

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

在模型中:

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;?还有更好的方法吗?

1 个答案:

答案 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哈希语法(: