Vous êtes ici :Installation Ruby, cc, SQLite
Prérequis
PourquoiCyberTrain compile via Spinel vers du C : il lui faut donc une chaîne de compilation C et les en-têtes SQLite ; la commande cybertrain est une gem, il lui faut donc Ruby.
- Ruby 3.2+ et RubyGems, uniquement pour la commande
cybertrain; les applications ne tournent jamais sur CRuby. - Spinel
2026.09.12, non testé avec d’autres versions. Vous ne l’installez pas à la main : la première commande qui en a besoin compile cette version dans~/.cybertrain(quelques minutes, une seule fois), sauf si unspinelde cette version est déjà dans lePATH. - Une chaîne de compilation C (
cc, ou le compilateur désigné parCC) etmake, plusgitetcurl, pour compiler Spinel, qui compile chaque programme en C. - En-têtes et bibliothèque SQLite 3 :
libsqlite3-devsous Debian/Ubuntu ; fournis avec les Command Line Tools de Xcode sous macOS. - Les en-têtes OpenSSL (
libssl-dev) sont facultatifs : sans eux, Spinel est compilé sans son paquetopenssl, que CyberTrain n’utilise pas.
cybertrain doctor vérifie ces outils et en-têtes ainsi que l’installation de Spinel ; quand un élément requis manque, il affiche aussi la commande d’installation pour votre plateforme.
Comment CyberTrain installe SpinelCe qui se passe la première fois qu’une commande a besoin de spin
cybertrain new, db, server, build et spin cherchent d’abord Spinel 2026.09.12 : un spinel et un spin de cette version déjà dans le PATH, sinon la copie sous ~/.cybertrain/spinel/2026.09.12/, sinon ils compilent cette copie à partir du tag de version de Spinel (git clone, make deps, make -j, make install), une seule fois, en quelques minutes. Le journal est ~/.cybertrain/log/spinel-2026.09.12-build.log ; s’il manque un outil nécessaire à la compilation, cybertrain indique lequel et affiche la commande d’installation pour votre plateforme avant de commencer.
cybertrain setup fait la même chose à la demande (--force recompile la copie sous ~/.cybertrain) et, quand c’est sa propre copie qui est utilisée, affiche la ligne qui la place dans votre PATH, pour lancer spin vous-même :
export PATH="$HOME/.cybertrain/bin:$PATH"
~/.cybertrain/bin contient des liens spinel et spin vers la version utilisée. S’il n’est pas dans le PATH, cybertrain spin ARGS... lance spin avec cette version depuis n’importe quel répertoire. CYBERTRAIN_HOME remplace ~/.cybertrain, et CYBERTRAIN_SPINEL_HOME=PREFIX utilise à la place le Spinel installé dans PREFIX (ce doit être cette version). Un spinel d’une autre version présent dans le PATH n’est jamais utilisé ni modifié.
Pas de machine sur laquelle installer ? Le bac à sable ouvre ce blog dans un GitHub Codespace avec les étapes 02, 03, 04 et 07 déjà faites.
Vous êtes ici :Installation la CLI cybertrain
Installer la CLI
Pourquoicybertrain est la commande qui crée les applications, génère les scaffolds de ressources, migre, sert et compile.
La première ligne l’installe depuis RubyGems. Le reste est le tutoriel entier en un coup d’œil ; les étapes ci-dessous le reprennent une commande à la fois.
gem install cybertrain
cybertrain new blog # installe Spinel 2026.09.12 la première fois (quelques minutes)
cd blog
cybertrain g scaffold article title:string body:text
cybertrain db migrate
cybertrain server # http://127.0.0.1:3000
cybertrain build # production: dist/blog, dist/public/, dist/storage/, dist/tmp/
La commande cybertrain est en Ruby pur et la gem l’exécute sous CRuby (3.2 ou plus récent) ; ni le framework ni Spinel ne sont dans la gem. Ses commandes (cybertrain help les affiche) :
cybertrain new NAME [--path DIR | --version V | --git URL [--ref R]] [--skip-spin]crée une application dans NAME, puis y lancespin locketspin run gen, en installant d’abord Spinel si nécessaire.cybertrain generate scaffold NAME field:type ...(aliasg scaffold) ajoute une ressource à l’application du répertoire courant.cybertrain db migrategénère, applique les migrations en attente, puis génère à nouveau (spin run gen; spin run db -- migrate; spin run gen) ;cybertrain migrationest conservé comme alias.cybertrain db COMMAND...lance toute autre commande de base de données aprèsspin run gen:status,rollback [N](qui génère à nouveau ensuite),schema:dump,create.cybertrain server [PORT]génère, puis démarre le serveur de développement (spin run gen; spin run NAME, ouspin run NAME -- PORT; port 3000 par défaut).cybertrain buildcompile NAME avecapp/views/intégré et assembledist/(le binaire,public/,storage/,tmp/).cybertrain spin ARGS...lancespinavec le Spinel épinglé, depuis n’importe quel répertoire :cybertrain spin test,cybertrain spin run gen -- --check.cybertrain setup [--force]installe Spinel2026.09.12dans~/.cybertrain, sauf si unspinelde cette version est déjà dans lePATH;--forcerecompile la copie sous~/.cybertrain.cybertrain doctorvérifie la chaîne de compilation C, les en-têtes SQLite et l’installation de Spinel.cybertrain versionaffiche la version (cybertrain 0.2.1) ;cybertrain helpaffiche l’aide.
db, server et build s’exécutent dans une application ; NAME est le [package] name de son spin.toml.
Vous êtes ici :Vous écrivez blog/
Créer l’application
PourquoiVous obtenez les conventions de répertoires de Rails, plus trois programmes dans bin/ : bin/blog.rb (l’application), bin/gen.rb (le générateur) et bin/db.rb (les migrations en développement).
cybertrain new blog
cd blog
cybertrain new écrit l’application avec un spin.toml qui pointe vers la version correspondant à la CLI :
[package]
name = "blog"
version = "0.1.0"
[dependencies]
cybertrain = { git = "https://github.com/saeki-mototsune/cybertrain", ref = "v0.2.1" }
Ensuite, il s’assure que Spinel est installé (la première fois, cela signifie compiler Spinel 2026.09.12 dans ~/.cybertrain : quelques minutes, une seule fois ; voir l’étape 00) et lance spin lock (spin récupère le framework dans son cache, ~/.cache/spin/packages/, et épingle le commit dans spin.lock) et spin run gen, un peu comme rails new lance bundle install, pour que cybertrain server fonctionne tout de suite. Il termine en affichant les étapes suivantes. Rien du framework n’est copié dans l’application ; commitez spin.toml et spin.lock.
Si Spinel ou l’une des deux étapes spin échoue, les fichiers restent en place, new sort avec le code 1 et affiche ce qu’il faut lancer une fois la cause corrigée (pour Spinel : cybertrain setup, puis cybertrain spin lock && cybertrain spin run gen dans le nouveau répertoire). --skip-spin se contente d’écrire les fichiers ; --path DIR fait plutôt dépendre l’application d’une copie locale du dépôt, --git URL [--ref R] du framework situé à URL (à la branche ou au tag R), et --version V d’une version de l’index, une fois que cybertrain sera sur un spin-index.
Les noms d’application utilisent des lettres minuscules, des chiffres et _, et commencent par une lettre. L’arborescence générée :
blog/
spin.toml config/app.rb config/routes.rb
db/schema.rb db/migrate/
app/controllers/application_controller.rb app/models/ app/views/ app/helpers/
public/ bin/blog.rb bin/gen.rb bin/db.rb
gen/ storage/ tmp/ test/
bin/blog.rb est l’unique point d’entrée de l’application, le programme que compile spin build blog : ./blog sert l’application, ./blog migrate et ./blog db status gèrent la base de données. bin/gen.rb lance le générateur (spin run gen), et bin/db.rb lance le migrateur pendant le développement (cybertrain db migrate l’utilise, car bin/blog.rb ne peut pas compiler tant que la première migration n’a pas généré gen/models/). gen/ contient déjà views.rb, la table des vues intégrées, versionnée et vide.
ApplicationController commence ainsi, pour qu’un enregistrement introuvable donne une 404 via rescue_from :
class ApplicationController < Cybertrain::Controller
rescue_from Cybertrain::RecordNotFound, with: :record_not_found
private
def record_not_found
render plain: "Not Found", status: :not_found
end
end
Vous êtes ici :Vous écrivez app/, db/migrate/
Scaffold d’un article
PourquoiUne seule commande écrit toute la tranche CRUD, à la Rails.
cybertrain generate scaffold article title:string body:text
Elle écrit une migration, un modèle, un contrôleur et cinq vues (index, show, new, edit, _form), et insère resources :articles dans config/routes.rb. Les types de champ sont string, text, integer, float, boolean, date, datetime et references ; le type par défaut est string. Le cybertrain g scaffold du démarrage rapide est la même commande.
Les fichiers existants ne sont jamais écrasés : chaque fichier est signalé comme create, identical ou exist.
Vous êtes ici :Vous écrivez config/routes.rb
Définir la route racine
Pourquoi/ doit afficher la liste des articles.
# config/routes.rb
Cybertrain::Routes.draw do
root "articles#index"
resources :articles
end
Les routes sont lues à la compilation : spin run gen les transforme en gen/routes.rb. cybertrain server, cybertrain db migrate et cybertrain build le lancent pour vous.
Vous êtes ici :Vous écrivez app/models/
Ajouter des validations au modèle
PourquoiComme dans le guide Rails, ajoutez à la main une validation de longueur.
# app/models/article.rb -- only the first string field gets a default
# presence validation; add anything past that yourself.
class Article
validates :title, presence: true
validates :body, presence: true, length: { minimum: 10 }
end
class Article n’a pas de superclasse écrite : la superclasse Cybertrain::Model, les colonnes et les finders viennent de gen/models/article.rb. Un enregistrement qui échoue signale des messages tels que « Title can't be blank » et « Body is too short (minimum is 10 characters) ».
Vous êtes ici :Vous écrivez app/controllers/
Le contrôleur ArticlesController
PourquoiUn CRUD complet, à la Rails jusqu’aux strong params et au flash suivi d’une redirection, à deux choses près que Spinel impose partout.
# app/controllers/articles_controller.rb (index/show/edit are as plain as
# Rails' own scaffold; update mirrors create)
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
def new_action # a `new` method would shadow ArticlesController.new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
flash[:notice] = "Article was successfully created."
redirect_to article_path(@article), status: :see_other
else
render :new, status: :unprocessable_entity
end
end
private
def set_article
@article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:title, :body)
end
end
Le scaffold écrit le contrôleur complet, avec aussi index, show, edit, update et destroy ; l’extrait ci-dessus les omet. Dans index, @articles = Article.all.to_a : le .to_a est obligatoire.
Les variables d’instance sont trouvées par un balayage lexical des @ivar = : relancez donc cybertrain spin run gen après en avoir ajouté une ; le serveur de développement le fait pour vous.
Vous êtes ici :Compilation, puis exécution gen/ → build/bin/blog
Générer, migrer, lancer
PourquoiTransformer le schéma et les routes en gen/, créer la base SQLite, démarrer le serveur.
cybertrain db migrate # spin run gen; spin run db -- migrate; spin run gen
cybertrain server # http://127.0.0.1:3000; recompile après une modification Ruby, les vues se rechargent sans recompilation
spin run gen lit db/schema.rb et config/routes.rb, analyse app/ et écrit gen/ ; spin run db -- migrate applique db/migrate/*.rb et réécrit db/schema.rb. L’ordre compte : le premier spin run gen place la nouvelle migration dans gen/migrations.rb, que requiert bin/db.rb, et le second construit les modèles à partir du db/schema.rb réécrit.
cybertrain server affiche chaque commande spin qu’il lance, puis la bannière de démarrage de l’application. Le premier démarrage compile l’application, il prend donc un moment :
=> Booting cybertrain 0.2.1
=> development environment (1 worker)
=> Watching app/, config/ and db/schema.rb for changes
* Listening on http://127.0.0.1:3000
Use Ctrl-C to stop
- Relancez
cybertrain spin run genet commitezgen/après avoir modifié le schéma, les routes, ou les callbacks et ivars d’un contrôleur ou d’un modèle. Le serveur de développement le fait pour vous. - Changez le port avec
cybertrain server 4000(ouPORT=4000). cybertrain dbajoute le--dontspin run dba besoin avant ses arguments ;cybertrain db statuset les autres commandes de base de données fonctionnent de la même façon.
Vous êtes ici :Vous écrivez db/migrate/, app/
Scaffold des commentaires
PourquoiLe type references vous donne la clé étrangère dont l’association est déduite.
cybertrain generate scaffold comment commenter:string body:text article:references
cybertrain db migrate # le scaffold a écrit une migration pour la table comments
article:references ajoute une colonne article_id, un index et une clé étrangère — de quoi donner à Article/Comment leur association ; il n’y a pas de has_many/belongs_to à écrire, spin run gen la lit dans la clé étrangère. Le schéma obtenu :
s.create_table "comments" do |t|
t.string "commenter"
t.text "body"
t.integer "article_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
s.add_index "comments", ["article_id"], name: "index_comments_on_article_id"
s.add_foreign_key "comments", "articles", column: "article_id"
Le scaffold insère une ligne resources :comments indépendante, que l’étape suivante remplace.
Vous êtes ici :Vous écrivez config/routes.rb
Imbriquer les routes
PourquoiLe guide Rails imbrique les commentaires sous les articles.
Cybertrain::Routes.draw do
root "articles#index"
# A nested block takes the mapper explicitly (Spinel's instance_eval
# trampoline only rewires `self` for the outermost block): `articles.resources`.
resources :articles do |articles|
articles.resources :comments, only: [:create, :destroy]
end
end
Vous êtes ici :Vous écrivez app/controllers/
Le contrôleur CommentsController
PourquoiSeules deux actions sont accessibles, et l’article parent vient de l’URL (params[:article_id]), pas de :id.
class CommentsController < ApplicationController
before_action :set_article
def create
@comment = Comment.new(comment_params)
@comment.article_id = @article.id
if @comment.save
flash[:notice] = "Comment was successfully created."
else
flash[:alert] = "Comment could not be saved: #{@comment.errors.full_messages.join(", ")}"
end
redirect_to article_path(@article), status: :see_other
end
def destroy
@comment = Comment.where(article_id: @article.id).find(params[:id])
@comment.destroy
flash[:notice] = "Comment was successfully destroyed."
redirect_to article_path(@article), status: :see_other
end
private
def set_article
@article = Article.find(params[:article_id])
end
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
Ajoutez ensuite des validations au modèle de commentaire :
class Comment
validates :commenter, presence: true
validates :body, presence: true
end
Article#comments est ici un Array : le @article.comments.find(params[:id]) de Rails devient donc Comment.where(article_id: @article.id).find(params[:id]). Un commentaire placé sous le mauvais article renvoie une 404.
Vous êtes ici :Exécution app/views/, lues depuis le disque en développement, intégrées par cybertrain build
Vues et partiels
PourquoiLa page d’un article liste ses commentaires et se termine par un formulaire lié à [@article, @comment].
Supprimez d’abord les vues de commentaire index, show, new et edit, désormais inaccessibles, et donnez à ArticlesController#show un @comment vide :
def show
@comment = Comment.new
end
Affichez ensuite les commentaires et le formulaire sur la page de l’article :
<%# app/views/articles/show.html.erb %>
<% @article.comments.each do |comment| %>
<%= render "comments/comment", comment: comment %>
<% end %>
<%= render "comments/form" %>
<%# app/views/comments/_form.html.erb %>
<%= form_with(model: [@article, @comment]) do |f| %>
<%= f.label :commenter %> <%= f.text_field :commenter %>
<%= f.label :body %> <%= f.text_area :body %>
<%= f.submit %>
<% end %>
Le partiel de commentaire, tel qu’il est livré dans l’exemple :
<%# locals: (comment:) %>
<div class="comment">
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<%= button_to "Destroy comment", [@article, comment], method: :delete %>
</div>
button_to ... [@article, comment] et form_with(model: [@article, @comment]) se résolvent tous deux selon la même règle [parent, child] : un nouvel enfant est routé vers la collection imbriquée (article_comments_path), un enfant persisté vers le membre imbriqué (article_comment_path) — générés dans gen/routes.rb à partir du bloc resources imbriqué ci-dessus.
Vous êtes ici :Compilation cybertrain spin test
Tester
Pourquoiminitest et RSpec trouvent les tests par réflexion, ce qu’un compilateur AOT ne peut pas faire ; CyberTrain fournit donc Cybertrain::Test.
Un test de modèle
require_relative "support/blog_test" # the app, BLOG and Cybertrain::Test
test "title must be present" do
article = Article.new(body: "a body long enough to pass length")
refute article.save
assert_includes article.errors.full_messages, "Title can't be blank"
end
Assertions : assert, refute, assert_equal, assert_nil, assert_includes, assert_raises("Class") { }, flunk. L’exemple utilise aussi assert_response et assert_redirected_to.
Un parcours HTTP complet
require_relative "support/blog_test" # the app, BLOG and Cybertrain::Test
test "POST /articles creates an article and redirects to it" do
client = Cybertrain::Test::Client.new(BLOG)
client.get("/articles/new")
res = client.post("/articles", { "authenticity_token" => BlogTest.form_token(client),
"article[title]" => "Hello Rails",
"article[body]" => "I am on Rails! This is my first article." })
assert_redirected_to res, BlogTest.article_path(Article.last)
assert_response res, :see_other
end
Cybertrain::Test::Client pilote une application dans le même processus (sans socket) avec une cookie jar, de sorte que les sessions et le CSRF se comportent comme derrière un navigateur. Les requêtes sans le jeton reçoivent une 403.
Lancer les tests
cybertrain spin test
cybertrain spin test lance spin test avec le Spinel épinglé ; avec la chaîne d’outils dans le PATH (étape 00), spin test fait la même chose. Il compile chaque test/*.rb en un programme distinct et compare (diff) sa sortie (stdout et stderr ensemble) à un snapshot test/<name>.rb.expected versionné. Les fichiers de test/support/ sont requis, pas exécutés. Pour écrire un snapshot :
cybertrain spin test --regen test/<name>.rb
rm -f build/test/<name>; cybertrain spin test test/<name>.rb; [ -x build/test/<name> ] && ./build/test/<name> > test/<name>.rb.expected 2>&1
--regen écrit le snapshot à partir de la sortie de CRuby. Les tests qui accèdent à SQLite via FFI ne peuvent pas tourner sous CRuby : leur snapshot vient donc du binaire compilé (la deuxième ligne ci-dessus). cybertrain spin test sort avec un code non nul tant qu’un snapshot est périmé, il est donc suivi de ; et non de && ; la vérification [ -x ] saute la capture si la compilation a échoué, ce qui conserve l’ancien snapshot. Les tests qui ne peuvent pas tourner sous CRuby ont besoin de ce snapshot avant que cybertrain spin test puisse réussir ; les autres tests sans snapshot sont comparés à la sortie de CRuby.
Vous êtes ici :Exécution dist/blog
Compiler pour le déploiement
PourquoiUne seule commande compile l’application dans un dossier dist/ déployable, avec toutes les vues intégrées.
cybertrain build
Elle écrit dist/ : le binaire de l’application avec toutes les vues intégrées, plus les fichiers qu’il utilise, à côté de lui. Elle lance spin run gen -- --embed-views, spin build blog et spin run gen (qui remet en place le gen/views.rb versionné et vide, même après un échec ou un Ctrl-C), puis assemble :
dist/blogle framework compilé, les contrôleurs, les modèles et l’interpréteur de templates, avec toutes les vues intégréesdist/public/ressources statiques et pages d’erreur, ou laissez un reverse proxy le servirdist/storage/créé vide ; contient le fichier SQLite par défaut (une recompilation ne le vide jamais)dist/tmp/créé vide (une recompilation ne le vide jamais)
Copiez dist/ sur une machine avec le même OS et le même CPU (le binaire est lié à la SQLite du système) et lancez-le là-bas. Un binaire compilé est en mode production par défaut (CYBERTRAIN_ENV permet de le changer). Définissez CYBERTRAIN_SECRET_KEY_BASE ; à titre d’illustration :
cd dist
export CYBERTRAIN_SECRET_KEY_BASE="$(openssl rand -hex 32)"
./blog migrate && ./blog
./blog migrate applique les migrations compilées dans le binaire, et ./blog db status affiche leur état ; PORT définit le port. Gardez le même secret d’un redémarrage à l’autre : le changer met fin à toutes les sessions.
Cybertrain::Config lit d’abord l’environnement ; config/app.rb a le dernier mot :
| Variable | Attribut | Par défaut |
|---|---|---|
CYBERTRAIN_ENV | env | "development" ; "production" dans un binaire issu de cybertrain build (aussi "test") |
PORT | port | 3000 |
CYBERTRAIN_DATABASE | database_path | storage/#{env}.sqlite3 |
CYBERTRAIN_SECRET_KEY_BASE | secret_key_base | requis en production ; en dev/test, un secret est généré automatiquement dans tmp/secret_key |
SPINEL_WORKERS | workers | 1 |
CYBERTRAIN_HOST | host | "127.0.0.1" ; 0.0.0.0 écoute sur toutes les interfaces (conteneurs) |
CYBERTRAIN_SESSION_SAME_SITE | session_same_site | "Lax" ; aussi Strict ou None (None ajoute toujours Secure) ; toute autre valeur arrête le serveur au démarrage |
CYBERTRAIN_SESSION_PARTITIONED | session_partitioned | false ; 1 ou true ajoute Partitioned (et Secure) |
# config/app.rb
Cybertrain.configure do |c|
c.port = 3000
c.workers = 1
end
- Le serveur parle du HTTP/1.1 simple : placez nginx ou Caddy devant pour le TLS et HTTP/2. Le cookie de session est
Secureen production, servez donc l’application en HTTPS (ne définissezc.session_secure = falsedansconfig/app.rbque si la production est vraiment en http simple). - En production, l’application refuse de démarrer sans
CYBERTRAIN_SECRET_KEY_BASE. - En production, une exception renvoie une 500, et une réponse d’erreur brute (la 404 en texte brut du routeur,
head :not_found,render plain:) est remplacée parpublic/<status>.htmlquand ce fichier existe ;cybertrain newécrit404.htmlet500.html. SIGTERMcesse d’accepter des connexions, laisse se terminer les requêtes en cours et quitte quand elles sont terminées, ou après ledrain_timeoutdu serveur (10 s).- La surveillance des fichiers et la boucle de recompilation ne tournent jamais en production. Un
cybertrain serveren cours d’exécution saute ses recompilations tant quecybertrain builddétienttmp/cybertrain-build.lock.
Bon à savoir
PourquoiDes limites et particularités à connaître avant qu’elles ne vous surprennent.
- Les sessions ne contiennent que des String, dans un cookie signé HMAC.
- SQLite uniquement, via la FFI de Spinel.
- Pas de backtraces : les erreurs affichent la classe, le message, la ligne de requête et le nom/la ligne du template.
- Un attribut mal orthographié dans une vue s’affiche vide au lieu de lever une erreur ; c’est une lacune connue.
- Les associations viennent uniquement des clés étrangères du schéma ; pas de
has_many :through,includes,pluck,dependent:, d’enums, de STI ni d’associations polymorphes. - Pas de
namespace, derespond_to/formats, deconstraintsni demount;render json:est minimal. - Dans un modèle généré par scaffold, seul le premier champ string reçoit une validation de présence par défaut.
- Le générateur utilise un balayage lexical (des regex), pas un parseur, sur
app/controllersetapp/models. - Les vues sont intégrées à la compilation : une modification qui ne touche que les vues nécessite
cybertrain buildet un redémarrage avant d’atteindre la production. - Compilez
dist/sur le même OS et le même CPU que la machine qui l’exécute : le binaire est lié à la SQLite du système. SPINEL_WORKERSvaut1par défaut.- Seul Spinel
2026.09.12est testé.
Différences avec Rails
PourquoiLe tableau complet du README, en un seul endroit.
| Rails | CyberTrain |
|---|---|
rails console | Pas de console — Spinel n’a pas d’eval |
| Modifiez le code, l’application en cours d’exécution le prend en compte | Le Ruby demande une recompilation ; en développement, cybertrain server s’en charge pour vous (recompilation, arrêt, execv) |
| Modifiez une vue, aucun rechargement nécessaire | Pareil — en développement, les vues sont analysées depuis le disque à chaque requête |
def new | def new_action (new masquerait Klass.new(ctx)) |
before_action { do_thing } (self implicite) | before_action { |c| c.do_thing } — pas d’instance_exec sur un bloc stocké, donc les callbacks reçoivent explicitement le contrôleur/l’enregistrement |
resources :posts do resources :comments end | resources :posts do |posts| posts.resources :comments end — les blocs imbriqués reçoivent le mapper explicitement |
| ERB est compilé en méthode ; n’importe quel objet, n’importe quelle méthode | Interprété selon une grammaire/table de dispatch fixe |
| La session contient n’importe quel objet sérialisé (marshal) | Valeurs de session de type String uniquement, cookie signé HMAC |
| De nombreux adaptateurs de base de données | SQLite uniquement, via FFI |
has_many :through, includes, pluck, dependent:, enums, STI, associations polymorphes | Non implémenté ; les associations viennent uniquement des clés étrangères du schéma |
namespace, format/respond_to, constraints, mount | Non implémenté — noms plats, render json: uniquement |
| Backtrace complète lors d’une exception | Classe, message, ligne de requête, nom/ligne du template — Spinel n’expose pas de backtraces |
Rack, ses middlewares, et n’importe quelle gem d’un Gemfile | Pas de compatibilité Rack ; un petit ensemble fixe de middlewares ; le spin-index propre à Spinel, limité à ce qui compile avec son sous-ensemble de Ruby |
| minitest / RSpec | Cybertrain::Test — les runners fondés sur la réflexion ne peuvent pas fonctionner avec une compilation AOT |
| Authentification, mailers, jobs, ActionCable, asset pipeline, i18n | Non développé (hors du MVP) |
Liste non exhaustive.