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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PgCron
|
|
4
|
+
# Migration methods for pg_cron schedules, following F(x)'s conventions
|
|
5
|
+
# exactly: a name, an optional `version` naming a file in db/cron, an optional
|
|
6
|
+
# `sql_definition` that takes its place, and `revert_to_version` so a rollback
|
|
7
|
+
# restores the previous definition instead of dropping the job outright.
|
|
8
|
+
#
|
|
9
|
+
# Replaces schedule_pg_cron_job / unschedule_pg_cron_job / update_pg_cron_job,
|
|
10
|
+
# which took only a name and read a YAML file. Those had no versioning, so a
|
|
11
|
+
# changed schedule left no record of what it had been and a rollback could
|
|
12
|
+
# only delete the job.
|
|
13
|
+
module Statements
|
|
14
|
+
# Create a new cron job.
|
|
15
|
+
#
|
|
16
|
+
# @param name [String, Symbol] The job's name. pg_cron keys on jobname, so
|
|
17
|
+
# this is its identity — scheduling over an existing name replaces it.
|
|
18
|
+
# @param version [Integer] The version number, used to find the definition
|
|
19
|
+
# file in `db/cron`. Defaults to `1`.
|
|
20
|
+
# @param sql_definition [String] The SQL for the job. An error is raised if
|
|
21
|
+
# `sql_definition` and `version` are both set, as they are mutually
|
|
22
|
+
# exclusive.
|
|
23
|
+
# @return [void]
|
|
24
|
+
#
|
|
25
|
+
# @example Create from `db/cron/drain_juicefs_events_v01.sql`
|
|
26
|
+
# create_cron_job(:drain_juicefs_events, version: 1)
|
|
27
|
+
#
|
|
28
|
+
# @example Create from provided SQL string
|
|
29
|
+
# create_cron_job(:drain_juicefs_events, sql_definition: <<~SQL)
|
|
30
|
+
# SELECT cron.schedule(
|
|
31
|
+
# 'drain_juicefs_events',
|
|
32
|
+
# '* * * * *',
|
|
33
|
+
# $$SELECT juicefs_index_events(500)$$
|
|
34
|
+
# );
|
|
35
|
+
# SQL
|
|
36
|
+
#
|
|
37
|
+
def create_cron_job(name, version: nil, sql_definition: nil, revert_to_version: nil)
|
|
38
|
+
validate_cron_version_and_sql_definition_exclusive!(version, sql_definition)
|
|
39
|
+
version ||= 1
|
|
40
|
+
|
|
41
|
+
return unless PgCron.database.pg_cron_enabled?
|
|
42
|
+
|
|
43
|
+
PgCron.database.create_job(resolve_cron_sql_definition(sql_definition, name, version))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Drop a cron job by name.
|
|
47
|
+
#
|
|
48
|
+
# @param name [String, Symbol] The job's name.
|
|
49
|
+
# @param revert_to_version [Integer] Used to reverse this on
|
|
50
|
+
# `rake db:rollback`; passed as `version` to {#create_cron_job}.
|
|
51
|
+
# @return [void]
|
|
52
|
+
#
|
|
53
|
+
def drop_cron_job(name, revert_to_version: nil)
|
|
54
|
+
return unless PgCron.database.pg_cron_enabled?
|
|
55
|
+
|
|
56
|
+
PgCron.database.drop_job(name)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Update a cron job.
|
|
60
|
+
#
|
|
61
|
+
# One statement, not unschedule-then-schedule: cron.schedule() against an
|
|
62
|
+
# existing jobname REPLACES that job. Dropping first would leave a window in
|
|
63
|
+
# which the schedule does not exist, and on a frequently-firing job that
|
|
64
|
+
# window is a missed run.
|
|
65
|
+
#
|
|
66
|
+
# @param name [String, Symbol] The job's name.
|
|
67
|
+
# @param version [Integer] The version number, used to find the definition
|
|
68
|
+
# file in `db/cron`.
|
|
69
|
+
# @param sql_definition [String] The SQL for the job.
|
|
70
|
+
# @param revert_to_version [Integer] The version to roll back to.
|
|
71
|
+
# @return [void]
|
|
72
|
+
#
|
|
73
|
+
def update_cron_job(name, version: nil, sql_definition: nil, revert_to_version: nil)
|
|
74
|
+
validate_cron_version_or_sql_definition_present!(version, sql_definition)
|
|
75
|
+
validate_cron_version_and_sql_definition_exclusive!(version, sql_definition)
|
|
76
|
+
|
|
77
|
+
return unless PgCron.database.pg_cron_enabled?
|
|
78
|
+
|
|
79
|
+
PgCron.database.update_job(name, resolve_cron_sql_definition(sql_definition, name, version))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# THE `cron_` ON THESE THREE NAMES IS LOAD-BEARING. Do not tidy it away.
|
|
85
|
+
#
|
|
86
|
+
# This module and F(x)'s Statements are both included into the SAME object,
|
|
87
|
+
# ActiveRecord::ConnectionAdapters::AbstractAdapter, and we are included
|
|
88
|
+
# second — so anything here with a name F(x) also uses SHADOWS F(x)'s copy
|
|
89
|
+
# for every migration in the application, not just for ours.
|
|
90
|
+
#
|
|
91
|
+
# All three helpers below were lifted from F(x) under F(x)'s own names, and
|
|
92
|
+
# `resolve_sql_definition` differs from it by one argument: F(x) passes the
|
|
93
|
+
# object kind (:function, :trigger, :view) as a fourth. So with this gem
|
|
94
|
+
# loaded, every create_function / create_trigger / update_function in the
|
|
95
|
+
# host application died on
|
|
96
|
+
#
|
|
97
|
+
# ArgumentError: wrong number of arguments (given 4, expected 3)
|
|
98
|
+
#
|
|
99
|
+
# before it reached the database, and the two validators were a matching
|
|
100
|
+
# arity away from doing the same. Nothing was wrong with the caller's SQL;
|
|
101
|
+
# the two DSLs simply could not both be in the room.
|
|
102
|
+
#
|
|
103
|
+
# Prefixed rather than made arity-tolerant, because "works as long as F(x)
|
|
104
|
+
# keeps that signature" is the same bug with a longer fuse. A name that is
|
|
105
|
+
# ours cannot collide at all.
|
|
106
|
+
VERSION_OR_SQL_DEFINITION_REQUIRED = "version or sql_definition must be specified"
|
|
107
|
+
private_constant :VERSION_OR_SQL_DEFINITION_REQUIRED
|
|
108
|
+
|
|
109
|
+
VERSION_AND_SQL_DEFINITION_EXCLUSIVE = "sql_definition and version cannot both be set"
|
|
110
|
+
private_constant :VERSION_AND_SQL_DEFINITION_EXCLUSIVE
|
|
111
|
+
|
|
112
|
+
def validate_cron_version_or_sql_definition_present!(version, sql_definition)
|
|
113
|
+
raise ArgumentError, VERSION_OR_SQL_DEFINITION_REQUIRED, caller if version.nil? && sql_definition.nil?
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def validate_cron_version_and_sql_definition_exclusive!(version, sql_definition)
|
|
117
|
+
raise ArgumentError, VERSION_AND_SQL_DEFINITION_EXCLUSIVE, caller if version.present? && sql_definition.present?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def resolve_cron_sql_definition(sql_definition, name, version)
|
|
121
|
+
return sql_definition.strip_heredoc if sql_definition
|
|
122
|
+
|
|
123
|
+
PgCron::Definition.job(name: name, version: version).to_sql
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
data/lib/pg_cron.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Definition first: Configuration reads Definition::DIRECTORY in its class body,
|
|
4
|
+
# so requiring it later raises NameError at boot.
|
|
5
|
+
require "pg_cron/definition"
|
|
6
|
+
require "pg_cron/job"
|
|
7
|
+
require "pg_cron/adapters/postgres"
|
|
8
|
+
require "pg_cron/configuration"
|
|
9
|
+
require "pg_cron/schema_dumper"
|
|
10
|
+
require "pg_cron/statements"
|
|
11
|
+
require "pg_cron/command_recorder"
|
|
12
|
+
require "pg_cron/railtie" if defined?(::Rails::Railtie)
|
|
13
|
+
|
|
14
|
+
module PgCron
|
|
15
|
+
def self.load
|
|
16
|
+
# Make PgCron::Statements available during migrations
|
|
17
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.include PgCron::Statements
|
|
18
|
+
|
|
19
|
+
# Make PgCron::Statements reversible for migration rollbacks
|
|
20
|
+
ActiveRecord::Migration::CommandRecorder.include PgCron::CommandRecorder
|
|
21
|
+
|
|
22
|
+
# Teach the schema dumper about cron jobs. Without this db/schema.rb carries
|
|
23
|
+
# every table and none of the schedules, so a db:schema:load database comes
|
|
24
|
+
# up silently missing them.
|
|
25
|
+
ActiveRecord::SchemaDumper.prepend PgCron::SchemaDumper
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The adapter cron statements run through.
|
|
29
|
+
#
|
|
30
|
+
# Was a SECOND connection, opened via ActiveRecord::Base.postgresql_connection
|
|
31
|
+
# against a database named `postgres`, and reconnected by hand. Two problems:
|
|
32
|
+
# postgresql_connection was removed in Rails 8, and the separate database was
|
|
33
|
+
# wrong for this setup anyway — cron.job lives in whatever cron.database_name
|
|
34
|
+
# points at, which is the application's own database, so the application's own
|
|
35
|
+
# connection is the one that can see it.
|
|
36
|
+
#
|
|
37
|
+
# It also has to be that connection for a second reason: pg_cron puts
|
|
38
|
+
# row-level security on cron.job filtering by username, so jobs created on a
|
|
39
|
+
# different connection as a different role are invisible to the app and to the
|
|
40
|
+
# schema dumper.
|
|
41
|
+
def self.connection
|
|
42
|
+
configuration.adapter
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "utils"
|
|
4
|
+
|
|
5
|
+
namespace :pg_cron do
|
|
6
|
+
desc "Tear down pg_cron PSQL extension for your development environment"
|
|
7
|
+
task :down, [:pg_cron_version] => :environment do |_t, args|
|
|
8
|
+
puts "Tearing down pg_cron"
|
|
9
|
+
validate_rails_environment
|
|
10
|
+
validate_homebrew_is_installed
|
|
11
|
+
validate_psql_is_running_with_brew
|
|
12
|
+
if pg_cron_is_installed(args[:pg_cron_version] || DEFAULT_PG_CRON_VERSION)
|
|
13
|
+
drop_extension
|
|
14
|
+
remove_pg_cron_from_shared_preload_libraries
|
|
15
|
+
restart_psql_service
|
|
16
|
+
end
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
puts "Something went wrong while tearing down pg_cron"
|
|
19
|
+
puts e
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :pg_cron do
|
|
4
|
+
desc "Schedule every job in db/cron at its highest version"
|
|
5
|
+
task schedule_all_jobs: :environment do |_t, _args|
|
|
6
|
+
latest_job_versions.each do |name, version|
|
|
7
|
+
ActiveRecord::Base.connection.create_cron_job(name, version: version)
|
|
8
|
+
end
|
|
9
|
+
puts "All the pg_cron jobs were successfully scheduled"
|
|
10
|
+
rescue StandardError => e
|
|
11
|
+
puts "Something went wrong while scheduling the pg_cron jobs"
|
|
12
|
+
puts e
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# Definitions are db/cron/<name>_v<NN>.sql, so a job with several versions has
|
|
18
|
+
# several files and only the HIGHEST is current. Globbing filenames the way
|
|
19
|
+
# this task did for YAML would schedule every historical version in turn and
|
|
20
|
+
# leave whichever sorted last in place.
|
|
21
|
+
def latest_job_versions
|
|
22
|
+
directory = Rails.root.join("db", PgCron::Definition::DIRECTORY)
|
|
23
|
+
|
|
24
|
+
Dir.glob("*_v*.sql", base: directory).each_with_object({}) do |file, latest|
|
|
25
|
+
match = file.match(/\A(?<name>.+)_v(?<version>\d+)\.sql\z/)
|
|
26
|
+
next unless match
|
|
27
|
+
|
|
28
|
+
name = match[:name]
|
|
29
|
+
version = match[:version].to_i
|
|
30
|
+
latest[name] = version if version > latest.fetch(name, 0)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :pg_cron do
|
|
4
|
+
desc "Schedule a job"
|
|
5
|
+
task :schedule_job, [:job_name] => :environment do |_t, args|
|
|
6
|
+
ActiveRecord::Base.connection.schedule_pg_cron_job(args[:job_name])
|
|
7
|
+
puts "The pg_cron job #{args[:job_name]} was successfully scheduled"
|
|
8
|
+
rescue StandardError => e
|
|
9
|
+
puts "Something went wrong while scheduling the pg_cron job with name #{args[:job_name]}"
|
|
10
|
+
puts e
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :pg_cron do
|
|
4
|
+
desc "Unschedule a job"
|
|
5
|
+
task :unschedule_job, [:job_name] => :environment do |_t, args|
|
|
6
|
+
ActiveRecord::Base.connection.unschedule_pg_cron_job(args[:job_name])
|
|
7
|
+
puts "The pg_cron job #{args[:job_name]} was successfully unscheduled"
|
|
8
|
+
rescue StandardError => e
|
|
9
|
+
puts "Something went wrong while unscheduling the pg_cron job with name #{args[:job_name]}"
|
|
10
|
+
puts e
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "utils"
|
|
4
|
+
|
|
5
|
+
namespace :pg_cron do
|
|
6
|
+
desc "Install and enable pg_cron PSQL extension for your development environment"
|
|
7
|
+
task :up, [:pg_cron_version] => :environment do |_t, args|
|
|
8
|
+
puts "Setting up pg_cron"
|
|
9
|
+
validate_rails_environment
|
|
10
|
+
validate_homebrew_is_installed
|
|
11
|
+
validate_latest_psql_is_installed
|
|
12
|
+
validate_psql_is_running_with_brew
|
|
13
|
+
if !pg_cron_is_installed(args[:pg_cron_version] || DEFAULT_PG_CRON_VERSION)
|
|
14
|
+
install_pg_cron
|
|
15
|
+
add_pg_cron_to_shared_preload_libraries
|
|
16
|
+
restart_psql_service
|
|
17
|
+
create_extension
|
|
18
|
+
else
|
|
19
|
+
restart_psql_service
|
|
20
|
+
end
|
|
21
|
+
rescue StandardError => e
|
|
22
|
+
puts "Something went wrong while setting up pg_cron"
|
|
23
|
+
puts e
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# rubocop:disable Style/SpecialGlobalVars
|
|
2
|
+
|
|
3
|
+
DEFAULT_PG_CRON_VERSION = "1.3.0".freeze
|
|
4
|
+
|
|
5
|
+
def validate_homebrew_is_installed
|
|
6
|
+
`brew --version`
|
|
7
|
+
raise "This rake task only support Darwin based OS" unless $?.success?
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def validate_psql_is_running_with_brew
|
|
11
|
+
fetch_psql_brew_service_version.present?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def fetch_psql_brew_service_version
|
|
15
|
+
psql_services = `brew services list | grep "started" | grep "postgresql"`
|
|
16
|
+
psql_services.split(" ").first.strip
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def validate_rails_environment
|
|
20
|
+
raise "This rake task should only be used in development" unless Rails.env.development?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# rubocop:disable Style/GuardClause
|
|
24
|
+
# rubocop:disable Layout/LineLength
|
|
25
|
+
def validate_latest_psql_is_installed
|
|
26
|
+
`brew list postgresql`
|
|
27
|
+
unless $?.success?
|
|
28
|
+
raise "Due to pg_cron compilation issues the postgresql brew package is needed, please install the latest psql brew package: brew install postgresql"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
# rubocop:enable Style/GuardClause
|
|
32
|
+
# rubocop:enable Layout/LineLength
|
|
33
|
+
|
|
34
|
+
def pg_cron_is_installed(requested_version)
|
|
35
|
+
result = PgCron.connection.execute <<-SQL
|
|
36
|
+
SELECT
|
|
37
|
+
extversion
|
|
38
|
+
FROM
|
|
39
|
+
pg_extension
|
|
40
|
+
WHERE
|
|
41
|
+
extname='pg_cron'
|
|
42
|
+
SQL
|
|
43
|
+
|
|
44
|
+
extension_version = result.first&.dig("extversion")
|
|
45
|
+
parsed_extension_version = extension_version ? parse_pg_cron_version(extension_version) : nil
|
|
46
|
+
parsed_requested_version = parse_pg_cron_version(requested_version)
|
|
47
|
+
parsed_extension_version == parsed_requested_version
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def parse_pg_cron_version(version)
|
|
51
|
+
major_minor_tuple = version.split(".").first(2)
|
|
52
|
+
{ major: major_minor_tuple[0], minor: major_minor_tuple[1] }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# rubocop:disable Layout/LineLength
|
|
56
|
+
def install_pg_cron
|
|
57
|
+
latest_pg_cron_path = `brew --prefix postgresql`.strip
|
|
58
|
+
compilation_path = "#{latest_pg_cron_path}/bin:$PATH"
|
|
59
|
+
pg_cron_version = DEFAULT_PG_CRON_VERSION
|
|
60
|
+
`cd /tmp && \
|
|
61
|
+
curl -s -L https://github.com/citusdata/pg_cron/archive/refs/tags/v#{pg_cron_version}.zip --output pg_cron.zip && \
|
|
62
|
+
unzip -u pg_cron.zip && \
|
|
63
|
+
cd pg_cron-#{pg_cron_version} && \
|
|
64
|
+
PATH=#{compilation_path} make && PATH=#{compilation_path} sudo make install && \
|
|
65
|
+
cp $(brew --prefix postgresql)/share/postgresql/extension/pg_cron* $(brew --prefix #{fetch_psql_brew_service_version})/share/#{fetch_psql_brew_service_version}/extension/ && \
|
|
66
|
+
rm -rf pg_cron*
|
|
67
|
+
`
|
|
68
|
+
end
|
|
69
|
+
# rubocop:enable Layout/LineLength
|
|
70
|
+
|
|
71
|
+
def add_pg_cron_to_shared_preload_libraries
|
|
72
|
+
psql_config_file_path = PgCron.connection.execute("SHOW config_file").first&.dig("config_file")
|
|
73
|
+
config_file_content = ""
|
|
74
|
+
File.open(psql_config_file_path, "r") do |file|
|
|
75
|
+
file.each_line do |line|
|
|
76
|
+
if line.match(/(#\s*)?shared_preload_libraries =.*/)
|
|
77
|
+
libraries = line.split("=").last.match(/'.*'/).to_a.first.gsub("'", "").split(",").map(&:strip).filter do |s|
|
|
78
|
+
s != "pg_cron"
|
|
79
|
+
end
|
|
80
|
+
libraries << "pg_cron"
|
|
81
|
+
line = "shared_preload_libraries = '#{libraries.join(",")}' \n"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
config_file_content << line
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
File.atomic_write(psql_config_file_path, "/tmp") { |file| file.write config_file_content }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def remove_pg_cron_from_shared_preload_libraries
|
|
92
|
+
psql_config_file_path = PgCron.connection.execute("SHOW config_file").first&.dig("config_file")
|
|
93
|
+
config_file_content = ""
|
|
94
|
+
File.open(psql_config_file_path, "r") do |file|
|
|
95
|
+
file.each_line do |line|
|
|
96
|
+
if line.match(/(#\s*)?shared_preload_libraries =.*/)
|
|
97
|
+
libraries = line.split("=").last.match(/'.*'/).to_a.first.gsub("'", "").split(",").map(&:strip).filter do |s|
|
|
98
|
+
s != "pg_cron"
|
|
99
|
+
end
|
|
100
|
+
line = "shared_preload_libraries = '#{libraries.join(",")}' \n"
|
|
101
|
+
line.prepend("#") if libraries.empty?
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
config_file_content << line
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
File.atomic_write(psql_config_file_path, "/tmp") { |file| file.write config_file_content }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def create_extension
|
|
112
|
+
PgCron.connection.enable_extension("pg_cron")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def restart_psql_service
|
|
116
|
+
`brew services restart #{fetch_psql_brew_service_version}`
|
|
117
|
+
sleep 1 # Prevent timing issues
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def drop_extension
|
|
121
|
+
PgCron.connection.disable_extension("pg_cron")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# rubocop:enable Style/SpecialGlobalVars
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Rails
|
|
2
|
+
|
|
3
|
+
*.rbc
|
|
4
|
+
capybara-*.html
|
|
5
|
+
.rspec
|
|
6
|
+
/db/*.sqlite3
|
|
7
|
+
/db/*.sqlite3-journal
|
|
8
|
+
/db/*.sqlite3-[0-9]*
|
|
9
|
+
/public/system
|
|
10
|
+
/coverage/
|
|
11
|
+
/spec/tmp
|
|
12
|
+
*.orig
|
|
13
|
+
rerun.txt
|
|
14
|
+
pickle-email-*.html
|
|
15
|
+
|
|
16
|
+
# Ignore all logfiles and tempfiles.
|
|
17
|
+
/log/*
|
|
18
|
+
/tmp/*
|
|
19
|
+
!/log/.keep
|
|
20
|
+
!/tmp/.keep
|
|
21
|
+
|
|
22
|
+
# TODO Comment out this rule if you are OK with secrets being uploaded to the repo
|
|
23
|
+
config/initializers/secret_token.rb
|
|
24
|
+
config/master.key
|
|
25
|
+
|
|
26
|
+
# Only include if you have production secrets in this file, which is no longer a Rails default
|
|
27
|
+
# config/secrets.yml
|
|
28
|
+
|
|
29
|
+
# dotenv, dotenv-rails
|
|
30
|
+
# TODO Comment out these rules if environment variables can be committed
|
|
31
|
+
.env
|
|
32
|
+
.env.*
|
|
33
|
+
|
|
34
|
+
## Environment normalization:
|
|
35
|
+
/.bundle
|
|
36
|
+
/vendor/bundle
|
|
37
|
+
|
|
38
|
+
# these should all be checked in to normalize the environment:
|
|
39
|
+
# Gemfile.lock, .ruby-version, .ruby-gemset
|
|
40
|
+
|
|
41
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
|
42
|
+
.rvmrc
|
|
43
|
+
|
|
44
|
+
# if using bower-rails ignore default bower_components path bower.json files
|
|
45
|
+
/vendor/assets/bower_components
|
|
46
|
+
*.bowerrc
|
|
47
|
+
bower.json
|
|
48
|
+
|
|
49
|
+
# Ignore pow environment settings
|
|
50
|
+
.powenv
|
|
51
|
+
|
|
52
|
+
# Ignore Byebug command history file.
|
|
53
|
+
.byebug_history
|
|
54
|
+
|
|
55
|
+
# Ignore node_modules
|
|
56
|
+
node_modules/
|
|
57
|
+
|
|
58
|
+
# Ignore precompiled javascript packs
|
|
59
|
+
/public/packs
|
|
60
|
+
/public/packs-test
|
|
61
|
+
/public/assets
|
|
62
|
+
|
|
63
|
+
# Ignore yarn files
|
|
64
|
+
/yarn-error.log
|
|
65
|
+
yarn-debug.log*
|
|
66
|
+
.yarn-integrity
|
|
67
|
+
|
|
68
|
+
# Ignore uploaded files in development
|
|
69
|
+
/storage/*
|
|
70
|
+
!/storage/.keep
|
|
71
|
+
/public/uploads
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.7.4
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# README
|
|
2
|
+
|
|
3
|
+
This README would normally document whatever steps are necessary to get the
|
|
4
|
+
application up and running.
|
|
5
|
+
|
|
6
|
+
Things you may want to cover:
|
|
7
|
+
|
|
8
|
+
* Ruby version
|
|
9
|
+
|
|
10
|
+
* System dependencies
|
|
11
|
+
|
|
12
|
+
* Configuration
|
|
13
|
+
|
|
14
|
+
* Database creation
|
|
15
|
+
|
|
16
|
+
* Database initialization
|
|
17
|
+
|
|
18
|
+
* How to run the test suite
|
|
19
|
+
|
|
20
|
+
* Services (job queues, cache servers, search engines, etc.)
|
|
21
|
+
|
|
22
|
+
* Deployment instructions
|
|
23
|
+
|
|
24
|
+
* ...
|
data/spec/dummy/Rakefile
ADDED
|
File without changes
|
|
File without changes
|
data/spec/dummy/bin/rake
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
4
|
+
# path to your application root.
|
|
5
|
+
APP_ROOT = File.expand_path('..', __dir__)
|
|
6
|
+
|
|
7
|
+
def system!(*args)
|
|
8
|
+
system(*args) || abort("\n== Command #{args} failed ==")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
FileUtils.chdir APP_ROOT do
|
|
12
|
+
# This script is a way to set up or update your development environment automatically.
|
|
13
|
+
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
|
|
14
|
+
# Add necessary setup steps to this file.
|
|
15
|
+
|
|
16
|
+
puts '== Installing dependencies =='
|
|
17
|
+
system! 'gem install bundler --conservative'
|
|
18
|
+
system('bundle check') || system!('bundle install')
|
|
19
|
+
|
|
20
|
+
# puts "\n== Copying sample files =="
|
|
21
|
+
# unless File.exist?('config/database.yml')
|
|
22
|
+
# FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
|
|
23
|
+
# end
|
|
24
|
+
|
|
25
|
+
puts "\n== Preparing database =="
|
|
26
|
+
system! 'bin/rails db:prepare'
|
|
27
|
+
|
|
28
|
+
puts "\n== Removing old logs and tempfiles =="
|
|
29
|
+
system! 'bin/rails log:clear tmp:clear'
|
|
30
|
+
|
|
31
|
+
puts "\n== Restarting application server =="
|
|
32
|
+
system! 'bin/rails restart'
|
|
33
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative "boot"
|
|
2
|
+
|
|
3
|
+
require "rails"
|
|
4
|
+
# Pick the frameworks you want:
|
|
5
|
+
require "active_model/railtie"
|
|
6
|
+
# require "active_job/railtie"
|
|
7
|
+
require "active_record/railtie"
|
|
8
|
+
# require "active_storage/engine"
|
|
9
|
+
require "action_controller/railtie"
|
|
10
|
+
# require "action_mailer/railtie"
|
|
11
|
+
# require "action_mailbox/engine"
|
|
12
|
+
# require "action_text/engine"
|
|
13
|
+
require "action_view/railtie"
|
|
14
|
+
# require "action_cable/engine"
|
|
15
|
+
# require "sprockets/railtie"
|
|
16
|
+
# require "rails/test_unit/railtie"
|
|
17
|
+
|
|
18
|
+
# Require the gems listed in Gemfile, including any gems
|
|
19
|
+
# you've limited to :test, :development, or :production.
|
|
20
|
+
Bundler.require(*Rails.groups)
|
|
21
|
+
|
|
22
|
+
module Dummy
|
|
23
|
+
class Application < Rails::Application
|
|
24
|
+
# Initialize configuration defaults for originally generated Rails version.
|
|
25
|
+
config.load_defaults 6.1
|
|
26
|
+
|
|
27
|
+
# Configuration for the application, engines, and railties goes here.
|
|
28
|
+
#
|
|
29
|
+
# These settings can be overridden in specific environments using the files
|
|
30
|
+
# in config/environments, which are processed later.
|
|
31
|
+
#
|
|
32
|
+
# config.time_zone = "Central Time (US & Canada)"
|
|
33
|
+
# config.eager_load_paths << Rails.root.join("extras")
|
|
34
|
+
|
|
35
|
+
# Only loads a smaller set of middleware suitable for API only apps.
|
|
36
|
+
# Middleware like session, flash, cookies can be added back manually.
|
|
37
|
+
# Skip views, helpers and assets when generating a new resource.
|
|
38
|
+
config.api_only = true
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3Y2y6yJJt66Phu+Yrv75qlm7edlJxbzIJuKJckgv0Zof/JfCUNXYJqfi1qx2IKMiKrptpTq8j/MrdhqYIw3Tnol4/Rd7+QYlVBZ2fLDh5CUxcqonj0rE3Xzxm9iXm5H8UQtee2GAfQibhLUalF7FbYx9/92UZ36hKfnAdIO75y9gXR5KkiwXSjvOUExA/4RMIsC+QngYpiC1J3JP3aIbymW+KSv0XGbMN5XQDs7/gENUsfTi936UCVz7TmCyz84zdGjwIpKBX1IPDyCIDxs3sb/ndV/TvlSBON9aE/f4juGl4nDAT9GPSPLEjJ0kfrIFs65JWhLceJVPfjIiWqSIUWFCxuFvtdGp4dCmriJhuedjef0PhXIZm2HsWmrRY5w44B28RhGubc/f9/pTZKDqd2UTY839I0SP+i9H--PGXsXE4chMSqSuEV--joWbNc15jbK1ds0sFOcjJw==
|