نظام القوالب
يستخدم FORGE نظام قوالب قوي لتوليد كود عالي الجودة ومتسق من العقود المحددة مسبقاً.
نظرة عامة على البنية
┌─────────────────────────────────────────────────────────────────┐
│ نظام القوالب │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ العقود │───▶│ محرك Tera │───▶│ كود مولّد │ │
│ │ (YAML/JSON) │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ المخططات │ │ الفلاتر │ │ التحقق │ │
│ │ والتحقق │ │ المخصصة │ │ من الكود │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘محرك القوالب
يستخدم FORGE محرك Tera للقوالب - وهو محرك قوالب مستوحى من Jinja2/Django مكتوب بلغة Rust.
الميزات الرئيسية
| الميزة | الوصف |
|---|---|
| الوراثة | تمديد القوالب الأساسية |
| التضمين | إعادة استخدام مقاطع القوالب |
| الماكرو | دوال قابلة لإعادة الاستخدام |
| الفلاتر | تحويل البيانات |
| الشروط | المنطق الشرطي |
| الحلقات | التكرار على المجموعات |
الصياغة الأساسية
jinja2
{# This is a comment #}
{# Variables #}
{{ variable }}
{{ object.property }}
{# Filters #}
{{ name | upper }}
{{ items | length }}
{# Conditions #}
{% if condition %}
...
{% elif other_condition %}
...
{% else %}
...
{% endif %}
{# Loops #}
{% for item in items %}
{{ item.name }}
{% endfor %}
{# Includes #}
{% include "partial.tera" %}بنية القالب
templates/
├── manifest.yaml # Template definition
├── backends/
│ └── rust/
│ └── api/
│ └── src/
│ ├── models/
│ │ └── {{model}}.rs.tera
│ ├── handlers/
│ │ └── {{model}}.rs.tera
│ ├── services/
│ │ └── {{model}}.rs.tera
│ └── routes/
│ └── {{model}}.rs.tera
├── frontends/
│ └── next/
│ ├── admin/
│ │ └── src/
│ │ └── app/
│ │ └── [locale]/
│ │ └── admin/
│ │ └── {{model}}/
│ │ └── page.tsx.tera
│ └── web/
│ └── src/
│ └── app/
│ └── [locale]/
│ └── {{model}}/
│ └── page.tsx.tera
├── database/
│ ├── migrations/
│ │ └── {{timestamp}}_{{name}}.sql.tera
│ └── seeders/
│ └── {{name}}.sql.tera
└── docker/
├── Dockerfile.tera
└── docker-compose.yml.teraملف Manifest
كل قالب يتضمن ملف manifest.yaml يحدد البيانات الوصفية والتبعيات:
yaml
name: base
version: "1.0.0"
description: "Base template with authentication and permissions"
# Requirements
requires:
rust: ">=1.75.0"
node: ">=20.0.0"
postgres: ">=15.0"
# Included features
features:
- authentication
- authorization
- users
- roles
- permissions
- admin_dashboard
- web_app
- api_docs
# Dependencies
dependencies:
backend:
- axum: "0.7"
- sqlx: "0.7"
- sea-orm: "0.12"
frontend:
- next: "14"
- react: "18"
- tailwindcss: "3"
# Variables
variables:
project_name:
type: string
required: true
database_url:
type: string
required: true
jwt_secret:
type: string
required: true
# Hooks
hooks:
post_generate:
- "cargo fmt"
- "npm run format"سياق التوليد
عند توليد الكود، يتم توفير السياق التالي للقوالب:
rust
#[derive(Serialize)]
pub struct GenerationContext {
// Project information
pub project: ProjectInfo,
// Model definitions
pub models: Vec<Model>,
// API endpoints
pub endpoints: Vec<Endpoint>,
// Permissions
pub permissions: Vec<Permission>,
// Configuration
pub config: Config,
// Metadata
pub meta: Meta,
}متغيرات المشروع
jinja2
{{ project.name }} {# Project name #}
{{ project.version }} {# Version #}
{{ project.description }} {# Description #}
{{ project.authors }} {# Authors #}متغيرات النموذج
jinja2
{% for model in models %}
{{ model.name }} {# Model name (PascalCase) #}
{{ model.table_name }} {# Table name (snake_case) #}
{{ model.plural }} {# Plural form #}
{% for field in model.fields %}
{{ field.name }} {# Field name #}
{{ field.type }} {# Rust type #}
{{ field.db_type }} {# Database type #}
{{ field.nullable }} {# Nullable? #}
{{ field.default }} {# Default value #}
{% endfor %}
{% endfor %}الفلاتر المخصصة
يوفر FORGE فلاتر مخصصة لتحويل البيانات:
فلاتر تحويل الحالة
jinja2
{{ "user_profile" | pascal_case }} {# Output: UserProfile #}
{{ "UserProfile" | snake_case }} {# Output: user_profile #}
{{ "user_profile" | camel_case }} {# Output: userProfile #}
{{ "user_profile" | kebab_case }} {# Output: user-profile #}
{{ "user" | plural }} {# Output: users #}
{{ "users" | singular }} {# Output: user #}فلاتر تحويل النوع
jinja2
{{ field.type | to_rust_type }} {# Output: String, i32, etc. #}
{{ field.type | to_ts_type }} {# Output: string, number, etc. #}
{{ field.type | to_sql_type }} {# Output: VARCHAR, INTEGER, etc. #}فلاتر المساعدة
jinja2
{{ fields | required_fields }} {# Required fields only #}
{{ fields | optional_fields }} {# Optional fields only #}
{{ fields | sortable_fields }} {# Sortable fields #}
{{ fields | searchable_fields }} {# Searchable fields #}عملية التوليد
┌─────────────────────────────────────────────────────────────────┐
│ عملية التوليد │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. قراءة العقود │
│ ┌──────────────────────────────────────────────┐ │
│ │ contracts/*.yaml │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 2. التحقق من العقود │
│ ┌──────────────────────────────────────────────┐ │
│ │ JSON Schema Validation │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 3. بناء السياق │
│ ┌──────────────────────────────────────────────┐ │
│ │ GenerationContext │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 4. تطبيق القوالب │
│ ┌──────────────────────────────────────────────┐ │
│ │ Tera::render() │ │
│ └──────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 5. كتابة الملفات │
│ ┌──────────────────────────────────────────────┐ │
│ │ Generated Code Files │ │
│ └──────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘أمثلة على القوالب
قالب نموذج Rust
jinja2
// src/models/{{ model.name | snake_case }}.rs
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "{{ model.table_name }}")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: Uuid,
{% for field in model.fields %}
{% if field.nullable %}
pub {{ field.name }}: Option<{{ field.type | to_rust_type }}>,
{% else %}
pub {{ field.name }}: {{ field.type | to_rust_type }},
{% endif %}
{% endfor %}
pub created_at: DateTimeUtc,
pub updated_at: DateTimeUtc,
}قالب صفحة React
jinja2
// app/[locale]/admin/{{ model.name | kebab_case }}/page.tsx
import { DataTable } from "@/components/ui/data-table";
import { columns } from "./columns";
import { get{{ model.name | plural | pascal_case }} } from "@/lib/api/{{ model.name | kebab_case }}";
export default async function {{ model.name | pascal_case }}Page() {
const data = await get{{ model.name | plural | pascal_case }}();
return (
<div className="container mx-auto py-10">
<DataTable columns={columns} data={data} />
</div>
);
}قالب ترحيل قاعدة البيانات
jinja2
-- migrations/{{ timestamp }}_create_{{ model.table_name }}.sql
CREATE TABLE {{ model.table_name }} (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
{% for field in model.fields %}
{{ field.name }} {{ field.db_type }}{% if not field.nullable %} NOT NULL{% endif %}{% if field.default %} DEFAULT {{ field.default }}{% endif %},
{% endfor %}
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Create indexes
{% for field in model.fields | searchable_fields %}
CREATE INDEX idx_{{ model.table_name }}_{{ field.name }} ON {{ model.table_name }}({{ field.name }});
{% endfor %}تخصيص القوالب
يمكنك تخصيص القوالب الحالية أو إنشاء قوالب جديدة:
تجاوز قالب موجود
bash
# Copy template for customization
forge template override models/model.rs.tera
# File created at:
# .forge/templates/models/model.rs.teraإضافة قالب جديد
bash
# Create new template
forge template create my-component
# File created at:
# .forge/templates/my-component.teraأفضل الممارسات
1. استخدام الماكرو لإعادة الاستخدام
jinja2
{% macro render_field(field) %}
{% if field.type == "string" %}
<Input name="{{ field.name }}" />
{% elif field.type == "number" %}
<NumberInput name="{{ field.name }}" />
{% elif field.type == "boolean" %}
<Checkbox name="{{ field.name }}" />
{% endif %}
{% endmacro %}
{% for field in model.fields %}
{{ self::render_field(field=field) }}
{% endfor %}2. التعليقات التوضيحية
jinja2
{#
Template: model.rs.tera
Purpose: Generate SeaORM models
Required variables: model
#}3. التعامل مع الحالات الحدية
jinja2
{% if model.fields | length > 0 %}
{# Generate fields #}
{% else %}
{# No fields - use default #}
{% endif %}المزيد من المعلومات
- القالب الأساسي - القالب الأساسي المتضمن
- القوالب المتوفرة - القوالب الجاهزة للاستخدام
- إنشاء قوالب مخصصة - دليل إنشاء القوالب