belt 0.4.5 → 0.4.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1436a1cb03096bd9de50abd65ecfbb973fc0f054c7e45902bdadd78516806a0c
4
- data.tar.gz: 88ddfe6f1b1789a539bd71d407a736514ef091d2b126fc8c3296e5ee2f4e23ca
3
+ metadata.gz: f29c42b3eb7869a90ab1a0a22d56fd6b1304dbd6e22f32503669b3c9b0974890
4
+ data.tar.gz: f313115ee77ceafb82a4c424c6721f6776f3b869c1b71cc1e458332bc16b53fd
5
5
  SHA512:
6
- metadata.gz: a5e7bee23ca7077b19273c9c7605da6c42a1ec8226332d629a6d7a51b9f3ece913cc415005b905a32774eb0b8e47159fa2abeee08e5b3b4888008981a116ad82
7
- data.tar.gz: 33ffbdc6d97e3600f09d90877d0edde784f2d5b377d3250d4044ff456256ecb7642936f7c4531b47dc88d7be69c6160e3b694873a6f7d712fb532e62cbb97d85
6
+ metadata.gz: 57150cb2d0934d723b6fafb9eaee652dc933c4522fcae787518d2e03990da13e1c9f229d498b990a86fb1daa90ec3916e4e049e411a418a4534e76f4274a658e
7
+ data.tar.gz: 8e2d42b8ba9417dacdf725a66cc0e0a0d37e3a7ae59c3085a50f40c7c1e38eb298be49fa0a0ac3d3489e445e3907d85174bc07f45959a385ccbe55a605a5016d
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.4.6
4
+
5
+ ### Added
6
+
7
+ - **Agent Skill (`SKILL.md`) — installable via `npx skills add stowzilla/belt`.**
8
+ A root-level `SKILL.md` with trigger-rich frontmatter, a lean activation body
9
+ (core patterns + decision guide), and on-demand `references/` docs (CLI, routing,
10
+ controllers, models+auth, deploy/ops, plugins). A CI workflow validates the
11
+ frontmatter and that every reference link resolves.
12
+ - **`SKILL.md` + `references/` now ship inside the `belt` gem.** The gemspec
13
+ `files` manifest was extended so a `gem install belt` lands the skill on disk
14
+ alongside the runtime. The `skills` CLI resolves git repos and local paths (not
15
+ RubyGems), so a gem install won't *auto-register* the skill — but the files are
16
+ present and can be picked up via a local-path install
17
+ (`npx skills add <gem-install-dir>`), and belt.dev tooling can vendor them.
4
18
 
5
19
  ### Bug Fix
6
20
 
