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

发布于 2024-12-25 01:43:48 字数 479 浏览 1 评论 0原文

我想确保用户无法创建与我现有的路由冲突的用户名。我还希望能够拒绝我可能定义的未来路线。我正在考虑像这样完成此操作:

在模型中:

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

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

I want to ensure users can't create usernames that clash with my existing routes. I would also like the ability to deny future routes I may define. I am thinking of accomplishing this like so:

In the model:

class User < ActiveRecord::Base
  @@invalid_usernames = %w()

  cattr_accessor :invalid_usernames

  validates :username, :exclusion { :in => @@invalid_usernames }
end

In some initializer:

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

Is this "Rails way"? Is there a better way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

无戏配角 2025-01-01 01:43:48

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

app/models/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

config/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

注释

我最初尝试在 after_initialize 期间设置 User.invalid_usernames 但发现它需要在 to_prepare 期间设置(即在开发模式下每个请求之前,以及在生产模式中的第一个请求),因为模型会在每次请求之前在开发过程中重新加载,并且原始设置会丢失。

然而,我after_initialize期间设置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 而使用 class_attribute

我也选择使用User.invalid_usernamesSet 而不是数组,因为不需要存储和与重复项进行比较,而且这是一个透明的更改。

我还更改为 Ruby 1.9 哈希语法(:

Here's my own answer, tested and working with Rails 3.1.3 and Ruby 1.9.3

app/models/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

config/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

Notes

I initially tried setting User.invalid_usernames during after_initialize but found it needs to be set during to_prepare (i.e. before each request in development mode, and before the first request in production mode) since the models are reloaded in development before each request and the original setting is lost.

I am however also setting User.invalid_usernames during after_initialize because the routes don't seem to be available during to_prepare when running in the test environment. Another workaround I tried for this, which does work, is to force load the routes during 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

I like this because it's DRY and easy to read. But I'm wary of reloading the routes on every request, even if it is only in development mode. I'd rather use something a little harder to read if it means I fully understand the impact. Open to criticisms!

I also ditched cattr_accessor for class_attribute when I found out the former applies to the entire class hierarchy (i.e. changing its value on a subclass would affect the superclass)

I also chose to use a Set for User.invalid_usernames instead of an array as there's no need to store and compare against dupes and it was a transparent change.

I also changed to Ruby 1.9 hash syntax (:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文