По историческим причинам мой импортер Google+ импортировал пользователей на уровень доверия 1 (TL1), чтобы дать беженцам из Google+ фору. Они были импортированы с некорректными адресами электронной почты (.invalid), и мы исправляем это, когда они входят в систему и привязываются к своей существующей учётной записи через свой идентификатор Google с помощью аутентификации Google.
Однако теперь моя группа trust_level_1 раздулась тысячами пользователей, которые ещё не подписались на их контент, и если они появятся через несколько лет (как иногда и происходит), нет причин, по которым они должны входить как «Базовые» пользователи; им следует быть «Новыми» пользователями. Этап начальной настройки давно пройден. Я хотел бы понизить тех пользователей, которые никогда не входили в систему, до уровня TL0 до тех пор, пока они не появятся и не начнут изучать сайт, полагаясь на обычный процесс повышения.
Я попробовал сделать это в консоли Rails:
User.where(trust_level: 1) do |u|
u.change_trust_level!(TrustLevel[0]) if !Email.is_valid?(u.primary_email&.email)
u.save!
end
Это заняло несколько минут с полной загрузкой процессора, но ничего не сделало. (Я также пробовал другие варианты на этом пути, например if !u.primary_email.is_valid? и if !Email.is_valid?(u.primary_email), и я знаю, что реальная проблема в том, что я касаюсь Ruby раз в год или около того…) Уверен, что я упускаю что-то очевидное для других. Буду признателен за подсказку, если кто-нибудь сможет её придумать!
User.where(trust_level: 1).each do |u|
u.change_trust_level!(TrustLevel[0]) if !Email.is_valid?(u.primary_email&.email)
u.save!
end
Это привело к падению моего сайта, когда он работал с большой нагрузкой по вводу-выводу, но при этом никаких изменений не произошло. Значит, я всё ещё делаю что-то не так, и мне немного не по себе продолжать экспериментировать с этим на рабочей системе.
Я понял, что is_valid? означает не то, что я думал. Теперь я ищу буквально строку, и проверил это, выведя адрес электронной почты. Также я заметил, что change_trust_level уже сохраняет пользователя, поэтому мне не нужно делать это дважды.
User.where(trust_level: 1).each do |u|
if u.primary_email&.email.end_with?(".invalid")
u.change_trust_level!(TrustLevel[0])
end
end
Но количество пользователей с trust_level_1 всё равно не меняется, значит, я всё ещё упускаю что-то важное.
Можно ли в этом случае использовать массовую административную задачу?
Хотя я только что протестировал ваш второй код, и он сработал у меня для тестового пользователя (email: test_thirteen@here.invalid, был TL1, теперь TL0).
User.where(trust_level: 1).each do |u|
if u.primary_email&.email.end_with?(".invalid")
u.change_trust_level!(TrustLevel[0])
end
end
Я вручную повысил в ранге некоторых пользователей, которые были хорошо известны существующим участникам сообщества, поэтому массовый пересчёт не дал бы желаемого результата. (Я нашёл ту страницу во время поиска.)
То последнее действие на самом деле работало, как и у вас. Проблема была в том, что я смотрел на страницу обзора групп по адресу /g, обновлял её и искал, чтобы количество участников группы trust_level_1 уменьшилось. Пока я запускал процесс, отображаемое число не менялось, поэтому я остановил его с помощью Ctrl-C. Утром на той же странице в группе trust_level_1 теперь отображается на тысячи участников меньше. Теперь я понял, что это число кэшируется, и мне следовало было нажать на эту карточку и посмотреть на счётчик на странице /g/trust_level_1.
Теперь я понимаю, что мне следовало выполнить Group.ensure_consistency!, чтобы исправить счётчики на этой странице.
Вот всё это, выражено более компактно и идиоматично:
User.where(trust_level: 1).each do |u|
u.change_trust_level!(TrustLevel[0]) if u.primary_email&.email.end_with?(".invalid")
end
Group.ensure_consistency!
Случилось так, что у меня есть запись о нескольких пользователях только с некорректным адресом электронной почты, которые, судя по записям, фактически использовали сайт и выполнили требования для TL1. Я не знаю, как это произошло, но сейчас мне это не важно. Меня волнует лишь то, чтобы обработать основную массу из тысяч пользователей, не беспокоясь о нескольких исключениях. Если это случится с кем-то ещё, вот что сработало у меня:
User.where(trust_level: 1).each do |u|
begin
u.change_trust_level!(TrustLevel[0]) if u.primary_email&.email.end_with?(".invalid")
rescue
end
end
Group.ensure_consistency!
def import_google_user(id, name)
if !@emails[id].present?
google_user_info = UserAssociatedAccount.find_by(provider_name: 'google_oauth2', provider_uid: id.to_i)
if google_user_info.nil?
# создаём нового пользователя Google в системе; ожидаем, что этот пользователь
# объединится при последующем входе с помощью аутентификации Google
# Обратите внимание: поскольку адрес электронной почты не включён в данные G+, мы
# не знаем, есть ли у него уже другая учётная запись, ещё не связанная
# с Google OAuth2. Если они не вошли в систему, к их учётной записи будет
# привязан адрес @gplus.invalid
email = "#{id}@gplus.invalid"
Это означает, что меня интересует только primary_email, и мне не нужно начинать с UserEmail. Я уже шёл по этому пути, прежде чем понял, что любые изменения, которые сделают этот адрес не primary_email, заставят меня вообще не захотеть их менять. Поэтому здесь речь идёт именно о пользователях, чей единственный (и, следовательно, основной) адрес электронной почты буквально заканчивается на .invalid — именно так я и поступил.