data/SKILL.md ADDED
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: belt
3
+ license: MIT
4
+ description: >-
5
+ Build, run, and deploy serverless Ruby apps on AWS Lambda with the Belt
6
+ framework (Rails-inspired). Use when working in a Belt app or the belt gem:
7
+ scaffolding apps/models/controllers/frontends, defining routes with the
8
+ Belt routing DSL, writing BeltController actions, modeling DynamoDB data
9
+ with ActiveItem, wiring Cognito auth, configuring the Lambda handler,
10
+ running `belt` CLI commands (new, generate, deploy, console, routes, logs,
11
+ server, setup, plugin, explain, db:copy/db:seed), Terraform via belt,
12
+ backups, observability, or authoring Belt plugins. Triggers on "belt new",
13
+ "belt deploy", "belt generate", "belt routes", "BeltController", "ActiveItem",
14
+ "Belt::LambdaHandler", "belt console", "belt server", "conveyor-belt". Do NOT
15
+ activate for physical belts, conveyor hardware, or unrelated Ruby web
16
+ frameworks like Rails/Sinatra outside a Belt project.
17
+ metadata:
18
+ author: stowzilla
19
+ version: "0.1.0"
20
+ ---
21
+
22
+ # Belt
23
+
24
+ Belt is a Rails-inspired framework for serverless Ruby on AWS Lambda. It ships a
25
+ runtime (`BeltController`, `Belt::LambdaHandler`, `Belt::ActionRouter`,
26
+ `Belt::Authentication`), a DynamoDB ORM (`ActiveItem`), logging/metrics
27
+ (`lambda_loadout`), a routing DSL consumed by the **conveyor-belt** Terraform
28
+ provider, and a `belt` CLI. Optional features (`belt-messaging`, `belt-pay`)
29
+ ship as separate plugin gems.
30
+
31
+ ## First: orient yourself
32
+
33
+ Belt has an authoritative, in-repo docs system. **Use it before guessing.**
34
+
35
+ ```bash
36
+ belt explain <topic> # routing controllers models deployment generators
37
+ # lambda_handler observability console backups
38
+ # data_seeding plugins structure frontend authentication
39
+ belt --help # full command list
40
+ belt routes # list every endpoint (verb, path, controller#action)
41
+ belt doctor # check deps + AWS config
42
+ ```
43
+
44
+ - **Working in a Belt app?** Read the app's own `AGENTS.md` (scaffolded by `belt new`).
45
+ - **Working on the belt gem itself?** Read `AGENTS.md` + `README.md` at the gem root.
46
+ - The `belt explain` docs live at `lib/belt/docs/*.md` in the gem — the single
47
+ source of truth for CLI behavior, controller lifecycle, and routing.
48
+
49
+ ## Decision guide
50
+
51
+ | I want to… | Do this |
52
+ |---|---|
53
+ | Scaffold an app | `belt new <name> --frontend react` |
54
+ | Add a resource | `belt generate scaffold post title:string body:text` |
55
+ | See all routes | `belt routes` (add `-f json` for tooling) |
56
+ | Deploy to AWS | `belt deploy <env>` (`--auto` to skip prompt) |
57
+ | Poke at data live | `belt console <env>` |
58
+ | Run a local frontend | `belt server` (see references for env targeting) |
59
+ | Add Cognito auth | `belt generate auth` → `cognito_authenticatable` in a model |
60
+ | Build a plugin | `belt plugin new <name>` |
61
+ | Learn a concept | `belt explain <topic>` |
62
+
63
+ ## Core patterns (memorize these)
64
+
65
+ **Controller** — assigns become the JSON body; explicit helpers win.
66
+
67
+ ```ruby
68
+ class PostsController < BeltController::Base
69
+ before_action :authenticate_user!
70
+
71
+ def index
72
+ @posts = Post.where(user_id: current_user.id, index: "UserIndex")
73
+ end
74
+
75
+ def create
76
+ @post = Post.create!(params.require(:post).permit(:title, :body).to_h)
77
+ response_status :created # → 201 + { post: {...} }
78
+ end
79
+
80
+ def destroy
81
+ Post.find(params["id"]).destroy
82
+ head :no_content
83
+ end
84
+ end
85
+ ```
86
+
87
+ **Model** — ActiveItem over DynamoDB.
88
+
89
+ ```ruby
90
+ class Post < ActiveItem::Base
91
+ self.primary_key = :id
92
+ attr_accessor :id, :user_id, :title, :body, :created_at
93
+ validates :title, presence: true
94
+ before_create { self.id ||= SecureRandom.uuid }
95
+ end
96
+ ```
97
+
98
+ **Routes DSL** — `infrastructure/routes.tf.rb`. `gateway`/`function` pick the
99
+ Lambda; `namespace`/`scope` only affect paths + controller module.
100
+
101
+ ```ruby
102
+ Belt.application.routes.draw do
103
+ gateway :api, auth: :cognito do
104
+ resources :posts do
105
+ member { post :publish } # /posts/:post_id/publish
106
+ collection { get :recent } # /posts/recent
107
+ end
108
+ end
109
+ end
110
+ ```
111
+
112
+ **Lambda entry point** — `Belt::LambdaHandler` gives observability, CORS
113
+ preflight, JSON parsing, and error wrapping for free.
114
+
115
+ ```ruby
116
+ require "belt"
117
+ include Belt::LambdaHandler
118
+ ROUTER = Belt::ActionRouter.new(routes: Routes::API, gateway: "api")
119
+
120
+ def execute(path:, body:, event:)
121
+ ROUTER.route(event: event, body: body)
122
+ end
123
+ ```
124
+
125
+ ## Golden rules for agents
126
+
127
+ 1. **`belt explain <topic>` before inventing behavior.** The docs are canonical.
128
+ 2. **`namespace`/`scope` never change which Lambda serves a route** — only
129
+ `gateway` and `function` do. Do not conflate them.
130
+ 3. **Controllers need no registration.** Belt discovers them by convention.
131
+ 4. **Runtime code stays in gems; generators copy only what the app must own.**
132
+ 5. **Never commit secrets** (`.env`, AWS keys, real account IDs).
133
+ 6. **`rubocop` + `rspec` must pass** before a PR on the belt gem.
134
+
135
+ ## Deeper references
136
+
137
+ Load these only when the task needs them:
138
+
139
+ - [references/cli.md](references/cli.md) — every `belt` command, flags, env vars, Terraform shorthand
140
+ - [references/routing.md](references/routing.md) — full routing DSL, nested resources, request/response model inference
141
+ - [references/controllers.md](references/controllers.md) — callbacks, strong params, responses, error handling, formats
142
+ - [references/models-and-auth.md](references/models-and-auth.md) — ActiveItem + Cognito authentication
143
+ - [references/deploy-and-ops.md](references/deploy-and-ops.md) — deploy lifecycle, environments, backups, seeding, observability
144
+ - [references/plugins.md](references/plugins.md) — authoring Belt plugin gems and the GeneratorRegistry contract
data/lib/belt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Belt
4
- VERSION = '0.4.5'
4
+ VERSION = '0.4.6'
5
5
  end
