65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的模板引擎

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.07/5 (10投票s)

2008年8月19日

CPOL

3分钟阅读

viewsIcon

41349

downloadIcon

349

为任何语言或平台生成源代码和数据库脚本。

引言

代码生成是灵活、快速应用开发的一个基本方面。有许多开源和商业解决方案可用,例如Microsoft T4CodeSmith Tools,但通常,这些工具针对的是特定技术或平台。通过一些努力,您可以利用 Ruby 或 Python 等现代脚本语言中的模板处理库来创建强大的、开放式的模板引擎。您可以将这些模板引擎与任何语言或平台一起使用,以生成源代码、数据库脚本、配置文件或任何其他基于文本的文件。

背景

此特定解决方案使用开源 Ruby 语言来处理模板,并使用一个简单的、灵活的 XML 模式来表示您的领域模型。Ruby 在 OS X 系统上预先配置好,可以通过大多数流行的 Linux 发行版上的软件包管理器轻松安装,并且可以使用位于ruby-lang.org上的 Ruby 一键安装程序安装在 Windows 平台上。

使用代码

此示例将使用以下描述库目录的简单领域模式

<Library catalog="MyCatalog">

    <enumeration name="Category">
        <value>FICTION</value>
        <value>NON_FICTION</value>
        <value>REFERENCE</value>
        <value>PERIODICAL</value>
    </enumeration>

    <enumeration name="Format">
        <value>BOOK</value>
        <value>MAGAZINE</value>
        <value>COMPACT_DISC</value>
        <value>VHS</value>
        <value>DVD</value>
    </enumeration>

    <object name="Media">
        <property name="ID" type="string" />
        <property name="Title" type="string" />
        <property name="Category" type="Category" />
        <property name="Format" type="Format" />
        <property name="Author" type="string" />
        <property name="ReleaseDate" type="DateTime" />
    </object>

    <object name="Author">
        <property name="ID" type="string"/>
        <property name="First" type="string"/>
        <property name="Last" type="string"/>
    </object>

</Library> 

以下模板文件Enumerations.erb可用于从库域规范生成 C# 枚举

namespace <%= catalog %>
{

    <%- enumerations.each do |enumeration| -%>
    public enum <%= enumeration.name %>
    {
        <%- enumeration.values.each do |value| -%>
        <%= value %><%= ',' if value != enumeration.values.last %>
        <%- end -%>
    }

    <%- end -%>
}

运行命令ruby generator.rb library.xml Enumerations.erb会产生以下输出

namespace MyCatalog
{

    public enum Category
    {
        FICTION,
        NON_FICTION,
        REFERENCE,
        PERIODICAL
    }

    public enum Format
    {
        BOOK,
        MAGAZINE,
        COMPACT_DISC,
        VHS,
        DVD
    }

}

以下模板文件Objects.erb可用于从库域规范生成简单的 C# 域对象

using System;

namespace <%= catalog %>
{

    <%- objects.each do |object| -%>
    public partial class <%= object.name %>
    {
        <%- object.properties.each do |property| -%>
        <%= property.type %> <%= property.name %> { get; set; }
        <%- end -%>
    
        public object Clone()
        {
            <%= object.name %> copy = new <%= object.name %>();
            <%- object.properties.each do |property| -%>
            copy.<%= property.name %> = <%= property.name %>;
            <%- end -%>
            return copy;
        }
    }

    <%- end -%>
}

运行命令ruby generator.rb library.xml Objects.erb会产生以下输出

using System;

namespace MyCatalog
{

    public partial class Media
    {
        string ID { get; set; }
        string Title { get; set; }
        Category Category { get; set; }
        Format Format { get; set; }
        string Author { get; set; }
        DateTime ReleaseDate { get; set; }
        
        public object Clone()
        {
            Media copy = new Media();
            copy.ID = ID;
            copy.Title = Title;
            copy.Category = Category;
            copy.Format = Format;
            copy.Author = Author;
            copy.ReleaseDate = ReleaseDate;
            return copy;
        }
    }

    public partial class Author
    {
        string ID { get; set; }
        string First { get; set; }
        string Last { get; set; }
        
        public object Clone()
        {
            Author copy = new Author();
            copy.ID = ID;
            copy.First = First;
            copy.Last = Last;
            return copy;
        }
    }

}

如果您需要添加或修改域对象,只需更新您的 XML 模式即可。使用正确的模板,您将不再需要维护您的持久层或任何其他样板代码,并且您对模板所做的任何错误修复或修改都将立即在您的所有域对象中可用。最终,您将花费更少的时间来维护您的管道代码,而将更多的时间花在真正的开发上。

在修订控制下拥有您的领域规范会打开更多的可能性。您将拥有一个数据库备份和版本控制方案,该方案与您的存储库修订(以及可能的构建编号)相对应。您可以从存储库中的任何时间点重建数据库表。您可以比较模式的两个版本并生成数据库回滚和更改脚本。为新开发人员设置数据库将是一项简单的任务。

关注点

驱动此模板引擎的 Ruby 脚本非常短。 为了演示起见,我删除了一些功能,但您将可以在引擎以及模板中访问整个 Ruby 语言,从而可以以您想要的任何方式扩展引擎。

我包含另一个(琐碎且设计不佳的)域规范,以演示特定于域的 XML 标签的用法。 您可以使用您想要的任何标签名称,并指定您想要的任何属性。一个要求是name属性必须存在于任何复杂元素中。 XML Text字段也不能用于表示简单列表以外的任何内容(如下面的ingredient元素所演示)。

<kingdom domain="Animal"/>

    <animal name="Duck" species="Bird">
        <recipe name="Bacon Wrapped Duck" difficulty="Medium">
            <ingredient>10 duck breasts</ingredient>
            <ingredient>3 lbs peppered bacon</ingredient>
            <ingredient>2 jars banana pepper rings</ingredient>
            <ingredient>Italian dressing</ingredient>
        </recipe>
        <recipe name="Plain Duck" difficulty="Easy"/>
        <feature name="Feet" value="Webbed" color="Yellow"/>
        <feature name="Tasty" value="true"/>
    </animal>
    
    ...

</kingdom>

以下模板代码段将允许您遍历您的食谱

<%- animals.each do |animal| -%>
    <%- animal.recipes.each do |recipe| -%>

        ...

    <%- end -%>
<%- end -%>

如果有足够的兴趣,我可以组合一个更实用的解决方案,演示模式验证、可空数据类型、多种语言、继承、持久性和构建集成。如有任何问题或意见,请随时发送电子邮件至 baker.alex@gmail.com。

有用的链接

© . All rights reserved.