diff --git a/NEWS.md b/NEWS.md index 2da0cf764..dd4059795 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,7 @@ Unreleased * Introduce `suspenders:factories` generator * Introduce `suspenders:advisories` generator * Introduce `suspenders:styles` generator +* Introduce `suspenders:jobs` generator 20230113.0 (January, 13, 2023) diff --git a/README.md b/README.md index c1105ad55..5c01735ee 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,15 @@ Also creates additional stylesheets if using PostCSS. [cssbundling-rails]: https://github.com/rails/cssbundling-rails [modern-normalize]: https://github.com/sindresorhus/modern-normalize + +### Jobs + +Installs [Sidekiq][] for background job processing and configures ActiveJob for job queueing. + +`bin/rails g suspenders:jobs` + + [Sidekiq]: https://github.com/sidekiq/sidekiq + ## Contributing See the [CONTRIBUTING] document. diff --git a/lib/generators/suspenders/jobs_generator.rb b/lib/generators/suspenders/jobs_generator.rb new file mode 100644 index 000000000..d4fa42c53 --- /dev/null +++ b/lib/generators/suspenders/jobs_generator.rb @@ -0,0 +1,36 @@ +module Suspenders + module Generators + class JobsGenerator < Rails::Generators::Base + source_root File.expand_path("../../templates/active_job", __FILE__) + desc "Installs Sidekiq for background job processing." + + def add_sidekiq_gem + gem "sidekiq" + Bundler.with_unbundled_env { run "bundle install" } + end + + def initialize_active_job + copy_file "active_job.rb", "config/initializers/active_job.rb" + end + + def configure_active_job + environment "config.active_job.queue_adapter = :sidekiq" + environment "config.action_mailer.deliver_later_queue_name = nil" + environment "config.action_mailbox.queues.routing = nil" + environment "config.active_storage.queues.analysis = nil" + environment "config.active_storage.queues.purge = nil" + environment "config.active_storage.queues.mirror = nil" + environment "config.active_job.queue_adapter = :inline", env: "test" + end + + def configure_procfile + if Rails.root.join("Procfile.dev").exist? + append_to_file "Procfile.dev", "worker: bundle exec sidekiq" + else + say "Add default Procfile.dev" + create_file "Procfile.dev", "worker: bundle exec sidekiq" + end + end + end + end +end diff --git a/lib/generators/templates/active_job/active_job.rb b/lib/generators/templates/active_job/active_job.rb new file mode 100644 index 000000000..ce7cbd7c2 --- /dev/null +++ b/lib/generators/templates/active_job/active_job.rb @@ -0,0 +1,14 @@ +require "active_job/logging" +require "active_job/log_subscriber" + +ActiveSupport::Notifications.unsubscribe("enqueue.active_job") + +module ActiveJob + module Logging + class EnqueueLogSubscriber < LogSubscriber + define_method :enqueue, instance_method(:enqueue) + end + end +end + +ActiveJob::Logging::EnqueueLogSubscriber.attach_to(:active_job) diff --git a/test/generators/suspenders/jobs_generator_test.rb b/test/generators/suspenders/jobs_generator_test.rb new file mode 100644 index 000000000..57de3553e --- /dev/null +++ b/test/generators/suspenders/jobs_generator_test.rb @@ -0,0 +1,122 @@ +require "test_helper" +require "generators/suspenders/jobs_generator" + +module Suspenders + module Generators + class JobsGeneratorTest < Rails::Generators::TestCase + include Suspenders::TestHelpers + + tests Suspenders::Generators::JobsGenerator + destination Rails.root + setup :prepare_destination + teardown :restore_destination + + test "adds gems to Gemfile" do + expected_output = <<~RUBY + gem "sidekiq" + RUBY + + run_generator + + assert_file app_root("Gemfile") do |file| + assert_match(expected_output, file) + end + end + + test "installs gems with Bundler" do + output = run_generator + + assert_match(/bundle install/, output) + end + + test "generator has a description" do + description = "Installs Sidekiq for background job processing." + + assert_equal description, Suspenders::Generators::JobsGenerator.desc + end + + test "configures ActiveJob logging" do + expected_configuration = <<~RUBY + require "active_job/logging" + require "active_job/log_subscriber" + + ActiveSupport::Notifications.unsubscribe("enqueue.active_job") + + module ActiveJob + module Logging + class EnqueueLogSubscriber < LogSubscriber + define_method :enqueue, instance_method(:enqueue) + end + end + end + + ActiveJob::Logging::EnqueueLogSubscriber.attach_to(:active_job) + RUBY + + run_generator + + assert_file app_root("config/initializers/active_job.rb") do |file| + assert_equal(expected_configuration, file) + end + end + + test "adds ActiveJob configuration to the application file" do + run_generator + + assert_file app_root("config/application.rb") do |file| + assert_match(/config.active_job.queue_adapter = :sidekiq/, file) + assert_match(/config.action_mailer.deliver_later_queue_name = nil/, file) + assert_match(/config.action_mailbox.queues.routing = nil/, file) + assert_match(/config.active_storage.queues.analysis = nil/, file) + assert_match(/config.active_storage.queues.purge = nil/, file) + assert_match(/config.active_storage.queues.mirror = nil/, file) + end + end + + test "adds ActiveJob configuration to the test environment file" do + run_generator + + assert_file app_root("config/environments/test.rb") do |file| + assert_match(/config.active_job.queue_adapter = :inline/, file) + end + end + + test "creates a Procfile.dev with Sidekiq configuration" do + run_generator + + assert_file app_root("Procfile.dev") do |file| + assert_match(/worker: bundle exec sidekiq/, file) + end + end + + test "adds Sidekiq configuration if procfile exists" do + proc_file = <<~TEXT + TEXT + + File.write(app_root("Procfile.dev"), proc_file) + + run_generator + + assert_file app_root("Procfile.dev") do |file| + assert_match(/worker: bundle exec sidekiq/, file) + end + end + + private + + def prepare_destination + touch "Gemfile" + backup_file "config/application.rb" + backup_file "config/environments/test.rb" + end + + def restore_destination + remove_file_if_exists "Gemfile" + remove_file_if_exists "config/initializers/active_job.rb" + remove_file_if_exists "Procfile.dev" + restore_file "config/application.rb" + restore_file "config/environments/test.rb" + end + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 4fcaa7ebc..0495cd10b 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -77,8 +77,7 @@ class Application < Rails::Application end def backup_file(file) - FileUtils.mv app_root(file), app_root("#{file}.bak") - touch file + FileUtils.copy app_root(file), app_root("#{file}.bak") end def restore_file(file)