Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Working on an app

The commands you use while writing an app: melee new, dev, check, push, logs, env.

All of these read melee.toml in the app directory (name, server, url) and take --dir <path> if you are not standing in it. melee new is the exception: it writes that file.

melee new

melee new notes && cd notes
melee dev

Creates notes/ with a working two-route app: melee.toml, spin.toml, app.rb, views/layout.erb and views/index.erb, public/app.css, a first migration, a .meleeignore and a README. The name must be lowercase letters, digits and dashes, not starting with a dash, at most 40 characters — the same rule the server applies. It refuses a directory that exists and is not empty.

melee dev

ADMIN_SECRET=letmein melee dev        # http://127.0.0.1:4567

Compiles the templates and migrations, then runs the app under CRuby with a small development server. It prints one line per request:

melee dev: prepared 5 templates, 1 migrations
melee dev: http://127.0.0.1:4567 (Ctrl-C to stop)
GET /admin -> 200 (4.2 ms)
GET /admin.css -> 200 (public/)
  • Port from PORT, default 4567. One request at a time, no keep-alive, no TLS.
  • Files under public/ are served straight from disk, as the real server does.
  • The database is .melee/app.sqlite inside the app directory. Delete it to start fresh.
  • Environment variables come from your shell, standing in for melee env.
  • The session secret is MELEE_SESSION_SECRET, defaulting to a fixed development value.
  • No reload. Changing app.rb, a template or a migration means stopping and starting it again.

The error page

Under melee dev only, a 500 renders a page with the exception class and message, the request, the params, the session and a CRuby backtrace. It exists to tell you what happened while you are writing the app. Production has no backtrace at all (see logging.md), so do not build a habit on it.

melee check

melee check

The build without the deploy: the same template and migration steps, then a full Spinel compile of the app and the stdlib. Nothing is sent anywhere. It is the fastest way to find out whether the code compiles.

Every diagnostic is one line, file:line: message, and template errors are mapped back from the generated Ruby to the .erb you wrote:

views/broken.erb:2: unexpected end-of-input, assuming it is closing the parent top level context
db/migrations/002_bad.sql:1: near "TABEL": syntax error
app.rb:30: views/login.erb requires `error`

What is caught here: Ruby syntax in app.rb and lib/**/*.rb (reported against the file you edited, before Spinel runs), template syntax, every render/partial call against the template’s declared locals, migration SQL (each file is run against a scratch database), unknown requires, eval, thread primitives, a rescue clause that reaches its class through a constant alias, the other things dialect.md forbids, and a call to a top-level helper that does not exist. What is not caught: wrong arity, calling a method on nil, and any undefined method reached through an explicit receiverdb.frist(...), "x".nope and Household.get("home").refrsh all compile and raise NoMethodError when the line runs. So click through the app under melee dev before pushing.

melee push

melee push

Tars the app directory and sends it to the server named in melee.toml; the server builds it, and on success activates the new release and prints the URL. build/, .git/, .melee* and spin.lock are never sent, and a .meleeignore file (one path or name per line) excludes more.

A failed build answers with the same file:line: message diagnostics and deploys nothing; the previous release keeps serving.

melee logs, env, restart

melee logs             # last 50 lines
melee logs -f          # follow
melee logs --tail 200
melee env ADMIN_SECRET letmein     # set a variable; the app restarts on its next request
melee restart                      # stop the warm process; the next request starts it again
melee apps                         # what is deployed on this server
melee open                         # print and open the app's URL

melee env is the only place secrets belong. They arrive in the app as ENV["ADMIN_SECRET"].

Generated files

The build step writes three files. They are regenerated on every dev, check and push; never edit them and do not commit them.

FileFrom
.melee/views.rbviews/*.erb — one method per template plus the render/partial dispatch
.melee/migrations.rbdb/migrations/*.sql embedded as Melee.migrate([[name, sql], ...])
bin/<name>.rbthe entry point: requires the two above, then app.rb, then starts the runtime

The same thing by hand

melee new writes these; this is what they are.

melee.toml:

name = "hello"
server = "http://127.0.0.1:7070"
url = "http://hello.localhost:8080"

The control API wants a token on every request. melee looks for it in MELEE_TOKEN, else a token_file path named here — which has to be outside the app directory, because melee push uploads everything in it. The development server writes one for you, so the local loop needs nothing here.

spin.toml:

[package]
name = "hello"
version = "0.1.0"

[dependencies]
melee = { path = "../melee/stdlib" }

app.rb:

# frozen_string_literal: true
title "Hello"

get "/" do
  "<h1>Hello from #{app_title}</h1>"
end

Then melee dev and open http://127.0.0.1:4567.