0

我正在尝试实现第一段是配置文件别名或 ID 的路由:

resources :profiles, :path => '' do
    ...
end

而且我需要验证其他(更高)路线的第一段尚未采用别名。我现在拥有的是:

validates :alias, :exclusion => {:in => Rails.application.routes.routes.map{|r| r.path.spec.to_s.split('(').first.split('/').second}.compact.uniq },
                  ....

在开发中一切正常。在生产Rails.application.routes.routes.map...中返回空数组。但仅在模型内部验证,如果我将它放在某个地方只是为了测试它会按预期返回所有路线的第一段数组。我做错了什么或者也许有更好的解决方案?

4

1 回答 1

1

我猜你有时间问题。当您的模型在生产模式下加载时,您的路由表Rails.application.routes可能尚未构建;但是,在开发模式下,您的模型可能会在每次请求时重新加载,因此Rails.application.routes将在您的模型加载和validates调用时填充:

validates :alias, :exclusion => { :in => Rails.application.routes.routes.map { ... } }

被执行。

一个简单的解决方案是切换到验证方法:

class Model < ActiveRecord::Base
    validate :alias_isnt_a_route, :if => :alias_changed?

private

    def alias_isnt_a_route
        routes = Rails.application.routes.routes.map { ... }
        if(routes.include?(alias))
            errors.add(:alias, "Alias #{alias} is already used for a route")
    end
end

这样,您在Rails.application.routes需要检查别名之前不会查看,到那时,路线将已加载。当然,如果您愿意,您可以缓存路由前缀列表。

您还需要在应用程序的初始化阶段添加一些完整性检查。您的生产环境中的某个人可能会在您在开发'pancakes'时添加路线时添加,例如,作为他们的别名/pancakes:您的验证将错过这个新的冲突。像这样简单的东西:

config.after_initialize do
    Rails.application.reload_routes! # Make sure the routes have been loaded.
    # Compare all the existing aliases against the route prefixes and raise an
    # exception if there is a conflict.
end

在你的config/application.rb就足够了。

于 2012-06-04T17:23:36.517 回答