pg_cron 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rubocop.yml +25 -0
- data/CHANGELOG.md +56 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +121 -0
- data/LICENSE +21 -0
- data/README.md +54 -0
- data/Rakefile +16 -0
- data/bin/console +15 -0
- data/bin/increment-version +37 -0
- data/bin/release-gem +32 -0
- data/bin/setup +8 -0
- data/lib/generators/pg_cron/job/USAGE +19 -0
- data/lib/generators/pg_cron/job/job_generator.rb +116 -0
- data/lib/generators/pg_cron/job/templates/db/migrate/create_job.erb +5 -0
- data/lib/generators/pg_cron/job/templates/db/migrate/update_job.erb +5 -0
- data/lib/generators/pg_cron/migration_helper.rb +48 -0
- data/lib/generators/pg_cron/name_helper.rb +29 -0
- data/lib/generators/pg_cron/version_helper.rb +53 -0
- data/lib/pg_cron/adapters/postgres/connection.rb +26 -0
- data/lib/pg_cron/adapters/postgres/jobs.rb +48 -0
- data/lib/pg_cron/adapters/postgres/query_executor.rb +37 -0
- data/lib/pg_cron/adapters/postgres.rb +116 -0
- data/lib/pg_cron/command_recorder.rb +50 -0
- data/lib/pg_cron/configuration.rb +37 -0
- data/lib/pg_cron/definition.rb +74 -0
- data/lib/pg_cron/job.rb +68 -0
- data/lib/pg_cron/railtie.rb +22 -0
- data/lib/pg_cron/schema_dumper.rb +35 -0
- data/lib/pg_cron/statements.rb +126 -0
- data/lib/pg_cron/version.rb +5 -0
- data/lib/pg_cron/version.rb.erb +5 -0
- data/lib/pg_cron.rb +44 -0
- data/lib/tasks/pg_cron/down.rake +21 -0
- data/lib/tasks/pg_cron/schedule_all_jobs.rake +33 -0
- data/lib/tasks/pg_cron/schedule_job.rake +12 -0
- data/lib/tasks/pg_cron/unschedule_job.rake +12 -0
- data/lib/tasks/pg_cron/up.rake +25 -0
- data/lib/tasks/pg_cron/utils.rb +124 -0
- data/spec/dummy/.gitignore +71 -0
- data/spec/dummy/.ruby-version +1 -0
- data/spec/dummy/README.md +24 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +33 -0
- data/spec/dummy/config/application.rb +40 -0
- data/spec/dummy/config/boot.rb +3 -0
- data/spec/dummy/config/credentials.yml.enc +1 -0
- data/spec/dummy/config/database.yml +86 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +58 -0
- data/spec/dummy/config/environments/production.rb +96 -0
- data/spec/dummy/config/environments/test.rb +49 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +8 -0
- data/spec/dummy/config/initializers/cors.rb +16 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +33 -0
- data/spec/dummy/config/puma.rb +43 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/db/pg_cron_jobs/dummy_job.yml +3 -0
- data/spec/dummy/db/seeds.rb +7 -0
- data/spec/dummy/lib/tasks/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/robots.txt +1 -0
- data/spec/dummy/tmp/.keep +0 -0
- data/spec/dummy/vendor/.keep +0 -0
- data/spec/pg_cron/configuration_spec.rb +20 -0
- data/spec/pg_cron/fx_coexistence_spec.rb +53 -0
- data/spec/pg_cron/statements_spec.rb +9 -0
- data/spec/spec_helper.rb +6 -0
- metadata +245 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f0af5c531c4a322d7ebb5e47372655ec43255307332bf0c7598e06da1aa4f071
|
|
4
|
+
data.tar.gz: c088b6bde067d9a7b1cb374def26313cef93b0d4429dcbea26835b1f355ae49b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 06a0c5a1d04197d4230759ddcc2cd952bcfb0095a057dc5495cd3a6c4781466e0e9e4ee664a524a540987251782853c36e5225d280a894877cf8775e70886827
|
|
7
|
+
data.tar.gz: 33fcf9a7ea6754a710476e04c542b37d58b177484a58781b09589c86e64070df7e2f4279c9a89ddfd3ad4c0b73a819b0b08244388842904959c52398ef31c4b5
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 2.7
|
|
3
|
+
Exclude:
|
|
4
|
+
- 'spec/**/*'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Style/StringLiterals:
|
|
8
|
+
Enabled: true
|
|
9
|
+
EnforcedStyle: double_quotes
|
|
10
|
+
|
|
11
|
+
Style/StringLiteralsInInterpolation:
|
|
12
|
+
Enabled: true
|
|
13
|
+
EnforcedStyle: double_quotes
|
|
14
|
+
|
|
15
|
+
Style/Documentation:
|
|
16
|
+
Enabled: false
|
|
17
|
+
|
|
18
|
+
Layout/LineLength:
|
|
19
|
+
Max: 120
|
|
20
|
+
|
|
21
|
+
Metrics/MethodLength:
|
|
22
|
+
Enabled: false
|
|
23
|
+
|
|
24
|
+
Metrics/AbcSize:
|
|
25
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [2.0.0] - 2026-08-04
|
|
4
|
+
|
|
5
|
+
Restructured on F(x)'s model. Every change below is breaking; there is no
|
|
6
|
+
upgrade path that does not touch existing job definitions.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- **Definitions are versioned SQL, not YAML.** `db/pg_cron_jobs/<name>.yml`
|
|
11
|
+
becomes `db/cron/<name>_v<NN>.sql`, holding the `cron.schedule()` call
|
|
12
|
+
verbatim. A job's body was always SQL; YAML meant a SQL string inside a YAML
|
|
13
|
+
scalar, so nothing could syntax-check it, dollar-quoting had to survive two
|
|
14
|
+
levels of escaping, and it could not be dumped back out.
|
|
15
|
+
- **Migration methods renamed and reshaped.** `schedule_pg_cron_job` /
|
|
16
|
+
`unschedule_pg_cron_job` / `update_pg_cron_job` become `create_cron_job` /
|
|
17
|
+
`drop_cron_job` / `update_cron_job`, taking F(x)'s arguments: `version:`,
|
|
18
|
+
`sql_definition:` (mutually exclusive) and `revert_to_version:`.
|
|
19
|
+
- **The module is `PgCron` and the gem is `pg_cron`** (was `PgCronRails` /
|
|
20
|
+
`pg_cron_rails`).
|
|
21
|
+
- **The adapter is split** into `Adapters::Postgres` plus `Connection`, `Jobs`
|
|
22
|
+
and `QueryExecutor`, matching F(x)'s layout.
|
|
23
|
+
- **The generator is namespaced and versioned**: `rails g pg_cron:job NAME`.
|
|
24
|
+
Running it again for an existing job copies the current definition to the next
|
|
25
|
+
version and writes an update migration carrying `revert_to_version`.
|
|
26
|
+
- `pg_cron:schedule_all_jobs` reads `db/cron/*_v*.sql` and schedules the HIGHEST
|
|
27
|
+
version of each job. Globbing filenames the old way scheduled every historical
|
|
28
|
+
version in turn and left whichever sorted last in place.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **A schema dumper.** Scheduled jobs are written into `db/schema.rb` as
|
|
33
|
+
`create_cron_job` lines. Without this the schema was wrong rather than merely
|
|
34
|
+
incomplete: a `db:schema:load` database came up with every table and none of
|
|
35
|
+
the schedules, silently.
|
|
36
|
+
- Reversibility. `CommandRecorder` inverts all three statements, and raises
|
|
37
|
+
`ActiveRecord::IrreversibleMigration` when a drop or update has no
|
|
38
|
+
`revert_to_version` instead of deleting the job.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Definitions are executed as given rather than assembled by interpolating
|
|
43
|
+
values into a heredoc, where a job name or command containing a quote produced
|
|
44
|
+
broken SQL.
|
|
45
|
+
- `update_cron_job` verifies the job exists first. `cron.schedule()` would
|
|
46
|
+
otherwise silently create one that was expected to be there, hiding a
|
|
47
|
+
migration applied out of order.
|
|
48
|
+
|
|
49
|
+
## [1.0.1] - 2021-10-26
|
|
50
|
+
|
|
51
|
+
- Fix PgCron manual configuration
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## [1.0.0] - 2021-10-19
|
|
55
|
+
|
|
56
|
+
- Initial release
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
pg_cron_rails (1.0.1)
|
|
5
|
+
activerecord (>= 6.0.0)
|
|
6
|
+
activesupport (>= 6.0.0)
|
|
7
|
+
railties (>= 6.0.0)
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: https://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
actionpack (6.1.4.1)
|
|
13
|
+
actionview (= 6.1.4.1)
|
|
14
|
+
activesupport (= 6.1.4.1)
|
|
15
|
+
rack (~> 2.0, >= 2.0.9)
|
|
16
|
+
rack-test (>= 0.6.3)
|
|
17
|
+
rails-dom-testing (~> 2.0)
|
|
18
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
19
|
+
actionview (6.1.4.1)
|
|
20
|
+
activesupport (= 6.1.4.1)
|
|
21
|
+
builder (~> 3.1)
|
|
22
|
+
erubi (~> 1.4)
|
|
23
|
+
rails-dom-testing (~> 2.0)
|
|
24
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
25
|
+
activemodel (6.1.4.1)
|
|
26
|
+
activesupport (= 6.1.4.1)
|
|
27
|
+
activerecord (6.1.4.1)
|
|
28
|
+
activemodel (= 6.1.4.1)
|
|
29
|
+
activesupport (= 6.1.4.1)
|
|
30
|
+
activesupport (6.1.4.1)
|
|
31
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
32
|
+
i18n (>= 1.6, < 2)
|
|
33
|
+
minitest (>= 5.1)
|
|
34
|
+
tzinfo (~> 2.0)
|
|
35
|
+
zeitwerk (~> 2.3)
|
|
36
|
+
ast (2.4.2)
|
|
37
|
+
builder (3.2.4)
|
|
38
|
+
concurrent-ruby (1.1.9)
|
|
39
|
+
crass (1.0.6)
|
|
40
|
+
diff-lcs (1.4.4)
|
|
41
|
+
erubi (1.10.0)
|
|
42
|
+
i18n (1.8.10)
|
|
43
|
+
concurrent-ruby (~> 1.0)
|
|
44
|
+
loofah (2.12.0)
|
|
45
|
+
crass (~> 1.0.2)
|
|
46
|
+
nokogiri (>= 1.5.9)
|
|
47
|
+
method_source (1.0.0)
|
|
48
|
+
minitest (5.14.4)
|
|
49
|
+
nokogiri (1.12.5-arm64-darwin)
|
|
50
|
+
racc (~> 1.4)
|
|
51
|
+
parallel (1.21.0)
|
|
52
|
+
parser (3.0.2.0)
|
|
53
|
+
ast (~> 2.4.1)
|
|
54
|
+
pg (1.2.3)
|
|
55
|
+
racc (1.6.0)
|
|
56
|
+
rack (2.2.3)
|
|
57
|
+
rack-test (1.1.0)
|
|
58
|
+
rack (>= 1.0, < 3)
|
|
59
|
+
rails-dom-testing (2.0.3)
|
|
60
|
+
activesupport (>= 4.2.0)
|
|
61
|
+
nokogiri (>= 1.6)
|
|
62
|
+
rails-html-sanitizer (1.4.2)
|
|
63
|
+
loofah (~> 2.3)
|
|
64
|
+
railties (6.1.4.1)
|
|
65
|
+
actionpack (= 6.1.4.1)
|
|
66
|
+
activesupport (= 6.1.4.1)
|
|
67
|
+
method_source
|
|
68
|
+
rake (>= 0.13)
|
|
69
|
+
thor (~> 1.0)
|
|
70
|
+
rainbow (3.0.0)
|
|
71
|
+
rake (13.0.6)
|
|
72
|
+
regexp_parser (2.1.1)
|
|
73
|
+
rexml (3.2.5)
|
|
74
|
+
rspec-core (3.10.1)
|
|
75
|
+
rspec-support (~> 3.10.0)
|
|
76
|
+
rspec-expectations (3.10.1)
|
|
77
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
78
|
+
rspec-support (~> 3.10.0)
|
|
79
|
+
rspec-mocks (3.10.2)
|
|
80
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
81
|
+
rspec-support (~> 3.10.0)
|
|
82
|
+
rspec-rails (5.0.2)
|
|
83
|
+
actionpack (>= 5.2)
|
|
84
|
+
activesupport (>= 5.2)
|
|
85
|
+
railties (>= 5.2)
|
|
86
|
+
rspec-core (~> 3.10)
|
|
87
|
+
rspec-expectations (~> 3.10)
|
|
88
|
+
rspec-mocks (~> 3.10)
|
|
89
|
+
rspec-support (~> 3.10)
|
|
90
|
+
rspec-support (3.10.2)
|
|
91
|
+
rubocop (1.22.1)
|
|
92
|
+
parallel (~> 1.10)
|
|
93
|
+
parser (>= 3.0.0.0)
|
|
94
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
95
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
96
|
+
rexml
|
|
97
|
+
rubocop-ast (>= 1.12.0, < 2.0)
|
|
98
|
+
ruby-progressbar (~> 1.7)
|
|
99
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
|
100
|
+
rubocop-ast (1.12.0)
|
|
101
|
+
parser (>= 3.0.1.1)
|
|
102
|
+
ruby-progressbar (1.11.0)
|
|
103
|
+
thor (1.1.0)
|
|
104
|
+
tzinfo (2.0.4)
|
|
105
|
+
concurrent-ruby (~> 1.0)
|
|
106
|
+
unicode-display_width (2.1.0)
|
|
107
|
+
zeitwerk (2.5.1)
|
|
108
|
+
|
|
109
|
+
PLATFORMS
|
|
110
|
+
arm64-darwin-20
|
|
111
|
+
|
|
112
|
+
DEPENDENCIES
|
|
113
|
+
bundler
|
|
114
|
+
pg
|
|
115
|
+
pg_cron_rails!
|
|
116
|
+
rake (~> 13.0)
|
|
117
|
+
rspec-rails
|
|
118
|
+
rubocop (~> 1.7)
|
|
119
|
+
|
|
120
|
+
BUNDLED WITH
|
|
121
|
+
2.2.29
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Agendrix
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# pg_cron Rails
|
|
2
|
+
[pg_cron](https://github.com/citusdata/pg_cron) jobs scheduling for Rails applications
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
By default, no configuration is needed to use this gem.
|
|
8
|
+
|
|
9
|
+
PgCron schedules your pg_cron jobs in the default PSQL database using your current database connection user.
|
|
10
|
+
|
|
11
|
+
You can also customize PgCron database connection configuration by creating an initializer:
|
|
12
|
+
```ruby
|
|
13
|
+
PgCron.configure do |config|
|
|
14
|
+
config.connection_config = {
|
|
15
|
+
username: username
|
|
16
|
+
password: password
|
|
17
|
+
database: database
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
**Create pg_cron job**
|
|
25
|
+
1. Generate a job:
|
|
26
|
+
```bash
|
|
27
|
+
rails generate pg_cron_job daily_vacuum
|
|
28
|
+
create db/pg_cron_jobs/daily_vacuum.yml
|
|
29
|
+
create db/migrate/[TIMESTAMP]_add_daily_vacuum_pg_cron_job.rb
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
2. Define `db/pg_cron_jobs/daily_vacuum.yml` job params.
|
|
33
|
+
```yml
|
|
34
|
+
daily_vacuum:
|
|
35
|
+
cron: "3 5 * * *"
|
|
36
|
+
statement: "VACUUM VERBOSE ANALYZE"
|
|
37
|
+
database: <%= ActiveRecord::Base.connection.current_database %> # This parameter is optional
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
3. Run the migration
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
rails db:migrate
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## Methods
|
|
48
|
+
|
|
49
|
+
The methods added to `ActiveRecord::Migration` are defined in [PgCron::Statements](https://github.com/agendrix/pg-cron-rails/blob/main/lib/pg_cron/statements.rb)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
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,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rake/testtask"
|
|
5
|
+
|
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
|
7
|
+
t.libs << "test"
|
|
8
|
+
t.libs << "lib"
|
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require "rubocop/rake_task"
|
|
13
|
+
|
|
14
|
+
RuboCop::RakeTask.new
|
|
15
|
+
|
|
16
|
+
task default: %i[test rubocop]
|
data/bin/console
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "test"
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
+
# require "pry"
|
|
12
|
+
# Pry.start
|
|
13
|
+
|
|
14
|
+
require "irb"
|
|
15
|
+
IRB.start(__FILE__)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "erb"
|
|
5
|
+
require_relative "../lib/pg_cron/version"
|
|
6
|
+
|
|
7
|
+
USAGE = "Usage: bin/increment-version <major|minor|patch>"
|
|
8
|
+
|
|
9
|
+
segment = ARGV[0]
|
|
10
|
+
unless %w[major minor patch].include?(segment)
|
|
11
|
+
warn USAGE
|
|
12
|
+
exit 1
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
current = PgCron::VERSION
|
|
16
|
+
major, minor, patch = current.split(".").map(&:to_i)
|
|
17
|
+
|
|
18
|
+
case segment
|
|
19
|
+
when "major" then major += 1; minor = 0; patch = 0
|
|
20
|
+
when "minor" then minor += 1; patch = 0
|
|
21
|
+
when "patch" then patch += 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
version = "#{major}.#{minor}.#{patch}"
|
|
25
|
+
|
|
26
|
+
template_path = File.expand_path("../lib/pg_cron/version.rb.erb", __dir__)
|
|
27
|
+
output_path = File.expand_path("../lib/pg_cron/version.rb", __dir__)
|
|
28
|
+
|
|
29
|
+
template = ERB.new(File.read(template_path))
|
|
30
|
+
result = template.result(binding)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
File.write(output_path, result)
|
|
34
|
+
|
|
35
|
+
`bundle install`
|
|
36
|
+
|
|
37
|
+
puts "#{current} -> #{version}"
|
data/bin/release-gem
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../lib/pg_cron/version"
|
|
5
|
+
|
|
6
|
+
local_version = PgCron::VERSION
|
|
7
|
+
gem_name = "pg_cron"
|
|
8
|
+
gemspec = "pg_cron.gemspec"
|
|
9
|
+
|
|
10
|
+
puts "Local version: #{local_version}"
|
|
11
|
+
|
|
12
|
+
remote_output = `gem specification #{gem_name} version --remote 2>&1`
|
|
13
|
+
|
|
14
|
+
if $?.success?
|
|
15
|
+
remote_version = remote_output[/version: (.+)/, 1]&.strip
|
|
16
|
+
puts "Remote version: #{remote_version}"
|
|
17
|
+
|
|
18
|
+
if Gem::Version.new(local_version) <= Gem::Version.new(remote_version)
|
|
19
|
+
abort "ERROR: Local version (#{local_version}) has not been incremented past remote (#{remote_version})"
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
puts "Gem not yet published remotely, proceeding with first release"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
puts "Building #{gemspec}..."
|
|
26
|
+
system("gem build #{gemspec}") || abort("ERROR: gem build failed")
|
|
27
|
+
|
|
28
|
+
gem_file = "#{gem_name}-#{local_version}.gem"
|
|
29
|
+
puts "Pushing #{gem_file}..."
|
|
30
|
+
system("gem push #{gem_file}") || abort("ERROR: gem push failed")
|
|
31
|
+
|
|
32
|
+
puts "Released #{gem_name} #{local_version}"
|
data/bin/setup
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Create a new pg_cron job for your application. This will create a new job
|
|
3
|
+
definition file and the accompanying migration.
|
|
4
|
+
|
|
5
|
+
Run it again for an existing job to start the next version from the current
|
|
6
|
+
definition; the migration it writes is an update with revert_to_version set.
|
|
7
|
+
|
|
8
|
+
When --no-migration is passed, skips generating a migration.
|
|
9
|
+
|
|
10
|
+
Examples:
|
|
11
|
+
rails generate pg_cron:job drain_juicefs_events
|
|
12
|
+
|
|
13
|
+
create: db/cron/drain_juicefs_events_v01.sql
|
|
14
|
+
create: db/migrate/[TIMESTAMP]_create_cron_job_drain_juicefs_events.rb
|
|
15
|
+
|
|
16
|
+
rails generate pg_cron:job drain_juicefs_events # a second time
|
|
17
|
+
|
|
18
|
+
create: db/cron/drain_juicefs_events_v02.sql
|
|
19
|
+
create: db/migrate/[TIMESTAMP]_update_cron_job_drain_juicefs_events_to_version_2.rb
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
require "generators/pg_cron/version_helper"
|
|
6
|
+
require "generators/pg_cron/migration_helper"
|
|
7
|
+
require "generators/pg_cron/name_helper"
|
|
8
|
+
|
|
9
|
+
module PgCron
|
|
10
|
+
module Generators
|
|
11
|
+
# Generates a cron job definition and its migration.
|
|
12
|
+
#
|
|
13
|
+
# rails generate pg_cron:job drain_juicefs_events
|
|
14
|
+
#
|
|
15
|
+
# Structured exactly as Fx::Generators::FunctionGenerator: the same
|
|
16
|
+
# class_option, the same three phases (directory, definition, migration),
|
|
17
|
+
# and the same versioning behaviour — running it again for an existing job
|
|
18
|
+
# COPIES the previous definition to the next version rather than creating an
|
|
19
|
+
# empty file, so an edit starts from what is currently deployed and the
|
|
20
|
+
# migration it writes is an update with revert_to_version set.
|
|
21
|
+
#
|
|
22
|
+
# @api private
|
|
23
|
+
class JobGenerator < Rails::Generators::NamedBase
|
|
24
|
+
include Rails::Generators::Migration
|
|
25
|
+
|
|
26
|
+
source_root File.expand_path("templates", __dir__)
|
|
27
|
+
|
|
28
|
+
DEFINITION_PATH = %w[db cron].freeze
|
|
29
|
+
|
|
30
|
+
class_option :migration, type: :boolean
|
|
31
|
+
|
|
32
|
+
def create_cron_directory
|
|
33
|
+
return if job_definition_path.exist?
|
|
34
|
+
|
|
35
|
+
empty_directory(job_definition_path)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def create_job_definition
|
|
39
|
+
if version_helper.creating_new?
|
|
40
|
+
create_file(definition.path)
|
|
41
|
+
else
|
|
42
|
+
copy_file(previous_definition.full_path, definition.full_path)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def create_migration_file
|
|
47
|
+
return if migration_helper.skip_creation?
|
|
48
|
+
|
|
49
|
+
template_info = migration_helper.migration_template_info(
|
|
50
|
+
file_name: file_name,
|
|
51
|
+
updating_existing: version_helper.updating_existing?,
|
|
52
|
+
version: version_helper.current_version
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
migration_template(
|
|
56
|
+
template_info.fetch(:template),
|
|
57
|
+
template_info.fetch(:filename)
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.next_migration_number(dir)
|
|
62
|
+
::ActiveRecord::Generators::Base.next_migration_number(dir)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
no_tasks do
|
|
66
|
+
def previous_version
|
|
67
|
+
version_helper.previous_version
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def version
|
|
71
|
+
version_helper.current_version
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def migration_class_name
|
|
75
|
+
if version_helper.updating_existing?
|
|
76
|
+
migration_helper.update_migration_class_name(
|
|
77
|
+
class_name: class_name,
|
|
78
|
+
version: version
|
|
79
|
+
)
|
|
80
|
+
else
|
|
81
|
+
super
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def formatted_name
|
|
86
|
+
PgCron::Generators::NameHelper.format_for_migration(singular_name)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def job_definition_path
|
|
93
|
+
@_job_definition_path ||= Rails.root.join(*DEFINITION_PATH)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def version_helper
|
|
97
|
+
@_version_helper ||= PgCron::Generators::VersionHelper.new(
|
|
98
|
+
file_name: file_name,
|
|
99
|
+
definition_path: job_definition_path
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def migration_helper
|
|
104
|
+
@_migration_helper ||= PgCron::Generators::MigrationHelper.new(options)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def definition
|
|
108
|
+
version_helper.definition_for_version(version: version)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def previous_definition
|
|
112
|
+
version_helper.definition_for_version(version: previous_version)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgCron
|
|
4
|
+
module Generators
|
|
5
|
+
# Chooses the migration template and filename.
|
|
6
|
+
#
|
|
7
|
+
# Copied from Fx::Generators::MigrationHelper, minus the object_type
|
|
8
|
+
# argument: this gem has one kind of object, so the templates are named
|
|
9
|
+
# create_job / update_job rather than being interpolated per type.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
class MigrationHelper
|
|
13
|
+
def initialize(options)
|
|
14
|
+
@options = options
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def skip_creation?
|
|
18
|
+
!should_create_migration?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def update_migration_class_name(class_name:, version:)
|
|
22
|
+
"UpdateCronJob#{class_name}ToVersion#{version}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def migration_template_info(file_name:, updating_existing:, version:)
|
|
26
|
+
if updating_existing
|
|
27
|
+
{
|
|
28
|
+
template: "db/migrate/update_job.erb",
|
|
29
|
+
filename: "db/migrate/update_cron_job_#{file_name}_to_version_#{version}.rb"
|
|
30
|
+
}
|
|
31
|
+
else
|
|
32
|
+
{
|
|
33
|
+
template: "db/migrate/create_job.erb",
|
|
34
|
+
filename: "db/migrate/create_cron_job_#{file_name}.rb"
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
attr_reader :options
|
|
42
|
+
|
|
43
|
+
def should_create_migration?
|
|
44
|
+
options.fetch(:migration, true)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgCron
|
|
4
|
+
module Generators
|
|
5
|
+
# Formats a job name for use in a generated migration.
|
|
6
|
+
#
|
|
7
|
+
# Copied from Fx::Generators::NameHelper. F(x) quotes names containing a dot
|
|
8
|
+
# because a function or trigger can be schema-qualified; a pg_cron jobname
|
|
9
|
+
# is a plain string in cron.job and never is, but the same rule is kept so a
|
|
10
|
+
# name with a dot still produces valid Ruby rather than a stray constant.
|
|
11
|
+
#
|
|
12
|
+
# @api private
|
|
13
|
+
class NameHelper
|
|
14
|
+
def self.format_for_migration(name)
|
|
15
|
+
if name.include?(".")
|
|
16
|
+
"\"#{name}\""
|
|
17
|
+
else
|
|
18
|
+
":#{name}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.validate_and_format(name)
|
|
23
|
+
raise ArgumentError, "Name cannot be blank" if name.blank?
|
|
24
|
+
|
|
25
|
+
format_for_migration(name)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|