Skip to content

نظام القوالب

يستخدم 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 %}

المزيد من المعلومات

Released under the MIT License.