Scaffold Generator
Quickly generate complete MVC resources with models, controllers, views, tests, and migrations.
Basic Usage
Generate a scaffold for a resource:
Terminal
$ soli generate scaffold users $ soli generate scaffold products name:string price:decimal
What Gets Generated
Running soli generate scaffold users creates:
Model
app/models/users_model.soli
Controller
app/controllers/users_controller.soli
Views
app/views/users/ (index, show, new, edit, _form)
Tests
tests/models/users_test.soli, tests/controllers/users_controller_test.soli
Migration
db/migrations/*create_users_*.soli
Routes
Auto-added to config/routes.soli
Field Types
Specify fields with name:type syntax:
| Type | HTML Input | Example |
|---|---|---|
| string | text | name:string |
| text | textarea | content:text |
| email (unique index) | email:email | |
| password | password (unique index) | password:password |
| integer | number | age:integer |
| float | number | price:float |
| boolean | checkbox | active:boolean |
| date | date picker | birthdate:date |
Generated Code
Running soli generate scaffold users name:string email:text generates:
Model
app/models/users_model.soli
class Users extends Model {
// Fields
// name (string)
// email (text)
// Validations (auto-generated)
validates("name", { "presence": true })
validates("email", { "presence": true })
// Callbacks
before_save("normalize_fields")
}
Controller
app/controllers/users_controller.soli
class UsersController extends Controller {
fn index(req: Any) -> Any {
let users = Users.all();
return render("users/index", {
"users": users,
"title": "UsersController"
});
}
fn show(req: Any) -> Any {
let id = req["params"]["id"];
let user = Users.find(id);
return render("users/show", {
"user": user,
"title": "View User"
});
}
fn create(req: Any) -> Any {
let result = Users.create(req["params"]);
if result["valid"] == true {
return redirect("/users");
}
return render("users/new", {
"user": result,
"title": "New User"
});
}
fn update(req: Any) -> Any {
let id = req["params"]["id"];
Users.update(id, req["params"]);
return redirect("/users");
}
fn delete(req: Any) -> Any {
let id = req["params"]["id"];
Users.delete(id);
return redirect("/users");
}
}
Views
Index View
app/views/users/index.html.erb
<div class="p-6">
<h1 class="text-2xl font-bold">Users</h1>
<table class="w-full">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% for user in users %>
<tr>
<td><%= user["id"] %></td>
<td><%= user["name"] %></td>
<td>
<a href="/users/<%= user["id"] %>">Show</a>
<a href="/users/<%= user["id"] %>/edit">Edit</a>
</td>
</tr>
<% end %>
</tbody>
</table>
<a href="/users/new">New User</a>
</div>
Form Partial (shared by new/edit)
app/views/users/_form.html.erb
<form action="/users" method="POST">
<input type="text" name="name" value="<%= user["name"] %>">
<input type="text" name="email" value="<%= user["email"] %>">
<button type="submit">Submit</button>
</form>
New View
app/views/users/new.html.erb
<h1 class="text-2xl font-bold mb-6">New User</h1>
<%= render("users/_form", { "user": user }) %>
<a href="/users" class="text-indigo-400 hover:text-indigo-300">← Back to Users</a>
Edit View
app/views/users/edit.html.erb
<h1 class="text-2xl font-bold mb-6">Edit User</h1>
<%= render("users/_form", { "user": user }) %>
<a href="/users" class="text-indigo-400 hover:text-indigo-300">← Back to Users</a>
Show View
app/views/users/show.html.erb
<div class="p-6">
<h1 class="text-2xl font-bold mb-4">User Details</h1>
<dl class="space-y-4">
<div>
<dt class="text-sm text-gray-400">ID</dt>
<dd class="text-lg text-white"><%= user["id"] %></dd>
</div>
<div>
<dt class="text-sm text-gray-400">Name</dt>
<dd class="text-lg text-white"><%= user["name"] %></dd>
</div>
</dl>
<a href="/users/<%= user["id"] %>/edit" class="bg-yellow-600 text-white px-4 py-2 rounded">Edit</a>
<a href="/users" class="text-indigo-400 ml-4">← Back</a>
</div>
Migration
db/migrations/*create_users_*.soli
fn up(db: Any) -> Any {
db.create_collection("users");
}
fn down(db: Any) -> Any {
db.drop_collection("users");
}
Tests
tests/models/users_test.soli
describe("UsersModel", fn() {
test("should create valid record", fn() {
let data = { "name": "Test User" };
let result = Users.create(data);
assert_true(result["valid"], "Create should return valid");
})
})
Controller Test
tests/controllers/users_controller_test.soli
describe("UsersController", fn() {
test("index action should render users list", fn() {
let req = { "params": {}, "session": {} };
let result = UsersController.index(req);
assert_true(result["status"] == 200, "Index should return 200");
})
test("show action should return single user", fn() {
let req = { "params": { "id": "test-id" }, "session": {} };
let result = UsersController.show(req);
assert_true(result["status"] == 200, "Show should return 200");
})
test("create action should redirect on success", fn() {
let req = { "params": { "name": "Test" }, "session": {} };
let result = UsersController.create(req);
assert_true(result["redirect"] != null, "Create should redirect");
})
test("delete action should redirect", fn() {
let req = { "params": { "id": "test-id" }, "session": {} };
let result = UsersController.delete(req);
assert_true(result["redirect"] != null, "Delete should redirect");
})
})
Auto-Validations
Fields with types string, text, email, password, and url automatically get presence: true validation:
app/models/users_model.soli
class Users extends Model {
// Fields
// name (string)
// email (email)
// Validations (auto-generated)
validates("name", { "presence": true })
validates("email", { "presence": true })
// Callbacks
before_save("normalize_fields")
}
Generated Routes
Scaffold automatically adds RESTful routes:
| HTTP | Path | Action |
|---|---|---|
| GET | /users | index |
| GET | /users/new | new |
| POST | /users | create |
| GET | /users/:id | show |
| GET | /users/:id/edit | edit |
| PUT | /users/:id | update |
| DELETE | /users/:id | delete |
Example
Generate a complete blog posts resource:
Terminal
$ soli generate scaffold posts title:string content:text author:string published:boolean Success! Created scaffold for posts
Next Steps
-
Run migrations:
soli db:migrate up -
Start server:
soli serve . --dev -
Visit:
http://localhost:3000/posts