Rails 验证以确保用户名不会与现有路由冲突?
我想确保用户无法创建与我现有的路由冲突的用户名。我还希望能够拒绝我可能定义的未来路线。我正在考虑像这样完成此操作:
在模型中:
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是我自己的答案,经过测试并使用 Rails 3.1.3 和 Ruby 1.9.3
app/models/user.rb
config/application.rb
注释
我最初尝试在
after_initialize 期间设置
但发现它需要在User.invalid_usernames
to_prepare
期间设置(即在开发模式下每个请求之前,以及在生产模式中的第一个请求),因为模型会在每次请求之前在开发过程中重新加载,并且原始设置会丢失。然而,我也在
after_initialize
期间设置User.invalid_usernames
,因为路线在to_prepare
期间似乎不可用在测试环境中运行时。我尝试过的另一种解决方法确实有效,那就是在to_prepare
期间强制加载路由:我喜欢这个,因为它干燥且易于阅读。但我对每次请求时重新加载路由都持谨慎态度,即使只是在开发模式下也是如此。如果我完全理解其影响,我宁愿使用一些难以阅读的内容。虚心接受批评!
当我发现前者适用于整个类层次结构(即更改子类上的值会影响超类)时,我也放弃了
cattr_accessor
而使用class_attribute
我也选择使用
User.invalid_usernames
的Set
而不是数组,因为不需要存储和与重复项进行比较,而且这是一个透明的更改。我还更改为 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
config/application.rb
Notes
I initially tried setting
User.invalid_usernames
duringafter_initialize
but found it needs to be set duringto_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
duringafter_initialize
because the routes don't seem to be available duringto_prepare
when running in the test environment. Another workaround I tried for this, which does work, is to force load the routes duringto_prepare
: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
forclass_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
forUser.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 (: