develoz-rails 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/.github/workflows/ci.yml +31 -0
- data/.github/workflows/release.yml +459 -0
- data/.rubocop.yml +52 -0
- data/.sisyphus/notepads/develoz-rails-template/learnings.md +855 -0
- data/.tool-versions +2 -0
- data/CHANGELOG.md +28 -0
- data/CONTRIBUTING.md +120 -0
- data/README.md +76 -0
- data/Rakefile +18 -0
- data/config/generators.yml +105 -0
- data/docs/guide/installation.md +574 -0
- data/exe/develoz +6 -0
- data/install +98 -0
- data/lib/develoz/canonical_fetcher.rb +56 -0
- data/lib/develoz/cli.rb +84 -0
- data/lib/develoz/generators/base.rb +134 -0
- data/lib/develoz/manifest.rb +44 -0
- data/lib/develoz/options.rb +55 -0
- data/lib/develoz/version.rb +5 -0
- data/lib/develoz/version_resolver.rb +103 -0
- data/lib/develoz.rb +15 -0
- data/lib/generators/develoz/active_resource/active_resource_generator.rb +25 -0
- data/lib/generators/develoz/active_resource/templates/application_resource.rb.tt +7 -0
- data/lib/generators/develoz/active_resource/templates/example_resource.rb.tt +5 -0
- data/lib/generators/develoz/admin/admin_generator.rb +44 -0
- data/lib/generators/develoz/admin/templates/admin_base_controller.rb.tt +7 -0
- data/lib/generators/develoz/admin/templates/admin_layout.html.erb.tt +38 -0
- data/lib/generators/develoz/admin/templates/dashboard_controller.rb.tt +7 -0
- data/lib/generators/develoz/admin/templates/dashboard_index.html.erb.tt +2 -0
- data/lib/generators/develoz/admin/templates/routes.rb.tt +3 -0
- data/lib/generators/develoz/agents_docs/agents_docs_generator.rb +50 -0
- data/lib/generators/develoz/agents_docs/templates/.github/pull_request_template.md.tt +13 -0
- data/lib/generators/develoz/agents_docs/templates/AGENTS.md.tt +36 -0
- data/lib/generators/develoz/agents_docs/templates/docs/development.md.tt +121 -0
- data/lib/generators/develoz/agents_docs/templates/docs/performance.md.tt +46 -0
- data/lib/generators/develoz/agents_docs/templates/docs/testing.md.tt +92 -0
- data/lib/generators/develoz/agents_docs/templates/spec/cassettes/Example_API/fetches_data_from_an_external_API.yml.tt +23 -0
- data/lib/generators/develoz/agents_docs/templates/spec/requests/example_api_spec.rb.tt +14 -0
- data/lib/generators/develoz/agents_docs/templates/spec/support/faraday.rb.tt +19 -0
- data/lib/generators/develoz/agents_docs/templates/spec/support/vcr.rb.tt +11 -0
- data/lib/generators/develoz/api/api_generator.rb +55 -0
- data/lib/generators/develoz/api/templates/app/blueprints/example_blueprint.rb.tt +6 -0
- data/lib/generators/develoz/api/templates/app/controllers/api/v1/base_controller.rb.tt +96 -0
- data/lib/generators/develoz/api/templates/config/initializers/blueprinter.rb.tt +16 -0
- data/lib/generators/develoz/api/templates/config/initializers/rswag_api.rb.tt +15 -0
- data/lib/generators/develoz/api/templates/config/initializers/rswag_ui.rb.tt +17 -0
- data/lib/generators/develoz/api/templates/spec/requests/api/v1/examples_spec.rb.tt +25 -0
- data/lib/generators/develoz/auth/auth_generator.rb +82 -0
- data/lib/generators/develoz/auth/templates/app/controllers/concerns/authentication.rb.tt +53 -0
- data/lib/generators/develoz/auth/templates/app/controllers/passwords_controller.rb.tt +35 -0
- data/lib/generators/develoz/auth/templates/app/controllers/sessions_controller.rb.tt +22 -0
- data/lib/generators/develoz/auth/templates/app/mailers/passwords_mailer.rb.tt +8 -0
- data/lib/generators/develoz/auth/templates/app/models/current.rb.tt +5 -0
- data/lib/generators/develoz/auth/templates/app/models/user.rb.tt +7 -0
- data/lib/generators/develoz/auth/templates/app/views/passwords/edit.html.erb.tt +9 -0
- data/lib/generators/develoz/auth/templates/app/views/passwords_mailer/reset.html.erb.tt +6 -0
- data/lib/generators/develoz/auth/templates/app/views/sessions/new.html.erb.tt +11 -0
- data/lib/generators/develoz/auth/templates/db/migrate/create_users.rb.tt +13 -0
- data/lib/generators/develoz/auth/templates/spec/requests/passwords_spec.rb.tt +60 -0
- data/lib/generators/develoz/auth/templates/spec/requests/sessions_spec.rb.tt +37 -0
- data/lib/generators/develoz/ci/ci_generator.rb +66 -0
- data/lib/generators/develoz/ci/templates/.github/workflows/ci.yml.tt +18 -0
- data/lib/generators/develoz/ci/templates/.haml-lint.yml.tt +12 -0
- data/lib/generators/develoz/ci/templates/.markdownlint.json.tt +10 -0
- data/lib/generators/develoz/ci/templates/.reek.yml.tt +28 -0
- data/lib/generators/develoz/ci/templates/.rubocop.yml.tt +22 -0
- data/lib/generators/develoz/ci/templates/.stylelintrc.json.tt +4 -0
- data/lib/generators/develoz/ci/templates/.yamllint.tt +14 -0
- data/lib/generators/develoz/ci/templates/Rakefile.tt +73 -0
- data/lib/generators/develoz/ci/templates/bin/ci.tt +6 -0
- data/lib/generators/develoz/ci/templates/biome.json.tt +41 -0
- data/lib/generators/develoz/ci/templates/config/ci.rb.tt +23 -0
- data/lib/generators/develoz/concerns/concerns_generator.rb +48 -0
- data/lib/generators/develoz/concerns/templates/app/models/concerns/configurable.rb.tt +120 -0
- data/lib/generators/develoz/concerns/templates/app/models/concerns/optimized_finders.rb.tt +93 -0
- data/lib/generators/develoz/concerns/templates/app/models/concerns/searchable_concern.rb.tt +31 -0
- data/lib/generators/develoz/concerns/templates/app/models/concerns/transitionable.rb.tt +144 -0
- data/lib/generators/develoz/concerns/templates/app/models/configuration.rb.tt +104 -0
- data/lib/generators/develoz/concerns/templates/db/migrate/add_status_transitions.rb.tt +30 -0
- data/lib/generators/develoz/concerns/templates/db/migrate/create_configurations.rb.tt +25 -0
- data/lib/generators/develoz/concerns/templates/spec/models/concerns/configurable_spec.rb.tt +172 -0
- data/lib/generators/develoz/concerns/templates/spec/models/concerns/optimized_finders_spec.rb.tt +200 -0
- data/lib/generators/develoz/concerns/templates/spec/models/concerns/searchable_concern_spec.rb.tt +84 -0
- data/lib/generators/develoz/concerns/templates/spec/models/concerns/transitionable_spec.rb.tt +234 -0
- data/lib/generators/develoz/concerns/templates/spec/models/configuration_spec.rb.tt +223 -0
- data/lib/generators/develoz/database/database_generator.rb +44 -0
- data/lib/generators/develoz/database/templates/config/database.yml.tt +53 -0
- data/lib/generators/develoz/database/templates/config/initializers/pg_search.rb.tt +5 -0
- data/lib/generators/develoz/database/templates/db/migrate/create_pg_extensions.rb.tt +13 -0
- data/lib/generators/develoz/db_backup/db_backup_generator.rb +47 -0
- data/lib/generators/develoz/db_backup/templates/backup_rake.tt +48 -0
- data/lib/generators/develoz/db_backup/templates/bin_db_backup.tt +25 -0
- data/lib/generators/develoz/db_backup/templates/compose_service.tt +14 -0
- data/lib/generators/develoz/doc_specs/doc_specs_generator.rb +33 -0
- data/lib/generators/develoz/doc_specs/templates/bin/generate-docs.tt +392 -0
- data/lib/generators/develoz/doc_specs/templates/lib/tasks/docs_check.rake.tt +8 -0
- data/lib/generators/develoz/doc_specs/templates/spec/support/doc_screenshot_helper.rb.tt +17 -0
- data/lib/generators/develoz/doc_specs/templates/spec/system/example_doc_spec.rb.tt +38 -0
- data/lib/generators/develoz/docker/docker_generator.rb +54 -0
- data/lib/generators/develoz/docker/templates/bin_dev.tt +5 -0
- data/lib/generators/develoz/docker/templates/bin_docker_entrypoint.tt +8 -0
- data/lib/generators/develoz/docker/templates/bin_run.tt +152 -0
- data/lib/generators/develoz/docker/templates/bin_setup.tt +15 -0
- data/lib/generators/develoz/docker/templates/docker-compose.yml.tt +59 -0
- data/lib/generators/develoz/docker/templates/dockerfile_dev.tt +24 -0
- data/lib/generators/develoz/docs_render/docs_render_generator.rb +46 -0
- data/lib/generators/develoz/docs_render/templates/app/assets/stylesheets/documentation.scss.tt +145 -0
- data/lib/generators/develoz/docs_render/templates/app/controllers/docs_controller.rb.tt +7 -0
- data/lib/generators/develoz/docs_render/templates/app/javascript/docs.js.tt +15 -0
- data/lib/generators/develoz/docs_render/templates/app/models/document.rb.tt +189 -0
- data/lib/generators/develoz/docs_render/templates/app/views/docs/show.html.erb.tt +7 -0
- data/lib/generators/develoz/docs_render/templates/config/initializers/redcarpet_rouge.rb.tt +16 -0
- data/lib/generators/develoz/frontend_core/frontend_core_generator.rb +40 -0
- data/lib/generators/develoz/frontend_core/templates/.annotaterb.yml.tt +16 -0
- data/lib/generators/develoz/frontend_core/templates/config/importmap.rb.tt +12 -0
- data/lib/generators/develoz/frontend_core/templates/config/initializers/pagy.rb.tt +10 -0
- data/lib/generators/develoz/install/install_generator.rb +51 -0
- data/lib/generators/develoz/kamal/kamal_generator.rb +59 -0
- data/lib/generators/develoz/kamal/templates/accessories_postgres.yml.tt +14 -0
- data/lib/generators/develoz/kamal/templates/deploy.yml.tt +43 -0
- data/lib/generators/develoz/kamal/templates/dockerfile_prod.tt +54 -0
- data/lib/generators/develoz/kamal/templates/kamal_secrets.tt +22 -0
- data/lib/generators/develoz/maintenance/maintenance_generator.rb +29 -0
- data/lib/generators/develoz/maintenance/templates/app/tasks/maintenance/example_task.rb.tt +20 -0
- data/lib/generators/develoz/maintenance/templates/lib/tasks/maintenance_counters.rake.tt +27 -0
- data/lib/generators/develoz/push/push_generator.rb +61 -0
- data/lib/generators/develoz/push/templates/app/javascript/pwa/subscription.js.tt +57 -0
- data/lib/generators/develoz/push/templates/app/models/push_subscription.rb.tt +14 -0
- data/lib/generators/develoz/push/templates/app/services/push_notification_service.rb.tt +36 -0
- data/lib/generators/develoz/push/templates/app/views/pwa/sw_push_handlers.js.tt +49 -0
- data/lib/generators/develoz/push/templates/constants_env.tt +5 -0
- data/lib/generators/develoz/push/templates/db/migrate/create_push_subscriptions.rb.tt +15 -0
- data/lib/generators/develoz/pwa/pwa_generator.rb +46 -0
- data/lib/generators/develoz/pwa/templates/app/controllers/pwa_controller.rb.tt +17 -0
- data/lib/generators/develoz/pwa/templates/app/javascript/pwa/registration.js.tt +25 -0
- data/lib/generators/develoz/pwa/templates/app/views/pwa/manifest.json.erb.tt +24 -0
- data/lib/generators/develoz/pwa/templates/app/views/pwa/offline.html.erb.tt +51 -0
- data/lib/generators/develoz/pwa/templates/app/views/pwa/service-worker.js.tt +109 -0
- data/lib/generators/develoz/pwa/templates/routes.rb.tt +3 -0
- data/lib/generators/develoz/solid/solid_generator.rb +52 -0
- data/lib/generators/develoz/solid/templates/app/jobs/application_job.rb.tt +7 -0
- data/lib/generators/develoz/solid/templates/config/cable.yml.tt +10 -0
- data/lib/generators/develoz/solid/templates/config/cache.yml.tt +16 -0
- data/lib/generators/develoz/solid/templates/config/initializers/mission_control.rb.tt +8 -0
- data/lib/generators/develoz/solid/templates/config/initializers/solid.rb.tt +7 -0
- data/lib/generators/develoz/solid/templates/config/queue.yml.tt +18 -0
- data/lib/generators/develoz/solid/templates/config/recurring.yml.tt +20 -0
- data/lib/generators/develoz/strict_loading/strict_loading_generator.rb +17 -0
- data/lib/generators/develoz/strict_loading/templates/config/initializers/strict_loading.rb.tt +13 -0
- data/lib/generators/develoz/testing/templates/rspec.tt +2 -0
- data/lib/generators/develoz/testing/templates/rspec_parallel.tt +2 -0
- data/lib/generators/develoz/testing/templates/spec/rails_helper.rb.tt +32 -0
- data/lib/generators/develoz/testing/templates/spec/spec_helper.rb.tt +31 -0
- data/lib/generators/develoz/testing/testing_generator.rb +39 -0
- data/lib/generators/develoz/tooling/templates/constants.rb.tt +17 -0
- data/lib/generators/develoz/tooling/templates/env.example.tt +4 -0
- data/lib/generators/develoz/tooling/templates/vscode/extensions.json.tt +8 -0
- data/lib/generators/develoz/tooling/templates/vscode/settings.json.tt +26 -0
- data/lib/generators/develoz/tooling/templates/vscode/tasks.json.tt +26 -0
- data/lib/generators/develoz/tooling/tooling_generator.rb +33 -0
- data/lib/generators/develoz/ui/templates/bin/setup_develoz_ui.tt +20 -0
- data/lib/generators/develoz/ui/ui_generator.rb +57 -0
- data/lib/generators/develoz/versioning/templates/app/helpers/application_helper.rb.tt +7 -0
- data/lib/generators/develoz/versioning/templates/app/views/shared/_app_version.html.erb.tt +3 -0
- data/lib/generators/develoz/versioning/templates/spec/helpers/application_helper_spec.rb.tt +11 -0
- data/lib/generators/develoz/versioning/versioning_generator.rb +48 -0
- data/lib/tasks/canonical.rake +27 -0
- data/templates/.keep +0 -0
- data/templates/CANONICAL_SOURCES.md +93 -0
- data/templates/pull_request_template.md.tt +13 -0
- metadata +383 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>You're offline</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
min-height: 100vh;
|
|
14
|
+
margin: 0;
|
|
15
|
+
background: #f5f5f5;
|
|
16
|
+
color: #333;
|
|
17
|
+
}
|
|
18
|
+
.container {
|
|
19
|
+
text-align: center;
|
|
20
|
+
padding: 2rem;
|
|
21
|
+
}
|
|
22
|
+
h1 {
|
|
23
|
+
font-size: 1.5rem;
|
|
24
|
+
margin-bottom: 0.5rem;
|
|
25
|
+
}
|
|
26
|
+
p {
|
|
27
|
+
color: #666;
|
|
28
|
+
margin-bottom: 1.5rem;
|
|
29
|
+
}
|
|
30
|
+
button {
|
|
31
|
+
padding: 0.75rem 1.5rem;
|
|
32
|
+
font-size: 1rem;
|
|
33
|
+
background: #333;
|
|
34
|
+
color: #fff;
|
|
35
|
+
border: none;
|
|
36
|
+
border-radius: 0.375rem;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
}
|
|
39
|
+
button:hover {
|
|
40
|
+
background: #555;
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
43
|
+
</head>
|
|
44
|
+
<body>
|
|
45
|
+
<div class="container">
|
|
46
|
+
<h1>You're offline</h1>
|
|
47
|
+
<p>You appear to be offline. Please check your connection and try again.</p>
|
|
48
|
+
<button onclick="window.location.reload()">Retry</button>
|
|
49
|
+
</div>
|
|
50
|
+
</body>
|
|
51
|
+
</html>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Develoz PWA service worker - offline caching + offline fallback page
|
|
2
|
+
// Push notification handlers are added by the develoz:push generator (T27)
|
|
3
|
+
|
|
4
|
+
const STATIC_CACHE = "develoz-static-v1"
|
|
5
|
+
const PAGES_CACHE = "develoz-pages-v1"
|
|
6
|
+
const OFFLINE_URL = "/offline"
|
|
7
|
+
|
|
8
|
+
// App shell assets to pre-cache on install
|
|
9
|
+
const APP_SHELL = [
|
|
10
|
+
"/",
|
|
11
|
+
OFFLINE_URL
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
// install: pre-cache the app shell
|
|
15
|
+
self.addEventListener("install", (event) => {
|
|
16
|
+
event.waitUntil(
|
|
17
|
+
caches.open(STATIC_CACHE).then((cache) => cache.addAll(APP_SHELL))
|
|
18
|
+
)
|
|
19
|
+
self.skipWaiting()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// activate: clean up old caches
|
|
23
|
+
self.addEventListener("activate", (event) => {
|
|
24
|
+
const validCaches = [STATIC_CACHE, PAGES_CACHE]
|
|
25
|
+
event.waitUntil(
|
|
26
|
+
caches.keys().then((cacheNames) =>
|
|
27
|
+
Promise.all(
|
|
28
|
+
cacheNames
|
|
29
|
+
.filter((name) => !validCaches.includes(name))
|
|
30
|
+
.map((name) => caches.delete(name))
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
self.clients.claim()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// fetch: cache-first for app shell, network-first for everything else
|
|
38
|
+
self.addEventListener("fetch", (event) => {
|
|
39
|
+
const request = event.request
|
|
40
|
+
|
|
41
|
+
// Only handle GET requests
|
|
42
|
+
if (request.method !== "GET") return
|
|
43
|
+
|
|
44
|
+
const url = new URL(request.url)
|
|
45
|
+
|
|
46
|
+
// Same-origin only
|
|
47
|
+
if (url.origin !== self.location.origin) return
|
|
48
|
+
|
|
49
|
+
// Cache-first for static assets (JS, CSS, fonts, images)
|
|
50
|
+
if (isStaticAsset(request)) {
|
|
51
|
+
event.respondWith(cacheFirst(request, STATIC_CACHE))
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Network-first for HTML pages with offline fallback
|
|
56
|
+
if (request.mode === "navigate" || request.headers.get("accept")?.includes("text/html")) {
|
|
57
|
+
event.respondWith(networkFirstWithOffline(request))
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Network-first for everything else
|
|
62
|
+
event.respondWith(networkFirst(request, PAGES_CACHE))
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
function isStaticAsset(request) {
|
|
66
|
+
const destination = request.destination
|
|
67
|
+
return ["style", "script", "font", "image", "manifest"].includes(destination)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function cacheFirst(request, cacheName) {
|
|
71
|
+
const cache = await caches.open(cacheName)
|
|
72
|
+
const cached = await cache.match(request)
|
|
73
|
+
if (cached) return cached
|
|
74
|
+
try {
|
|
75
|
+
const response = await fetch(request)
|
|
76
|
+
if (response.ok) cache.put(request, response.clone())
|
|
77
|
+
return response
|
|
78
|
+
} catch {
|
|
79
|
+
return new Response("", { status: 504, statusText: "Gateway Timeout" })
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function networkFirst(request, cacheName) {
|
|
84
|
+
const cache = await caches.open(cacheName)
|
|
85
|
+
try {
|
|
86
|
+
const response = await fetch(request)
|
|
87
|
+
if (response.ok) cache.put(request, response.clone())
|
|
88
|
+
return response
|
|
89
|
+
} catch {
|
|
90
|
+
const cached = await cache.match(request)
|
|
91
|
+
return cached || new Response("", { status: 504, statusText: "Gateway Timeout" })
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function networkFirstWithOffline(request) {
|
|
96
|
+
const cache = await caches.open(PAGES_CACHE)
|
|
97
|
+
try {
|
|
98
|
+
const response = await fetch(request)
|
|
99
|
+
if (response.ok) cache.put(request, response.clone())
|
|
100
|
+
return response
|
|
101
|
+
} catch {
|
|
102
|
+
const cached = await cache.match(request)
|
|
103
|
+
if (cached) return cached
|
|
104
|
+
// Fall back to the offline page
|
|
105
|
+
const offlineCache = await caches.open(STATIC_CACHE)
|
|
106
|
+
const offlinePage = await offlineCache.match(OFFLINE_URL)
|
|
107
|
+
return offlinePage || new Response("You're offline", { status: 503, headers: { "Content-Type": "text/html" } })
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "develoz"
|
|
4
|
+
|
|
5
|
+
module Develoz
|
|
6
|
+
module Generators
|
|
7
|
+
class SolidGenerator < Develoz::Generators::Base
|
|
8
|
+
def self.source_root
|
|
9
|
+
File.expand_path("templates", __dir__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_solid_gems
|
|
13
|
+
add_gem "solid_queue"
|
|
14
|
+
add_gem "solid_cache"
|
|
15
|
+
add_gem "solid_cable"
|
|
16
|
+
add_gem "mission_control-jobs"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_queue_config
|
|
20
|
+
template "config/queue.yml.tt", "config/queue.yml"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def create_cache_config
|
|
24
|
+
template "config/cache.yml.tt", "config/cache.yml"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def create_cable_config
|
|
28
|
+
template "config/cable.yml.tt", "config/cable.yml"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_recurring_config
|
|
32
|
+
template "config/recurring.yml.tt", "config/recurring.yml"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create_application_job
|
|
36
|
+
template "app/jobs/application_job.rb.tt", "app/jobs/application_job.rb"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def create_mission_control_initializer
|
|
40
|
+
template "config/initializers/mission_control.rb.tt", "config/initializers/mission_control.rb"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def create_solid_initializer
|
|
44
|
+
template "config/initializers/solid.rb.tt", "config/initializers/solid.rb"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def insert_mission_control_route
|
|
48
|
+
insert_route('mount MissionControl::Jobs::Engine, at: "/jobs"')
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
class ApplicationJob < ActiveJob::Base
|
|
2
|
+
# Automatically retry jobs that encountered a deadlock
|
|
3
|
+
# retry_on ActiveRecord::Deadlocked
|
|
4
|
+
|
|
5
|
+
# Most jobs are safe to ignore if the underlying records are no longer available
|
|
6
|
+
# discard_on ActiveJob::DeserializationError
|
|
7
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
default: &default
|
|
2
|
+
store_options:
|
|
3
|
+
# Cap age of oldest cache entry to fulfill retention policies
|
|
4
|
+
# max_age: 5184000
|
|
5
|
+
max_size: 268435456
|
|
6
|
+
namespace: <%= ENV.fetch("RAILS_ENV", "development") %>
|
|
7
|
+
|
|
8
|
+
development:
|
|
9
|
+
<<: *default
|
|
10
|
+
|
|
11
|
+
test:
|
|
12
|
+
<<: *default
|
|
13
|
+
|
|
14
|
+
production:
|
|
15
|
+
database: cache
|
|
16
|
+
<<: *default
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Mission Control - Jobs authentication
|
|
4
|
+
# Disable HTTP basic auth — Mission Control is gated behind the app's
|
|
5
|
+
# authentication system instead (e.g. Rails 8 authentication via --auth).
|
|
6
|
+
# When no authentication is configured, Mission Control is accessible
|
|
7
|
+
# without credentials in development and test environments.
|
|
8
|
+
MissionControl::Jobs.http_basic_auth_enabled = false
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
default: &default
|
|
2
|
+
dispatchers:
|
|
3
|
+
- polling_interval: 1
|
|
4
|
+
batch_size: 500
|
|
5
|
+
workers:
|
|
6
|
+
- queues: "*"
|
|
7
|
+
threads: 3
|
|
8
|
+
processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %>
|
|
9
|
+
polling_interval: 0.1
|
|
10
|
+
|
|
11
|
+
development:
|
|
12
|
+
<<: *default
|
|
13
|
+
|
|
14
|
+
test:
|
|
15
|
+
<<: *default
|
|
16
|
+
|
|
17
|
+
production:
|
|
18
|
+
<<: *default
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# examples:
|
|
2
|
+
# periodic_cleanup:
|
|
3
|
+
# class: CleanSoftDeletedRecordsJob
|
|
4
|
+
# queue: background
|
|
5
|
+
# args: [ 1000, { batch_size: 500 } ]
|
|
6
|
+
# schedule: every hour
|
|
7
|
+
# periodic_cleanup_with_command:
|
|
8
|
+
# command: "SoftDeletedRecord.due.delete_all"
|
|
9
|
+
# priority: 2
|
|
10
|
+
# schedule: at 5am every day
|
|
11
|
+
|
|
12
|
+
development:
|
|
13
|
+
clear_solid_queue_finished_jobs:
|
|
14
|
+
command: "SolidQueue::Job.clear_finished_in_batches(sleep_between_batches: 0.3)"
|
|
15
|
+
schedule: every hour at minute 12
|
|
16
|
+
|
|
17
|
+
production:
|
|
18
|
+
clear_solid_queue_finished_jobs:
|
|
19
|
+
command: "SolidQueue::Job.clear_finished_in_batches(sleep_between_batches: 0.3)"
|
|
20
|
+
schedule: every hour at minute 12
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "develoz"
|
|
4
|
+
|
|
5
|
+
module Develoz
|
|
6
|
+
module Generators
|
|
7
|
+
class StrictLoadingGenerator < Develoz::Generators::Base
|
|
8
|
+
def self.source_root
|
|
9
|
+
File.expand_path("templates", __dir__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create_strict_loading_initializer
|
|
13
|
+
template "config/initializers/strict_loading.rb.tt", "config/initializers/strict_loading.rb"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Strict loading configuration
|
|
4
|
+
# Enables strict loading by default with n_plus_one_only mode.
|
|
5
|
+
# Violations raise in test, log in development and production.
|
|
6
|
+
Rails.application.config.active_record.strict_loading_by_default = true
|
|
7
|
+
Rails.application.config.active_record.strict_loading_mode = :n_plus_one_only
|
|
8
|
+
|
|
9
|
+
ActiveRecord.action_on_strict_loading_violation =
|
|
10
|
+
case Rails.env
|
|
11
|
+
when "test" then :raise
|
|
12
|
+
else :log
|
|
13
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require_relative "../config/environment"
|
|
5
|
+
require "rspec/rails"
|
|
6
|
+
require "factory_bot"
|
|
7
|
+
require "capybara/rspec"
|
|
8
|
+
require "selenium-webdriver"
|
|
9
|
+
|
|
10
|
+
Dir[Rails.root.join("spec/support/**/*.rb")].sort.each { |f| require f }
|
|
11
|
+
|
|
12
|
+
RSpec.configure do |config|
|
|
13
|
+
config.fixture_paths = [ "#{::Rails.root}/spec/fixtures" ]
|
|
14
|
+
config.use_transactional_fixtures = true
|
|
15
|
+
config.infer_spec_type_from_file_location!
|
|
16
|
+
config.filter_rails_from_backtrace!
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Capybara.register_driver :headless_chrome do |app|
|
|
20
|
+
options = Selenium::WebDriver::Chrome::Options.new
|
|
21
|
+
options.add_argument("--headless=new")
|
|
22
|
+
options.add_argument("--no-sandbox")
|
|
23
|
+
options.add_argument("--disable-gpu")
|
|
24
|
+
Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
Capybara.javascript_driver = :headless_chrome
|
|
28
|
+
|
|
29
|
+
FactoryBot.definition_file_paths = [ File.expand_path("factories", __dir__) ]
|
|
30
|
+
FactoryBot.find_definitions
|
|
31
|
+
|
|
32
|
+
ActiveRecord::Migration.maintain_test_schema!
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "simplecov"
|
|
4
|
+
require "simplecov-lcov"
|
|
5
|
+
|
|
6
|
+
SimpleCov.formatters = [
|
|
7
|
+
SimpleCov::Formatter::HTMLFormatter,
|
|
8
|
+
SimpleCov::Formatter::LcovFormatter
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
SimpleCov.start do
|
|
12
|
+
enable_coverage :branch
|
|
13
|
+
minimum_coverage line: 100, branch: 100
|
|
14
|
+
coverage_dir "public/coverage"
|
|
15
|
+
command_name "RSpec_#{ENV['TEST_ENV_NUMBER']}" if ENV['TEST_ENV_NUMBER']
|
|
16
|
+
use_merging true
|
|
17
|
+
add_filter %w[/spec/ /config/ /db/]
|
|
18
|
+
add_filter %w[/bin/ *.sh]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
RSpec.configure do |config|
|
|
22
|
+
config.expect_with :rspec do |expectations|
|
|
23
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
config.mock_with :rspec do |mocks|
|
|
27
|
+
mocks.verify_partial_doubles = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
31
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "develoz"
|
|
4
|
+
|
|
5
|
+
module Develoz
|
|
6
|
+
module Generators
|
|
7
|
+
class TestingGenerator < Develoz::Generators::Base
|
|
8
|
+
def self.source_root
|
|
9
|
+
File.expand_path("templates", __dir__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_test_gems
|
|
13
|
+
add_gem "rspec-rails", group: %i[development test]
|
|
14
|
+
add_gem "capybara", group: %i[test]
|
|
15
|
+
add_gem "selenium-webdriver", group: %i[test]
|
|
16
|
+
add_gem "simplecov", require: false, group: %i[test]
|
|
17
|
+
add_gem "simplecov-lcov", require: false, group: %i[test]
|
|
18
|
+
add_gem "parallel_tests", group: %i[test]
|
|
19
|
+
add_gem "factory_bot_rails", group: %i[test]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def create_rspec_config
|
|
23
|
+
template "rspec.tt", ".rspec"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_spec_helper
|
|
27
|
+
template "spec/spec_helper.rb.tt", "spec/spec_helper.rb"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_rails_helper
|
|
31
|
+
template "spec/rails_helper.rb.tt", "spec/rails_helper.rb"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_rspec_parallel_config
|
|
35
|
+
template "rspec_parallel.tt", ".rspec_parallel"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
## Assign all environment variables used by the Rails app to constants for easy access and tracking
|
|
4
|
+
module Constants
|
|
5
|
+
DATABASE_HOST = ENV.fetch("DATABASE_HOST", "localhost")
|
|
6
|
+
DATABASE_PORT = ENV.fetch("DATABASE_PORT", 5432)
|
|
7
|
+
DATABASE_NAME = ENV.fetch("DATABASE_NAME", "<%= app_name %>")
|
|
8
|
+
TEST_DATABASE_NAME = ENV.fetch("TEST_DATABASE_NAME", "#{DATABASE_NAME}_test")
|
|
9
|
+
REDIS_URL = ENV.fetch("REDIS_URL", "redis://localhost:6379/#{ENV.fetch("TEST_ENV_NUMBER", 1)}")
|
|
10
|
+
RAILS_MAX_THREADS = ENV.fetch("RAILS_MAX_THREADS", `nproc || echo 2`.strip.to_i * 5)
|
|
11
|
+
SERVER_PORT = ENV.fetch("SERVER_PORT", 3000)
|
|
12
|
+
HOST = ENV.fetch("APP_HOST") { ENV.fetch("HOST", "localhost:#{SERVER_PORT}") }
|
|
13
|
+
CACHING_DEV = ENV.fetch("CACHING_DEV", nil) == "true" # Enable caching in development if set to "true"
|
|
14
|
+
MEMCACHIER_SERVERS = ENV.fetch("MEMCACHIER_SERVERS", "").split(",")
|
|
15
|
+
APP_NAME = ENV.fetch("APP_NAME", "<%= app_class %>")
|
|
16
|
+
# additional constants appended by generators
|
|
17
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"[ruby]": {
|
|
3
|
+
"editor.defaultFormatter": "Shopify.ruby-lsp",
|
|
4
|
+
"editor.formatOnSave": true,
|
|
5
|
+
"editor.codeActionsOnSave": {
|
|
6
|
+
"source.fixAll.rubocop": "explicit"
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"files.trimTrailingWhitespace": true,
|
|
10
|
+
"files.insertFinalNewline": true,
|
|
11
|
+
"editor.formatOnSave": true,
|
|
12
|
+
"editor.rulers": [80, 120],
|
|
13
|
+
"files.exclude": {
|
|
14
|
+
"**/tmp": true,
|
|
15
|
+
"**/log": true,
|
|
16
|
+
"**/.git": true,
|
|
17
|
+
"**/coverage": true,
|
|
18
|
+
"**/.rspec_status": true
|
|
19
|
+
},
|
|
20
|
+
"search.exclude": {
|
|
21
|
+
"**/node_modules": true,
|
|
22
|
+
"**/tmp": true,
|
|
23
|
+
"**/log": true,
|
|
24
|
+
"**/coverage": true
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "2.0.0",
|
|
3
|
+
"tasks": [
|
|
4
|
+
{
|
|
5
|
+
"label": "rspec",
|
|
6
|
+
"type": "shell",
|
|
7
|
+
"command": "bundle",
|
|
8
|
+
"args": ["exec", "rspec"],
|
|
9
|
+
"problemMatcher": [],
|
|
10
|
+
"group": {
|
|
11
|
+
"kind": "test",
|
|
12
|
+
"isDefault": true
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"label": "rubocop",
|
|
17
|
+
"type": "shell",
|
|
18
|
+
"command": "bundle",
|
|
19
|
+
"args": ["exec", "rubocop"],
|
|
20
|
+
"problemMatcher": [],
|
|
21
|
+
"group": {
|
|
22
|
+
"kind": "build"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "develoz"
|
|
4
|
+
|
|
5
|
+
module Develoz
|
|
6
|
+
module Generators
|
|
7
|
+
class ToolingGenerator < Develoz::Generators::Base
|
|
8
|
+
def self.source_root
|
|
9
|
+
File.expand_path("templates", __dir__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create_vscode
|
|
13
|
+
template "vscode/settings.json.tt", ".vscode/settings.json"
|
|
14
|
+
template "vscode/extensions.json.tt", ".vscode/extensions.json"
|
|
15
|
+
template "vscode/tasks.json.tt", ".vscode/tasks.json"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_env_files
|
|
19
|
+
template "env.example.tt", ".env.example"
|
|
20
|
+
create_file ".env", "" unless File.exist?(File.join(destination_root, ".env"))
|
|
21
|
+
ensure_gitignore(".env")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def create_constants
|
|
25
|
+
template "constants.rb.tt", "config/initializers/constants.rb"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def add_dotenv
|
|
29
|
+
add_gem "dotenv-rails", group: %i[development test]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Setup script for the develoz-ui submodule.
|
|
5
|
+
# Initializes the develoz-ui git submodule at vendor/develoz-ui.
|
|
6
|
+
#
|
|
7
|
+
# If the submodule is not initialized, the develoz_ui gem will fail to load
|
|
8
|
+
# in development/test environments where it uses `path: "vendor/develoz-ui"`.
|
|
9
|
+
# In production, the gem is fetched from GitHub directly.
|
|
10
|
+
|
|
11
|
+
echo "Initializing develoz-ui submodule..."
|
|
12
|
+
git submodule update --init --recursive vendor/develoz-ui
|
|
13
|
+
|
|
14
|
+
if [ ! -d "vendor/develoz-ui" ] || [ -z "$(ls -A vendor/develoz-ui 2>/dev/null)" ]; then
|
|
15
|
+
echo "ERROR: develoz-ui submodule is not initialized."
|
|
16
|
+
echo "Run 'bin/setup_develoz_ui' to initialize it."
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
echo "develoz-ui submodule is ready."
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "develoz"
|
|
4
|
+
|
|
5
|
+
module Develoz
|
|
6
|
+
module Generators
|
|
7
|
+
class UiGenerator < Develoz::Generators::Base
|
|
8
|
+
def self.source_root
|
|
9
|
+
File.expand_path("templates", __dir__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_develoz_ui_gems
|
|
13
|
+
inject_once(
|
|
14
|
+
into: "Gemfile",
|
|
15
|
+
content: <<~RUBY
|
|
16
|
+
group :development, :test do
|
|
17
|
+
gem "develoz_ui", path: "vendor/develoz-ui"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
group :production do
|
|
21
|
+
gem "develoz_ui", github: "develoz-com/develoz-ui"
|
|
22
|
+
end
|
|
23
|
+
RUBY
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def create_gitmodules
|
|
28
|
+
gitmodules_path = File.join(destination_root, ".gitmodules")
|
|
29
|
+
File.write(gitmodules_path, "") unless File.exist?(gitmodules_path)
|
|
30
|
+
|
|
31
|
+
inject_once(
|
|
32
|
+
into: ".gitmodules",
|
|
33
|
+
content: "[submodule \"vendor/develoz-ui\"]\n" \
|
|
34
|
+
"\tpath = vendor/develoz-ui\n" \
|
|
35
|
+
"\turl = git@github.com:develoz-com/develoz-ui.git"
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def create_setup_script
|
|
40
|
+
template "bin/setup_develoz_ui.tt", "bin/setup_develoz_ui"
|
|
41
|
+
dest = File.join(destination_root, "bin/setup_develoz_ui")
|
|
42
|
+
File.chmod(0o755, dest)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def inject_importmap_pins
|
|
46
|
+
inject_once(
|
|
47
|
+
into: "config/importmap.rb",
|
|
48
|
+
content: <<~RUBY
|
|
49
|
+
# develoz-ui Stimulus controllers
|
|
50
|
+
pin "develoz-ui", to: "develoz-ui/index.js"
|
|
51
|
+
pin_all_from "vendor/develoz-ui/app/javascript/controllers", under: "develoz-ui/controllers"
|
|
52
|
+
RUBY
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|