data/references/cli.md ADDED
@@ -0,0 +1,76 @@
1
+ # Belt CLI reference
2
+
3
+ Run `belt --help` for the live list, or `belt <command> --help` for a specific
4
+ command. `belt explain <topic>` gives conceptual docs. `BELT_ENV` sets the
5
+ default environment so you can omit the `<env>` argument.
6
+
7
+ ## Commands
8
+
9
+ | Command | What it does |
10
+ |---|---|
11
+ | `belt new <app> [--frontend react]` | Create a new Belt app. `-v` lists every created file. |
12
+ | `belt generate <thing> <name>` (alias `g`) | Generate `scaffold`, `model`, `controller`, `frontend`, `views`, `environment`, `dns`, `auth`, or a plugin generator. |
13
+ | `belt destroy <thing> <name>` (alias `d`) | Remove what `generate` created. |
14
+ | `belt routes [-g PATTERN] [-f json] [--namespace N]` | Show/inspect routes; generate Ruby route constants for the runtime router. |
15
+ | `belt contracts [-g PATTERN] [-f json]` | Show API request/response contracts. |
16
+ | `belt lambda-config [-e ENV] [-f json\|terraform]` | Show merged Lambda configuration. |
17
+ | `belt console [env]` (alias `c`) | Interactive IRB with the app booted. `--run "expr"` for runner mode. |
18
+ | `belt logs [lambda] [-f] [-s 5m] [-e env]` | Tail Lambda logs. |
19
+ | `belt tasks [-g PATTERN] [-a]` (alias `-T`) | List rake tasks. Any rake task can be run directly: `belt lambda:build_layer`. |
20
+ | `belt setup <state\|tables <env>\|frontend>` | Create S3 state bucket / generate DynamoDB tables / frontend infra. |
21
+ | `belt doctor` | Check system deps + AWS config. |
22
+ | `belt plugin new <name>` | Scaffold a Belt plugin gem. |
23
+ | `belt explain <topic>` | Explain a concept (see topic list below). |
24
+ | `belt deploy [env] [--auto] [--skip-backup] [--backup-only]` | Deploy to AWS (init → plan → apply, runs backups first if configured). |
25
+ | `belt deploy frontend <env> [--frontend NAME]` | Build + deploy frontend(s). |
26
+ | `belt dns <deploy\|add <env>\|show>` | Manage the root DNS zone. |
27
+ | `belt frontend <env <env>\|list>` | Write `<frontend>/.env` from TF outputs, or list frontends. |
28
+ | `belt server [--frontend NAME]` (alias `s`) | Start local dev server. |
29
+ | `belt db:copy <from> <to> [--force]` | Copy DynamoDB data between environments. |
30
+ | `belt db:seed [env] [--force]` | Run `config/seeds.rb` against an environment. |
31
+ | `belt version` | Show Belt version. |
32
+
33
+ ### Terraform shorthand
34
+
35
+ `belt <action> [env]` maps to Terraform: `init`, `plan`, `apply`, `destroy`,
36
+ `output`. Example: `belt apply wups`, `belt output prod`.
37
+
38
+ > ⚠ `belt destroy` is ambiguous: `belt destroy <env>` runs terraform destroy,
39
+ > while `belt destroy scaffold post` removes generated code. Belt disambiguates
40
+ > by argument shape.
41
+
42
+ ## `belt explain` topics
43
+
44
+ `routing`, `controllers`, `models`, `deployment`, `generators`,
45
+ `lambda_handler`, `observability`, `console`, `backups`, `data_seeding`,
46
+ `plugins`, `structure`, `frontend`, `authentication`.
47
+
48
+ ## Standalone vs project commands
49
+
50
+ These run anywhere (no Belt project needed): `new`, `version`, `doctor`,
51
+ `explain`. All others chdir to the detected project root first.
52
+
53
+ ## Environment variables
54
+
55
+ | Variable | Purpose |
56
+ |---|---|
57
+ | `BELT_ENV` | Default environment for env-scoped commands |
58
+ | `ENVIRONMENT` | Verbose error responses (`dev*`, `local`, `test`) |
59
+ | `BELT_METRICS_NAMESPACE` | CloudWatch metrics namespace (default `Belt`) |
60
+ | `ACTION` | Service name for logging (falls back to function name) |
61
+ | `ERROR_NOTIFICATION_TOPIC_ARN` | SNS topic for error alerts |
62
+ | `CORS_ALLOWED_ORIGINS` | Comma-separated origins (overrides domain vars) |
63
+ | `CUSTOMER_APP_DOMAIN` / `OPS_APP_DOMAIN` | CORS domains |
64
+
65
+ ## Common flows
66
+
67
+ ```bash
68
+ belt new blog --frontend react
69
+ belt generate scaffold post title:string content:text
70
+ belt routes
71
+ belt deploy dev
72
+ belt deploy prod --auto
73
+ belt console prod --run "Post.count"
74
+ belt logs api -f -e prod
75
+ belt db:copy prod dev
76
+ ```
@@ -0,0 +1,73 @@
1
+ # BeltController
2
+
3
+ `BeltController::Base` gives Rails-like callbacks, strong params, response
4
+ helpers, and error handling. Run `belt explain controllers` for canonical docs.
5
+
6
+ ## Implicit responses
7
+
8
+ Instance variables assigned in an action become the JSON body by default:
9
+
10
+ ```ruby
11
+ def index
12
+ @posts = Post.all # → { "posts": [ ... ] }
13
+ end
14
+ ```
15
+
16
+ Explicit helpers always override implicit assigns.
17
+
18
+ ## Callbacks
19
+
20
+ ```ruby
21
+ before_action :authenticate_user!
22
+ before_action :require_admin!, except: [:health]
23
+ skip_before_action :authenticate_user!, only: [:health]
24
+ ```
25
+
26
+ ## Strong parameters
27
+
28
+ ```ruby
29
+ params.require(:user).permit(:name, :email, address: [:street, :city])
30
+ ```
31
+
32
+ ## Response helpers
33
+
34
+ ```ruby
35
+ success_response({ id: "123" }) # 200 JSON + CORS
36
+ success_response({ id: "123" }, :created) # 201 (symbol or int)
37
+ error_response("Not found", :not_found) # 404 JSON error
38
+ error_response("Nope", :unprocessable_entity) # 422
39
+ html_response("<h1>Hi</h1>") # 200 HTML + CORS
40
+ head :no_content # 204 empty
41
+ response_status :created # 201 + implicit assigns
42
+ ```
43
+
44
+ ## Error handling
45
+
46
+ ```ruby
47
+ rescue_from MyError, with: :handle_it
48
+
49
+ def handle_it(exception, _context = {})
50
+ error_response(exception.message, 422)
51
+ end
52
+ ```
53
+
54
+ ## Default format (JSON vs HTML)
55
+
56
+ ```ruby
57
+ # App-wide (lambda/config/environment.rb)
58
+ Belt.configure { |c| c.default_format = :json } # default
59
+
60
+ # Per-controller
61
+ class PagesController < ApplicationController
62
+ self.default_format = :html # implicitly renders views/<controller>/<action>.html.erb
63
+ end
64
+ ```
65
+
66
+ - `:json` (default): assigns → `success_response({ ... })`.
67
+ - `:html`: Belt implicitly renders the ERB template. Missing template raises
68
+ `Belt::TemplateNotFound` (no silent JSON fallback).
69
+
70
+ ## Controller discovery
71
+
72
+ No registration needed. Belt looks in the app namespace module first, then
73
+ `Belt.all_controller_paths`.
@@ -0,0 +1,77 @@
1
+ # Deploy, environments, backups, seeding & observability
2
+
3
+ Run `belt explain deployment`, `belt explain backups`, `belt explain
4
+ data_seeding`, and `belt explain observability` for canonical docs.
5
+
6
+ ## Deploy lifecycle
7
+
8
+ `belt deploy [env]` runs pre-deploy backups (if configured) → terraform init →
9
+ plan → apply. The **conveyor-belt** Terraform provider packages Ruby into
10
+ Lambdas, creates API Gateway routes from the routing DSL, generates IAM for
11
+ DynamoDB access, and sets up CloudWatch log groups.
12
+
13
+ ```bash
14
+ belt deploy dev
15
+ belt deploy prod --auto # skip confirmation
16
+ belt deploy prod --skip-backup # CI re-runs
17
+ belt deploy prod --backup-only # recovery point, no deploy
18
+ ```
19
+
20
+ Provider config (Terraform):
21
+
22
+ ```hcl
23
+ terraform {
24
+ required_providers {
25
+ conveyor-belt = { source = "stowzilla/conveyor-belt", version = "~> 0.0.1" }
26
+ }
27
+ }
28
+ ```
29
+
30
+ ## Environments
31
+
32
+ Each env has `infrastructure/<env>/` (main.tf, backend.tf, variables.tf,
33
+ terraform.tfvars, outputs.tf, belt.rb). Create with `belt generate environment
34
+ <name> [parent]`. Terraform shorthand: `belt init|plan|apply|destroy|output <env>`.
35
+ Set `BELT_ENV` to omit the env arg.
36
+
37
+ ## Backups (pre-deploy, config-driven)
38
+
39
+ `infrastructure/<env>/belt.rb`:
40
+
41
+ ```ruby
42
+ Belt.configure do |config|
43
+ config.backups do
44
+ dynamodb :all # PITR check + on-demand snapshot per table
45
+ cognito :users, :pool_config # export to backup bucket
46
+ s3 :legal_documents # sync to backup bucket
47
+ retention snapshots: 90, cognito: 10, s3: 10
48
+ end
49
+ end
50
+ ```
51
+
52
+ Simple mode: `config.backups = true` (DynamoDB, all tables, 90-day retention).
53
+ Omit the block entirely for lightweight dev envs. Belt auto-creates
54
+ `<app>-backups-<env>` (versioned, public access blocked) on first run. Table
55
+ names come from `terraform output`, so the first-ever deploy skips backups.
56
+
57
+ ## Data seeding
58
+
59
+ ```bash
60
+ belt db:copy prod dev [--force] # copy DynamoDB between envs (matches by stripped prefix)
61
+ belt db:seed [env] [--force] # run config/seeds.rb in the booted console context
62
+ ```
63
+
64
+ `db:copy` skips non-empty destination tables by default. `db:seed` refuses to
65
+ run against an env that already has data unless `--force`.
66
+
67
+ ## Observability
68
+
69
+ `Belt::LambdaHandler` wires these global facades automatically:
70
+
71
+ ```ruby
72
+ Belt::Observability::Logger.info("Something happened", user_id: "123")
73
+ Belt::Observability::Metrics.track_event("OrderCreated", model: "Order")
74
+ ```
75
+
76
+ Backed by `lambda_loadout` (structured logging + CloudWatch EMF metrics + error
77
+ alerting via `ERROR_NOTIFICATION_TOPIC_ARN`).
@@ -0,0 +1,71 @@
1
+ # Models (ActiveItem) & Cognito authentication
2
+
3
+ Run `belt explain models` and `belt explain authentication` for canonical docs.
4
+
5
+ ## ActiveItem (DynamoDB ORM)
6
+
7
+ ```ruby
8
+ require "activeitem"
9
+
10
+ class Post < ActiveItem::Base
11
+ self.primary_key = :id
12
+ attr_accessor :id, :user_id, :title, :body, :created_at
13
+
14
+ validates :title, presence: true
15
+ before_create { self.id ||= SecureRandom.uuid }
16
+ end
17
+ ```
18
+
19
+ Supports queries, validations, associations, and transactions. Query a GSI:
20
+
21
+ ```ruby
22
+ Post.where(user_id: current_user.id, index: "UserIndex")
23
+ Post.find(id)
24
+ Post.create!(attrs)
25
+ ```
26
+
27
+ Table schema is declared in `infrastructure/schema.tf.rb`:
28
+
29
+ ```ruby
30
+ Belt.application.schema.define do
31
+ model :post do
32
+ partition_key :id, :string
33
+ global_secondary_index :UserIndex, partition_key: :user_id
34
+ end
35
+ end
36
+ ```
37
+
38
+ DynamoDB tables generated by Belt default to PITR enabled and (in prod)
39
+ deletion protection enabled.
40
+
41
+ ## Authentication — Cognito owns auth, Belt owns the record
42
+
43
+ ```ruby
44
+ class User < ApplicationRecord
45
+ cognito_authenticatable
46
+ end
47
+ ```
48
+
49
+ One line supplies: Cognito `sub` as primary key, identity attributes (`email`,
50
+ `name`, `role`, `email_verified`, `last_seen_on`), an `EmailIndex` GSI,
51
+ just-in-time provisioning from a token, and `#admin?` for platform staff.
52
+
53
+ Controllers get helpers for free — no `include`, no config:
54
+
55
+ | Helper | Meaning |
56
+ |---|---|
57
+ | `current_user` | User record or nil (memoized per request) |
58
+ | `user_signed_in?` | Is there a Cognito identity on this request? |
59
+ | `authenticate_user!` | `before_action` guard → 401 |
60
+ | `cognito_admin?` | Does the token carry a staff Cognito group? |
61
+
62
+ ```ruby
63
+ class ProfilesController < ApplicationController
64
+ before_action :authenticate_user!
65
+ def show = @profile = current_user
66
+ end
67
+ ```
68
+
69
+ `belt generate auth` creates the user pool **and** scaffolds the model + table.
70
+ See `belt explain authentication` for the `after_cognito_sync` hook, platform
71
+ staff handling, and both token shapes. Upgrading an existing app? See `UPGRADING.md`.
@@ -0,0 +1,64 @@
1
+ # Authoring Belt plugins
2
+
3
+ Belt stays lean; optional capabilities ship as **separate gems** that plug into
4
+ the CLI and runtime. Run `belt explain plugins` for canonical docs. Reference
5
+ implementations: `belt-messaging`, `belt-pay`.
6
+
7
+ ## Discovery contract (GeneratorRegistry)
8
+
9
+ No central registry, no initializer. Belt discovers a generator when:
10
+
11
+ 1. The gem is in the app's `Gemfile` and bundled.
12
+ 2. It ships `lib/belt/generators/<name>_generator.rb`.
13
+ 3. The class is `Belt::Generators::<Name>Generator`.
14
+ 4. It implements `.run(args)` (required); optionally `.destroy(args)` and `.description`.
15
+
16
+ After `bundle install`, `belt generate <name>` and `belt destroy <name>` just work.
17
+
18
+ ## Scaffold a plugin
19
+
20
+ ```bash
21
+ belt plugin new notifications # → ./belt-notifications/
22
+ belt plugin new pay --path ~/Code --summary "Stripe payments for Belt"
23
+ ```
24
+
25
+ Point an app at a local plugin while developing:
26
+
27
+ ```ruby
28
+ # app Gemfile
29
+ gem "belt-notifications", path: "../belt-notifications"
30
+ ```
31
+
32
+ `belt deploy` vendors `path:` gems into `vendor/cache` so conveyor-belt can
33
+ package them.
34
+
35
+ ## Canonical layout
36
+
37
+ ```
38
+ belt-messaging/
39
+ ├── belt-messaging.gemspec
40
+ ├── lib/
41
+ │ ├── belt-messaging.rb # require entrypoint
42
+ │ └── belt/
43
+ │ ├── messaging.rb # Belt::Messaging API
44
+ │ ├── messaging/{configuration,version}.rb
45
+ │ ├── messaging/controllers/ # default controllers (optional)
46
+ │ ├── messaging/templates/ # ERB for the generator
47
+ │ └── generators/messaging_generator.rb # ← auto-discovered
48
+ └── spec/
49
+ ```
50
+
51
+ **Runtime code stays in the gem.** Generators copy only what the host app must
52
+ own — Terraform modules, Lambda entrypoints, optional controller overrides.
53
+ Prefer gem defaults + `belt g <plugin> --controllers` over dumping everything
54
+ into the app.
55
+
56
+ ## Generator checklist
57
+
58
+ 1. Terraform module → `infrastructure/modules/<name>/`
59
+ 2. Lambda config → `config/lambda/<name>.yml`
60
+ 3. Lambda entrypoint → `lambda/<name>.rb` via `Belt::LambdaHandler`
61
+ 4. Routes/schema injection when needed
62
+ 5. Optional `--controllers` for app-local overrides
63
+ 6. Matching `destroy` path
64
+ 7. `.description` + `--help`
@@ -0,0 +1,78 @@
1
+ # Belt routing DSL
2
+
3
+ Routes live in `infrastructure/routes.tf.rb` and are read both by the
4
+ **conveyor-belt** Terraform provider (for infra) and by `belt routes` (which
5
+ generates the runtime route constants at `lambda/lib/routes/<namespace>_routes.rb`).
6
+
7
+ Run `belt explain routing` for the canonical docs.
8
+
9
+ ## Four keywords
10
+
11
+ | Keyword | Purpose | Changes which Lambda? |
12
+ |---|---|---|
13
+ | `gateway` | API Gateway + default Lambda | **Yes** — sets default for routes inside |
14
+ | `function` | Route to a different Lambda | **Yes** — overrides gateway default |
15
+ | `namespace` | Path prefix + controller module | No — code organization only |
16
+ | `scope` | Path/module/auth grouping | No — grouping + shared options |
17
+
18
+ **Critical:** `namespace` and `scope` are purely organizational. Only `gateway`
19
+ and `function` determine the serving Lambda.
20
+
21
+ ```ruby
22
+ Belt.application.routes.draw do
23
+ gateway :api, auth: :cognito do
24
+ resources :posts # lambda: api, /posts, posts controller
25
+
26
+ namespace :admin do
27
+ resources :users # /admin/users, admin/users controller
28
+ end
29
+
30
+ function :worker do
31
+ resources :jobs # lambda: worker, /jobs, jobs controller
32
+ end
33
+
34
+ scope path: 'v2', module: 'legacy' do
35
+ resources :widgets # /v2/widgets, legacy/widgets controller
36
+ end
37
+ end
38
+ end
39
+ ```
40
+
41
+ ## Nested resources
42
+
43
+ ```ruby
44
+ resources :projects do
45
+ resource :billing, only: [:show], tables: [:memberships] # singular, no :id
46
+
47
+ resources :webhooks do
48
+ member { post :test } # POST /projects/:project_id/webhooks/:webhook_id/test
49
+ end
50
+
51
+ resources :surfaces do
52
+ collection { get :teams } # GET /projects/:project_id/surfaces/teams
53
+ member { put :assign } # PUT /projects/:project_id/surfaces/:surface_id/assign
54
+ end
55
+
56
+ scope path: 'billing', controller: :billing, tables: [:memberships] do
57
+ get '/', action: :show
58
+ post :checkout
59
+ end
60
+ end
61
+ ```
62
+
63
+ - **Action inference:** `post :checkout` uses `checkout` as both path segment and action.
64
+ - **Controller inheritance:** `member`/`collection` inherit the parent resource's controller.
65
+ - **`tables:`** declares DynamoDB access for IAM generation.
66
+
67
+ ## Request/response model inference (for `belt routes` / contracts)
68
+
69
+ Resolution order (highest first):
70
+
71
+ 1. **Explicit per-route:** `put "/items/:id", request_model: :update_item`
72
+ 2. **Hash per-action:** `resources :items, request_model: { create: :create_item }`
73
+ 3. **Convention cascade** (POST/PUT/PATCH only):
74
+ - `:<verb>_<gateway>_<singular>` → e.g. `:create_customer_item`
75
+ - `:<verb>_<singular>` → e.g. `:create_item`
76
+
77
+ **Response model:** singular of the resource name → `resources :items` looks for
78
+ `model :item` in `contracts.rb`. Applies to all verbs. No match = no model documented.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: belt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stowzilla
@@ -97,6 +97,7 @@ files:
97
97
  - CHANGELOG.md
98
98
  - LICENSE.txt
99
99
  - README.md
100
+ - SKILL.md
100
101
  - exe/belt
101
102
  - lib/belt.rb
102
103
  - lib/belt/action_router.rb
@@ -262,6 +263,12 @@ files:
262
263
  - lib/templates/views/Index.jsx.erb
263
264
  - lib/templates/views/New.jsx.erb
264
265
  - lib/templates/views/Show.jsx.erb
266
+ - references/cli.md
267
+ - references/controllers.md
268
+ - references/deploy-and-ops.md
269
+ - references/models-and-auth.md
270
+ - references/plugins.md
271
+ - references/routing.md
265
272
  homepage: https://github.com/stowzilla/belt
266
273
  licenses:
267
274
  - MIT