Add stimulus-rails gem and wire up 7 controllers: - measurements_view_controller: view toggle (compact/wide) via localStorage - measurements_controller: grouped rows MutationObserver - charts_controller: Plotly chart rendering - form_controller: keyboard shortcuts (Escape/Enter) and submit validation - details_controller: quantity picker state, focusout close, MutationObserver - readout_unit_controller: default unit button enable/disable + PATCH submission - drag_controller: drag-and-drop for quantity reparenting and unit rebasing Remove all inline onclick/onkeydown/ondrag*/onsubmit handlers from templates. Remove all window.* global exports from application.js. Remove bare <script> block from measurements/_form.html.erb. Remove turbo:load listeners for behavior now in controller connect(). application.js now only contains: Turbo Stream custom action definitions and the showPage visibility listener. Document Stimulus conventions in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.0 KiB
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:
cp config/application.rb.dist config/application.rb
cp config/database.yml.dist config/database.yml
cp config/puma.rb.dist config/puma.rb
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
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). Cacheddepthandpathnamefields are recomputed via recursive CTEs on write. Direct assignment to cached fields is blocked. - Unit — optional hierarchy via
base_idandmultiplierfor unit conversion. Multiplier precision/scale is validated by a custom validator. - Readout — single measurement:
value(IEEE 754 float),quantity,unit,category. - Measurement —
ActiveModel::Modelform 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_cachedmechanism (seeApplicationRecord) - action_view/ — record identifier suffixes
- Miscellaneous:
Array#delete_bang,BigDecimalscientific 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
Use Stimulus for all JS behavior
This app uses Hotwire = Turbo + Stimulus. All JavaScript behavior must be in Stimulus controllers under app/javascript/controllers/. Never use:
- Inline HTML event handlers:
onclick="...",onkeydown="...",ondragstart="..."etc. - Global
window.*function exports - Bare
<script>blocks in templates turbo:loadlisteners for behavior that belongs in a controller'sconnect()lifecycle
Instead:
- Put behavior in a Stimulus controller method
- Wire it with
data-action="event->controller#method"in the template - Use
data-controller="name"on the root element,data-[name]-target="targetName"for targets,data-[name]-[valueName]-value="..."for values - Use
connect()/disconnect()for setup/teardown (MutationObservers, event listeners, etc.)
Controller filename foo_bar_controller.js → identifier foo-bar → data-controller="foo-bar".
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, noTurbo.renderStreamMessage().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