trek 0.1.20 → 0.1.22

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.npmignore +3 -0
  3. data/Gemfile.lock +86 -82
  4. data/README.md +8 -0
  5. data/app/assets/stylesheets/trek/_colors.css +181 -0
  6. data/app/components/trek/button_component.rb +9 -3
  7. data/app/components/trek/dashboard/tile_component/tile_component.html.slim +1 -1
  8. data/app/components/trek/dashboard/tile_component.rb +2 -0
  9. data/app/components/trek/form/actions_component/actions_component.css +6 -0
  10. data/app/components/trek/form/image_field_component/image_field_component.css +8 -0
  11. data/app/components/trek/form/image_field_component/image_field_component.html.slim +20 -8
  12. data/app/components/trek/form/image_field_component/image_field_component.js +23 -1
  13. data/app/components/trek/form/image_field_component.rb +13 -0
  14. data/app/components/trek/form/switch_box_component.rb +1 -0
  15. data/app/components/trek/tabs_component/tabs_component.css +1 -0
  16. data/app/controllers/concerns/trek/model.rb +4 -1
  17. data/config/locales/trek.en.yml +3 -0
  18. data/config/locales/trek.fr.yml +1 -0
  19. data/docs/.vitepress/config.mjs +133 -0
  20. data/docs/.vitepress/theme/index.js +17 -0
  21. data/docs/.vitepress/theme/style.css +139 -0
  22. data/docs/customize.md +63 -7
  23. data/docs/getting-started.md +160 -0
  24. data/docs/index.md +39 -0
  25. data/docs/public/logo.svg +1 -0
  26. data/docs/reference/components.md +40 -0
  27. data/docs/reference/concerns/contentable.md +28 -0
  28. data/docs/reference/concerns/formattable.md +24 -0
  29. data/docs/reference/concerns/fragmentable.md +33 -0
  30. data/docs/reference/concerns/keyable.md +18 -0
  31. data/docs/reference/concerns/orderable.md +26 -0
  32. data/docs/reference/concerns/pageable.md +35 -0
  33. data/docs/reference/concerns/pathable.md +28 -0
  34. data/docs/reference/concerns/phonable.md +28 -0
  35. data/docs/reference/concerns/search-engine-optimizable.md +19 -0
  36. data/docs/reference/concerns/searchable.md +22 -0
  37. data/docs/reference/concerns/sectionable.md +24 -0
  38. data/docs/reference/concerns/sluggable.md +21 -0
  39. data/docs/reference/concerns/taggable.md +20 -0
  40. data/docs/reference/concerns/translatable.md +35 -0
  41. data/docs/reference/concerns/versionable.md +27 -0
  42. data/docs/reference/controllers.md +23 -0
  43. data/docs/reference/environment-variables.md +9 -0
  44. data/docs/reference/formatters.md +30 -0
  45. data/docs/reference/forms.md +21 -0
  46. data/docs/reference/generators/admin-user.md +17 -0
  47. data/docs/reference/generators/install.md +42 -0
  48. data/docs/reference/generators/scaffold.md +37 -0
  49. data/docs/reference/generators/taxonomies.md +21 -0
  50. data/docs/reference/icons.md +11 -0
  51. data/docs/reference/index.md +47 -0
  52. data/docs/reference/models/current.md +11 -0
  53. data/docs/reference/models/external-link.md +16 -0
  54. data/docs/reference/models/fragment.md +28 -0
  55. data/docs/reference/models/menu-node.md +30 -0
  56. data/docs/reference/models/page-path.md +19 -0
  57. data/docs/reference/models/page-version.md +30 -0
  58. data/docs/reference/models/page.md +48 -0
  59. data/docs/reference/models/tag.md +41 -0
  60. data/docs/reference/models/user.md +65 -0
  61. data/docs/reference/policies.md +30 -0
  62. data/docs/reference/uploaders.md +19 -0
  63. data/docs/what-is-trek.md +37 -0
  64. data/esbuild.config.js +10 -7
  65. data/lib/generators/trek/templates/initializers/shrine.rb +1 -0
  66. data/lib/trek/version.rb +1 -1
  67. data/netlify.toml +4 -0
  68. data/package.json +10 -5
  69. data/renovate.json +4 -0
  70. data/yarn.lock +2015 -235
  71. metadata +48 -2
