maintenance_job 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f87c4d865f2b160eb5e14f814c45d19a5228246695ce1e42677ed294c2f0d5c4
4
+ data.tar.gz: dc71e1078490b57e46eda10f13d8d58e2466f8b70fc93675cf6ad746d2bef85e
5
+ SHA512:
6
+ metadata.gz: 910e40a0a7d79b6167d5eb59c856aff58ad07abf1cdc6efb033eff5237b3bdc5fdcc7adf19023dc9796758215922f78583f8360e843b22a94080ceb3074054c9
7
+ data.tar.gz: 62f529f6e5701f7488696e51734211e5dff69472a38ea844f52f88d5410bfaf2d9955b51513f599d81f0acb02712e107cbad9c29ab2a959a5ac03ac76491226b
data/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2021 Ayush Newatia
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # MaintenanceJob
2
+
3
+ [![Tests](https://github.com/ayushn21/maintenance_job/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/ayushn21/maintenance_job/actions/workflows/tests.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/maintenance_job.svg)](https://badge.fury.io/rb/maintenance_job)
5
+
6
+ `MaintenanceJob` is a mechanism to run testable one-off jobs at deploy time to manipulate data in the database.
7
+
8
+ Theoretically this could be done in database migrations or rake tasks, but that's a terrible idea because the changes can't be tested. In the case of rake tasks, it would also require access to the prod environment to run the tasks which is not always feasible.
9
+
10
+ This gem was heavily inspired by [this RailsConf 2020 talk](https://railsconf.org/2020/2020/video/alec-clarke-measure-twice-cut-once) by [@alecclarke](https://github.com/alecclarke).
11
+
12
+ ## Installation
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'maintenance_job'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ ```bash
22
+ $ bundle install && bin/rails maintenance_job:install
23
+ ```
24
+
25
+ This will copy the database migration that tracks which maintenance jobs have been run and create a directory under `app/jobs` for all your maintenance jobs.
26
+
27
+ ## Usage
28
+
29
+ ### Run maintenance jobs on deploy
30
+
31
+ Add `bin/rails maintenance_job:execute_pending_jobs` to your deploy script right after you run the database migrations.
32
+
33
+ ### Creating a maintenance job
34
+
35
+ To create a new maintenance job, run:
36
+
37
+ ```bash
38
+ $ bin/rails generate maintenance_job job_to_be_done
39
+ ```
40
+
41
+ This will create two files:
42
+
43
+ - `app/jobs/maintenance/job_to_be_done_job.rb`
44
+ - `test/jobs/maintenance/job_to_be_done_job_test.rb`
45
+
46
+ Implement your data manipulation, write some tests for it and deploy!
47
+
48
+ ### Ensuring all jobs have been run in local environments
49
+
50
+ If you'd like to ensure any pending maintenance jobs have been run in the development environment, add the following line to your `ApplicationController`:
51
+
52
+ `include MaintenanceJob::EnsureNoPendingJobs if Rails.env.development?`
53
+
54
+ If there are any pending jobs in development, the developer will see an actionable error in the browser where they can run the pending jobs and continue.
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork it (https://github.com/ayushn21/maintenance_job/fork)
59
+ 2. Clone the fork using `git clone` to your local development machine.
60
+ 3. Create your feature branch (`git checkout -b my-new-feature`)
61
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
62
+ 5. Push to the branch (`git push origin my-new-feature`)
63
+ 6. Create a new Pull Request
64
+
65
+ ## License
66
+
67
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
9
+
10
+ require "rake/testtask"
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = false
16
+ end
17
+
18
+ task default: :test
@@ -0,0 +1,13 @@
1
+ module MaintenanceJob
2
+ module EnsureNoPendingJobs
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_action :ensure_no_pending_maintenance_jobs
7
+ end
8
+
9
+ def ensure_no_pending_maintenance_jobs
10
+ raise MaintenanceJob::PendingJobsError if MaintenanceJob::Base.pending_jobs.present?
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module MaintenanceJob
2
+ class PendingJobsError < StandardError
3
+ include ActiveSupport::ActionableError
4
+
5
+ action "Run pending maintenance jobs" do
6
+ MaintenanceJob::Base.run_pending_jobs
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ module MaintenanceJob
2
+ class Base < ActiveJob::Base
3
+ queue_as :default
4
+ discard_on ActiveRecord::RecordNotUnique
5
+ discard_on ActiveJob::DeserializationError
6
+
7
+ before_perform { self.class.record_run }
8
+
9
+ def perform
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def self.record_run
14
+ Run.create(identifier: identifier)
15
+ end
16
+
17
+ def self.runnable?
18
+ Run.find_by(identifier: identifier).nil?
19
+ end
20
+
21
+ def self.pending_jobs
22
+ descendants.select(&:runnable?)
23
+ end
24
+
25
+ def self.run_pending_jobs
26
+ pending_jobs.each(&:perform_now)
27
+ end
28
+
29
+ def self.identifier
30
+ raise NotImplementedError
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,4 @@
1
+ module MaintenanceJob
2
+ class Run < ActiveRecord::Base
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ class CreateMaintenanceJobRuns < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :maintenance_job_runs, id: :string, primary_key: :identifier
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ Description:
2
+ Generates a MaintenanceJob and corresponding test case to run one off
3
+ tasks at deploy time.
4
+
5
+ Example:
6
+ bin/rails generate maintenance_job PopulateSomeNewColumn
7
+
8
+ This will create:
9
+ app/jobs/maintenance/populate_some_new_column_job.rb
10
+ test/jobs/maintenance/populate_some_new_column_job_test.rb
@@ -0,0 +1,8 @@
1
+ class MaintenanceJobGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path('templates', __dir__)
3
+
4
+ def generate_job_files
5
+ template "maintenance_job.rb.erb", "app/jobs/maintenance/#{file_name}_job.rb"
6
+ template "maintenance_job_test.rb.erb", "test/jobs/maintenance/#{file_name}_job_test.rb"
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ class Maintenance::<%= class_name %>Job < MaintenanceJob::Base
2
+ def perform
3
+ # Implement your fix here
4
+ end
5
+
6
+ def self.identifier
7
+ <%= Time.now.strftime("%Y%m%d%H%M%S").inspect %>
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ require "test_helper"
2
+
3
+ class Maintenance::<%= class_name %>JobTest < ActiveJob::TestCase
4
+ test "it fixes things" do
5
+ assert false, "ensure the maintenance fix is tested!"
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module MaintenanceJob
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace MaintenanceJob
4
+
5
+ initializer "maintenance_job.eager_load_namespace" do
6
+ autoloader = Rails.autoloaders.main
7
+ if autoloader.respond_to?(:on_setup)
8
+ autoloader.on_setup do
9
+ Maintenance.constants(false).each { |cname| Maintenance.const_get(cname) }
10
+ end
11
+ else
12
+ autoloader.preload(Rails.root.join("app", "jobs", "maintenance"))
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module MaintenanceJob
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,5 @@
1
+ require "maintenance_job/version"
2
+ require "maintenance_job/engine"
3
+
4
+ module MaintenanceJob
5
+ end
@@ -0,0 +1,16 @@
1
+ namespace :maintenance_job do
2
+ desc "Install the database migrations and file skeleton required for MaintenanceJob"
3
+ task :install do
4
+ Dir.mkdir(Rails.root.join("app", "jobs", "maintenance"))
5
+ File.write(Rails.root.join("app", "jobs", "maintenance", ".keep"), "")
6
+ File.write(Rails.root.join("app", "jobs", "maintenance.rb"), "module Maintenance; end")
7
+ puts "Created Maintenance module in Jobs directory"
8
+
9
+ Rake::Task["maintenance_job:install:migrations"].execute
10
+ end
11
+
12
+ desc "Runs all pending maintenance jobs"
13
+ task :execute_pending_jobs => :environment do
14
+ MaintenanceJob::Base.run_pending_jobs
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maintenance_job
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ayush Newatia
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-10-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6.1'
27
+ description:
28
+ email:
29
+ - ayush@hey.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE.md
35
+ - README.md
36
+ - Rakefile
37
+ - app/controllers/concerns/maintenance_job/ensure_no_pending_jobs.rb
38
+ - app/errors/maintenance_job/pending_jobs_error.rb
39
+ - app/jobs/maintenance_job/base.rb
40
+ - app/models/maintenance_job/run.rb
41
+ - db/migrate/20210903143918_create_maintenance_job_runs.rb
42
+ - lib/generators/USAGE
43
+ - lib/generators/maintenance_job_generator.rb
44
+ - lib/generators/templates/maintenance_job.rb.erb
45
+ - lib/generators/templates/maintenance_job_test.rb.erb
46
+ - lib/maintenance_job.rb
47
+ - lib/maintenance_job/engine.rb
48
+ - lib/maintenance_job/version.rb
49
+ - lib/tasks/maintenance_job_tasks.rake
50
+ homepage: https://github.com/ayushn21/maintenance_job
51
+ licenses:
52
+ - MIT
53
+ metadata:
54
+ allowed_push_host: https://rubygems.org
55
+ homepage_uri: https://github.com/ayushn21/maintenance_job
56
+ source_code_uri: https://github.com/ayushn21/maintenance_job
57
+ changelog_uri: https://github.com/ayushn21/maintenance_job/blob/main/CHANGELOG.md
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.2.22
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Mechanism to run testable one-off jobs at deploy time to manipulate data
77
+ test_files: []