feat: Initial observations endpoint
This commit is contained in:
228
AGENTS.md
Normal file
228
AGENTS.md
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
# AGENTS.md - Yockboard Development Guide
|
||||||
|
|
||||||
|
This document provides guidelines for agentic coding agents working on the Yockboard Rails application.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
- **Framework**: Ruby on Rails 8.1.2
|
||||||
|
- **Ruby Version**: 3.4.7
|
||||||
|
- **Database**: PostgreSQL
|
||||||
|
- **Testing**: Minitest (Rails default)
|
||||||
|
- **JavaScript**: Turbo + Stimulus with Importmap
|
||||||
|
|
||||||
|
## Build / Lint / Test Commands
|
||||||
|
|
||||||
|
### Development Server
|
||||||
|
```bash
|
||||||
|
./bin/dev # Start development server (alias for bin/rails server)
|
||||||
|
bin/rails server -p 3000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
bin/rails test
|
||||||
|
|
||||||
|
# Run a single test file
|
||||||
|
bin/rails test test/models/user_test.rb
|
||||||
|
|
||||||
|
# Run a single test by name
|
||||||
|
bin/rails test test/models/user_test.rb -n test_user_validity
|
||||||
|
|
||||||
|
# Run system tests (browser-based)
|
||||||
|
bin/rails test:system
|
||||||
|
|
||||||
|
# Run tests in specific directory
|
||||||
|
bin/rails test test/controllers/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linting & Code Style
|
||||||
|
```bash
|
||||||
|
# Run RuboCop linter
|
||||||
|
bin/rubocop
|
||||||
|
|
||||||
|
# Run specific RuboCop check
|
||||||
|
bin/rubocop app/models/
|
||||||
|
|
||||||
|
# Auto-fix RuboCop issues
|
||||||
|
bin/rubocop -a
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security Scanning
|
||||||
|
```bash
|
||||||
|
# Scan for Rails security vulnerabilities
|
||||||
|
bin/brakeman --no-pager
|
||||||
|
|
||||||
|
# Scan for known gem vulnerabilities
|
||||||
|
bin/bundler-audit
|
||||||
|
|
||||||
|
# Audit JS dependencies
|
||||||
|
bin/importmap audit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database
|
||||||
|
```bash
|
||||||
|
bin/rails db:create db:migrate # Setup database
|
||||||
|
bin/rails db:test:prepare # Prepare test database
|
||||||
|
bin/rails db:reset # Reset database
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI Pipeline (in .github/workflows/ci.yml)
|
||||||
|
- `scan_ruby`: Brakeman + bundler-audit
|
||||||
|
- `scan_js`: Importmap audit
|
||||||
|
- `lint`: RuboCop
|
||||||
|
- `test`: Full test suite with PostgreSQL
|
||||||
|
- `system-test`: System/integration tests
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### General Philosophy
|
||||||
|
Follow the **Rails Omakase** style (via `rubocop-rails-omakase`). This is the default Rails team style.
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
- Models: `app/models/`
|
||||||
|
- Controllers: `app/controllers/`
|
||||||
|
- Views: `app/views/controller_name/`
|
||||||
|
- Helpers: `app/helpers/`
|
||||||
|
- Jobs: `app/jobs/`
|
||||||
|
- Mailers: `app/mailers/`
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
- **Classes/Modules**: `PascalCase` (e.g., `UserAccount`, `OrderProcessor`)
|
||||||
|
- **Database tables**: `snake_case_plurals` (e.g., `user_accounts`, `orders`)
|
||||||
|
- **Files**: `snake_case.rb` (e.g., `user_account.rb`, `order_processor.rb`)
|
||||||
|
- **Methods/variables**: `snake_case` (e.g., `user_accounts`, `process_order`)
|
||||||
|
- **Boolean methods**: End with `?` (e.g., `valid?`, `confirmed?`)
|
||||||
|
- **Bang methods**: End with `!` for mutating variants (e.g., `save!`, `update!`)
|
||||||
|
- **Query methods**: End with `?` or use plural names (e.g., `admin?`, `users`)
|
||||||
|
|
||||||
|
### Routing
|
||||||
|
- Use RESTful routes with `resources` when applicable
|
||||||
|
- Use namespace for admin/API routes
|
||||||
|
- Prefer shallow nesting: `resources :posts, shallow: true do resources :comments end`
|
||||||
|
|
||||||
|
### Models
|
||||||
|
- Inherit from `ApplicationRecord`
|
||||||
|
- Use concerns for shared behavior in `app/models/concerns/`
|
||||||
|
- Define associations explicitly
|
||||||
|
- Use scopes for common queries
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class User < ApplicationRecord
|
||||||
|
has_many :posts, dependent: :destroy
|
||||||
|
|
||||||
|
scope :active, -> { where(active: true) }
|
||||||
|
|
||||||
|
def full_name
|
||||||
|
"#{first_name} #{last_name}".strip
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Controllers
|
||||||
|
- Inherit from `ApplicationController`
|
||||||
|
- Use strong parameters for mass assignment
|
||||||
|
- Follow RESTful actions pattern
|
||||||
|
- Keep controllers thin; push logic to models/services
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class UsersController < ApplicationController
|
||||||
|
def index
|
||||||
|
@users = User.active.order(:created_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@user = User.new(user_params)
|
||||||
|
if @user.save
|
||||||
|
redirect_to @user, notice: "User created"
|
||||||
|
else
|
||||||
|
render :new, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def user_params
|
||||||
|
params.require(:user).permit(:first_name, :last_name, :email)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Views
|
||||||
|
- Use ERB with HTML
|
||||||
|
- Use Turbo Frames/Streams for SPA-like behavior
|
||||||
|
- Prefer Stimulus controllers over vanilla JS
|
||||||
|
- Use partials for reusable components
|
||||||
|
|
||||||
|
### Testing (Minitest)
|
||||||
|
- Test files go in `test/` mirroring `app/` structure
|
||||||
|
- Use `test/test_helper.rb` for shared setup
|
||||||
|
- Use fixtures in `test/fixtures/` for test data
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class UserTest < ActiveSupport::TestCase
|
||||||
|
test "valid user" do
|
||||||
|
user = User.new(email: "test@example.com", name: "Test")
|
||||||
|
assert user.valid?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "invalid without email" do
|
||||||
|
user = User.new(name: "Test")
|
||||||
|
assert_not user.valid?
|
||||||
|
assert_includes user.errors[:email], "must be present"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- Use ` rescue` blocks for expected errors
|
||||||
|
- Use `fail` or `raise` for unexpected states
|
||||||
|
- Flash messages for user feedback
|
||||||
|
- Render appropriate HTTP status codes
|
||||||
|
|
||||||
|
### Database
|
||||||
|
- Use migrations for schema changes: `bin/rails generate migration AddFieldToTable`
|
||||||
|
- Use `change` method for reversible migrations
|
||||||
|
- Add indexes for foreign keys and frequently queried columns
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- Add gems to appropriate groups in `Gemfile`
|
||||||
|
- Run `bundle install` after adding gems
|
||||||
|
- Use `Gemfile.lock` for reproducible builds
|
||||||
|
|
||||||
|
### Git Conventions
|
||||||
|
- Write clear, concise commit messages
|
||||||
|
- Create feature branches for new work
|
||||||
|
- Run linter and tests before committing
|
||||||
|
|
||||||
|
## Important File Locations
|
||||||
|
|
||||||
|
- Routes: `config/routes.rb`
|
||||||
|
- Application config: `config/application.rb`
|
||||||
|
- Environment configs: `config/environments/`
|
||||||
|
- Database config: `config/database.yml`
|
||||||
|
- Test config: `test/test_helper.rb`
|
||||||
|
|
||||||
|
## Common Tasks
|
||||||
|
|
||||||
|
### Generate New Resource
|
||||||
|
```bash
|
||||||
|
bin/rails generate scaffold ModelName field:type field:type
|
||||||
|
bin/rails db:migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Specific Gem Command
|
||||||
|
```bash
|
||||||
|
bundle exec brakeman --no-pager
|
||||||
|
bundle exec rubocop
|
||||||
|
bundle exec rails test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- This project uses **Solid** gems for cache/queue/cable (SolidCache, SolidQueue, SolidCable)
|
||||||
|
- Asset pipeline: Propshaft (Rails 8 default)
|
||||||
|
- JS: Importmap with Turbo and Stimulus
|
||||||
|
- Deployment: Kamal (see `.kamal/`)
|
||||||
7
app/controllers/api/api_controller.rb
Normal file
7
app/controllers/api/api_controller.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module API
|
||||||
|
class APIController < ApplicationController
|
||||||
|
skip_forgery_protection
|
||||||
|
end
|
||||||
|
end
|
||||||
15
app/controllers/api/v1/weather/observations_controller.rb
Normal file
15
app/controllers/api/v1/weather/observations_controller.rb
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module API
|
||||||
|
module V1
|
||||||
|
module Weather
|
||||||
|
class ObservationsController < APIController
|
||||||
|
def create
|
||||||
|
Rails.logger.debug 'Create new observation'
|
||||||
|
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Name of your application. Used to uniquely configure containers.
|
# Name of your application. Used to uniquely configure containers.deploy
|
||||||
service: yockboard
|
service: yockboard
|
||||||
|
|
||||||
# Name of the container image (use your-user/app-name on external registries).
|
# Name of the container image (use your-user/app-name on external registries).
|
||||||
@@ -7,7 +7,7 @@ image: yockboard
|
|||||||
# Deploy to these servers.
|
# Deploy to these servers.
|
||||||
servers:
|
servers:
|
||||||
web:
|
web:
|
||||||
- 192.168.0.1
|
- 103.163.186.137
|
||||||
# job:
|
# job:
|
||||||
# hosts:
|
# hosts:
|
||||||
# - 192.168.0.1
|
# - 192.168.0.1
|
||||||
@@ -20,9 +20,9 @@ servers:
|
|||||||
#
|
#
|
||||||
# Don't use this when deploying to multiple web servers (then you have to terminate SSL at your load balancer).
|
# Don't use this when deploying to multiple web servers (then you have to terminate SSL at your load balancer).
|
||||||
#
|
#
|
||||||
# proxy:
|
proxy:
|
||||||
# ssl: true
|
ssl: true
|
||||||
# host: app.example.com
|
host: yockboard.yock.dev
|
||||||
|
|
||||||
# Where you keep your container images.
|
# Where you keep your container images.
|
||||||
registry:
|
registry:
|
||||||
@@ -41,6 +41,7 @@ env:
|
|||||||
secret:
|
secret:
|
||||||
- RAILS_MASTER_KEY
|
- RAILS_MASTER_KEY
|
||||||
clear:
|
clear:
|
||||||
|
PORT: 3005
|
||||||
# Run the Solid Queue Supervisor inside the web server's Puma process to do jobs.
|
# Run the Solid Queue Supervisor inside the web server's Puma process to do jobs.
|
||||||
# When you start using multiple servers, you should split out job processing to a dedicated machine.
|
# When you start using multiple servers, you should split out job processing to a dedicated machine.
|
||||||
SOLID_QUEUE_IN_PUMA: true
|
SOLID_QUEUE_IN_PUMA: true
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ require "active_support/core_ext/integer/time"
|
|||||||
|
|
||||||
Rails.application.configure do
|
Rails.application.configure do
|
||||||
# Settings specified here will take precedence over those in config/application.rb.
|
# Settings specified here will take precedence over those in config/application.rb.
|
||||||
|
config.hosts = [
|
||||||
|
"yockboard.yock.dev", # Allow requests from example.com
|
||||||
|
]
|
||||||
|
|
||||||
# Make code changes take effect immediately without server restart.
|
# Make code changes take effect immediately without server restart.
|
||||||
config.enable_reloading = true
|
config.enable_reloading = true
|
||||||
|
|||||||
@@ -80,10 +80,9 @@ Rails.application.configure do
|
|||||||
config.active_record.attributes_for_inspect = [ :id ]
|
config.active_record.attributes_for_inspect = [ :id ]
|
||||||
|
|
||||||
# Enable DNS rebinding protection and other `Host` header attacks.
|
# Enable DNS rebinding protection and other `Host` header attacks.
|
||||||
# config.hosts = [
|
config.hosts = [
|
||||||
# "example.com", # Allow requests from example.com
|
"yockboard.yock.dev", # Allow requests from example.com
|
||||||
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
|
]
|
||||||
# ]
|
|
||||||
#
|
#
|
||||||
# Skip DNS rebinding protection for the default health check endpoint.
|
# Skip DNS rebinding protection for the default health check endpoint.
|
||||||
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
|
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
|
||||||
|
|||||||
@@ -11,6 +11,6 @@
|
|||||||
# end
|
# end
|
||||||
|
|
||||||
# These inflection rules are supported but not enabled by default:
|
# These inflection rules are supported but not enabled by default:
|
||||||
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||||
# inflect.acronym "RESTful"
|
inflect.acronym 'API'
|
||||||
# end
|
end
|
||||||
|
|||||||
@@ -11,4 +11,12 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
# Defines the root path route ("/")
|
# Defines the root path route ("/")
|
||||||
# root "posts#index"
|
# root "posts#index"
|
||||||
|
|
||||||
|
namespace :api do
|
||||||
|
namespace :v1 do
|
||||||
|
namespace :weather do
|
||||||
|
resources :observations
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
8
db/migrate/20260216224032_create_observations.rb
Normal file
8
db/migrate/20260216224032_create_observations.rb
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
class CreateObservations < ActiveRecord::Migration[8.1]
|
||||||
|
def change
|
||||||
|
create_table :observations do |t|
|
||||||
|
t.jsonb :request_body
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
22
db/schema.rb
generated
Normal file
22
db/schema.rb
generated
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# This file is auto-generated from the current state of the database. Instead
|
||||||
|
# of editing this file, please use the migrations feature of Active Record to
|
||||||
|
# incrementally modify your database, and then regenerate this schema definition.
|
||||||
|
#
|
||||||
|
# This file is the source Rails uses to define your schema when running `bin/rails
|
||||||
|
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
||||||
|
# be faster and is potentially less error prone than running all of your
|
||||||
|
# migrations from scratch. Old migrations may fail to apply correctly if those
|
||||||
|
# migrations use external dependencies or application code.
|
||||||
|
#
|
||||||
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
|
ActiveRecord::Schema[8.1].define(version: 2026_02_16_224032) do
|
||||||
|
# These are extensions that must be enabled in order to support this database
|
||||||
|
enable_extension "pg_catalog.plpgsql"
|
||||||
|
|
||||||
|
create_table "observations", force: :cascade do |t|
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.jsonb "request_body"
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user