@@ -0,0 +1,37 @@
1
+ # Scaffold
2
+
3
+ ```sh
4
+ rails g trek:scaffold Book title intro:text
5
+ ```
6
+
7
+ Akin to Rails', Trek's scaffold generates, with a single command:
8
+
9
+ - the `Book` model and migration (calling the Rails `model` generator behind the scenes)
10
+ - the `Admin::BooksController`, inheriting from [`Trek::ResourceController`](/reference/controllers)
11
+ - the Slim views: `index`, `show`, `new`, `edit` and the `_form` partial
12
+ - the `Admin::BookPolicy`, inheriting from [`Trek::ResourcePolicy`](/reference/policies)
13
+ - the `resources :books` route in the `admin` namespace
14
+ - the locale files for the model and its admin panel, in every configured locale (auto-translated via DeepL when `DEEPL_AUTH_KEY` is set)
15
+ - the entry in the admin dashboard menu
16
+
17
+ ## Field types
18
+
19
+ Standard Rails attribute types are supported (`string`, `text`, `integer`, `float`, `decimal`, `boolean`, `date`, `datetime`, `references`…) and mapped to the matching [form helpers](/reference/forms): `text` renders the content editor, `boolean` a switch box, `references` a collection select, and so on.
20
+
21
+ ## Opt-in concerns
22
+
23
+ The generated model ships with commented-out hooks for Trek's most useful [concerns](/reference/), ready to be enabled when needed:
24
+
25
+ ```ruby
26
+ class Book < ApplicationRecord
27
+ # include Trek::Formattable
28
+ # format_attributes Trek::TypographyFormatter, :title
29
+
30
+ # include Trek::Translatable
31
+ # translate_attributes :title
32
+
33
+ # include RecordImageUploader::Attachment(:image)
34
+
35
+ strip_attributes
36
+ end
37
+ ```
@@ -0,0 +1,21 @@
1
+ # Taxonomies
2
+
3
+ ```sh
4
+ rails g trek:taxonomies
5
+ ```
6
+
7
+ Sets up a tagging & categorization system:
8
+
9
+ - the [`Tag`, `TagCategory` and `Tagging` models](/reference/models/tag) with their migrations
10
+ - the admin panel to manage tags and categories
11
+
12
+ Once installed, make any model taggable with the [`Trek::Taggable`](/reference/concerns/taggable) concern:
13
+
14
+ ```ruby
15
+ class Article < ApplicationRecord
16
+ include Trek::Taggable
17
+ end
18
+
19
+ article.tags << Tag.find_or_create_by(name: "Ruby")
20
+ article.tag_names # => ["Ruby"]
21
+ ```
@@ -0,0 +1,11 @@
1
+ # Icons
2
+
3
+ Trek bundles ~40 SVG icons, rendered with `Trek::IconComponent`:
4
+
5
+ ```ruby
6
+ render Trek::IconComponent.new(:search)
7
+ ```
8
+
9
+ Available keys include: `add`, `arrow-bar-left`, `arrow-bar-right`, `bold`, `check`, `chevron-left`, `chevron-right`, `chevron-double`, `close`, `cursor`, `danger`, `download`, `eraser`, `eye`, `grid`, `hr`, `italic`, `layout`, `link`, `list`, `logout`, `paragraph`, `pic`, `profile`, `quotation`, `rating`, `redo`, `undo`, `search`…
10
+
11
+ [MenuNode](/reference/models/menu-node) records can reference an icon via their `icon_key` to display it in the admin sidebar.
@@ -0,0 +1,47 @@
1
+ # Reference manual
2
+
3
+ A comprehensive list of features and settings included in Trek.
4
+
5
+ ## Generators
6
+
7
+ - [Installer](/reference/generators/install) — `rails g trek:install`
8
+ - [Admin user](/reference/generators/admin-user) — `rails g trek:admin:user`
9
+ - [Scaffold](/reference/generators/scaffold) — `rails g trek:scaffold`
10
+ - [Taxonomies](/reference/generators/taxonomies) — `rails g trek:taxonomies`
11
+
12
+ ## Models
13
+
14
+ Generated into your application — they're yours to read and modify.
15
+
16
+ - [Page](/reference/models/page), [PageVersion](/reference/models/page-version), [PagePath](/reference/models/page-path)
17
+ - [Fragment](/reference/models/fragment)
18
+ - [MenuNode](/reference/models/menu-node), [ExternalLink](/reference/models/external-link)
19
+ - [User](/reference/models/user) (and its session & password-reset form models)
20
+ - [Tag, TagCategory & Tagging](/reference/models/tag) (with the taxonomies generator)
21
+ - [Current](/reference/models/current)
22
+
23
+ ## Concerns
24
+
25
+ Trek's behaviors, packaged as concerns you can mix into any model:
26
+
27
+ - Content: [Contentable](/reference/concerns/contentable), [Fragmentable](/reference/concerns/fragmentable), [Sectionable](/reference/concerns/sectionable), [Formattable](/reference/concerns/formattable)
28
+ - I18n & URLs: [Translatable](/reference/concerns/translatable), [Sluggable](/reference/concerns/sluggable), [Pathable](/reference/concerns/pathable)
29
+ - Pages: [Pageable](/reference/concerns/pageable), [Versionable](/reference/concerns/versionable), [SearchEngineOptimizable](/reference/concerns/search-engine-optimizable)
30
+ - Utilities: [Orderable](/reference/concerns/orderable), [Keyable](/reference/concerns/keyable), [Searchable](/reference/concerns/searchable), [Taggable](/reference/concerns/taggable), [Phonable](/reference/concerns/phonable)
31
+
32
+ ## Back-office
33
+
34
+ - [Controllers](/reference/controllers) — `Trek::BaseController`, `Trek::ResourceController`, panels
35
+ - [Forms](/reference/forms) — `Trek::FormBuilder` and its custom helpers
36
+ - [Formatters](/reference/formatters) — typography & ProseMirror-to-HTML pipeline
37
+ - [Policies](/reference/policies) — ActionPolicy integration
38
+ - [Uploaders](/reference/uploaders) — Shrine-based attachments
39
+
40
+ ## Front-end
41
+
42
+ - [Components](/reference/components) — the ViewComponents powering the admin UI
43
+ - [Icons](/reference/icons) — the bundled SVG icon set
44
+
45
+ ## Configuration
46
+
47
+ - [Environment variables](/reference/environment-variables)
@@ -0,0 +1,11 @@
1
+ # Current
2
+
3
+ Request-scoped global context, built on `ActiveSupport::CurrentAttributes` and generated into `app/models/current.rb`.
4
+
5
+ Trek uses it to cache per-request data — for instance the loaded icon set:
6
+
7
+ ```ruby
8
+ Current.icons
9
+ ```
10
+
11
+ Extend it with your own request-scoped attributes as your application grows (current user, tenant, locale…).
@@ -0,0 +1,16 @@
1
+ # ExternalLink
2
+
3
+ Links pointing outside your application, usable in [menus](/reference/models/menu-node) and content.
4
+
5
+ ## Columns
6
+
7
+ | Column | Type | Notes |
8
+ | --- | --- | --- |
9
+ | `key` | string | unique, optional |
10
+ | `url` | string | required |
11
+
12
+ ## Behavior
13
+
14
+ - `has_many :menu_nodes, as: :linkable, dependent: :destroy`
15
+ - `key` uniqueness (allow nil), `url` presence
16
+ - `to_s` returns the URL
@@ -0,0 +1,28 @@
1
+ # Fragment
2
+
3
+ Reusable chunks of content — a footer text, a hero tagline, a contact blurb — editable from the admin and usable anywhere in your views. Fragments are grouped by `namespace` and identified by `key`.
4
+
5
+ ## Columns
6
+
7
+ | Column | Type | Notes |
8
+ | --- | --- | --- |
9
+ | `key` | string | unique within its namespace |
10
+ | `namespace` | string | grouping (e.g. `footer`, `home`) |
11
+ | `title` | jsonb | translated label |
12
+ | `content` | jsonb | ProseMirror document |
13
+ | `position` | integer | ordering within the namespace |
14
+
15
+ ## Behavior
16
+
17
+ - Includes [Contentable](/reference/concerns/contentable), [Translatable](/reference/concerns/translatable) (`title`, `content`), [Keyable](/reference/concerns/keyable) and `strip_attributes`
18
+ - `acts_as_list scope: [:namespace]` with an `ordered` scope
19
+ - `to_s` returns `"namespace.key"`
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ fragment = Fragment.find_by(namespace: "footer", key: "tagline")
25
+ fragment.formatted_content # => safe HTML
26
+ ```
27
+
28
+ Pages can also be composed from fragments via the [Fragmentable](/reference/concerns/fragmentable) concern and the `page_fragments` configuration matrix.
@@ -0,0 +1,30 @@
1
+ # MenuNode
2
+
3
+ Hierarchical menu builder: each node links to a [Page](/reference/models/page), an [ExternalLink](/reference/models/external-link), or any custom linkable model.
4
+
5
+ ## Columns
6
+
7
+ | Column | Type | Notes |
8
+ | --- | --- | --- |
9
+ | `key` | string | unique within its parent, optional |
10
+ | `title` / `label` | jsonb | translated texts |
11
+ | `parent_id` | bigint | tree hierarchy |
12
+ | `position` | integer | ordering among siblings |
13
+ | `linkable_type` / `linkable_id` | string / bigint | polymorphic target |
14
+ | `icon_key` | string | optional [icon](/reference/icons) |
15
+ | `modifiers` | string | styling flags |
16
+
17
+ ## Behavior
18
+
19
+ - `has_closure_tree` for the hierarchy, [Orderable](/reference/concerns/orderable) for positions
20
+ - `belongs_to :linkable, polymorphic: true, optional: true`
21
+ - Includes [Translatable](/reference/concerns/translatable) and `strip_attributes`
22
+
23
+ ## Notable methods
24
+
25
+ | Method | Returns |
26
+ | --- | --- |
27
+ | `to_s` | title, label or key |
28
+ | `to_param` | key or id |
29
+ | `icon?` / `icon` | whether an icon is set / the `IconComponent` for it |
30
+ | `external_link?` | whether the target is an `ExternalLink` |
@@ -0,0 +1,19 @@
1
+ # PagePath
2
+
3
+ Stable, translatable URLs for [pages](/reference/models/page). Trek keeps every path a page has ever had, so old URLs keep working (redirecting to the current one) after a page is renamed or moved.
4
+
5
+ ## Columns
6
+
7
+ | Column | Type | Notes |
8
+ | --- | --- | --- |
9
+ | `page_id` | bigint | required |
10
+ | `path` | jsonb | translated full path (e.g. `services/design`) |
11
+
12
+ ## Behavior
13
+
14
+ - `belongs_to :page` — all historical paths of the page
15
+ - `has_one :current_page` — the page this path is the *current* path for (via `pages.current_path_id`)
16
+ - Includes [Translatable](/reference/concerns/translatable) — paths are localized per locale
17
+ - Records are created and updated automatically by the page's `after_save_commit` callback; you never manage them by hand
18
+
19
+ See [Pathable](/reference/concerns/pathable) for the page-side API (`full_path`, `find_at_full_path`…).
@@ -0,0 +1,30 @@
1
+ # PageVersion
2
+
3
+ Version history for [pages](/reference/models/page): every snapshot of a versioned page's content and image. There is always exactly one *current* version per page; the others are archived.
4
+
5
+ ## Columns
6
+
7
+ | Column | Type | Notes |
8
+ | --- | --- | --- |
9
+ | `page_id` | bigint | required |
10
+ | `name` | string | version label, auto-generated from the page title and date |
11
+ | `content` | jsonb | ProseMirror content snapshot |
12
+ | `image_data` | jsonb | Shrine attachment snapshot |
13
+ | `current_since` | datetime | set ⇔ this is the current version |
14
+
15
+ ## Behavior
16
+
17
+ - `belongs_to :page` — with `Page#versions` ordered most recent first
18
+ - Validations guarantee **one and only one** current version per page
19
+ - `after_initialize` sets a default name like `"Page title (2026-06-10)"`
20
+ - Includes [Sectionable](/reference/concerns/sectionable), `RecordImageUploader::Attachment(:image)` and `strip_attributes`
21
+
22
+ ## Notable methods
23
+
24
+ | Method | Returns |
25
+ | --- | --- |
26
+ | `current?` | whether this version is the live one |
27
+ | `other_versions` | the page's other versions |
28
+ | `current_image` / `current_image_url` | this version's image |
29
+
30
+ See [Versionable](/reference/concerns/versionable) for the page-side API (`create_current_version_from_page!`, `current_content`…).
@@ -0,0 +1,48 @@
1
+ # Page
2
+
3
+ Hierarchical, translatable content pages with SEO metadata, versioning and stable URL paths. Generated into `app/models/page.rb`.
4
+
5
+ ## Columns
6
+
7
+ | Column | Type | Notes |
8
+ | --- | --- | --- |
9
+ | `key` | string | internal identifier, optional |
10
+ | `slug` | jsonb | translated URL slug |
11
+ | `title` | jsonb | translated title |
12
+ | `description` | jsonb | translated description |
13
+ | `content` | jsonb | ProseMirror document |
14
+ | `pageable_type` / `pageable_id` | string / bigint | polymorphic link to a content model |
15
+ | `parent_id` | bigint | tree hierarchy |
16
+ | `position` | integer | ordering among siblings |
17
+ | `image_data` | jsonb | Shrine attachment |
18
+ | `versioned` | boolean | enables [versioning](/reference/concerns/versionable), default `false` |
19
+ | `sectioned` | boolean | enables [sections](/reference/concerns/sectionable), default `false` |
20
+ | `current_path_id` | bigint | current [PagePath](/reference/models/page-path) |
21
+
22
+ ## Included concerns
23
+
24
+ [Translatable](/reference/concerns/translatable) (`slug`, `title`, `description`), [Formattable](/reference/concerns/formattable), [Contentable](/reference/concerns/contentable), [Fragmentable](/reference/concerns/fragmentable), [Orderable](/reference/concerns/orderable), [Pathable](/reference/concerns/pathable), [SearchEngineOptimizable](/reference/concerns/search-engine-optimizable), [Sectionable](/reference/concerns/sectionable), [Sluggable](/reference/concerns/sluggable), [Versionable](/reference/concerns/versionable), [Keyable](/reference/concerns/keyable), plus `RecordImageUploader::Attachment(:image)` and `strip_attributes`.
25
+
26
+ ## Associations
27
+
28
+ - `has_closure_tree` — parent/children hierarchy (siblings, ancestors, descendants)
29
+ - `belongs_to :pageable, polymorphic: true, optional: true` — the content model this page represents (see [Pageable](/reference/concerns/pageable))
30
+ - `has_many :versions` → [PageVersion](/reference/models/page-version)
31
+ - `has_many :paths` and `belongs_to :current_path` → [PagePath](/reference/models/page-path)
32
+ - `has_many :menu_nodes, as: :linkable` → [MenuNode](/reference/models/menu-node)
33
+
34
+ ## Validations & scopes
35
+
36
+ - `title` presence; `slug` presence (unless root) and uniqueness within the parent
37
+ - `alpha_ordered` — orders by translated title in the current locale
38
+
39
+ ## Notable methods
40
+
41
+ | Method | Returns |
42
+ | --- | --- |
43
+ | `full_path` | the page's current URL path |
44
+ | `find_at_full_path(path)` | class method — page lookup by path |
45
+ | `parsed_content` / `formatted_content` / `content_text` | ProseMirror content as hash / HTML / plain text |
46
+ | `current_version` / `current_content` / `current_image` | versioned content accessors |
47
+ | `seo_values` / `meta_tags` | SEO metadata with fallbacks |
48
+ | `fragments` | the [fragments](/reference/models/fragment) attached to this page key |
@@ -0,0 +1,41 @@
1
+ # Tag, TagCategory & Tagging
2
+
3
+ The taxonomy models, installed by the [taxonomies generator](/reference/generators/taxonomies). Make any model taggable with the [Taggable](/reference/concerns/taggable) concern.
4
+
5
+ ## Tag
6
+
7
+ | Column | Type | Notes |
8
+ | --- | --- | --- |
9
+ | `key` | string | required, unique within its category |
10
+ | `name` | string | required display name |
11
+ | `category_id` | bigint | optional [TagCategory](#tagcategory) |
12
+ | `position` | integer | ordering within the category |
13
+ | `taggings_count` | integer | counter cache |
14
+
15
+ - `belongs_to :category`, `has_many :taggings`, `has_many :taggables, through: :taggings`
16
+ - Scopes: `ordered`, `in_category(key)`, `in_categories(keys)`
17
+
18
+ ## TagCategory
19
+
20
+ | Column | Type | Notes |
21
+ | --- | --- | --- |
22
+ | `key` | string | required, unique |
23
+ | `name` | string | required display name |
24
+ | `position` | integer | ordering |
25
+ | `tags_count` | integer | counter cache |
26
+
27
+ - `has_many :tags` (ordered)
28
+ - Scopes: `ordered`, `with_tags`
29
+
30
+ ## Tagging
31
+
32
+ The polymorphic join between a `Tag` and any taggable record, unique per (`taggable`, `tag`) pair, with counter caches on both sides.
33
+
34
+ ```ruby
35
+ class Article < ApplicationRecord
36
+ include Trek::Taggable
37
+ end
38
+
39
+ article.tags << Tag.in_category("topics").first
40
+ article.tag_names # => ["Ruby"]
41
+ ```
@@ -0,0 +1,65 @@
1
+ # User
2
+
3
+ The host application's `User` model, into which Trek injects authentication (Sorcery), roles, theme & locale preferences and the invitation workflow.
4
+
5
+ ## Columns added by Trek
6
+
7
+ | Column | Type | Notes |
8
+ | --- | --- | --- |
9
+ | `name` | string | display name |
10
+ | `role` | integer | enum, default `guest` |
11
+ | `theme` | integer | enum, default `auto` |
12
+ | `locale` | string | admin UI language |
13
+
14
+ Plus the Sorcery authentication columns (`email`, `crypted_password`, reset tokens…).
15
+
16
+ ## Roles
17
+
18
+ `Trek::Users::Roles` defines the role enum and access levels:
19
+
20
+ ```ruby
21
+ enum :role, { admin: 5, editor: 4, user: 3, reader: 2, guest: 1 }, default: "guest"
22
+ ```
23
+
24
+ - `privileged?` — `true` for `admin` and `editor`; this is what the default [policies](/reference/policies) check
25
+ - `User.human_roles` — translated role names for form selects
26
+
27
+ ## Themes
28
+
29
+ `Trek::Users::Themes` lets each user pick the admin theme:
30
+
31
+ ```ruby
32
+ enum :theme, { auto: 0, light: 1, dark: 2 }, default: "auto"
33
+ ```
34
+
35
+ `User.human_themes` returns translated theme names.
36
+
37
+ ## Locales
38
+
39
+ `Trek::Users::Locales` provides `User.human_locales` — the available admin UI languages with translated labels, sourced from `I18n.available_locales`.
40
+
41
+ ## Invitations
42
+
43
+ `Trek::Users::Invitable` adds a transient `send_invite` flag: when set on creation, the user receives an invitation email with a password-reset link.
44
+
45
+ ```ruby
46
+ User.create(email: "new@example.com", role: :editor, send_invite: true)
47
+ ```
48
+
49
+ ## Validations
50
+
51
+ `Trek::Users::Validations` enforces:
52
+
53
+ - `email` — presence, uniqueness, valid format
54
+ - `password` — minimum 8 characters (on creation or password change)
55
+
56
+ ## Scopes
57
+
58
+ `Trek::Users::Scopes` adds `User.ordered` — alphabetical by name, then email.
59
+
60
+ ## Form models
61
+
62
+ Two non-persisted `ActiveModel::Model` companions handle the auth flows:
63
+
64
+ - **`Trek::UserSession`** — `email` + `password`, used by the login form
65
+ - **`Trek::UserPasswordReset`** — `email`, looks up the `user` to send a reset link to
@@ -0,0 +1,30 @@
1
+ # Policies
2
+
3
+ Authorization relies on [ActionPolicy](https://actionpolicy.evilmartians.io). Trek provides `Trek::ApplicationPolicy`, `Trek::ResourcePolicy` and policies for its own models (`PagePolicy`, `FragmentPolicy`, `UserPolicy`).
4
+
5
+ Scaffolded policies inherit from `Trek::ResourcePolicy` and control access per [role](/reference/models/user#roles):
6
+
7
+ ```ruby
8
+ module Admin
9
+ class BookPolicy < Trek::ResourcePolicy
10
+ def index? = user.privileged?
11
+ def create? = user.privileged?
12
+ def manage? = user.privileged?
13
+ def destroy? = user.privileged?
14
+
15
+ def permitted_attributes
16
+ user.privileged? ? %i[title intro] : []
17
+ end
18
+
19
+ relation_scope do |relation|
20
+ next relation if user.privileged?
21
+
22
+ relation.none
23
+ end
24
+ end
25
+ end
26
+ ```
27
+
28
+ - `permitted_attributes` doubles as strong parameters: `Trek::ResourceController` only permits what the policy allows for the current user.
29
+ - `relation_scope` filters index queries per role.
30
+ - `user.privileged?` is true for the `admin` and `editor` roles.
@@ -0,0 +1,19 @@
1
+ # Uploaders
2
+
3
+ File uploads are handled by [Shrine](https://shrinerb.com), with Uppy on the front-end:
4
+
5
+ - `ImageUploader` — base image uploader
6
+ - `RecordImageUploader` — attach an image to any record: `include RecordImageUploader::Attachment(:image)`
7
+ - `ModelImageUploader` — model-level image uploader (used by content editor images)
8
+
9
+ ## Storage
10
+
11
+ Configured per environment in the generated Shrine initializer (`config/initializers/shrine.rb`):
12
+
13
+ | Environment | Storage |
14
+ | --- | --- |
15
+ | production | S3 (via the [`S3_*` ENV variables](/reference/environment-variables)) |
16
+ | development | local filesystem (`public/uploads/`) |
17
+ | test | in-memory |
18
+
19
+ Image processing uses libvips, blurhash placeholders are generated for progressive loading, and direct uploads go through the Shrine `upload_endpoint` (20 MB max).
@@ -0,0 +1,37 @@
1
+ # What's Trek?
2
+
3
+ Trek is a modern content management & back-office system for Ruby on Rails, built by [Etamin Studio](https://etamin.studio). It is designed to get you started fast with useful conventions, then let you customize the back-office to your project's needs.
4
+
5
+ Trek ships as a Rails engine (a gem plus an NPM package) that integrates into your own Rails application. Unlike hosted CMSs, your content lives in your database, your models stay plain Active Record, and everything remains customizable with regular Rails code.
6
+
7
+ ## What you get
8
+
9
+ - **A complete admin panel** — layout, navigation, search, pagination, light & dark themes, all built with [ViewComponent](https://viewcomponent.org), [Stimulus](https://stimulus.hotwired.dev) and [Turbo](https://turbo.hotwired.dev).
10
+ - **Pages** — hierarchical, translatable pages with version history, SEO metadata and stable, redirect-aware URL paths.
11
+ - **Fragments** — reusable chunks of content (a footer text, a hero tagline…) editable from the admin and usable anywhere in your views.
12
+ - **A rich text editor** — based on [Tiptap](https://tiptap.dev), with image uploads (Uppy), link insertion and content stored as structured ProseMirror JSON.
13
+ - **Authentication & authorization** — [Sorcery](https://github.com/Sorcery/sorcery)-based sign in, password resets, invitations, and [ActionPolicy](https://actionpolicy.evilmartians.io) policies with `admin`, `editor`, `user`, `reader` and `guest` roles.
14
+ - **Internationalization** — content translations with [Mobility](https://github.com/shioyama/mobility), locale management with [i18n-tasks](https://github.com/glebm/i18n-tasks), and optional automatic translation through DeepL.
15
+ - **File uploads** — [Shrine](https://shrinerb.com)-backed attachments with S3 or local storage, image processing via libvips, and blurhash placeholders.
16
+ - **Generators** — an installer that sets up everything above, and a scaffold generator that creates a full admin panel for any model in one command.
17
+
18
+ ## Philosophy
19
+
20
+ Trek follows the Rails way:
21
+
22
+ - **Convention over configuration.** The installer makes opinionated choices (PostgreSQL, Slim, esbuild, PostCSS, RSpec, Standard…) so you don't have to. Every choice can be revisited afterwards — the generated code lives in your application.
23
+ - **Code generation over runtime magic.** Models like `User`, `Page` or `Fragment` are generated into your app with their concerns included, rather than hidden inside the engine. You can read them, edit them, and remove what you don't need.
24
+ - **Progressive customization.** Start with the defaults, then override views, components, policies or styles as your project grows.
25
+
26
+ ## Requirements
27
+
28
+ | Dependency | Version |
29
+ | --- | --- |
30
+ | Ruby | 3.1+ |
31
+ | Rails | 7.0+ |
32
+ | PostgreSQL | 12+ |
33
+ | Node | 20+ |
34
+ | Yarn | 3.x |
35
+ | libvips | — |
36
+
37
+ Ready to try it? Head over to the [quick start](/getting-started).
data/esbuild.config.js CHANGED
@@ -7,7 +7,9 @@ if (process.env.NODE_ENV === undefined) {
7
7
  process.env.NODE_ENV = process.env.RAILS_ENV || "development";
8
8
  }
9
9
 
10
- let config = esbuild.build({
10
+ const watch = process.argv.includes("--watch");
11
+
12
+ const options = {
11
13
  plugins: [
12
14
  rails(),
13
15
  postCssPlugin({
@@ -17,8 +19,7 @@ let config = esbuild.build({
17
19
  entryPoints: glob("app/javascript/*.*"),
18
20
  bundle: true,
19
21
  platform: "browser",
20
- watch: process.argv.includes("--watch"),
21
- sourcemap: !process.argv.includes("--watch"),
22
+ sourcemap: !watch,
22
23
  outdir: "app/assets/builds",
23
24
  loader: {
24
25
  ".png": "file",
@@ -28,9 +29,11 @@ let config = esbuild.build({
28
29
  },
29
30
  logLevel: "debug",
30
31
  minify: process.env.NODE_ENV === "production",
31
- define: { DEBUG: process.env.NODE_ENV !== "production" },
32
- });
32
+ define: { DEBUG: JSON.stringify(process.env.NODE_ENV !== "production") },
33
+ };
33
34
 
34
- if (!process.argv.includes("--watch")) {
35
- config.catch(() => process.exit(1));
35
+ if (watch) {
36
+ esbuild.context(options).then((ctx) => ctx.watch());
37
+ } else {
38
+ esbuild.build(options).catch(() => process.exit(1));
36
39
  }
@@ -35,6 +35,7 @@ end
35
35
 
36
36
  Shrine.plugin :activerecord
37
37
  Shrine.plugin :cached_attachment_data # for retaining the cached file across form redisplays
38
+ Shrine.plugin :remove_attachment
38
39
  Shrine.plugin :restore_cached_data # re-extract metadata when attaching a cached file
39
40
  Shrine.plugin :determine_mime_type, analyzer: :marcel
40
41
  Shrine.plugin :pretty_location
data/lib/trek/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Trek
4
- VERSION = "0.1.20"
4
+ VERSION = "0.1.22"
5
5
  end
data/netlify.toml ADDED
@@ -0,0 +1,4 @@
1
+ # Node version is read from .node-version
2
+ [build]
3
+ command = "yarn docs:build"
4
+ publish = "docs/.vitepress/dist"
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etaminstudio/trek",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "A modern CMS for Ruby on Rails",
5
5
  "main": "app/javascript/trek.js",
6
6
  "repository": {
@@ -30,8 +30,7 @@
30
30
  "tom-select": "^2.2.2"
31
31
  },
32
32
  "devDependencies": {
33
- "esbuild": "^0.15.18",
34
- "esbuild-plugin-import-glob": "^0.1.1",
33
+ "esbuild": "^0.28.0",
35
34
  "esbuild-rails": "^1.0.7",
36
35
  "esbuild-style-plugin": "^1.6.3",
37
36
  "eslint": "^8.49.0",
@@ -48,7 +47,13 @@
48
47
  "prettier": "2.8.8",
49
48
  "stylelint": "^15.11.0",
50
49
  "stylelint-config-standard": "^31.0.0",
51
- "stylelint-order": "^6.0.4"
50
+ "stylelint-order": "^6.0.4",
51
+ "vitepress": "^1.6.4"
52
52
  },
53
- "packageManager": "yarn@3.8.0"
53
+ "packageManager": "yarn@3.8.0",
54
+ "scripts": {
55
+ "docs:dev": "vitepress dev docs",
56
+ "docs:build": "vitepress build docs",
57
+ "docs:preview": "vitepress preview docs"
58
+ }
54
59
  }
data/renovate.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": ["local>devops/renovate-runner"]
4
+ }