From 9534078669aba30a73b720f11ae635ff171b9d51 Mon Sep 17 00:00:00 2001 From: Tim Kelly Date: Mon, 18 Mar 2024 20:48:21 -0500 Subject: [PATCH] Add pending ActionableError --- CHANGELOG.md | 5 +++-- doc/how_to_guides/usage.md | 10 ++++++++++ lib/onesie.rb | 1 + lib/onesie/check_pending.rb | 34 ++++++++++++++++++++++++++++++++++ lib/onesie/errors.rb | 21 +++++++++++++++++++++ lib/onesie/manager.rb | 4 ++++ lib/onesie/railtie.rb | 10 ++++++++++ onesie.gemspec | 4 ++-- spec/onesie/manager_spec.rb | 28 ++++++++++++++++++++++++++++ 9 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 lib/onesie/check_pending.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 751d34d..1499b6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added `.DS_Store` to `.gitignore` -- Added support for Rails > 5 +- Added support for Rails > 6 +- Add Onesie CheckPending page in development ### Changed - Switch `colorize` gem for `rainbow` @@ -20,7 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `bpdocs.yml` - Removed support for Ruby 2.6.x - Removed debugging dependencies -- Removed support for Rails 4.x +- Removed support for Rails 4.x and 5.x ### Fixed - Fix documentation formatting for MKDocs diff --git a/doc/how_to_guides/usage.md b/doc/how_to_guides/usage.md index 05ac8a9..e1e8cb3 100644 --- a/doc/how_to_guides/usage.md +++ b/doc/how_to_guides/usage.md @@ -93,3 +93,13 @@ bundle exec rake onesie:rerun['20220105140152_my_task'] # Reruns MyTask ```bash rake onesies:describe # Prints a list of available onesies to run ``` + +## Onesie ActionableError +To enforce that pending onesie tasks are always run, enable the follow Rails +configuration in development. + +```ruby +# config/environments/development.rb + config.onesie.task_error = :page_load` +``` +Rails will now raise an ActionableError if there are pending onesies. diff --git a/lib/onesie.rb b/lib/onesie.rb index 4e3781e..05e3b52 100644 --- a/lib/onesie.rb +++ b/lib/onesie.rb @@ -2,6 +2,7 @@ require 'rainbow/refinement' +require_relative 'onesie/check_pending' require_relative 'onesie/describe_tasks' require_relative 'onesie/errors' require_relative 'onesie/manager' diff --git a/lib/onesie/check_pending.rb b/lib/onesie/check_pending.rb new file mode 100644 index 0000000..670dbd8 --- /dev/null +++ b/lib/onesie/check_pending.rb @@ -0,0 +1,34 @@ +module Onesie + # This class is used to verify that all onesie tasks have been run before + # loading a web page if `config.onesie.task_error` is set to `:page_load`. + # Influenced by ActiveRecord::Migration::CheckPending + class CheckPending + def initialize(app, file_watcher: ActiveSupport::FileUpdateChecker) + @app = app + @needs_check = true + @mutex = Mutex.new + @file_watcher = file_watcher + end + + def call(env) + @mutex.synchronize do + @watcher ||= build_watcher do + @needs_check = true + Manager.check_pending! + @needs_check = false + end + + @needs_check ? @watcher.execute : @watcher.execute_if_updated + end + + @app.call(env) + end + + private + + def build_watcher(&block) + paths = Array(Manager.tasks_path) + @file_watcher.new([], paths, &block) + end + end +end diff --git a/lib/onesie/errors.rb b/lib/onesie/errors.rb index f36e872..17240fa 100644 --- a/lib/onesie/errors.rb +++ b/lib/onesie/errors.rb @@ -1,8 +1,29 @@ # frozen_string_literal: true +require 'active_support' +require 'active_support/rails' + module Onesie OnesieError = Class.new(StandardError) + class PendingTaskError < OnesieError # :nodoc: + include ActiveSupport::ActionableError + + action 'Run pending Onesies' do + Onesie::Manager.new.run_all + end + + def initialize(message = nil) + super(message || detailed_onesie_message) + end + + private + + def detailed_onesie_message + 'Onesie Tasks are pending. To resolve this issue, run: rake onesie:run_all' + end + end + # Raised when there is no matching Task for the provided Task filename. class TaskNotFoundError < OnesieError def initialize(filename) diff --git a/lib/onesie/manager.rb b/lib/onesie/manager.rb index bd745d3..2deaac4 100644 --- a/lib/onesie/manager.rb +++ b/lib/onesie/manager.rb @@ -11,6 +11,10 @@ class << self self.tasks_path = 'onesie/tasks' + def self.check_pending! + raise PendingTaskError if new.pending_tasks? + end + def initialize(runner: Onesie::Runner) @runner = runner end diff --git a/lib/onesie/railtie.rb b/lib/onesie/railtie.rb index 074bfe1..c17ee25 100644 --- a/lib/onesie/railtie.rb +++ b/lib/onesie/railtie.rb @@ -4,6 +4,16 @@ module Onesie class Railtie < Rails::Railtie # :nodoc: + config.onesie = ActiveSupport::OrderedOptions.new + + initializer 'onesie.task_error' do |app| + if config.onesie.task_error == :page_load + config.app_middleware.insert_after ::ActiveRecord::Migration::CheckPending, + Onesie::CheckPending, + file_watcher: app.config.file_watcher + end + end + rake_tasks do load('onesie/tasks/onesie.rake') end diff --git a/onesie.gemspec b/onesie.gemspec index dbcaa9f..fba28ff 100644 --- a/onesie.gemspec +++ b/onesie.gemspec @@ -28,7 +28,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_runtime_dependency 'activerecord', '> 5' - spec.add_runtime_dependency 'railties', '> 5' + spec.add_runtime_dependency 'activerecord', '> 6' + spec.add_runtime_dependency 'railties', '> 6' spec.add_runtime_dependency 'rainbow', '~> 3' end diff --git a/spec/onesie/manager_spec.rb b/spec/onesie/manager_spec.rb index 65fc284..e4eb4c8 100644 --- a/spec/onesie/manager_spec.rb +++ b/spec/onesie/manager_spec.rb @@ -8,6 +8,34 @@ allow(described_class).to receive(:tasks_path).and_return('spec/support/tasks') end + describe '.check_pending!' do + let(:double) { instance_double(described_class) } + + before do + allow(described_class).to receive(:new).and_return(double) + end + + context 'when there are no pending tasks' do + before { allow(manager).to receive(:pending_tasks?).and_return(false) } + + it 'does not raise an Error' do + expect { + described_class.check_pending! + }.not_to raise_error + end + end + + context 'when there are pending tasks' do + before { allow(manager).to receive(:pending_tasks?).and_return(true) } + + it 'raises a PendingTaskError' do + expect { + described_class.check_pending! + }.to raise_error(Onesie::PendingTaskError) + end + end + end + describe '#tasks' do it 'returns an array of TaskProxies' do expect(manager.tasks).to include(Onesie::TaskProxy)