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,59 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:18
|
|
4
|
+
environment:
|
|
5
|
+
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
|
6
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
|
|
7
|
+
POSTGRES_DB: ${POSTGRES_DB:-<%= app_name %>_development}
|
|
8
|
+
ports:
|
|
9
|
+
- "5432:5432"
|
|
10
|
+
volumes:
|
|
11
|
+
- postgres_data:/var/lib/postgresql
|
|
12
|
+
healthcheck:
|
|
13
|
+
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
|
|
14
|
+
interval: 5s
|
|
15
|
+
timeout: 5s
|
|
16
|
+
retries: 5
|
|
17
|
+
|
|
18
|
+
selenium:
|
|
19
|
+
image: selenium/standalone-chromium:latest
|
|
20
|
+
ports:
|
|
21
|
+
- "4444:4444"
|
|
22
|
+
- "7900:7900"
|
|
23
|
+
volumes:
|
|
24
|
+
- /dev/shm:/dev/shm
|
|
25
|
+
|
|
26
|
+
mailcatcher:
|
|
27
|
+
image: schickling/mailcatcher:latest
|
|
28
|
+
ports:
|
|
29
|
+
- "1080:1080"
|
|
30
|
+
- "1025:1025"
|
|
31
|
+
|
|
32
|
+
app:
|
|
33
|
+
build:
|
|
34
|
+
context: .
|
|
35
|
+
dockerfile: Dockerfile.dev
|
|
36
|
+
depends_on:
|
|
37
|
+
postgres:
|
|
38
|
+
condition: service_healthy
|
|
39
|
+
selenium:
|
|
40
|
+
condition: service_started
|
|
41
|
+
mailcatcher:
|
|
42
|
+
condition: service_started
|
|
43
|
+
environment:
|
|
44
|
+
DATABASE_HOST: ${DATABASE_HOST:-postgres}
|
|
45
|
+
DATABASE_PORT: ${DATABASE_PORT:-5432}
|
|
46
|
+
DATABASE_USER: ${DATABASE_USER:-postgres}
|
|
47
|
+
DATABASE_PASSWORD: ${DATABASE_PASSWORD:-postgres}
|
|
48
|
+
SELENIUM_URL: ${SELENIUM_URL:-http://selenium:4444/wd/hub}
|
|
49
|
+
MAILCATCHER_URL: ${MAILCATCHER_URL:-http://mailcatcher:1080}
|
|
50
|
+
ports:
|
|
51
|
+
- "3000:3000"
|
|
52
|
+
volumes:
|
|
53
|
+
- .:/rails
|
|
54
|
+
- bundle_cache:/usr/local/bundle
|
|
55
|
+
command: bin/docker-entrypoint
|
|
56
|
+
|
|
57
|
+
volumes:
|
|
58
|
+
postgres_data:
|
|
59
|
+
bundle_cache:
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
FROM ruby:<%= RUBY_VERSION %>
|
|
2
|
+
|
|
3
|
+
RUN apt-get update -qq && apt-get install -y \
|
|
4
|
+
build-essential \
|
|
5
|
+
libpq-dev \
|
|
6
|
+
libvips \
|
|
7
|
+
nodejs \
|
|
8
|
+
npm \
|
|
9
|
+
git \
|
|
10
|
+
curl \
|
|
11
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
12
|
+
|
|
13
|
+
WORKDIR /rails
|
|
14
|
+
|
|
15
|
+
RUN gem install bundler
|
|
16
|
+
|
|
17
|
+
COPY Gemfile Gemfile.lock ./
|
|
18
|
+
RUN bundle install
|
|
19
|
+
|
|
20
|
+
COPY . .
|
|
21
|
+
|
|
22
|
+
EXPOSE 3000
|
|
23
|
+
|
|
24
|
+
CMD ["bin/docker-entrypoint"]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "develoz"
|
|
4
|
+
|
|
5
|
+
module Develoz
|
|
6
|
+
module Generators
|
|
7
|
+
class DocsRenderGenerator < Develoz::Generators::Base
|
|
8
|
+
def self.source_root
|
|
9
|
+
File.expand_path("templates", __dir__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def add_docs_gems
|
|
13
|
+
add_gem "redcarpet"
|
|
14
|
+
add_gem "rouge"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create_docs_controller
|
|
18
|
+
template "app/controllers/docs_controller.rb.tt", "app/controllers/docs_controller.rb"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create_document_model
|
|
22
|
+
template "app/models/document.rb.tt", "app/models/document.rb"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def create_docs_views
|
|
26
|
+
template "app/views/docs/show.html.erb.tt", "app/views/docs/show.html.erb"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create_docs_javascript
|
|
30
|
+
template "app/javascript/docs.js.tt", "app/javascript/docs.js"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def create_docs_stylesheet
|
|
34
|
+
template "app/assets/stylesheets/documentation.scss.tt", "app/assets/stylesheets/documentation.scss"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create_redcarpet_rouge_initializer
|
|
38
|
+
template "config/initializers/redcarpet_rouge.rb.tt", "config/initializers/redcarpet_rouge.rb"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def insert_docs_route
|
|
42
|
+
insert_route 'get "docs(/*id)" => "docs#show"'
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/generators/develoz/docs_render/templates/app/assets/stylesheets/documentation.scss.tt
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
section.documentation {
|
|
2
|
+
table {
|
|
3
|
+
margin-bottom: 20px;
|
|
4
|
+
|
|
5
|
+
th,
|
|
6
|
+
td {
|
|
7
|
+
border: 1px solid #dee2e6;
|
|
8
|
+
padding: 5px;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.box-body {
|
|
13
|
+
padding: 10px 30px;
|
|
14
|
+
max-width: 1200px;
|
|
15
|
+
|
|
16
|
+
h1,
|
|
17
|
+
h2,
|
|
18
|
+
h3,
|
|
19
|
+
h4 {
|
|
20
|
+
margin-top: 40px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pre.code-block {
|
|
24
|
+
margin-bottom: 20px;
|
|
25
|
+
padding: 16px 20px;
|
|
26
|
+
overflow-x: auto;
|
|
27
|
+
font-size: 0.875rem;
|
|
28
|
+
line-height: 1.5;
|
|
29
|
+
color: #c9d1d9;
|
|
30
|
+
background-color: #161b22;
|
|
31
|
+
border: 1px solid #30363d;
|
|
32
|
+
border-radius: 8px;
|
|
33
|
+
|
|
34
|
+
code {
|
|
35
|
+
padding: 0;
|
|
36
|
+
color: inherit;
|
|
37
|
+
white-space: pre;
|
|
38
|
+
background-color: transparent;
|
|
39
|
+
border: 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
:not(pre) > code {
|
|
44
|
+
padding: 0.2em 0.4em;
|
|
45
|
+
font-size: 0.875em;
|
|
46
|
+
color: #24292f;
|
|
47
|
+
background-color: rgb(175 184 193 / 20%);
|
|
48
|
+
border-radius: 6px;
|
|
49
|
+
|
|
50
|
+
.dark & {
|
|
51
|
+
color: #c9d1d9;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pre.mermaid {
|
|
56
|
+
margin-bottom: 20px;
|
|
57
|
+
padding: 0;
|
|
58
|
+
text-align: center;
|
|
59
|
+
background: transparent;
|
|
60
|
+
border: 0;
|
|
61
|
+
|
|
62
|
+
&:not([data-processed="true"]) {
|
|
63
|
+
visibility: hidden;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
blockquote {
|
|
68
|
+
margin: 20px 0;
|
|
69
|
+
padding: 12px 16px;
|
|
70
|
+
color: #495057;
|
|
71
|
+
background: #f8f9fa;
|
|
72
|
+
border-left: 4px solid #0d6efd;
|
|
73
|
+
border-radius: 0 6px 6px 0;
|
|
74
|
+
|
|
75
|
+
p:last-child {
|
|
76
|
+
margin-bottom: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
&.callout {
|
|
80
|
+
--callout-accent: #57606a;
|
|
81
|
+
--callout-background: #f6f8fa;
|
|
82
|
+
--callout-foreground: #24292f;
|
|
83
|
+
|
|
84
|
+
color: var(--callout-foreground);
|
|
85
|
+
background: var(--callout-background);
|
|
86
|
+
border-left-color: var(--callout-accent);
|
|
87
|
+
|
|
88
|
+
.dark & {
|
|
89
|
+
--callout-background: #161b22;
|
|
90
|
+
--callout-foreground: #c9d1d9;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
&.callout-note {
|
|
95
|
+
--callout-accent: #0969da;
|
|
96
|
+
|
|
97
|
+
.dark & {
|
|
98
|
+
--callout-accent: #58a6ff;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
&.callout-tip {
|
|
103
|
+
--callout-accent: #1a7f37;
|
|
104
|
+
|
|
105
|
+
.dark & {
|
|
106
|
+
--callout-accent: #3fb950;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
&.callout-important {
|
|
111
|
+
--callout-accent: #8250df;
|
|
112
|
+
|
|
113
|
+
.dark & {
|
|
114
|
+
--callout-accent: #a371f7;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
&.callout-warning {
|
|
119
|
+
--callout-accent: #9a6700;
|
|
120
|
+
|
|
121
|
+
.dark & {
|
|
122
|
+
--callout-accent: #d29922;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
&.callout-caution {
|
|
127
|
+
--callout-accent: #cf222e;
|
|
128
|
+
|
|
129
|
+
.dark & {
|
|
130
|
+
--callout-accent: #f85149;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.callout-title {
|
|
135
|
+
margin-bottom: 8px;
|
|
136
|
+
font-weight: 600;
|
|
137
|
+
color: var(--callout-accent);
|
|
138
|
+
|
|
139
|
+
.fa {
|
|
140
|
+
margin-right: 6px;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import mermaid from 'mermaid'
|
|
2
|
+
|
|
3
|
+
mermaid.initialize({
|
|
4
|
+
startOnLoad: false,
|
|
5
|
+
securityLevel: 'strict',
|
|
6
|
+
theme: window.darkMode ? 'dark' : 'default',
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const renderDiagrams = () => {
|
|
10
|
+
const nodes = document.querySelectorAll('pre.mermaid:not([data-processed="true"])')
|
|
11
|
+
if (nodes.length) mermaid.run({ nodes })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
document.addEventListener('turbo:load', renderDiagrams)
|
|
15
|
+
if (document.readyState === 'complete') renderDiagrams()
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Document
|
|
4
|
+
include ActiveModel::Model
|
|
5
|
+
|
|
6
|
+
class RecordNotFound < StandardError; end
|
|
7
|
+
|
|
8
|
+
FOLDER_INDEX_NAMES = [ "README", "00 - Index" ].freeze
|
|
9
|
+
|
|
10
|
+
class Renderer < Redcarpet::Render::HTML
|
|
11
|
+
include ActionView::Helpers::TagHelper
|
|
12
|
+
include ActionView::Helpers::SanitizeHelper
|
|
13
|
+
|
|
14
|
+
# Obsidian wikilink: [[Note]], [[Note#Heading]], [[Note|Alias]] (and combinations).
|
|
15
|
+
WIKILINK = /\[\[([^\]#|]+)(?:#([^\]|]+))?(?:\|([^\]]+))?\]\]/
|
|
16
|
+
CALLOUT = /\A<p>\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\](?:<br>\s*)?/i
|
|
17
|
+
CALLOUT_ICONS = {
|
|
18
|
+
"note" => "info-circle",
|
|
19
|
+
"tip" => "lightbulb-o",
|
|
20
|
+
"important" => "exclamation-circle",
|
|
21
|
+
"warning" => "warning",
|
|
22
|
+
"caution" => "ban"
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
def self.render(path, base_dir: nil)
|
|
26
|
+
new(base_dir:).render(path)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(base_dir: nil, **options)
|
|
30
|
+
@base_dir = base_dir
|
|
31
|
+
super(options.merge(with_toc_data: true, hard_wrap: true))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def sanitize(html)
|
|
35
|
+
tags = ActionView::Base.sanitized_allowed_tags + %w[table thead tbody th tr td img i]
|
|
36
|
+
attributes = ActionView::Base.sanitized_allowed_attributes + %w[style colspan src alt id class aria-hidden]
|
|
37
|
+
super(html, tags:, attributes:)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def markdown
|
|
41
|
+
@markdown ||= Redcarpet::Markdown.new(
|
|
42
|
+
self,
|
|
43
|
+
fenced_code_blocks: true,
|
|
44
|
+
tables: true,
|
|
45
|
+
autolink: true,
|
|
46
|
+
strikethrough: true
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def render(path)
|
|
51
|
+
sanitize(markdown.render(File.read(path)))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Convert Obsidian wikilinks to markdown links before parsing, leaving any
|
|
55
|
+
# text inside fenced or inline code untouched.
|
|
56
|
+
def preprocess(document)
|
|
57
|
+
replace_wikilinks(document)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def block_code(code, language)
|
|
61
|
+
# Mermaid diagrams are rendered client-side: emit the raw source in a
|
|
62
|
+
# <pre class="mermaid"> for mermaid.js, never syntax-highlighted.
|
|
63
|
+
return content_tag(:pre, code, class: "mermaid") if language == "mermaid"
|
|
64
|
+
|
|
65
|
+
lexer = Rouge::Lexer.find_fancy(language.to_s, code) || Rouge::Lexers::PlainText.new
|
|
66
|
+
formatter = Rouge::Formatters::HTMLInline.new(Rouge::Themes::Github.mode(:dark).new)
|
|
67
|
+
code = formatter.format(lexer.lex(code))
|
|
68
|
+
content_tag(:pre, class: "code-block") do
|
|
69
|
+
content_tag(:code, sanitize(code), class: "language-#{lexer.tag}")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def image(link, title, alt_text)
|
|
74
|
+
tag(:img, src: link, alt: alt_text, title:, class: "img-responsive")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def block_quote(quote)
|
|
78
|
+
callout = quote.match(CALLOUT)
|
|
79
|
+
|
|
80
|
+
if callout
|
|
81
|
+
type = callout[1].downcase
|
|
82
|
+
quote = quote.sub(CALLOUT, "<p>")
|
|
83
|
+
icon = tag.i(class: "fa fa-#{CALLOUT_ICONS.fetch(type)}", aria: { hidden: true })
|
|
84
|
+
callout_title = %(<p class="callout-title">#{icon} #{type.titleize}</p>)
|
|
85
|
+
|
|
86
|
+
%(<blockquote class="callout callout-#{type}">#{callout_title}\n#{quote}</blockquote>)
|
|
87
|
+
else
|
|
88
|
+
%(<blockquote>#{quote}</blockquote>)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def replace_wikilinks(document)
|
|
95
|
+
in_fence = false
|
|
96
|
+
|
|
97
|
+
document.each_line.map do |line|
|
|
98
|
+
if line.start_with?("```")
|
|
99
|
+
in_fence = !in_fence
|
|
100
|
+
line
|
|
101
|
+
elsif in_fence
|
|
102
|
+
line
|
|
103
|
+
else
|
|
104
|
+
replace_wikilinks_in_line(line)
|
|
105
|
+
end
|
|
106
|
+
end.join
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def replace_wikilinks_in_line(line)
|
|
110
|
+
line.split(/(`[^`]*`)/).map do |part|
|
|
111
|
+
if part.start_with?("`")
|
|
112
|
+
part
|
|
113
|
+
else
|
|
114
|
+
part.gsub(WIKILINK) do
|
|
115
|
+
wikilink_markdown(::Regexp.last_match(1), ::Regexp.last_match(2), ::Regexp.last_match(3))
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end.join
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def wikilink_markdown(name, heading, alias_text)
|
|
122
|
+
name = name.strip
|
|
123
|
+
label = alias_text&.strip
|
|
124
|
+
label ||= heading ? "#{name} > #{heading.strip}" : name
|
|
125
|
+
"[#{escape_markdown(label)}](#{wikilink_url(name, heading)})"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Escape characters the link label would otherwise trigger as markdown
|
|
129
|
+
# (emphasis, links, code) so names like assign_preorder render literally.
|
|
130
|
+
def escape_markdown(text)
|
|
131
|
+
text.gsub(/[\\_*\[\]`]/) { |char| "\\#{char}" }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def wikilink_url(name, heading)
|
|
135
|
+
segments = [ @base_dir, name ].compact_blank
|
|
136
|
+
url = "/docs/#{segments.map { |segment| ERB::Util.url_encode(segment) }.join('/')}"
|
|
137
|
+
url += "##{heading_anchor(heading)}" if heading
|
|
138
|
+
url
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Mirror Redcarpet's with_toc_data anchor generation for [[Note#Heading]]:
|
|
142
|
+
# drop non-alphanumerics (underscores included), then collapse whitespace to "-".
|
|
143
|
+
def heading_anchor(heading)
|
|
144
|
+
heading.strip.downcase.gsub(/[^a-z0-9\s]/, "").gsub(/\s+/, "-").gsub(/\A-+|-+\z/, "")
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
attr_reader :id, :name, :path
|
|
149
|
+
|
|
150
|
+
class << self
|
|
151
|
+
def where(folder: nil, name: nil)
|
|
152
|
+
glob = folder ? "docs/#{folder}/" : "{docs/**/,}"
|
|
153
|
+
Rails.root.glob("#{glob}#{name || "*"}.md").sort.map { |path| new(path) }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
alias all where
|
|
157
|
+
delegate :first, :last, to: :where
|
|
158
|
+
|
|
159
|
+
def find(name)
|
|
160
|
+
document = where(name:).first
|
|
161
|
+
document ||= FOLDER_INDEX_NAMES.lazy.filter_map { |index| where(folder: name, name: index).first }.first
|
|
162
|
+
|
|
163
|
+
return document if document
|
|
164
|
+
|
|
165
|
+
raise RecordNotFound, "Couldn't find document #{name}"
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def initialize(path)
|
|
170
|
+
@path = path
|
|
171
|
+
relative_path = Pathname(path).relative_path_from(Rails.root).to_s
|
|
172
|
+
@id = relative_path.match(%r{\A(?:docs/)?(?<id>.*)\.md\z})&.[](:id)
|
|
173
|
+
@name = File.basename(path, ".md")
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def render
|
|
177
|
+
Renderer.render(path, base_dir: File.dirname(id).then { |dir| dir == "." ? nil : dir })
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def title
|
|
181
|
+
name.titleize
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def breadcrumbs
|
|
185
|
+
parts = id.split("/")
|
|
186
|
+
|
|
187
|
+
parts.each_with_index.map { |part, i| { name: part.titleize, path: "/docs/#{parts[0..i].join('/')}" } }
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Redcarpet and Rouge configuration for documentation rendering.
|
|
4
|
+
#
|
|
5
|
+
# The Document model (app/models/document.rb) creates its own
|
|
6
|
+
# Redcarpet::Markdown instance with the required extensions
|
|
7
|
+
# (fenced_code_blocks, tables, autolink, strikethrough) and uses
|
|
8
|
+
# Rouge::Formatters::HTMLInline with the Github dark theme for syntax
|
|
9
|
+
# highlighting of fenced code blocks.
|
|
10
|
+
#
|
|
11
|
+
# Mermaid diagrams are rendered client-side by app/javascript/docs.js,
|
|
12
|
+
# which imports the mermaid package and processes <pre class="mermaid">
|
|
13
|
+
# elements after each Turbo navigation.
|
|
14
|
+
#
|
|
15
|
+
# To use Mermaid, pin it in config/importmap.rb:
|
|
16
|
+
# pin "mermaid", to: "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "develoz"
|
|
4
|
+
|
|
5
|
+
module Develoz
|
|
6
|
+
module Generators
|
|
7
|
+
class FrontendCoreGenerator < Develoz::Generators::Base
|
|
8
|
+
def self.source_root
|
|
9
|
+
File.expand_path("templates", __dir__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class_option :skip_pagy, type: :boolean, default: false
|
|
13
|
+
|
|
14
|
+
def add_frontend_gems
|
|
15
|
+
add_gem "importmap-rails"
|
|
16
|
+
add_gem "annotaterb", group: :development
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def add_pagy_gem
|
|
20
|
+
return if options[:skip_pagy]
|
|
21
|
+
|
|
22
|
+
add_gem "pagy"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def create_importmap_config
|
|
26
|
+
template "config/importmap.rb.tt", "config/importmap.rb"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create_pagy_initializer
|
|
30
|
+
return if options[:skip_pagy]
|
|
31
|
+
|
|
32
|
+
template "config/initializers/pagy.rb.tt", "config/initializers/pagy.rb"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create_annotaterb_config
|
|
36
|
+
template ".annotaterb.yml.tt", ".annotaterb.yml"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
:models: true
|
|
3
|
+
:routes: false
|
|
4
|
+
:active_admin: false
|
|
5
|
+
:additional_file_patterns: []
|
|
6
|
+
:exclude_tests: true
|
|
7
|
+
:exclude_fixtures: true
|
|
8
|
+
:exclude_factories: true
|
|
9
|
+
:exclude_serializers: true
|
|
10
|
+
:exclude_controllers: true
|
|
11
|
+
:exclude_helpers: true
|
|
12
|
+
:exclude_scaffolds: true
|
|
13
|
+
:classified_sort: true
|
|
14
|
+
:show_indexes: true
|
|
15
|
+
:show_foreign_keys: true
|
|
16
|
+
:position: after
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Pin npm packages by running ./bin/importmap
|
|
4
|
+
|
|
5
|
+
pin "application"
|
|
6
|
+
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
|
7
|
+
pin "@hotwired/stimulus", to: "stimulus.min.js"
|
|
8
|
+
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
|
|
9
|
+
pin "trix", to: "https://esm.sh/trix@2.1.18" # @2.1.18
|
|
10
|
+
pin "@rails/actiontext", to: "@rails--actiontext.js" # @8.1.100
|
|
11
|
+
pin "@rails/activestorage", to: "activestorage.esm.js"
|
|
12
|
+
pin_all_from "app/javascript/controllers", under: "controllers"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "develoz"
|
|
4
|
+
|
|
5
|
+
module Develoz
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < Develoz::Generators::Base
|
|
8
|
+
class_option :api, type: :boolean, default: false
|
|
9
|
+
class_option :auth, type: :boolean, default: false
|
|
10
|
+
class_option :pwa, type: :boolean, default: false
|
|
11
|
+
class_option :push, type: :boolean, default: false
|
|
12
|
+
class_option :active_resource, type: :boolean, default: false
|
|
13
|
+
class_option :admin, type: :boolean, default: false
|
|
14
|
+
class_option :ui, type: :boolean, default: false
|
|
15
|
+
class_option :kamal, type: :boolean, default: false
|
|
16
|
+
class_option :docker, type: :boolean, default: false
|
|
17
|
+
class_option :db_backup, type: :boolean, default: false
|
|
18
|
+
class_option :skip_pagy, type: :boolean, default: false
|
|
19
|
+
|
|
20
|
+
def install
|
|
21
|
+
develoz_options = build_options
|
|
22
|
+
Develoz::Manifest.for(develoz_options).each do |entry|
|
|
23
|
+
invoke_generator(entry.name, develoz_options.to_h)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
OPTION_FLAGS = %i[api auth pwa push active_resource admin ui kamal docker db_backup skip_pagy].freeze
|
|
30
|
+
private_constant :OPTION_FLAGS
|
|
31
|
+
|
|
32
|
+
def build_options
|
|
33
|
+
flags = OPTION_FLAGS.index_with { |flag| options[flag] }
|
|
34
|
+
Develoz::Options.new(**flags, app_name: app_name)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def invoke_generator(name, generator_options)
|
|
38
|
+
require "generators/develoz/#{name}/#{name}_generator"
|
|
39
|
+
klass = Develoz::Generators.const_get("#{name.camelize}Generator")
|
|
40
|
+
gen = klass.new([], generator_options, destination_root: destination_root)
|
|
41
|
+
klass.public_instance_methods(false).each { |method| gen.public_send(method) }
|
|
42
|
+
rescue LoadError
|
|
43
|
+
say "generator develoz:#{name} not yet available", :yellow
|
|
44
|
+
rescue NameError => e
|
|
45
|
+
raise unless e.name.to_s == "#{name.camelize}Generator"
|
|
46
|
+
|
|
47
|
+
say "generator develoz:#{name} not yet available", :yellow
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|