forked from fixin.me/fixin.me
setDefaultUnit and drop previously made raw fetch() requests and called Turbo.renderStreamMessage() manually. Now both create a temporary <form>, append hidden inputs, and call form.requestSubmit() — Turbo intercepts the submission natively, handling CSRF, stream responses, and lifecycle. Also document this convention in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
107 lines
5.0 KiB
Markdown
107 lines
5.0 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
Fixin.me is a "quantified self" Rails 7.2.3 application for personal data tracking. Users define hierarchical **quantities** (metrics to track), **units** (with optional conversion hierarchies), and **readouts** (individual measurements). There is also a non-persistent **measurement** model used as a form wrapper.
|
|
|
|
## Setup
|
|
|
|
Configuration files are distributed as `.dist` templates — copy and customize before use:
|
|
|
|
```bash
|
|
cp config/application.rb.dist config/application.rb
|
|
cp config/database.yml.dist config/database.yml
|
|
cp config/puma.rb.dist config/puma.rb
|
|
```
|
|
|
|
```bash
|
|
bundle config --local frozen true
|
|
bundle config --local path .gem
|
|
bundle config --local with mysql development test # or: pg, sqlite
|
|
bundle install
|
|
RAILS_ENV=development bundle exec rails db:create db:migrate db:seed
|
|
```
|
|
|
|
## Common Commands
|
|
|
|
```bash
|
|
bundle exec rails s # start server
|
|
bundle exec rails test # all unit/model/controller tests
|
|
bundle exec rails test:system # all system tests (Capybara + Selenium)
|
|
bundle exec rails test test/system/units_test.rb # single test file
|
|
bundle exec rails test --seed 64690 --name test_add_unit # single test by name
|
|
bundle exec rails db:seed:export # export default settings as seed file
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Data Model
|
|
|
|
- **Quantity** — hierarchical tree (self-referential `parent_id`). Cached `depth` and `pathname` fields are recomputed via recursive CTEs on write. Direct assignment to cached fields is blocked.
|
|
- **Unit** — optional hierarchy via `base_id` and `multiplier` for unit conversion. Multiplier precision/scale is validated by a custom validator.
|
|
- **Readout** — single measurement: `value` (IEEE 754 float), `quantity`, `unit`, `category`.
|
|
- **Measurement** — `ActiveModel::Model` form wrapper (not database-backed); bridges the readout creation form.
|
|
- **User** — Devise-managed with a status enum: `admin`, `active`, `restricted`, `locked`, `disabled`. Admins can disguise as other users.
|
|
|
|
### Hierarchical Queries
|
|
|
|
Both `Quantity` and `Unit` use recursive CTEs for tree traversal (ordered traversal, ancestors, progenies, common ancestors). `lib/core_ext/arel/` patches Arel to support CTE with `UPDATE`/`DELETE` statements, working around Rails issue #54658.
|
|
|
|
### Custom Extensions (`lib/core_ext/`)
|
|
|
|
- **arel/** — CTE support for UPDATE/DELETE
|
|
- **active_model/** — precision/scale validator used by `Unit#multiplier`
|
|
- **active_record/** — `attr_cached` mechanism (see `ApplicationRecord`)
|
|
- **action_view/** — record identifier suffixes
|
|
- Miscellaneous: `Array#delete_bang`, `BigDecimal` scientific notation
|
|
|
|
### Response Handling
|
|
|
|
Controllers respond to both HTML and Turbo Stream formats. Errors during Turbo Stream requests trigger a redirect with flash rather than rendering inline, handled in `ApplicationController`.
|
|
|
|
### Numeric Precision
|
|
|
|
Readout values are stored as IEEE 754 double-precision floats (not fixed-point decimals). Rationale in `DESIGN.md`: biological values span many orders of magnitude; 15-digit float precision is sufficient and avoids conversion overhead.
|
|
|
|
### Routes
|
|
|
|
```
|
|
measurements GET/POST /measurements
|
|
readouts GET/POST /readouts, DELETE /readouts/:id/discard
|
|
quantities CRUD + POST /quantities/:id/reparent
|
|
units CRUD + POST /units/:id/rebase
|
|
users CRUD + POST /users/:id/disguise, POST /users/revert
|
|
default/ namespace for default units import/export and admin panel
|
|
root → /units (authenticated), /sign_in (unauthenticated)
|
|
```
|
|
|
|
## JavaScript Conventions
|
|
|
|
### No manual fetch() — use Turbo
|
|
|
|
Never make AJAX requests with `fetch()` in JavaScript. Use Turbo's built-in mechanisms instead:
|
|
|
|
- **Links/buttons that trigger server actions**: use `data: {turbo_stream: true}` on the element (link or button_to form).
|
|
- **Dynamic form submissions from JS** (where HTML alone isn't enough): create a form element, append hidden inputs, and call `form.requestSubmit()`. Turbo intercepts it automatically — no manual CSRF handling, no `Turbo.renderStreamMessage()`.
|
|
```javascript
|
|
var form = document.createElement('form');
|
|
form.action = url; form.method = 'post'; form.dataset.turboStream = 'true';
|
|
// append hidden inputs...
|
|
form.addEventListener('turbo:submit-end', function() { form.remove(); });
|
|
document.body.appendChild(form);
|
|
form.requestSubmit();
|
|
```
|
|
- **Server-rendered HTML**: use ERB partials and Turbo Stream views (`*.turbo_stream.erb`), never build HTML in JavaScript.
|
|
|
|
### No HTML generation in JavaScript
|
|
|
|
Never use JavaScript to build and insert HTML (no `innerHTML =`, no `createElement` trees for content). Render HTML server-side in ERB partials; update the DOM via Turbo Stream actions (`replace`, `update`, `append`, etc.).
|
|
|
|
## Database Requirements
|
|
|
|
The database must support:
|
|
- Recursive CTEs with `UPDATE`/`DELETE` (MySQL ≥ 8.0, PostgreSQL, or SQLite3)
|
|
- Decimal precision of 30+ digits
|