Planned and executed phase001

This commit is contained in:
2026-09-13 21:46:57 -06:00
parent ff0a306668
commit 9793cd08bc
22 changed files with 695 additions and 20 deletions
+1
View File
@@ -0,0 +1 @@
--require spec_helper
+27
View File
@@ -0,0 +1,27 @@
# Agent Notes
## Project Shape
- Rails 8.1 app (`CarTracker`) on Ruby `4.0.6`; `.ruby-version` includes the `ruby-` prefix, while the Dockerfile uses `ARG RUBY_VERSION=4.0.6`.
- The app is still mostly generated Rails skeleton. Treat `DESIGN.md` as product direction, not proof of implemented models/routes.
- Database is SQLite under `storage/`. Production is configured with separate SQLite databases for primary, Solid Cache, Solid Queue, and Solid Cable.
- JavaScript uses importmap + Turbo + Stimulus; there is no `package.json` or npm install step. Pin JS dependencies with `bin/importmap`, not npm/yarn.
## Commands
- Setup/update dependencies and DB: `bin/setup --skip-server`. Omit `--skip-server` only when you want it to start `bin/dev` afterward.
- Run the app: `bin/dev` (it just execs `bin/rails server`).
- Repo CI script: `bin/ci` runs setup, RuboCop, bundler-audit, importmap audit, and strict Brakeman. It does not run specs.
- GitHub CI also runs Brakeman, bundler-audit, importmap audit, and RuboCop only; do not assume tests ran in CI.
- Style check: `bin/rubocop` (`rubocop-rails-omakase` via `.rubocop.yml`).
- Security checks: `bin/bundler-audit`, `bin/importmap audit`, `bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error`.
## Tests
- RSpec is installed; run specs with `bundle exec rspec`, not `bin/rails test`.
- Run a focused spec with `bundle exec rspec spec/path/to/file_spec.rb:LINE`.
- `.rspec` only requires `spec_helper`; Rails specs need `require "rails_helper"` explicitly.
- `DESIGN.md` says to develop features test-first. Add specs for new behavior even though current CI does not run them.
## Rails/Runtime Gotchas
- `config/application.rb` disables Rails system test generation and does not load `rails/test_unit/railtie`.
- `ApplicationController` rejects non-modern browsers with `allow_browser versions: :modern` and uses `stale_when_importmap_changes`.
- Solid Queue jobs can run with `bin/jobs`; Kamal production config currently runs Solid Queue inside Puma via `SOLID_QUEUE_IN_PUMA: true`.
- Production Docker is intended for Kamal/production, not local development; the entrypoint runs `bin/rails db:prepare` before starting the server.
+153
View File
@@ -0,0 +1,153 @@
# Vehicle Tracker Application Design
## Goal
A web application to replace manual spreadsheet tracking of vehicle maintenance
and fuel usage of a shared fleet.
## Architecture & Philosophy
* **Language:** Ruby on Rails
* **Style:** Snake_case
* **Principle:** Keep it simple. Do not over-optimize. CRUD is the priority.
* **Database:** Relational, tracking "Events" linked to specific "Entities."
* **Tests:** We should be test driven in our development. No feature should
be created without a test.
## Data models
### 1. User
This table will contain information about the users of the app. Mostly used to
know who updated what. This table will include:
* *Fields:* `name`, `email`
### 2. Vehicle
This table will contain information about the vehicles (assets) that we
track. They have no strict ownership relationship as they can be used/driven by
multiple operators. The vehicle information will include:
* *Fields:* `make`, `model`, `year`, `color`, `vin`,
`licence_place`, `current_odometer`, `fuel_tank_size`
### 3. MaintenanceSchedule
Configures the maintenance rules for a specific vehicle. This allows the
"overdue" check to be automated by comparing the current schedule against the
actual history.
* *Fields:* `vehicle_id`, `maintenance_type`, `mileage_interval`,
`time_interval`
* *Relationship:* Belongs to Vehicle
### 4. FuelEntry
Records specific refueling events.
* *Fields:* `vehicle_id`, `odometer` (at time of fill), `gallons_pumped`,
`price_paid`, `date`, `updated_by_user_id` (Tracks who submitted)
* *Relationship:* Belongs to Vehicle
### 5. MainetnanceEntry
Records maintenance and repair history.
* *Fields:* `vehicle_id`, `odometer` (at time of service), `description`
(Should exactly match a `maintenance_type` value from the
MaintenanceSchedule), `cost`, `date`, `updated_by_user_id` (Tracks who
submitted)
* *Relationship:* Belongs to Vehicle
## Data Relationships
* **Vehicle** has_many **MaintenanceSchedules**
* **Vehicle** has_many **FuelEntries**
* **Vehicle** has_many **MaintenanceEntries**
* **User** has_many **FuelEntries** (as the actor)
* **User** has_many **MaintenanceEntries** (as the actor)
## Data Flow
### Scenario A: Fueling
1. User navigates to `Vehicle#show`.
2. User fills form with Odometer, Gallons, Price.
3. User submits -> `FuelEntry` is saved to DB.
4. `current_odometer` is updated on the Vehicle record.
### Scenario B: Maintenance
1. User navigates to `Vehicle#show`.
2. User fills form with Description, Cost, Date.
3. User submits -> `MaintenanceEntry` is saved to DB.
### Scenario C: Overdue Alerts
1. On `Vehicle#show`, query the most recent `MaintenanceEntry`.
2. Compare `MaintenanceEntry.date` vs `Date.today`.
3. If difference > 6 months, display alert message to User.
Note that there is in the MaintenanceSchedule data, there is a `time_interval`
field that defines the length of time for maintenance. There is also a milage
that should be used as well and it is an or operation for them.
## User Authentication
Because this is an internal application, I plan on something like this:
The server will every month generate a key. This key will be printed out in a
QR code format. The users will then scan the QR code to collect the key. When
they make a request, this key is sent along with it and is used to verify the
user. Ideally each user would use the QR code to prove that I know them, enter
a name and then get a session token that is used instead of sending the raw QR
code. The idea is that it is proof that I know who they are by them posessing
information that only I have (the QR code) and then in the future they should
use the token to prove who they are.
Again, because this is an internal application, there should not be any
passwords. Users are not really created to be logged in, more because I need to
track who is doing what with the equipment.
## Database choice
I have no hard requirements. I would prefer the database to be easy to use for
development. This application will really only ever have up to 100 users so I
do not need high performance. SQLite should work fantastic in this case.
## Test framework
The test framework is rspec. You can run the tests with `bin/rails test`
## Vehicle ownership
Vehicles can be used by multiple operators. We do not need to track who is
driving though. We just want to know about who is submitting updates and
enteries.
## Rails information
```
$ rails --version
Rails 8.1.3.1
```
## Deployment
This will be a local development deployment. Once we are ready, it will be
packaged into a Docker container and put on a production server. This container
is designed to run behind a reverse proxy that is already in place.
# Final notes
The relationship between the user and vehicle is minimal. This is on purpose
because we really are only using the user to track who is making changes. We do
not really care if it is the actual driver or not.
For overdue alerts, we will send notifications to the users using a
notification service. This is not yet set up so we will not worry about it. We
can leave stubs though for that to happen. The notification service just
requires a curl request so it is simple to add later. For now, just log it into
the console so that we can test it is working.
For data access, it would be nice to have 'admin' and 'regular' users however
that is a distinction that we do not yet need. Remember to keep things simple
but flexible in the future. Do not wall us in with how code is done.
Each edit by the users will be a seperate database entry. There should be no
user editing the same entry at any time. The only possible issue that I can see
is if we have two users in the same vehicle and they both submit a fuel
entry. In this case, we would just delete one of the entries. At no point in
time should two users be editing the same record.
+10
View File
@@ -41,6 +41,11 @@ gem "thruster", require: false
gem "image_processing", "~> 1.2"
group :development, :test do
# Testing frameworks
gem "rspec-rails"
gem "factory_bot_rails"
gem "capybara"
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
@@ -50,6 +55,11 @@ group :development, :test do
# Static analysis for security vulnerabilities [https://brakemanscanner.org/]
gem "brakeman", require: false
gem "rubocop"
gem "rubocop-rails"
gem "rubocop-rspec"
gem "rubocop-rspec_rails"
# Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
gem "rubocop-rails-omakase", require: false
end
+68
View File
@@ -75,6 +75,8 @@ GEM
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
addressable (2.9.0)
public_suffix (>= 2.0.2, < 8.0)
ast (2.4.3)
base64 (0.3.0)
bcrypt_pbkdf (1.1.2)
@@ -88,6 +90,15 @@ GEM
bundler-audit (0.9.3)
bundler (>= 1.2.0)
thor (~> 1.0)
capybara (3.40.0)
addressable
matrix
mini_mime (>= 0.1.3)
nokogiri (~> 1.11)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
concurrent-ruby (1.3.8)
connection_pool (3.0.2)
crass (1.0.7)
@@ -95,6 +106,7 @@ GEM
debug (1.11.1)
irb (~> 1.10)
reline (>= 0.3.8)
diff-lcs (1.6.2)
dotenv (3.2.0)
drb (2.2.3)
ed25519 (1.4.0)
@@ -102,6 +114,11 @@ GEM
erubi (1.13.1)
et-orbi (1.4.2)
tzinfo
factory_bot (6.6.0)
activesupport (>= 6.1.0)
factory_bot_rails (6.5.1)
factory_bot (~> 6.5)
railties (>= 6.1.0)
ffi (1.17.4-aarch64-linux-gnu)
ffi (1.17.4-aarch64-linux-musl)
ffi (1.17.4-arm-linux-gnu)
@@ -156,6 +173,7 @@ GEM
net-pop
net-smtp
marcel (1.2.1)
matrix (0.4.3)
mini_magick (5.4.0)
logger
mini_mime (1.1.5)
@@ -203,6 +221,7 @@ GEM
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
rack
public_suffix (7.0.5)
puma (8.0.2)
nio4r (~> 2.0)
raabro (1.5.0)
@@ -259,6 +278,23 @@ GEM
regexp_parser (2.12.0)
reline (0.7.0)
io-console (~> 0.5)
rspec-core (3.13.6)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.8)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-rails (8.0.4)
actionpack (>= 7.2)
activesupport (>= 7.2)
railties (>= 7.2)
rspec-core (>= 3.13.0, < 5.0.0)
rspec-expectations (>= 3.13.0, < 5.0.0)
rspec-mocks (>= 3.13.0, < 5.0.0)
rspec-support (>= 3.13.0, < 5.0.0)
rspec-support (3.13.7)
rubocop (1.91.0)
json (>= 2.3)
language_server-protocol (~> 3.17.0.2)
@@ -287,6 +323,14 @@ GEM
rubocop (>= 1.72)
rubocop-performance (>= 1.24)
rubocop-rails (>= 2.30)
rubocop-rspec (3.10.2)
lint_roller (~> 1.1)
regexp_parser (>= 2.0)
rubocop (~> 1.86, >= 1.86.2)
rubocop-rspec_rails (2.32.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec (~> 3.5)
ruby-progressbar (1.13.0)
ruby-vips (2.3.0)
ffi (~> 1.12)
@@ -347,6 +391,8 @@ GEM
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.8.3)
PLATFORMS
@@ -363,7 +409,9 @@ DEPENDENCIES
bootsnap
brakeman
bundler-audit
capybara
debug
factory_bot_rails
image_processing (~> 1.2)
importmap-rails
jbuilder
@@ -371,7 +419,12 @@ DEPENDENCIES
propshaft
puma (>= 5.0)
rails (~> 8.1.3, >= 8.1.3.1)
rspec-rails
rubocop
rubocop-rails
rubocop-rails-omakase
rubocop-rspec
rubocop-rspec_rails
solid_cable
solid_cache
solid_queue
@@ -395,6 +448,7 @@ CHECKSUMS
activerecord (8.1.3.1) sha256=0a2fb6c28f4938f6b013a3a549bec0a7e37d535f3dc8990e804bcc3258c0403b
activestorage (8.1.3.1) sha256=f555254f387b1cffa499d2fd3115d12635eadc5b15206a8534316a67036163ef
activesupport (8.1.3.1) sha256=85458765f25ea48b9019c46b6bb3fa5683197bf4280d9f06710a6e8d7a831376
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
bcrypt_pbkdf (1.1.2) sha256=c2414c23ce66869b3eb9f643d6a3374d8322dfb5078125c82792304c10b94cf6
@@ -404,17 +458,21 @@ CHECKSUMS
brakeman (8.0.6) sha256=759cc69341115e6c2dcd47b6fd8649a0b9bd540e3585ac8a0a94e31c66fee386
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
bundler-audit (0.9.3) sha256=81c8766c71e47d0d28a0f98c7eed028539f21a6ea3cd8f685eb6f42333c9b4e9
capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
crass (1.0.7) sha256=94868719948664c89ddcaf0a37c65048413dfcb1c869470a5f7a7ceb5390b295
date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
debug (1.11.1) sha256=2e0b0ac6119f2207a6f8ac7d4a73ca8eb4e440f64da0a3136c30343146e952b6
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
ed25519 (1.4.0) sha256=16e97f5198689a154247169f3453ef4cfd3f7a47481fde0ae33206cdfdcac506
erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
et-orbi (1.4.2) sha256=bb555dae668419cb24caa2a293a170e58be6d4df1e017c51f5030bdc133cd20c
factory_bot (6.6.0) sha256=1fc1b3b5620ec980a6a27aec1b6ec8c250ca82962e970e8a40f93e8d388d4b89
factory_bot_rails (6.5.1) sha256=d3cc4851eae4dea8a665ec4a4516895045e710554d2b5ac9e68b94d351bc6d68
ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df
ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39
ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564
@@ -437,6 +495,7 @@ CHECKSUMS
loofah (2.25.2) sha256=2007f746959ac65552456e04b433e83deb22759ab38c838b4445c70e43425918
mail (2.9.1) sha256=06574eca475253d6c18145dd70af80d0eb970182d55053497c5f4d797ea160e8
marcel (1.2.1) sha256=1678e9360e32f9eafa917c80029e2f6d10b2715c66a4b87b6d0da9b9cd1f859f
matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b
mini_magick (5.4.0) sha256=f120af581d9ed4ec52c57f35a67a605d112bb1d8f582d1415b147fda42d11d78
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
@@ -462,6 +521,7 @@ CHECKSUMS
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
propshaft (1.3.2) sha256=1d56a3e56a92c21bfc29caf07406b5386b00d4c47ddf357cf989a5a234b1389e
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
puma (8.0.2) sha256=c8ed871dfbbe66448ea9ffd46692342d9804d4071522b52b5331b7b6e7b686fb
raabro (1.5.0) sha256=3f998a7bc84f9c84df3ab580634d2e0a5bda4f0841168d56035f529c9877440a
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
@@ -479,11 +539,18 @@ CHECKSUMS
rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
rspec-rails (8.0.4) sha256=06235692fc0892683d3d34977e081db867434b3a24ae0dd0c6f3516bad4e22df
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
rubocop (1.91.0) sha256=9c82b7bf391c5d7e3798c5b9996e22a1fe3bd7468e351dfdeb96140c058296d0
rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
rubocop-performance (1.27.0) sha256=eeeb1374d062a368ee1c787b70eb0b0cc4b184cb1f8565f424760946146d61ce
rubocop-rails (2.37.0) sha256=6e1645add5060e0328f8ddda0d820f55697c591394398bf14bb9dccb62f14b7e
rubocop-rails-omakase (1.1.0) sha256=2af73ac8ee5852de2919abbd2618af9c15c19b512c4cfc1f9a5d3b6ef009109d
rubocop-rspec (3.10.2) sha256=0b3e2ecc592cd10ecbf0095bb58d1e357905276e069643523cc19eb7495f65e2
rubocop-rspec_rails (2.32.0) sha256=4a0d641c72f6ebb957534f539d9d0a62c47abd8ce0d0aeee1ef4701e892a9100
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
ruby-vips (2.3.0) sha256=e685ec02c13969912debbd98019e50492e12989282da5f37d05f5471442f5374
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
@@ -513,6 +580,7 @@ CHECKSUMS
web-console (4.3.0) sha256=e13b71301cdfc2093f155b5aa3a622db80b4672d1f2f713119cc7ec7ac6a6da4
websocket-driver (0.8.2) sha256=97c556b019bf3410b4961002ac501621e9322d3f8a7bc02161a09301cc4c4146
websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241
xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e
zeitwerk (2.8.3) sha256=2c85125a8467ce069e20123d1e709a08955c9d29c118c25b46b7b7fafdbb92e5
BUNDLED WITH
+41
View File
@@ -0,0 +1,41 @@
# About me
I am familar in these languages:
- Python
- Ruby
- C/C++
- VHDL
I have built multiple applications in these languages. Here are some examples
of what I have designed and built:
Using Ruby on Rails: A ticket framework that integrates with Stripe to be able
to sell tickets to concerts.
Using Flask/Django: A fiber optic network tracker that allows for clients to
update the connections in a fiber optic network based on the connections in
cases. The program is capable of auto routing new connections through boxes and
tracks who makes what changes for history of work.
Using C/C++: Many embedded projects. A bluetooth beacon tracker bot that can
follow a bluetooth device. A project that integrates with an FPGA using UART
for communication. Uses the FPGA for real time controll and data collection
while the C/C++ code is used for navigation and decision making
# Rules I have
Keep things simple. There is no need to overcomplicate the project that we are
working on. Yes we should be aware of future growth however "premature
optimization is the root of all evil" We should not be careless in how we
create and start building however we do not need to plan for every eventuality.
# Personal preferences
I use Emacs for my editor. I strongly prefer open source toolchains. Personally
I prefer snake_case unless the language convention specifies otherwise. My
favorite language is Ruby.
+11 -18
View File
@@ -1,24 +1,17 @@
# README
# Car Tracker
This README would normally document whatever steps are necessary to get the
application up and running.
Vehicle maintenance and fuel tracking application.
Things you may want to cover:
## Testing
* Ruby version
Run the application test suite with:
* System dependencies
```sh
bundle exec rspec
```
* Configuration
Run the full local CI baseline with:
* Database creation
* Database initialization
* How to run the test suite
* Services (job queues, cache servers, search engines, etc.)
* Deployment instructions
* ...
```sh
bin/ci
```
+4
View File
@@ -0,0 +1,4 @@
class HomeController < ApplicationController
def index
end
end
+4
View File
@@ -0,0 +1,4 @@
<main>
<h1>Car Tracker</h1>
<p>Vehicle maintenance and fuel tracking will be added here.</p>
</main>
+2
View File
@@ -3,6 +3,8 @@
CI.run do
step "Setup", "bin/setup --skip-server"
step "Test: RSpec", "bundle exec rspec"
step "Style: Ruby", "bin/rubocop"
step "Security: Gem audit", "bin/bundler-audit"
+1 -2
View File
@@ -9,6 +9,5 @@ Rails.application.routes.draw do
# get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
# get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
# Defines the root path route ("/")
# root "posts#index"
root "home#index"
end
+36
View File
@@ -0,0 +1,36 @@
# Phase 001 Execution
## Scope
- Prepared the Rails/RSpec project baseline before adding application models.
- Added a minimal visible root page for the application.
- Documented the project test command.
## Changes
- Confirmed `rspec-rails` and `factory_bot_rails` are present in the Gemfile.
- Added `capybara` for system spec support.
- Enabled RSpec spec type inference from file location.
- Included FactoryBot syntax methods in RSpec configuration.
- Configured system specs to use Capybara's `rack_test` driver by default.
- Added `HomeController#index` as a placeholder landing page.
- Set the root route to `home#index`.
- Added a minimal root page view.
- Added a request spec for `GET /`.
- Added a system spec for the placeholder landing page.
- Added `bundle exec rspec` to the CI sequence.
- Updated `README.md` with the RSpec command and local CI command.
## Decisions
- FactoryBot is the preferred future test data setup because it is already in the Gemfile.
- Capybara is required because `rspec-rails` system specs depend on it for browser-style interactions.
- `rack_test` is enough for the baseline placeholder system spec and avoids requiring Selenium before JavaScript browser coverage is needed.
- No model specs were added because there are no real application models yet.
- The root page is intentionally plain because Phase 001 only requires a visible landing point.
- The documented test command is `bundle exec rspec`, matching project agent notes.
## Verification
- Initial `bundle exec rspec` failed because `capybara` was not in the bundle.
- After adding `capybara`, `bundle exec rspec` failed because the default system spec driver expected `selenium-webdriver`.
- Configured system specs to use `rack_test`, then reran verification.
- Passed: `bundle exec rspec`
- Passed: `bin/rubocop`
- Passed: `bin/ci`
+21
View File
@@ -0,0 +1,21 @@
# Phase 001: Project Baseline And Testing Setup
## Goal
Establish a stable Rails/RSpec foundation before adding application behavior.
## Work
- Confirm `rspec-rails` is installed and usable.
- Ensure `bin/rails spec` or the project-preferred test command works.
- Add initial request/system/model spec patterns for future phases.
- Decide whether the app should use Rails fixtures or FactoryBot; prefer FactoryBot because it is already in the Gemfile.
- Add a simple root page placeholder so the app has a visible landing point.
## Deliverables
- Working test suite.
- Root route and minimal home page.
- Documented test command.
## Acceptance Criteria
- Test suite passes.
- Root path returns success.
- No application models are added without corresponding specs.
+28
View File
@@ -0,0 +1,28 @@
# Phase 002: Core Data Model
## Goal
Create the relational model foundation for users, vehicles, fuel entries, maintenance entries, and maintenance schedules.
## Work
- Create `User` with `name` and `email`.
- Create `Vehicle` with `make`, `model`, `year`, `color`, `vin`, `licence_plate`, `current_odometer`, and `fuel_tank_size`.
- Create `MaintenanceSchedule` belonging to `Vehicle`.
- Create `FuelEntry` belonging to `Vehicle` and `User` as `updated_by_user`.
- Create `MaintenanceEntry` belonging to `Vehicle` and `User` as `updated_by_user`.
- Add validations for required fields and basic numeric constraints.
- Add model specs for associations, validations, and simple behavior.
## Notes
- Correct likely typo from design: `licence_place` should probably be `licence_plate`.
- Correct likely typo from design: `MainetnanceEntry` should be `MaintenanceEntry`.
## Deliverables
- Database migrations.
- ActiveRecord models.
- Model specs.
- Factories.
## Acceptance Criteria
- All model specs pass.
- Associations match the design.
- Invalid records are rejected with useful validation errors.
+22
View File
@@ -0,0 +1,22 @@
# Phase 003: Vehicle CRUD And Detail Page
## Goal
Build the main vehicle management interface.
## Work
- Add `VehiclesController`.
- Add routes for vehicle index, show, new, create, edit, update, and destroy.
- Build simple ERB views for vehicle CRUD.
- Make `vehicles#index` the root page.
- On `vehicles#show`, display vehicle details, maintenance schedules, fuel history, and maintenance history.
- Keep layout simple and mobile-friendly.
## Deliverables
- Vehicle CRUD UI.
- Vehicle request specs.
- Basic navigation.
## Acceptance Criteria
- Users can create, view, update, and delete vehicles.
- Vehicle show page is the main operational screen.
- Request specs cover successful and invalid create/update paths.
+25
View File
@@ -0,0 +1,25 @@
# Phase 004: Fuel And Maintenance Event Entry
## Goal
Allow users to record fuel and maintenance events from the vehicle detail page.
## Work
- Add nested routes under vehicles for fuel entries and maintenance entries.
- Add create forms on `vehicles#show`.
- Save each fuel submission as a new `FuelEntry`.
- Save each maintenance submission as a new `MaintenanceEntry`.
- Update `Vehicle.current_odometer` when a fuel entry is created if the submitted odometer is newer.
- Track `updated_by_user_id` on each event.
- Add request specs and model specs for event creation.
## Deliverables
- Fuel entry creation flow.
- Maintenance entry creation flow.
- Odometer update behavior.
- Event history display on vehicle show page.
## Acceptance Criteria
- Fuel submissions create records and update vehicle odometer.
- Maintenance submissions create records.
- Invalid submissions re-render or redirect with validation errors.
- Every event records the submitting user.
+24
View File
@@ -0,0 +1,24 @@
# Phase 005: Lightweight Internal Authentication
## Goal
Implement simple internal identity tracking without passwords.
## Work
- Add a monthly access key concept for initial identification.
- Add a flow where a person enters/scans the current key, provides name/email, and receives a session.
- Store only a session user id/token in the browser after verification.
- Add `Current.user` or controller helper methods for accessing the active user.
- Require an identified user before creating fuel or maintenance entries.
- Keep admin/regular user roles out of scope for now.
## Deliverables
- Login/identification page.
- Session creation and clearing.
- User creation or lookup by email.
- Controller specs/request specs for authenticated and unauthenticated flows.
## Acceptance Criteria
- Passwords are not used.
- Raw QR/monthly key is not required on every request after session creation.
- Fuel and maintenance submissions are attributed to the active user.
- Unidentified users cannot submit entries.
+24
View File
@@ -0,0 +1,24 @@
# Phase 006: Maintenance Schedules And Overdue Alerts
## Goal
Use maintenance schedules and event history to show overdue maintenance status.
## Work
- Add CRUD for vehicle maintenance schedules.
- On `vehicles#show`, compare each schedule against maintenance history.
- Mark a schedule overdue when either mileage interval or time interval is exceeded.
- Use the matching maintenance entry description/type as the history key.
- Log overdue notifications to Rails logger for now.
- Keep external notification service integration stubbed and isolated.
## Deliverables
- Maintenance schedule management.
- Overdue calculation logic.
- Vehicle show alerts.
- Specs for mileage-based, time-based, and not-overdue cases.
## Acceptance Criteria
- Vehicle show page displays overdue alerts.
- Mileage and time intervals are treated as an OR condition.
- Notification behavior is logged, not sent externally.
- Overdue logic is covered by tests.
+77
View File
@@ -0,0 +1,77 @@
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
# Uncomment the line below in case you have `--require rails_helper` in the `.rspec` file
# that will avoid rails generators crashing because migrations haven't been run yet
# return unless Rails.env.test?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Rails.root.glob('spec/support/**/*.rb').sort_by(&:to_s).each { |f| require f }
# Ensures that the test database schema matches the current schema file.
# If there are pending migrations it will invoke `db:test:prepare` to
# recreate the test database by loading the schema.
# If you are not using ActiveRecord, you can remove these lines.
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
abort e.to_s.strip
end
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_paths = [
Rails.root.join('spec/fixtures')
]
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# You can uncomment this line to turn off ActiveRecord support entirely.
# config.use_active_record = false
# RSpec Rails uses metadata to mix in different behaviours to your tests,
# for example enabling you to call `get` and `post` in request specs. e.g.:
#
# RSpec.describe UsersController, type: :request do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://rspec.info/features/8-0/rspec-rails
#
# You can also infer these behaviours automatically by location, e.g.
# /spec/models would pull in the same behaviour as `type: :model` but this
# behaviour is considered legacy and will be removed in a future version.
#
config.infer_spec_type_from_file_location!
config.include FactoryBot::Syntax::Methods
config.before(:each, type: :system) do
driven_by :rack_test
end
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end
+12
View File
@@ -0,0 +1,12 @@
require "rails_helper"
RSpec.describe "Home" do
describe "GET /" do
it "returns a successful response" do
get root_path
expect(response).to have_http_status(:ok)
expect(response.body).to include("Car Tracker")
end
end
end
+94
View File
@@ -0,0 +1,94 @@
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause
# this file to always be loaded, without a need to explicitly require it in any
# files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, consider making
# a separate helper file that requires the additional dependencies and performs
# the additional setup, and require it from the spec files that actually need
# it.
#
# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
# This option will default to `true` in RSpec 4. It makes the `description`
# and `failure_message` of custom matchers include text for helper methods
# defined using `chain`, e.g.:
# be_bigger_than(2).and_smaller_than(4).description
# # => "be bigger than 2 and smaller than 4"
# ...rather than:
# # => "be bigger than 2"
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
# have no way to turn it off -- the option exists only for backwards
# compatibility in RSpec 3). It causes shared context metadata to be
# inherited by the metadata hash of host groups and examples, rather than
# triggering implicit auto-inclusion in groups with matching metadata.
config.shared_context_metadata_behavior = :apply_to_host_groups
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
=begin
# This allows you to limit a spec run to individual examples or groups
# you care about by tagging them with `:focus` metadata. When nothing
# is tagged with `:focus`, all examples get run. RSpec also provides
# aliases for `it`, `describe`, and `context` that include `:focus`
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
config.filter_run_when_matching :focus
# Allows RSpec to persist some state between runs in order to support
# the `--only-failures` and `--next-failure` CLI options. We recommend
# you configure your source control system to ignore this file.
config.example_status_persistence_file_path = "spec/examples.txt"
# Limits the available syntax to the non-monkey patched syntax that is
# recommended. For more details, see:
# https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/
config.disable_monkey_patching!
# Many RSpec users commonly either run the entire suite or an individual
# file, and it's useful to allow more verbose output when running an
# individual spec file.
if config.files_to_run.one?
# Use the documentation formatter for detailed output,
# unless a formatter has already been configured
# (e.g. via a command-line flag).
config.default_formatter = "doc"
end
# Print the 10 slowest examples and example groups at the
# end of the spec run, to help surface which specs are running
# particularly slow.
config.profile_examples = 10
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = :random
# Seed global randomization in this process using the `--seed` CLI option.
# Setting this allows you to use `--seed` to deterministically reproduce
# test failures related to randomization by passing the same `--seed` value
# as the one that triggered the failure.
Kernel.srand config.seed
=end
end
+10
View File
@@ -0,0 +1,10 @@
require "rails_helper"
RSpec.describe "Home" do
it "shows the placeholder landing page" do
visit root_path
expect(page).to have_content("Car Tracker")
expect(page).to have_content("Vehicle maintenance and fuel tracking will be added here.")
end
end