Db.rake не работает для моего плагина проверки email при пересборке приложения

Я разработал свой первый плагин, который при регистрации проверяет одноразовые адреса электронной почты через внешний API.

Пока работает как задумано…

Но теперь, похоже, при пересборке/обновлении приложения в файле db.rake возникла какая-то проблема:

rails aborted!
ActiveRecord::RecordInvalid: Validation failed: Email is not allowed from that email provider. Please use another email address.
(eval):39:in `block (2 levels) in run_file'
/src/lib/tasks/db.rake:222:in `block in <main>'
bin/rails:17:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

Вероятно, потому что эта строка в db.rake пытается загрузить некоторые данные, но не может, так как мой плагин проверяет недопустимые адреса электронной почты и вмешивается:

SeedFu.seed(SeedHelper.paths, SeedHelper.filter)

Однако в том же файле я обнаружил, что плагин может исключать определенные файлы с начальными данными из выполнения:

  def self.filter
    # Allows a plugin to exclude any specified seed data files from running
    DiscoursePluginRegistry.seedfu_filter.any? ?
      /^(?!.*(#{DiscoursePluginRegistry.seedfu_filter.to_a.join("|")})).*$/ : nil
  end
end

Но как это сделать? Есть ли примеры плагинов, которые так делают? Или мне нужно что-то другое, чтобы преодолеть эту проблему? Заранее большое спасибо за любую помощь.

Сообщение об ошибке, которое вы получаете, не содержит имени файла данных для заполнения (seed), вызывающего проблему?

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

after_initialize do
  register_seedfu_filter "00X_seed_filename"
  ...
end

Отлично.

Что касается возникшей ошибки, у меня больше нет информации.

== 20200819021210 AddUserSelectableColumnToColorSchemes: migrating ============
-- add_column(:color_schemes, :user_selectable, :bool, {:null=>false, :default=>false})
   -> 0.0062s
== 20200819021210 AddUserSelectableColumnToColorSchemes: migrated (0.0100s) ===

== 20200819203846 AddColorSchemeIdToUserOptions: migrating ====================
-- add_column(:user_options, :color_scheme_id, :integer)
   -> 0.0035s
== 20200819203846 AddColorSchemeIdToUserOptions: migrated (0.0051s) ===========

== 20200820174703 AddPartialTargetIdIndexToReviewables: migrating =============
-- add_index(:reviewables, [:target_id], {:where=>"target_type = 'Post'", :algorithm=>:concurrently, :name=>"index_reviewables_on_target_id_where_post_type_eq_post"})
   -> 0.0079s
== 20200820174703 AddPartialTargetIdIndexToReviewables: migrated (0.0091s) ====

rails aborted!
ActiveRecord::RecordInvalid: Validation failed: Email is not allowed from that email provider. Please use another email address.
(eval):39:in `block (2 levels) in run_file'
/src/lib/tasks/db.rake:222:in `block in <main>'
bin/rails:17:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

Есть ли возможность выяснить/отладить, какой именно файл вызывает проблему в моём небольшом новом плагине? Я подозреваю, что это что-то, связанное с созданием пользователей и их электронными почтами.

Вот дополнительная отладочная информация:

Summary

terrapop@terrapop:/var/www/discourse$ d/rails --trace db:migrate RAILS_ENV=development
** Invoke db:migrate (first_time)
** Invoke db:load_config (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute db:load_config
** Invoke environment
** Invoke set_locale (first_time)
** Execute set_locale
** Execute db:migrate
** Invoke db:_dump (first_time)
** Execute db:_dump
** Invoke db:structure:dump (first_time)
** Invoke db:load_config
** Execute db:structure:dump
rails aborted!
ActiveRecord::RecordInvalid: Validation failed: Email is not allowed from that email provider. Please use another email address.
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:80:in raise_validation_error' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/validations.rb:53:in save!’
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in block in save!' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in block in with_transaction_returning_status’
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:278:in transaction' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in transaction’
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in with_transaction_returning_status' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:318:in save!’
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/suppressor.rb:48:in save!' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:635:in block in update!’
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:375:in block in with_transaction_returning_status' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:278:in transaction’
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in transaction' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:366:in with_transaction_returning_status’
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/persistence.rb:633:in update!' (eval):39:in block (2 levels) in run_file’
/usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:46:in eval' /usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:46:in block (2 levels) in run_file’
/usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:58:in block in open' /usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:57:in open’
/usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:57:in open' /usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:36:in block in run_file’
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in block in transaction' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:280:in block in within_new_transaction’
/usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in block (2 levels) in synchronize' /usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in handle_interrupt’
/usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in block in synchronize' /usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in handle_interrupt’
/usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in synchronize' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:278:in within_new_transaction’
/usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:280:in transaction' /usr/local/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.2/lib/active_record/transactions.rb:212:in transaction’
/usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:35:in run_file' /usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:26:in block in run’
/usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:25:in each' /usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu/runner.rb:25:in run’
/usr/local/lib/ruby/gems/2.6.0/gems/seed-fu-2.3.9/lib/seed-fu.rb:29:in seed' /src/lib/tasks/db.rake:222:in block in ’
/usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/task.rb:281:in block in execute' /usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/task.rb:281:in each’
/usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/task.rb:281:in execute' /usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/task.rb:219:in block in invoke_with_call_chain’
/usr/local/lib/ruby/2.6.0/monitor.rb:235:in mon_synchronize' /usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/task.rb:199:in invoke_with_call_chain’
/usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/task.rb:188:in invoke' /usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/application.rb:160:in invoke_task’
/usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/application.rb:116:in block (2 levels) in top_level' /usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/application.rb:116:in each’
/usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/application.rb:116:in block in top_level' /usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/application.rb:125:in run_with_threads’
/usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/application.rb:110:in top_level' /usr/local/lib/ruby/gems/2.6.0/gems/railties-6.0.3.2/lib/rails/commands/rake/rake_command.rb:23:in block in perform’
/usr/local/lib/ruby/gems/2.6.0/gems/rake-13.0.1/lib/rake/application.rb:186:in standard_exception_handling' /usr/local/lib/ruby/gems/2.6.0/gems/railties-6.0.3.2/lib/rails/commands/rake/rake_command.rb:20:in perform’
/usr/local/lib/ruby/gems/2.6.0/gems/railties-6.0.3.2/lib/rails/command.rb:48:in invoke' /usr/local/lib/ruby/gems/2.6.0/gems/railties-6.0.3.2/lib/rails/commands.rb:18:in
/usr/local/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.8/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in require' /usr/local/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.8/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in block in require_with_bootsnap_lfi’
/usr/local/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.8/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in register' /usr/local/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.8/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in require_with_bootsnap_lfi’
/usr/local/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.8/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in require' /usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/dependencies.rb:324:in block in require’
/usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/dependencies.rb:291:in load_dependency' /usr/local/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/dependencies.rb:324:in require’
bin/rails:17:in `’
Tasks: TOP => db:migrate

Обновление:

Проблема найдена и исправлена:

def seed_primary_email
  UserEmail.seed do |ue|
    ue.id = DiscourseNarrativeBot::BOT_USER_ID
    ue.email = "discobot_email"
    ue.primary = true
    ue.user_id = DiscourseNarrativeBot::BOT_USER_ID
  end
end

в файле discourse/plugins/discourse-narrative-bot/db/fixtures/001_discobot.rb вызывала проблему.

Добавление register_seedfu_filter "001_discobot" решило проблему.

Спасибо @blake за то, что указали мне верное направление. Теперь всё работает.

Последний вопрос: на будущее… как зарегистрировать несколько фильтров? Например, так?

register_seedfu_filter "001_discobot|002_xxxxxx|003_yyyy"

Возможно, существует способ сделать это в одну строку, но для регистрации нескольких фильтров можно использовать несколько строк

register_seedfu_filter "001..."
register_seedfu_filter "002..."

Однако к этому API seedfu_filter нужно относиться с крайней осторожностью!

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

Возможно, вы могли бы исключить пользователей с id < 0, чтобы ваш плагин не проверял электронные адреса для system и discobot. Для этого можно использовать метод .human_users.

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

@blake

Я добавил следующие строки:

return unless value.present?
return unless defined? record.id
return unless record.password_validation_required?

в дополнение к:

return unless record.should_validate_email_address?

Так как disco_bot уже создан с id в db.migrate, теперь это работает.

Кроме того, мне пришлось добавить валидатор password_validation_required, иначе плагин срабатывал также при входе в систему и при изменении аватара пользователя.

Теперь это действительно привязано только к форме регистрации, и фильтр seedfu больше не требуется.