LINUX.ORG.RU

Rails: решение проблемы «unique username»


0

1

Известно что validates_unique_of не учитывает race conditions о чём сказано в документации. Там же рекомендуется использовать UNIQUE в DB при котором в случае дублирования поля будет InvalidStatement exception. Однако хотелось бы поймать его внутри model и добавить соответствующее сообщение errors.add(:username, «Username already in use») так же внутри model. Естественно напрашивается вариант override save, save! типа:

def save
super
rescue
errors.add(:username, "Username already in use")
end

Но выглядит не красиво, как хак. Может есть получше способ?


Вам шашечки или ехать?

Сабж.

И ещё: сообщения об ошибках лучше держать в config/locales, а save! перегружать не нужно — он специально задуман для бросков исключений.

Apple-ch ★★
()
Ответ на: комментарий от psp13

не понял что вы имеете ввиду

Ну смотри: для реализации этой функциональности ты добавил индекс в миграцию (одна строчка), rescue и добавление ошибки в модель (2 строчки). Итого три строчки, три! Даже в локаль ничего добавлять не нужно, там уже есть нужная месага!

Ты не манкипатчил рельсы, не использовал никакие хуки, хаки, ничего никуда не инжектил, даже eval не задействовал нигде! Просто три грёбаные строчки.

О какой красоте, мерзоте, чистоте, говноте тут может идти речь? Ты над каждой мелочью так заморачиваешься, что ли? Короче, не страдай хернёй, иначе работа никогда не будет сделана.

Apple-ch ★★
()
Ответ на: комментарий от Apple-ch

Кстати говоря если в таблице 2 UNIQUE (например типичная ситуация с users: username, email) то придётся парсить текст StatementInvalid что бы найти какой из них вызвал ошибку и дать соответствующее сообщение. Вот такой код почему то не работает:

def save(*)
super
rescue ActiveRecord::StatementInvalid
if User.find_by_username(@username)
  # username error
elsif User.find_by_email(@email)
  # email error
else
  raise
end
end

Кто скажет почему? Ну и вообще в таком большом фреймворке могли бы уж предусмотреть встроенную поддержку для этого. Хорошо что я внимательно отношусь к таким деталям, а то как вы говорите «над каждой мелочью не надо заморачиваться» - так вот и возникает быдло код, во многих туториалах об этом вообще ни слова. А в продакшн это грозит огромными проблемами.

psp13
() автор топика
Ответ на: комментарий от psp13

> Вот такой код почему то не работает:

А инстанс переменные @email и @username где вообще инициализируются? Телепаты ещё из летних отпусков не вышли ))

в таком большом фреймворке могли бы уж предусмотреть встроенную поддержку для этого

Большой репозиторий большого фреймворка находится по адресу https://github.com/rails/rails

Как форкать и делать пулл реквесты, написано где-то здесь: help.github.com

Give back to opensource!

в продакшн это грозит огромными проблемами

Не грозит. Сколько юзеров у тебя будет регацца в секунду? Даже если такая ситуация возникнет, один из них увидит 500.html — велика беда, тоже мне! Ты лучше эту страницу покреативней нарисуй, чтобы повеселить юзера, имя которого уже занято :)

Apple-ch ★★
()
Ответ на: комментарий от Apple-ch

Instance переменные разве удаляются после save? Они же изначально в объекте установлены. Форкать не обязательно, думаю можно обойтись простеньким плагином. В продакшн не грозит только если не забыть UNIQUE в DB поставить. А вот если забыть будет 2 юзера с одним и тем же username. Есть ли ещё тёмные места типа этого - вот что беспокоит.

psp13
() автор топика
Ответ на: комментарий от psp13

> Instance переменные разве удаляются после save? Они же изначально в объекте установлены

Здрасьте, приехали… Всё-таки я был неправ: такими вещами озадачиваться безусловно надо, но прежде всего необходимо выучить, блджад, матчасть!

только если не забыть UNIQUE в DB поставить

Ну знаешь ли, индекс так или иначе надо ставить на это поле, рельсы этого за тебя не сделают. А добавляя индекс, ты скорее всего вспомнишь про юник. Кроме того, генераторы существующих движков это учитывают, по крайней мере devise.

Apple-ch ★★
()
Ответ на: комментарий от Apple-ch

И всё же можно подробнее, куда исчезает Instance переменная которая была до save. По моему такое будет работать:

x = User.new(params[:user])
x.save
p x.username

Очень обманчиво выглядит validates_uniqueness_of. Кстати вот нахрена model и migration. А как же DRY? Вот лучше бы как в django всё в одном файле. Тогда бы и в БД можно было бы на основе validates_uniqueness_of ставить UNIQUE автоматически что бы не забыть случайно.

psp13
() автор топика
Ответ на: комментарий от psp13

И всё же можно подробнее, куда исчезает Instance переменная которая была до save

pry(main)> x = User.new(:username => "Apple-ch")
pry(main)> x.username
=> "Apple-ch"
pry(main)> x.instance_variable_get("@username")
=> nil
pry(main)> require "trollface.rb"

Внезапно, да? Меж тем, ларчик открывается просто.

Заботай эктив модел, потом переходи к порабощению миграций.

лучше бы как в django всё в одном файле

А ещё лучше тебе будет перейти на джанго :)

что бы не забыть случайно

Случайно такие вещи забывают те, кто не читает доку, гайды, в исходники не смотрит, да ещё и «чтобы» пишет раздельно…

Apple-ch ★★
()
Ответ на: комментарий от Apple-ch

так в чём отличие от x.instance_variable_get(«@username») от x.username? Почему же тогда x.username у меня возвращает «Apple-ch»? (В script/console)

psp13
() автор топика
Ответ на: комментарий от psp13

Последний раз тебе отвечаю

Отличие в том, первое явно берёт значение инстанс переменной, а второе посылает объекту сообщение, что обычно служит для вызова метода, хотя может вообще быть произвольной строкой. У нас — последний случай. В объекте вызывается method_missing, вся дальнейшая магия происходит в нём. Таким образом, чтение атрибута — это как бы вызов публичного метода, никакие инстанс переменные не используются!

В твоём примере с перегрузкой save должно быть как-то так:

def save(*)
super
rescue ActiveRecord::StatementInvalid
  if User.find_by_username(username)
    # username error
  elsif User.find_by_email(email)
    # email error
  else
    raise
  end
end

А теперь иди и впредь не греши^W^W^WRTFM.

Apple-ch ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.