Проверка Rails, чтобы убедиться, что имя пользователя не конфликтует с существующим маршрутом? - PullRequest
7 голосов
/ 03 января 2012

Я хочу убедиться, что пользователи не могут создавать имена пользователей, которые конфликтуют с моими существующими маршрутами.Мне также хотелось бы иметь возможность отрицать будущие маршруты, которые я могу определить.Я думаю о том, чтобы выполнить это следующим образом:

В модели:

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 way"?Есть ли лучший способ?

1 Ответ

6 голосов
/ 03 января 2012

Вот мой собственный ответ, проверенный и работающий с 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

Примечания

Сначала я попытался установить 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

Мне нравится это, потому что это СУХОЙ и легко читаемый.Но я опасаюсь перезагружать маршруты при каждом запросе, даже если он только в режиме разработки.Я бы предпочел использовать что-то на немного сложнее для чтения, если это означает, что я полностью понимаю влияние.Открыто для критики!

Я также отказался от cattr_accessor для class_attribute, когда узнал, что первое относится ко всей иерархии классов (т. Е. Изменение его значения в подклассе повлияет на суперкласс)

Я также решил использовать Set для User.invalid_usernames вместо массива, поскольку нет необходимости хранить и сравнивать с дупсами, и это было прозрачное изменение.

Я также изменил синтаксис хэша Ruby 1.9 (:

...