Skip to content

Instantly share code, notes, and snippets.

@blairanderson
Last active September 30, 2025 17:55
Show Gist options
  • Select an option

  • Save blairanderson/d195afd2f0de1eb66f6f9369179459c7 to your computer and use it in GitHub Desktop.

Select an option

Save blairanderson/d195afd2f0de1eb66f6f9369179459c7 to your computer and use it in GitHub Desktop.
Rails 8 + Cursor Rules
---
alwaysApply: true
---
# Command Line Rejection List
## Rails command RejectList
- rails server (do not ever run rails console)
- rails console (do not ever run rails console)
## Bun command RejectList
- bun run dev (this is part of our dev workflow - its always running and watching)
---
alwaysApply: true
---
# Rails best practice utilizes "Rails.application.credentials" insetad of ENV
Always look at Rails.application.credentials[SOMETHING][SOMETHING_ELSE] instead of ENV
Rails environment variables are stored inside an encrypted file.
On application load, the file is decrypted into Rails.application.credentials
Example for an OPEN-AI secret would be Rails.application.credentials.openai.api_key
Do NOT recommend env vars like ENV["OPENAI_KEY"] since we should be saving into credentials.
We use these commands to change dev/prod files:
- `$ EDITOR="nano" bin/rails credentials:edit --environment development`
- `$ EDITOR="nano" bin/rails credentials:edit --environment production`
---
alwaysApply: true
---
# Rails Importmap + Bun React App Template
A clean, copy‑pasteable pattern for shipping small React apps into a Rails **importmap-rails** project using **Bun** as the bundler. No webpack/vite.
See: Selective imports in importmap‑rails (for context): [https://github.com/rails/importmap-rails?tab=readme-ov-file#selectively-importing-modules](https://github.com/rails/importmap-rails?tab=readme-ov-file#selectively-importing-modules)
---
## Core ideas
* Keep each React app self‑contained under `frontend/<AppName>`.
* Bun builds ESM bundles into `vendor/javascript/apps/<AppName>/index.js`.
* Importmap pins those bundles under a stable alias like `apps/<alias>`.
* A tiny mount file in `app/javascript/` mounts the app only when its element exists on the page.
---
## React app conventions
* **Location:** `frontend/<AppName>` (PascalCase folder).
* **Entry:** `index.jsx` exports **`mount(el)`** that renders `App` via `createRoot(el).render(<App />)`.
* **Top component:** `frontend/<AppName>/<AppName>.jsx` exports the default React component.
* **Output:** Bun outputs **ESM** to `vendor/javascript/apps/<AppName>/index.js`.
* **Import alias:** In `config/importmap.rb`, pin `apps/<alias>` → `apps/<AppName>/index.js`.
* **Mount point:** Add `app/javascript/<alias>.js` that imports the alias and mounts on a specific element id.
* **Pin the mount point:** `pin "<alias>", to: "<alias>.js", preload: false`.
* **Include on the page:** `<%= javascript_import_module_tag "<alias>" %>` wherever you need the app.
> **Dependencies:** Use React 19 (`react`, `react-dom`). Keep deps minimal; prefer standard DOM APIs. Tailwind classes (if used) ride the Rails pipeline—no extra CSS tooling in the app bundle.
---
## Directory template
```
frontend/
<AppName>/
index.jsx # exports mount(el)
<AppName>.jsx # default React component
```
### `index.jsx` pattern
```jsx
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./<AppName>.jsx";
export function mount(el) {
createRoot(el).render(<App />);
}
```
---
## Example app: **Counter** (no lawncare examples)
### Files
**`frontend/Counter/index.jsx`**
```jsx
import React from "react";
import { createRoot } from "react-dom/client";
import Counter from "./Counter.jsx";
export function mount(el) {
createRoot(el).render(<Counter />);
}
```
**`frontend/Counter/Counter.jsx`**
```jsx
import React, { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div className="p-4 rounded-2xl shadow border inline-flex items-center gap-3">
<button className="px-3 py-1 rounded-lg border" onClick={() => setCount(c => c - 1)}>-</button>
<span className="min-w-10 text-center font-semibold">{count}</span>
<button className="px-3 py-1 rounded-lg border" onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
}
```
**`app/javascript/counter.js`** (mount file)
```js
import * as CounterApp from "apps/counter";
document.addEventListener("turbo:load", () => {
const el = document.getElementById("counter-app");
if (el) CounterApp.mount(el);
});
```
**`app/views/anything/show.html.erb`** (place it where you want the app)
```erb
<div id="counter-app"></div>
<%= javascript_import_module_tag "counter" %>
```
**`config/importmap.rb`** (pins)
```ruby
# Bundle produced by Bun
pin "apps/counter", to: "apps/Counter/index.js"
# Page-level mount file
pin "counter", to: "counter.js", preload: false
```
---
## Bun build & scripts
We use **Bun** for bundling. Do **not** add webpack/vite.
**`package.json`**
```json
{
"private": true,
"devDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"scripts": {
"dev": "bun build frontend/Counter frontend/<AnotherApp> --outdir=vendor/javascript/apps --target=browser --format=esm --minify --watch",
"build": "bun build frontend/Counter frontend/<AnotherApp> --outdir=vendor/javascript/apps --target=browser --format=esm --minify"
}
}
```
> Add each new folder (`frontend/<AppName>`) to both `dev` and `build` scripts. (Bun accepts directories as inputs and emits `index.js`.)
**`Procfile.dev`** (example)
```
web: bin/rails server
js: bun install && bun run dev
css: bin/rails tailwind:watch # if using Tailwind
```
> Start with `bin/dev` if you use the standard Rails foreman setup.
---
## Importmap pins (recap)
Add a pin per app bundle plus a pin for each page‑level mount file.
```ruby
# config/importmap.rb
pin "apps/<alias>", to: "apps/<AppName>/index.js"
pin "<alias>", to: "<alias>.js", preload: false
```
**Example:**
```ruby
pin "apps/counter", to: "apps/Counter/index.js"
pin "counter", to: "counter.js", preload: false
```
---
## Rails wiring (recap)
1. Create a mount file under `app/javascript/<alias>.js` that imports `apps/<alias>` and mounts if the element exists.
2. Add a `<div id="your-element-id">` in the target view.
3. Include `<%= javascript_import_module_tag "<alias>" %>` in that view.
---
## Add this pattern to a fresh Rails app (step‑by‑step)
1. **Install Bun** (once): [https://bun.sh/](https://bun.sh/)
2. **Ensure importmap is present** (Rails 7+):
* If not, run: `bin/rails importmap:install`.
3. **Add React deps**:
```sh
bun add react react-dom
```
4. **Create the frontend structure**:
```sh
mkdir -p frontend/Counter
$EDITOR frontend/Counter/index.jsx frontend/Counter/Counter.jsx
```
Paste the example code above.
5. **Wire importmap pins** in `config/importmap.rb`:
```ruby
pin "apps/counter", to: "apps/Counter/index.js"
pin "counter", to: "counter.js", preload: false
```
6. **Create the mount file**:
```sh
$EDITOR app/javascript/counter.js
```
Paste the mount snippet above.
7. **Add the view container & tag** where needed:
```erb
<div id="counter-app"></div>
<%= javascript_import_module_tag "counter" %>
```
8. **Add Bun scripts** to `package.json` and include `frontend/Counter` in both `dev` and `build`.
9. **Add a Procfile for dev** (or extend your existing `Procfile.dev`):
```
web: bin/rails server
js: bun install && bun run dev
css: bin/rails tailwind:watch # optional
```
10. **Build once (optional) & run dev**:
```sh
bun run build # one-time build (optional)
bin/dev # or: foreman start -f Procfile.dev
```
---
## Adding another app (checklist)
1. Create `frontend/<AppName>/{index.jsx,<AppName>.jsx}` exporting `mount(el)`.
2. Add the folder to **both** `dev` and `build` scripts in `package.json`.
3. `config/importmap.rb`: `pin "apps/<alias>", to: "apps/<AppName>/index.js"`.
4. Create mount file `app/javascript/<alias>.js` that imports `apps/<alias>` and mounts on your element id.
5. Pin the mount file: `pin "<alias>", to: "<alias>.js", preload: false`.
6. Add the container `<div id="…">` and `<%= javascript_import_module_tag "<alias>" %>` to the view.
---
## Copy‑paste prompt for teammates
> Paste this into ChatGPT (or your internal doc) to scaffold a new app:
```
Create a new React app named <AppName> for a Rails importmap project using Bun.
- Files: frontend/<AppName>/index.jsx exporting mount(el) and frontend/<AppName>/<AppName>.jsx default component.
- Output should be bundled by Bun to vendor/javascript/apps/<AppName>/index.js.
- Provide a mount file app/javascript/<alias>.js that imports from apps/<alias> and mounts on <div id="<element-id>"> when present.
- Include the importmap pins for apps/<alias> and <alias>, and the ERB snippet with javascript_import_module_tag "<alias>".
- Update package.json dev/build scripts to include frontend/<AppName>.
Return all snippets.
```
---
## Troubleshooting
* **Nothing renders:** Check the element id matches between the view and the mount file, and that the mount file is pinned + included with `javascript_import_module_tag`.
* **404 on `apps/.../index.js`:** Bun hasn’t emitted the bundle. Run `bun run build` or start `bun run dev`.
* **Multiple apps on a page:** Give each a unique element id and mount file alias.
* **ESM import errors:** Ensure `to: "apps/<AppName>/index.js"` paths match Bun’s output path exactly.
---
description: Use Bun instead of Node.js, npm, pnpm, or vite.
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
alwaysApply: false
---
# DOWNLOAD full contents from: https://github.com/oven-sh/bun/blob/main/src/init/rule.md
https://github.com/oven-sh/bun/blob/main/src/init/rule.md
---
description: Use Rails G `rails generate` for creating models, migrations, mailers, etc - must be made with rails generation cli
globs:
alwaysApply: true
---
# Use `rails g` command-line-interface for creating migrations
Migrations are needed to:
- add / remove indexes
- add / remove columns
- create tables
- create models (with tables)
I don't want you to create migrations manually (by creating a file).
You must use `bin/rails generate model {ModelName}` for creating models & tables
You must use `bin/rails generate migration {summary_of_migration_changes} *optional_column_names:column_types` for creating migration files to add/remove columns and indexes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment