---
title: "Generate Rails code with aontu templates"
description: "Follow a component tree, template markers, emit rules, and replacements from the model to Rails source files."
source: "https://aontu.dev/examples/rb-solar/rails-code/"
---

[Back to the Rails application overview](https://aontu.dev/examples/rb-solar)

# Generate Rails code with aontu templates

Follow a component tree, template markers, emit rules, and replacements from the model to Rails source files.

Rendered from [`test/system/rb-solar/doc/rails-code.md`](https://github.com/aontu-lang/aontu/blob/main/test/system/rb-solar/doc/rails-code.md) in the engine repository. The model, the generators, the committed application and the `check.sh` that renders and validates all of it are in [`test/system/rb-solar/`](https://github.com/aontu-lang/aontu/tree/main/test/system/rb-solar/).

The Rails example uses seven Ruby templates and one aontu document to write the application files marked as generated. A template specifies both the output paths and the rules that fill them. Read the [model guide](https://aontu.dev/examples/rb-solar/model) first for setup and the entity properties.

## Separate instructions from output

Open the [routes generator](https://github.com/aontu-lang/aontu/blob/main/test/system/rb-solar/gen/routes.rb). Lines beginning with `#-` contain aontu instructions. Other lines provide Ruby output:

```ruby
#- @"../model.aon"
#-
#- out: file("config/routes.rb", [
#-   [
# Generated by aontu from model.aon. Do not edit.
```

The import loads `model.aon` relative to the generator file. `file` names one output file, `config/routes.rb` relative to the output directory, and its second argument is the list of lines that fill it. A bare string among them is one line of target text.

Ruby treats every `#-` line as a comment. This lets the template remain parseable Ruby, with placeholders where generated values will go. Check its syntax:

```sh
ruby -c gen/routes.rb
```

A syntax check does not run the template as a Rails application. aontu must first evaluate the marked instructions and substitute model values.

## What the tree is

`file(name, children)` builds one node of a **component tree**: the shape a generator runtime writes to disk. `folder`, `line` and `content` are its neighbours; every prop each one takes is checked at the call, so a misspelled one is refused rather than dropped.

Each file supplies a name and its lines. The generator writes the target’s text explicitly; aontu knows no languages, and nothing here makes it invent a Rails implementation.

The generator’s import and its own declarations form one document. The imported file supplies `service` and `entity`; the generator adds `out`, computed from those values. You evaluate the generator, not `model.aon` on its own.

See [`file`](https://aontu.dev/docs/reference-language#filespec-stringmap-children-list--map) in the function index for the full list and what each one takes.

## Follow one value into a route

The root-route rule selects the service’s root value:

```ruby
#-   emit([$.service.root], {
#-     match: string
#-     replace: ROOT: _
#-     body: [
  root "ROOT"
#-     ]
#-   })
```

[`emit`](https://aontu.dev/docs/reference-language#transforming-emit) visits the selected values and uses the first matching rule. Here the selection is a one-item list containing `"planets#index"`. `match: string` admits that value, `_` refers to it, and `replace` substitutes it for `ROOT` in the output line.

Inspect the tree without writing anything:

```sh
aontu template gen/routes.rb > work/routes.aon
aontu model get out work/routes.aon
```

One of its lines is:

```ruby
  root "planets#index"
```

The other rules iterate over entities to write browser routes, API routes, and action routes. Replacement names must not overlap: aontu refuses a table with both `COLLECTION` and `COLLECTION_HELPER`.

## Produce one file per entity

The [Active Record generator](https://github.com/aontu-lang/aontu/blob/main/test/system/rb-solar/gen/model.rb) builds its files with `emit($.entity, ...)`. For each matching entity it computes the name from `.id`, such as `app/models/moon.rb`.

Inside that file, a second rule emits the class body. `.class` is the current entity’s class name. A nested selection of required fields writes presence validations:

```ruby
#-         emit(filter(.field, { required:true }), {
#-           match: name: string
#-           replace: FIELD: .name
#-           body: [
  validates :FIELD, presence: true
#-           ]
#-         })
```

`filter` selects fields with `required: true`; the rule reads each selected field’s `name`. For Moon, this produces validations for `diameter`, `id`, `kind`, `name`, and `planet_id`.

Write the model files into the Rails application directory:

```sh
aontu render gen/model.rb app
aontu render --check gen/model.rb app
```

The first command writes the files this generator names. The second builds them again and compares their bytes with the files on disk. It does not check files owned by other generators.

## Choose the generator for the output

| Generator | Reads | Writes under `app/` |
| --- | --- | --- |
| `routes.rb` | Service root, entity paths, and actions | `config/routes.rb` |
| `migrate.rb` | Ordered entities, fields, and indexes | `db/migrate/*_create_*.rb` |
| `seeds.rb` | Ordered entities and prepared seed rows | `db/seeds.rb` |
| `model.rb` | Entity classes, associations, and required fields | `app/models/*.rb` |
| `api_base.rb` | Error definitions | `app/controllers/api/base_controller.rb` |
| `api_controller.rb` | Entity fields, paths, parents, and actions | `app/controllers/api/*_controller.rb` |
| `ui_controller.rb` | Entities and their parents | `app/controllers/*_controller.rb` |
| `views.aon` | Fields, labels, and route helpers | `app/views/*/*.html.erb` |

The [view generator](https://github.com/aontu-lang/aontu/blob/main/test/system/rb-solar/gen/views.aon) uses ordinary aontu syntax with ERB lines in backtick strings. The template marker mechanism does not support ERB’s delimited comment syntax, so this generator is an aontu file rather than an ERB template.

## Understand the checks’ limits

aontu never parses the target: every line a generator writes is text whose syntax it has not validated. Ruby syntax checks and Rails behaviour tests provide the evidence about that text.

Writing the tree leaves files outside it alone. When a rule is removed or a generated path changes, review and remove the obsolete file yourself. The [change-and-check guide](https://aontu.dev/examples/rb-solar/change-and-check) shows the complete regeneration loop and a field change to try.
