scatter_gather 0.1.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/.cursor/rules/instructions.mdc +21 -0
- data/.github/dependabot.yml +12 -0
- data/.github/workflows/ci.yml +55 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +11 -0
- data/LICENSE.md +21 -0
- data/README.md +86 -0
- data/Rakefile +32 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/bin/test +5 -0
- data/lib/generators/install_generator.rb +33 -0
- data/lib/generators/scatter_gather_migration_001.rb.erb +16 -0
- data/lib/scatter_gather/version.rb +5 -0
- data/lib/scatter_gather.rb +196 -0
- data/lib/tasks/scatter_gather_tasks.rake +8 -0
- data/rbi/scatter_gather.rbi +135 -0
- data/scatter_gather-0.1.19.gem +0 -0
- data/scatter_gather.gemspec +46 -0
- data/sig/scatter_gather.rbs +116 -0
- data/test/dummy/Rakefile +8 -0
- data/test/dummy/app/assets/stylesheets/application.css +1 -0
- data/test/dummy/app/controllers/application_controller.rb +6 -0
- data/test/dummy/app/helpers/application_helper.rb +4 -0
- data/test/dummy/app/jobs/application_job.rb +9 -0
- data/test/dummy/app/mailers/application_mailer.rb +6 -0
- data/test/dummy/app/models/application_record.rb +5 -0
- data/test/dummy/app/views/layouts/application.html.erb +27 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/app/views/pwa/manifest.json.erb +22 -0
- data/test/dummy/app/views/pwa/service-worker.js +26 -0
- data/test/dummy/bin/dev +2 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +34 -0
- data/test/dummy/config/application.rb +28 -0
- data/test/dummy/config/boot.rb +7 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/database.sqlite3.yml +32 -0
- data/test/dummy/config/database.yml +32 -0
- data/test/dummy/config/environment.rb +7 -0
- data/test/dummy/config/environments/development.rb +71 -0
- data/test/dummy/config/environments/production.rb +91 -0
- data/test/dummy/config/environments/test.rb +55 -0
- data/test/dummy/config/initializers/content_security_policy.rb +27 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +10 -0
- data/test/dummy/config/initializers/inflections.rb +18 -0
- data/test/dummy/config/locales/en.yml +31 -0
- data/test/dummy/config/puma.rb +40 -0
- data/test/dummy/config/routes.rb +16 -0
- data/test/dummy/config/storage.yml +34 -0
- data/test/dummy/config.ru +8 -0
- data/test/dummy/db/migrate/20250101000001_add_scatter_gather_completions.rb +16 -0
- data/test/dummy/db/schema.rb +23 -0
- data/test/dummy/public/400.html +114 -0
- data/test/dummy/public/404.html +114 -0
- data/test/dummy/public/406-unsupported-browser.html +114 -0
- data/test/dummy/public/422.html +114 -0
- data/test/dummy/public/500.html +114 -0
- data/test/dummy/public/icon.png +0 -0
- data/test/dummy/public/icon.svg +3 -0
- data/test/scatter_gather_test.rb +180 -0
- data/test/test_helper.rb +17 -0
- metadata +285 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# Scatter-Gather Pattern for ActiveJob
|
3
|
+
#
|
4
|
+
# This module provides a scatter-gather pattern for coordinating job execution.
|
5
|
+
# Jobs can wait for other jobs to complete before executing, with configurable
|
6
|
+
# polling, retry, and timeout behavior.
|
7
|
+
#
|
8
|
+
# Example workflow:
|
9
|
+
# # Start some scatter jobs
|
10
|
+
# email_parser_job = EmailParserJob.perform_later(email_id: 123)
|
11
|
+
# attachment_processor_job = AttachmentProcessorJob.perform_later(email_id: 123)
|
12
|
+
# ai_categorizer_job = AICategorizerJob.perform_later(email_id: 123)
|
13
|
+
#
|
14
|
+
# # Create a gather job that waits for all dependencies to complete
|
15
|
+
# NotifyCompleteJob.gather(email_parser_job, attachment_processor_job, ai_categorizer_job).perform_later
|
16
|
+
#
|
17
|
+
# The gather job will:
|
18
|
+
# - Check if all dependencies are complete
|
19
|
+
# - If complete: enqueue the target job immediately
|
20
|
+
# - If not complete: poll every 2 seconds (configurable), re-enqueuing itself
|
21
|
+
# - After 10 attempts (configurable): discard with error reporting
|
22
|
+
#
|
23
|
+
# Configuration options:
|
24
|
+
# - max_attempts: Number of polling attempts before giving up (default: 10)
|
25
|
+
# - poll_interval: Time between polling attempts (default: 2.seconds)
|
26
|
+
#
|
27
|
+
# Example with custom configuration:
|
28
|
+
# TouchingJob.gather(jobs, poll_interval: 0.2.seconds, max_attempts: 4).perform_later(final_path)
|
29
|
+
module ScatterGather
|
30
|
+
extend ActiveSupport::Concern
|
31
|
+
DEFAULT_GATHER_CONFIG = T.let({
|
32
|
+
max_attempts: 10,
|
33
|
+
poll_interval: 2.seconds
|
34
|
+
}.freeze, T.untyped)
|
35
|
+
VERSION = T.let("0.1.0", T.untyped)
|
36
|
+
|
37
|
+
# sord omit - no YARD return type given, using untyped
|
38
|
+
# Updates the completions table with the status of this job
|
39
|
+
sig { returns(T.untyped) }
|
40
|
+
def register_completion_for_gathering; end
|
41
|
+
|
42
|
+
class Completion < ActiveRecord::Base
|
43
|
+
# sord omit - no YARD type given for "active_job_ids", using untyped
|
44
|
+
# sord omit - no YARD return type given, using untyped
|
45
|
+
sig { params(active_job_ids: T.untyped).returns(T.untyped) }
|
46
|
+
def self.collect_statuses(active_job_ids); end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Proxy class that mimics ActiveJob behavior for gather jobs
|
50
|
+
class GatherJobProxy
|
51
|
+
# sord omit - no YARD type given for "target_class", using untyped
|
52
|
+
# sord omit - no YARD type given for "ids", using untyped
|
53
|
+
# sord omit - no YARD type given for "config", using untyped
|
54
|
+
sig { params(target_class: T.untyped, ids: T.untyped, config: T.untyped).void }
|
55
|
+
def initialize(target_class, ids, config); end
|
56
|
+
|
57
|
+
# Mimic ActiveJob's perform_later method
|
58
|
+
#
|
59
|
+
# _@param_ `args` — Positional arguments to pass to the target job's perform method
|
60
|
+
#
|
61
|
+
# _@param_ `kwargs` — Keyword arguments to pass to the target job's perform method
|
62
|
+
#
|
63
|
+
# _@return_ — Enqueues the gather job
|
64
|
+
sig { params(args: T::Array[T.untyped], kwargs: T::Hash[T.untyped, T.untyped]).void }
|
65
|
+
def perform_later(*args, **kwargs); end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Custom exception for when gather job exhausts attempts
|
69
|
+
class DependencyTimeoutError < StandardError
|
70
|
+
# sord omit - no YARD type given for "max_attempts", using untyped
|
71
|
+
# sord omit - no YARD type given for "dependency_status", using untyped
|
72
|
+
sig { params(max_attempts: T.untyped, dependency_status: T.untyped).void }
|
73
|
+
def initialize(max_attempts, dependency_status); end
|
74
|
+
|
75
|
+
# sord omit - no YARD type given for :dependency_status, using untyped
|
76
|
+
# Returns the value of attribute dependency_status.
|
77
|
+
sig { returns(T.untyped) }
|
78
|
+
attr_reader :dependency_status
|
79
|
+
end
|
80
|
+
|
81
|
+
# Internal job class for polling and coordinating gather operations
|
82
|
+
class GatherJob < ActiveJob::Base
|
83
|
+
include ScatterGather
|
84
|
+
|
85
|
+
# sord omit - no YARD return type given, using untyped
|
86
|
+
sig { returns(T.untyped) }
|
87
|
+
def logger; end
|
88
|
+
|
89
|
+
# sord omit - no YARD type given for "wait_for_active_job_ids:", using untyped
|
90
|
+
# sord omit - no YARD type given for "target_job:", using untyped
|
91
|
+
# sord omit - no YARD type given for "gather_config:", using untyped
|
92
|
+
# sord omit - no YARD type given for "remaining_attempts:", using untyped
|
93
|
+
# sord omit - no YARD return type given, using untyped
|
94
|
+
sig do
|
95
|
+
params(
|
96
|
+
wait_for_active_job_ids: T.untyped,
|
97
|
+
target_job: T.untyped,
|
98
|
+
gather_config: T.untyped,
|
99
|
+
remaining_attempts: T.untyped
|
100
|
+
).returns(T.untyped)
|
101
|
+
end
|
102
|
+
def perform(wait_for_active_job_ids:, target_job:, gather_config:, remaining_attempts:); end
|
103
|
+
|
104
|
+
# sord omit - no YARD type given for "hash", using untyped
|
105
|
+
# sord omit - no YARD return type given, using untyped
|
106
|
+
sig { params(hash: T.untyped).returns(T.untyped) }
|
107
|
+
def tally_in_logger_format(hash); end
|
108
|
+
|
109
|
+
# sord omit - no YARD type given for "target_job", using untyped
|
110
|
+
# sord omit - no YARD return type given, using untyped
|
111
|
+
sig { params(target_job: T.untyped).returns(T.untyped) }
|
112
|
+
def perform_target_later_from_args(target_job); end
|
113
|
+
|
114
|
+
# sord omit - no YARD return type given, using untyped
|
115
|
+
# Updates the completions table with the status of this job
|
116
|
+
sig { returns(T.untyped) }
|
117
|
+
def register_completion_for_gathering; end
|
118
|
+
end
|
119
|
+
|
120
|
+
# The generator is used to install ScatterGather. It adds the migration that creates
|
121
|
+
# the scatter_gather_completions table.
|
122
|
+
# Run it with `bin/rails g scatter_gather:install` in your console.
|
123
|
+
class InstallGenerator < Rails::Generators::Base
|
124
|
+
include ActiveRecord::Generators::Migration
|
125
|
+
|
126
|
+
# sord omit - no YARD return type given, using untyped
|
127
|
+
# Generates migration file that creates the scatter_gather_completions table.
|
128
|
+
sig { returns(T.untyped) }
|
129
|
+
def create_migration_file; end
|
130
|
+
|
131
|
+
# sord omit - no YARD return type given, using untyped
|
132
|
+
sig { returns(T.untyped) }
|
133
|
+
def migration_version; end
|
134
|
+
end
|
135
|
+
end
|
Binary file
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/scatter_gather/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "scatter_gather"
|
7
|
+
spec.version = ScatterGather::VERSION
|
8
|
+
spec.authors = ["Julik Tarkhanov"]
|
9
|
+
spec.email = ["me@julik.nl"]
|
10
|
+
spec.license = "MIT"
|
11
|
+
|
12
|
+
spec.summary = "Effortless step workflows that embed nicely inside Rails"
|
13
|
+
spec.description = "Step workflows for Rails/ActiveRecord"
|
14
|
+
spec.homepage = "https://scattergather.dev"
|
15
|
+
spec.required_ruby_version = ">= 3.1.0"
|
16
|
+
|
17
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
18
|
+
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
spec.metadata["source_code_uri"] = "https://github.com/julik/scatter_gather"
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/julik/scatter_gather/blob/main/CHANGELOG.md"
|
22
|
+
|
23
|
+
spec.files = Dir.chdir(__dir__) do
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
File.basename(f).start_with?(".")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.add_dependency "activerecord", ">= 7"
|
34
|
+
spec.add_dependency "activejob"
|
35
|
+
spec.add_dependency "railties"
|
36
|
+
|
37
|
+
spec.add_development_dependency "minitest"
|
38
|
+
spec.add_development_dependency "rails", "~> 7.0"
|
39
|
+
spec.add_development_dependency "sqlite3"
|
40
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
41
|
+
spec.add_development_dependency "standard", "~> 1.50.0", "< 2.0"
|
42
|
+
spec.add_development_dependency "magic_frozen_string_literal"
|
43
|
+
spec.add_development_dependency "yard"
|
44
|
+
spec.add_development_dependency "redcarpet" # needed for the yard gem to enable Github Flavored Markdown
|
45
|
+
spec.add_development_dependency "sord"
|
46
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Scatter-Gather Pattern for ActiveJob
|
2
|
+
#
|
3
|
+
# This module provides a scatter-gather pattern for coordinating job execution.
|
4
|
+
# Jobs can wait for other jobs to complete before executing, with configurable
|
5
|
+
# polling, retry, and timeout behavior.
|
6
|
+
#
|
7
|
+
# Example workflow:
|
8
|
+
# # Start some scatter jobs
|
9
|
+
# email_parser_job = EmailParserJob.perform_later(email_id: 123)
|
10
|
+
# attachment_processor_job = AttachmentProcessorJob.perform_later(email_id: 123)
|
11
|
+
# ai_categorizer_job = AICategorizerJob.perform_later(email_id: 123)
|
12
|
+
#
|
13
|
+
# # Create a gather job that waits for all dependencies to complete
|
14
|
+
# NotifyCompleteJob.gather(email_parser_job, attachment_processor_job, ai_categorizer_job).perform_later
|
15
|
+
#
|
16
|
+
# The gather job will:
|
17
|
+
# - Check if all dependencies are complete
|
18
|
+
# - If complete: enqueue the target job immediately
|
19
|
+
# - If not complete: poll every 2 seconds (configurable), re-enqueuing itself
|
20
|
+
# - After 10 attempts (configurable): discard with error reporting
|
21
|
+
#
|
22
|
+
# Configuration options:
|
23
|
+
# - max_attempts: Number of polling attempts before giving up (default: 10)
|
24
|
+
# - poll_interval: Time between polling attempts (default: 2.seconds)
|
25
|
+
#
|
26
|
+
# Example with custom configuration:
|
27
|
+
# TouchingJob.gather(jobs, poll_interval: 0.2.seconds, max_attempts: 4).perform_later(final_path)
|
28
|
+
module ScatterGather
|
29
|
+
extend ActiveSupport::Concern
|
30
|
+
DEFAULT_GATHER_CONFIG: untyped
|
31
|
+
VERSION: untyped
|
32
|
+
|
33
|
+
# sord omit - no YARD return type given, using untyped
|
34
|
+
# Updates the completions table with the status of this job
|
35
|
+
def register_completion_for_gathering: () -> untyped
|
36
|
+
|
37
|
+
class Completion < ActiveRecord::Base
|
38
|
+
# sord omit - no YARD type given for "active_job_ids", using untyped
|
39
|
+
# sord omit - no YARD return type given, using untyped
|
40
|
+
def self.collect_statuses: (untyped active_job_ids) -> untyped
|
41
|
+
end
|
42
|
+
|
43
|
+
# Proxy class that mimics ActiveJob behavior for gather jobs
|
44
|
+
class GatherJobProxy
|
45
|
+
# sord omit - no YARD type given for "target_class", using untyped
|
46
|
+
# sord omit - no YARD type given for "ids", using untyped
|
47
|
+
# sord omit - no YARD type given for "config", using untyped
|
48
|
+
def initialize: (untyped target_class, untyped ids, untyped config) -> void
|
49
|
+
|
50
|
+
# Mimic ActiveJob's perform_later method
|
51
|
+
#
|
52
|
+
# _@param_ `args` — Positional arguments to pass to the target job's perform method
|
53
|
+
#
|
54
|
+
# _@param_ `kwargs` — Keyword arguments to pass to the target job's perform method
|
55
|
+
#
|
56
|
+
# _@return_ — Enqueues the gather job
|
57
|
+
def perform_later: (*::Array[untyped] args, **::Hash[untyped, untyped] kwargs) -> void
|
58
|
+
end
|
59
|
+
|
60
|
+
# Custom exception for when gather job exhausts attempts
|
61
|
+
class DependencyTimeoutError < StandardError
|
62
|
+
# sord omit - no YARD type given for "max_attempts", using untyped
|
63
|
+
# sord omit - no YARD type given for "dependency_status", using untyped
|
64
|
+
def initialize: (untyped max_attempts, untyped dependency_status) -> void
|
65
|
+
|
66
|
+
# sord omit - no YARD type given for :dependency_status, using untyped
|
67
|
+
# Returns the value of attribute dependency_status.
|
68
|
+
attr_reader dependency_status: untyped
|
69
|
+
end
|
70
|
+
|
71
|
+
# Internal job class for polling and coordinating gather operations
|
72
|
+
class GatherJob < ActiveJob::Base
|
73
|
+
include ScatterGather
|
74
|
+
|
75
|
+
# sord omit - no YARD return type given, using untyped
|
76
|
+
def logger: () -> untyped
|
77
|
+
|
78
|
+
# sord omit - no YARD type given for "wait_for_active_job_ids:", using untyped
|
79
|
+
# sord omit - no YARD type given for "target_job:", using untyped
|
80
|
+
# sord omit - no YARD type given for "gather_config:", using untyped
|
81
|
+
# sord omit - no YARD type given for "remaining_attempts:", using untyped
|
82
|
+
# sord omit - no YARD return type given, using untyped
|
83
|
+
def perform: (
|
84
|
+
wait_for_active_job_ids: untyped,
|
85
|
+
target_job: untyped,
|
86
|
+
gather_config: untyped,
|
87
|
+
remaining_attempts: untyped
|
88
|
+
) -> untyped
|
89
|
+
|
90
|
+
# sord omit - no YARD type given for "hash", using untyped
|
91
|
+
# sord omit - no YARD return type given, using untyped
|
92
|
+
def tally_in_logger_format: (untyped hash) -> untyped
|
93
|
+
|
94
|
+
# sord omit - no YARD type given for "target_job", using untyped
|
95
|
+
# sord omit - no YARD return type given, using untyped
|
96
|
+
def perform_target_later_from_args: (untyped target_job) -> untyped
|
97
|
+
|
98
|
+
# sord omit - no YARD return type given, using untyped
|
99
|
+
# Updates the completions table with the status of this job
|
100
|
+
def register_completion_for_gathering: () -> untyped
|
101
|
+
end
|
102
|
+
|
103
|
+
# The generator is used to install ScatterGather. It adds the migration that creates
|
104
|
+
# the scatter_gather_completions table.
|
105
|
+
# Run it with `bin/rails g scatter_gather:install` in your console.
|
106
|
+
class InstallGenerator < Rails::Generators::Base
|
107
|
+
include ActiveRecord::Generators::Migration
|
108
|
+
|
109
|
+
# sord omit - no YARD return type given, using untyped
|
110
|
+
# Generates migration file that creates the scatter_gather_completions table.
|
111
|
+
def create_migration_file: () -> untyped
|
112
|
+
|
113
|
+
# sord omit - no YARD return type given, using untyped
|
114
|
+
def migration_version: () -> untyped
|
115
|
+
end
|
116
|
+
end
|
data/test/dummy/Rakefile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
4
|
+
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
5
|
+
|
6
|
+
require_relative "config/application"
|
7
|
+
|
8
|
+
Rails.application.load_tasks
|
@@ -0,0 +1 @@
|
|
1
|
+
/* Application styles */
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ApplicationJob < ActiveJob::Base
|
4
|
+
# Automatically retry jobs that encountered a deadlock
|
5
|
+
# retry_on ActiveRecord::Deadlocked
|
6
|
+
|
7
|
+
# Most jobs are safe to ignore if the underlying records are no longer available
|
8
|
+
# discard_on ActiveJob::DeserializationError
|
9
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= content_for(:title) || "Dummy" %></title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
7
|
+
<meta name="mobile-web-app-capable" content="yes">
|
8
|
+
<%= csrf_meta_tags %>
|
9
|
+
<%= csp_meta_tag %>
|
10
|
+
|
11
|
+
<%= yield :head %>
|
12
|
+
|
13
|
+
<%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
|
14
|
+
<%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
|
15
|
+
|
16
|
+
<link rel="icon" href="/icon.png" type="image/png">
|
17
|
+
<link rel="icon" href="/icon.svg" type="image/svg+xml">
|
18
|
+
<link rel="apple-touch-icon" href="/icon.png">
|
19
|
+
|
20
|
+
<%# Includes all stylesheet files in app/assets/stylesheets %>
|
21
|
+
<%= stylesheet_link_tag "application" %>
|
22
|
+
</head>
|
23
|
+
|
24
|
+
<body>
|
25
|
+
<%= yield %>
|
26
|
+
</body>
|
27
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= yield %>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"name": "Dummy",
|
3
|
+
"icons": [
|
4
|
+
{
|
5
|
+
"src": "/icon.png",
|
6
|
+
"type": "image/png",
|
7
|
+
"sizes": "512x512"
|
8
|
+
},
|
9
|
+
{
|
10
|
+
"src": "/icon.png",
|
11
|
+
"type": "image/png",
|
12
|
+
"sizes": "512x512",
|
13
|
+
"purpose": "maskable"
|
14
|
+
}
|
15
|
+
],
|
16
|
+
"start_url": "/",
|
17
|
+
"display": "standalone",
|
18
|
+
"scope": "/",
|
19
|
+
"description": "Dummy.",
|
20
|
+
"theme_color": "red",
|
21
|
+
"background_color": "red"
|
22
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
// Add a service worker for processing Web Push notifications:
|
2
|
+
//
|
3
|
+
// self.addEventListener("push", async (event) => {
|
4
|
+
// const { title, options } = await event.data.json()
|
5
|
+
// event.waitUntil(self.registration.showNotification(title, options))
|
6
|
+
// })
|
7
|
+
//
|
8
|
+
// self.addEventListener("notificationclick", function(event) {
|
9
|
+
// event.notification.close()
|
10
|
+
// event.waitUntil(
|
11
|
+
// clients.matchAll({ type: "window" }).then((clientList) => {
|
12
|
+
// for (let i = 0; i < clientList.length; i++) {
|
13
|
+
// let client = clientList[i]
|
14
|
+
// let clientPath = (new URL(client.url)).pathname
|
15
|
+
//
|
16
|
+
// if (clientPath == event.notification.data.path && "focus" in client) {
|
17
|
+
// return client.focus()
|
18
|
+
// }
|
19
|
+
// }
|
20
|
+
//
|
21
|
+
// if (clients.openWindow) {
|
22
|
+
// return clients.openWindow(event.notification.data.path)
|
23
|
+
// }
|
24
|
+
// })
|
25
|
+
// )
|
26
|
+
// })
|
data/test/dummy/bin/dev
ADDED
data/test/dummy/bin/rake
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
APP_ROOT = File.expand_path("..", __dir__)
|
5
|
+
|
6
|
+
def system!(*args)
|
7
|
+
system(*args, exception: true)
|
8
|
+
end
|
9
|
+
|
10
|
+
FileUtils.chdir APP_ROOT do
|
11
|
+
# This script is a way to set up or update your development environment automatically.
|
12
|
+
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
|
13
|
+
# Add necessary setup steps to this file.
|
14
|
+
|
15
|
+
puts "== Installing dependencies =="
|
16
|
+
system("bundle check") || system!("bundle install")
|
17
|
+
|
18
|
+
# puts "\n== Copying sample files =="
|
19
|
+
# unless File.exist?("config/database.yml")
|
20
|
+
# FileUtils.cp "config/database.yml.sample", "config/database.yml"
|
21
|
+
# end
|
22
|
+
|
23
|
+
puts "\n== Preparing database =="
|
24
|
+
system! "bin/rails db:prepare"
|
25
|
+
|
26
|
+
puts "\n== Removing old logs and tempfiles =="
|
27
|
+
system! "bin/rails log:clear tmp:clear"
|
28
|
+
|
29
|
+
unless ARGV.include?("--skip-server")
|
30
|
+
puts "\n== Starting development server =="
|
31
|
+
$stdout.flush # flush the output before exec(2) so that it displays
|
32
|
+
exec "bin/dev"
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "boot"
|
4
|
+
|
5
|
+
require "rails/all"
|
6
|
+
|
7
|
+
# Require the gems listed in Gemfile, including any gems
|
8
|
+
# you've limited to :test, :development, or :production.
|
9
|
+
Bundler.require(*Rails.groups)
|
10
|
+
|
11
|
+
module Dummy
|
12
|
+
class Application < Rails::Application
|
13
|
+
config.load_defaults Rails::VERSION::STRING.to_f
|
14
|
+
|
15
|
+
# Please, add to the `ignore` list any other `lib` subdirectories that do
|
16
|
+
# not contain `.rb` files, or that should not be reloaded or eager loaded.
|
17
|
+
# Common ones are `templates`, `generators`, or `middleware`, for example.
|
18
|
+
config.autoload_lib(ignore: %w[assets tasks])
|
19
|
+
|
20
|
+
# Configuration for the application, engines, and railties goes here.
|
21
|
+
#
|
22
|
+
# These settings can be overridden in specific environments using the files
|
23
|
+
# in config/environments, which are processed later.
|
24
|
+
#
|
25
|
+
# config.time_zone = "Central Time (US & Canada)"
|
26
|
+
# config.eager_load_paths << Rails.root.join("extras")
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Set up gems listed in the Gemfile.
|
4
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
|
5
|
+
|
6
|
+
require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
|
7
|
+
$LOAD_PATH.unshift File.expand_path("../../../lib", __dir__)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# SQLite. Versions 3.8.0 and up are supported.
|
2
|
+
# gem install sqlite3
|
3
|
+
#
|
4
|
+
# Ensure the SQLite 3 gem is defined in your Gemfile
|
5
|
+
# gem "sqlite3"
|
6
|
+
#
|
7
|
+
default: &default
|
8
|
+
adapter: sqlite3
|
9
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
10
|
+
timeout: 5000
|
11
|
+
|
12
|
+
development:
|
13
|
+
<<: *default
|
14
|
+
database: storage/development.sqlite3
|
15
|
+
|
16
|
+
# Warning: The database defined as "test" will be erased and
|
17
|
+
# re-generated from your development database when you run "rake".
|
18
|
+
# Do not set this db to the same as development or production.
|
19
|
+
test:
|
20
|
+
<<: *default
|
21
|
+
database: storage/test.sqlite3
|
22
|
+
|
23
|
+
|
24
|
+
# SQLite3 write its data on the local filesystem, as such it requires
|
25
|
+
# persistent disks. If you are deploying to a managed service, you should
|
26
|
+
# make sure it provides disk persistence, as many don't.
|
27
|
+
#
|
28
|
+
# Similarly, if you deploy your application as a Docker container, you must
|
29
|
+
# ensure the database is located in a persisted volume.
|
30
|
+
production:
|
31
|
+
<<: *default
|
32
|
+
# database: path/to/persistent/storage/production.sqlite3
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# SQLite. Versions 3.8.0 and up are supported.
|
2
|
+
# gem install sqlite3
|
3
|
+
#
|
4
|
+
# Ensure the SQLite 3 gem is defined in your Gemfile
|
5
|
+
# gem "sqlite3"
|
6
|
+
#
|
7
|
+
default: &default
|
8
|
+
adapter: sqlite3
|
9
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
10
|
+
timeout: 5000
|
11
|
+
|
12
|
+
development:
|
13
|
+
<<: *default
|
14
|
+
database: storage/development.sqlite3
|
15
|
+
|
16
|
+
# Warning: The database defined as "test" will be erased and
|
17
|
+
# re-generated from your development database when you run "rake".
|
18
|
+
# Do not set this db to the same as development or production.
|
19
|
+
test:
|
20
|
+
<<: *default
|
21
|
+
database: storage/test.sqlite3
|
22
|
+
|
23
|
+
|
24
|
+
# SQLite3 write its data on the local filesystem, as such it requires
|
25
|
+
# persistent disks. If you are deploying to a managed service, you should
|
26
|
+
# make sure it provides disk persistence, as many don't.
|
27
|
+
#
|
28
|
+
# Similarly, if you deploy your application as a Docker container, you must
|
29
|
+
# ensure the database is located in a persisted volume.
|
30
|
+
production:
|
31
|
+
<<: *default
|
32
|
+
# database: path/to/persistent/storage/production.sqlite3
|