跨越边界: Streamlined,第 2 部分

探索 Streamlined 的元模型和定制策略

developerWorks
文档选项
将此页作为电子邮件发送

将此页作为电子邮件发送



级别: 中级

Bruce Tate (bruce.tate@j2life.com), 总裁, J2Life, LLC

2006 年 11 月 16 日

本系列文章包括两部分,第 1 部分 介绍了 Streamlined,这是个基于 Rails 的开放源码框架,它组合了 Ajax、元编程以及代码生成的强大功能,把 Rails 的效率提升到了一个新的层次。第 2 部分将探索 Streamlined 背后的元模型是如何支持定制的。

本系列文章包括两部分,在 第 1 部分 中,我展示了一种称为搭建的 Ruby on Rails 特性,介绍了它的限制,然后演示了称为 Streamlined 的框架如何可以把搭建提升到构建功能性用户界面这一层次。但是即使使用最高级的生成器,用户界面也总是需要些定制。如果不能有效地扩展所构建的内容,那么构建得再快,也不会拥有有效的应用程序开发框架。这篇文章将介绍 Streamlined 背后的定制模型的基础:Streamlined 元模型。

Streamlined 的快速回顾

关于本系列

跨越边界 系列中,,作者 Bruce Tate 宣扬了这样一种观点:当今的 Java 程序员通过学习其他方法和语言会得到其他思路。自从 Java 明显成为所有开发项目的最佳选择以来,编程前景已经改变。其他的框架正在影响搭建 Java 框架的方式,从其他语言学到的概念可以影响您的 Java 编程。您编写的 Python(Ruby、Smalltalk……)代码可以改变您处理 Java 编码的方式。

本系列介绍了与 Java 开发有根本不同但是却直接适用的编程概念和技术。在某些情况下,需要集成这些技术以利用它。在其他情况下,则可以直接应用这些概念。比起其他语言和框架能够影响 Java 社区的开发人员、框架甚至基本方法这一概念,单独的工具不是那么重要。

第 1 部分 中,我构建了一个简单的 Rails 应用程序,存储了一些进行山地单车运动的 trail 并将它们按其临近的城市加以组织。我开始时创建了一个模型,包含两个模型对象和它们之间的关系。然后运行 Streamlined 应用程序生成器,它生成了一个健壮的应用程序,生成的应用程序有以下特性:

  • 完整的用户界面,包含布局,带有链接指向每个模型的管理页面。
  • 每个模型有一个列表页面,可以按任意列排序或根据所有列的内容进行过滤。
  • 支持 Ajax 的列表,可以显示、编辑、删除或创建条目。
  • 导出程序可以生成视图的 XML 或 CSV 导出。

随着年轻的 Streamlined 框架的发展,对于基础应用程序模板所做的增强将可以用于所有 Streamlined 应用程序。目前已经策划的增强包括:基于 REST 和 SOAP 的 Web 服务,以及称为 皮肤 的样式模板。

定制默认应用程序

现在要定制 Trails 应用程序的代码。如果想和我一起编写代码,需要安装 Ruby、Rails 和 Streamlined (参阅 参考资料)并构建 第 1 部分 中的应用程序。将在这个基础上进行添加和改动来定制 Trails 应用程序。

如果还没这样做过,请通过输入 script/server 从项目目录中启动 Web 服务器(在 Windows 上是 ruby script/server)。把浏览器指向 http://localhost:3000/Trails/list 这个 URL,启动 Trails 视图。图 1 显示了基本的 Streamlined 视图。如果您已经添加了数据,那么会注意到与 Rails 搭建不同,Streamlined 替您管理了关系。还会注意到关系依赖 id 字段。


图 1. 默认 Streamlined 应用程序
默认 Streamlined 应用程序




回页首


Streamlined 定制策略

这个实现可以工作,但对用户来说不是最好的实现。用户希望看到赛道位于的城市的名称,而不仅仅是个 ID。实际上,这个应用程序的用户永远不需要看到 ID。在我解决这个问题之前,我要先介绍 Streamlined 的定制策略。

有些应用程序生成框架会强迫用户在这个时候修改生成的代码。Rails 搭建当然可以这样工作,但这仅仅是扩展生成的应用程序的三种方法之一:

  • 可以修改生成的代码。
  • 可以用编程方案(例如继承)和定制挂钩(例如 Visual Basic 脚本)编写扩展应用程序的新代码。
  • 可以改进可用的元数据,这样框架就可以生成更智能的应用程序。

这些方法各有各的用途。随着代码复杂性的提高,第一个策略变得越来越无用,因为用开发人员不可能编写复杂代码,这会加大开发人员的负担。应当把这个策略局限在扩展非常简单的代码上。Streamlined 通过添加新运作,使得可以容易地修改现有控制器。

第二种方法 —— 用新的定制代码扩展应用程序 —— 是多数快速应用程序开发环境采用的常用方法。一般来说,当框架设计者考虑添加定制的空间时,这个策略工作得最好。Rails 与 Ruby 的动态特性结合,提供了许多开发人员意想不到的定制点,使这个策略特别吸引人。在 Streamlined 中,经常会创建应用程序视图的新模板。也可以利用通用的 Rails 架构,在自己的横切关注点上(例如安全性)展开。

第三种方法,是改进的元模型,它是 Streamlined 的基础。在 第 1 部分 中,看到了搭建如何使用活动记录提供的元数据 —— 类型、列名称和关系 —— 来构建复杂的用户界面。在 Java 应用程序内,经常用 XML 配置实现这个策略。这当然是提供元数据的一种方法,但是要查看的数据太多,我担心有朝一日,我的眼睛会坏掉。





回页首


通过元模型修改显示内容

邮件合并元编程比喻

跨越边界 系列以前的文章中,已经看到过 Ruby 中的模板和元编程技术。请把 Ruby 模板想像成邮件合并文档。然后,Rails 使用元编程来生成和使用元模型(例如数据库模式或列定义),这好比邮件合并应用程序中的名称列表。Streamlined 引入了两段基本代码,模仿了这种方法。视图模板允许 Streamlined 不仅仅生成一个 “邮件合并” 文档,而且还会生成应用程序中的所有类似视图。Streamlined 通过活动记录、也通过定制元模型来利用元数据。这些部分与 Streamlined 生成器和库组合在一起,构成了一个很优秀的应用程序生成平台。

Streamlined 并不处理这个麻烦,而是支持通过元编程和模板的有效定制。要理解这个方法,请想像一下邮件合并应用程序。邮件合并应用程序得到名称列表和包含消息的模板。模板有些用来插入细节(例如名称、地址和电话号码)的定制位置。请把名称列表想像成用来定制模板的元数据。

Streamlined 就像个邮件合并 —— 只是它为每个模型对象使用元数据来创建整个应用程序,而不是邮件。要提供 视图模板 或使用 Streamlined 提供的默认视图模板。首先,要求 Streamlined 生成应用程序模板和所指定的每个模型类的元数据的占位器。接下来,通过元模型指定定制的用户界面选项,例如想让 Streamlined 如何显示关系。然后,告诉 Streamlined 如何呈现每个视图。Streamlined 把元模型中的数据应用到模板,把它们组合起来形成 所指定的每个模型的新视图,如图 2 所示:


图 2. Streamlined 元数据和模板
默认 Streamlined 应用程序

首先,我要介绍元模型。首先会看到元模板如何影响应用程序。然后,我要介绍如何用 Streamlined 模板定制应用程序。具体来说,请看 app/Streamlined/trail.rb 文件。将看到一个空白类。就是在这里可以定制 trails/list 视图的某些方面。请把这个文件编辑成如清单 1 一样:


清单 1. trail.rb 的 Streamlined 元模型
				
require File.dirname(__FILE__) + '/streamlined_ui'

class TrailUI < Streamlined::UI
  relationship :location, :fields => [:city, :state]
end

清单 1 代表 Trails 视图的元数据。稍后会看到,Streamlined 可以把这些值替换进视图模板,形成完整的用户界面屏幕。现在,重新装载浏览器。会看到刷新的视图,如图 3 所示:


图 3. 修订关系内容
默认 Streamlined 应用程序

请注意在关系表示中的变化。Location 列现在显示两个字段:城市和州。

Streamlined 的创造者们原本可以轻易地把定制数据添加到了活动记录。这个策略会把所有配置保持在同一位置。但是他们没有这样做,我非常感谢他们。他们的方法 —— 元数据在独立目录中 —— 有几个关键优势:

  • 独立元数据不会混合视图数据与模型数据。在模型和视图之间的清楚分离,对于降低耦合很重要,并使对视图的根本修改不会影响到模型,反之亦然。
  • streamlined 目录支持额外的元数据,而不会污染活动记录。
  • Streamlined 方法允许在一个地点合并常见视图特征,非常像活动记录为了持久性考虑而做的那样。

迄今为止,我已经修改了 Streamlined 在 Trails 视图中表示关系的方法。元编程框架经常需要支持一些外部决策,例如决定哪个列或内部字段对用户重要。默认情况下,Streamlined 排除了特殊的 Rails 字段,例如外键、主键、时间戳,而包含了其余字段。可以容易地用 user_columns 方法覆盖这些默认设置,这个方法中有 includeexclude 选项。例如,我需要做这样一个修改。Description 字段通常拥有文本的整个段落。通常,用户界面不会在这样的汇总列表中列出所有字段。我可以用元模型在列表视图中排除某些字段。方法是编辑 app/streamlined/trail.rb,加上排除 Description 列的代码,如清单 2 所示:


清单 2. 排除 description 列
				
require File.dirname(__FILE__) + '/streamlined_ui'

class TrailUI < Streamlined::UI
  relationship :location, :fields => [:city, :state]
  user_columns :exclude => [:description]
end

然后,重载浏览器,得到图 4 所示的结果:


图 4. 删除定制列
默认 Streamlined 应用程序




回页首


修改显示结构

Streamlined 更强大的方面之一就是其协助管理关系的方法。装入 http://localhost:3000/locations/list 的视图。进一步观看关系用户界面。会看到一个数字,它是与地点有关的赛道的数量。接下来,单击 location 列下的 Edit 链接。将看到一列复选框,如图 5 所示。这些都是数据库中的赛道,选中的代表它属于指定地点。这个窗口是关系的视图。


图 5. 默认关系视图
默认 streamlined 应用程序

Streamlined 框架提供了三种不同的关系视图:

  • 默认的 :membership 视图用复选框代表关系,如图 5 所示。选中的框表示成员。Streamlined 猜测要把哪个字段用于复选框,但是可以用叫作 view_fields 的选项覆盖它的猜测。

  • 图 6 中的 :inset_table 视图在内嵌表中显示相关字段。在这个示例中,可以立即在嵌入表中看到相关数据。



    图 6. :inset_table 关系视图
    默认 Streamlined 应用程序

    清单 3 显示了图 6 中视图的代码:



    清单 3. 关系的 Inset 表视图
    						
    require File.dirname(__FILE__) + '/streamlined_ui'
    
    class LocationUI < Streamlined::UI
      relationship :trails, :view => :inset_table
      
    end
    



  • 图 7 还显示了一个表格,它使用 :window 视图放置了一个按需弹出窗口而不是 inset:



    图 7. :window 关系视图
    默认 Streamlined 应用程序

    :window 关系视图的代码如清单 4 所示:



    清单 4. 关系的 Window 视图
    						
    require File.dirname(__FILE__) + '/streamlined_ui'
    
    class LocationUI < Streamlined::UI
      relationship :trails, :view => :window
      
    end
    

这些视图都支持 Ajax。当在成员视图中选中一个复选框时,视图发出 Ajax 调用,更新服务器上的相关模型。修改是即时的,立即会有用户反馈。Streamlined 有效地利用 Ajax 模糊了 Web 用户界面和富客户端之间的界线。完全支持 Ajax 的视图并不是元编程的典型特性,但是一旦有了良好的基础,就可以在不断提高的抽象层次上工作。

Streamlined 框架还允许您决定在关系总结字段中表示什么。在 图 3 中,会看到指定地点全部相关 Trails 的总结。也可以配置总结,显示合计、所有相关城市的字段或其他数学特性(例如它们的开放的平均数)。例如,可以指定 relationship :trails, :summary => :list, :fields => [:name, :difficulty],看到图 8 中的结果:


图 8. 总结列表
默认 Streamlined 应用程序

Ruby 的特性支持这个语法。总结行还是 relationship :trails, :summary => :list, :fields => [:name, :difficulty]。这个代码调用叫作 relationship 的方法,并传递进一些参数。可以更进一步,找出到底发生了什么。在 清单 4 中,看到了每个 Streamlined 配置都继承自 Streamlined::UIStreamlined 是个模块,UI 是这个模块中的类。)打开 app/streamlined/streamlined_ui.rb 文件,可以看到 relationship 的方法定义。在清单 5 中可以看到方法定义:


清单 5. StreamlinedUI.rb 文件中的关系方法
				
def relationship(rel, options = {})
  ensure_options_parity(options)
  initialize_relationships unless @relationships
  @relationships[rel] = {} unless @relationships[rel]
  @relationships[rel].merge!(options)
end

可以看到两个参数:reloptions。可能想看到更多参数,因为方法调用给出了针对 :trails:summary:fields 的参数。实际上,:trailsrel 参数,而 :summary => :list, :fields => [:name, difficulty] 是个名称-值对,代表 options 的单一哈希图。开始时,这个语法有点让人迷惑,但是它为 Ruby 提供了模拟已命名的可选参数的能力,而所需的语言的额外开支却很小。





回页首


扩展视图模板

迄今为止,我还不需要修改现有应用程序中的代码,但是 Streamlined 确实提供了可以让视图定制变得更容易的几种机制。可以容易地想像出需要在应用程序内修改一个视图甚至所有视图的场景。首先,我要介绍 Streamlined 如何处理单一视图。然后,我要展开讨论,包括进应用程序中的所有视图。

默认情况下,Streamlined 为应用程序中的每个模型创建一个视图,还有一个针对所有应用程序视图的视图模板。要覆盖单一视图,可以只修改这个实现。例如,用一个单词 Testing 代替 app/views/trails/list.rhtml 中的所有代码。然后,单击左侧栏中的 Trails。会看到图 9 中的视图。可以用这种方法定制系统中的任何单个视图。


图 9. 修改 Streamlined 视图
Caption

独立地修改视图是很好的,但是如果想获得元编程的全部威力,可能想用一个模板来构建应用程序中的全部视图。这种方法会提供最佳效果,因为是为整个应用程序编写一个模板。删除 app/views/trails 中的全部文件。这会在运行时要求 Streamlined 调用公共模板视图。Streamlined 覆盖叫作 render 的方法,以实现这个目的。停止和重启 Web 服务器(因为已经修改了底层结构)并重载浏览器。将看到 图 1 中的原始 Trails 视图恢复了。如果我能选择,我会通过用 no_views 选项调用 Streamlined 生成器,用一个视图模板生成我的初始应用程序。

可以在 app/views 中找到所有视图代码。侧栏、页头、页脚、菜单和公共文件都在 streamlined 目录中。管理关系的视图在 streamlined/relationships 中,部分主内容和主内容的视图在 streamlined/generic_views 中。

清单 6 显示了 Streamlined 的主模板 app/views/streamlined/generic_views/_list.rhtml 的一部分:


清单 6. Streamlined 模板的一部分(为了可读性添加了分行)
				
<% for item in @streamlined_items %>
<% alt = 1 - alt %>
   <tr <%= "class='odd'" if alt == 0 %>>
   <% for column in @model_ui.user_columns_for_display %>
      <td><%=h item.send(column.name) %></td>
   <% end %>

   <% for relationship in @model.reflect_on_all_associations %>
      <td>
         <div id="<%= relationship_div_id(relationship, item) %>">
         <%= render(:partial => "#{@model_ui.summary_def(relationship.name).partial}", 
         :locals=> {:item=> item, :relationship=> relationship, 
         :fields=> @model_ui.summary_def(relationship.name).fields})%>
         </div> 
         <%=link_to_function("Edit", "Streamlined.Relationships.open_relationship('#{
         relationship_div_id(relationship, item)}', this, 
         '/#{params[:controller]}')")%>
      </td>
   <% end %>
   
   ...deleted code to render links...
   <% end %>

请注意两个 for 循环。每个循环都反映活动记录和 Streamlined 的元数据。第一个循环在每个 Streamlined 列上迭代。Streamlined 通过从活动记录元数据中提取全部列、删除某些特定于 Rails 的列(例如 id)、排除或包含在 Streamlined 元数据中指定的每个列(如 清单 2 所示)来确定有哪个列要参与。第二个循环在活动目录模型中的所有关系上迭代,并呈现每个关系的适当部分。

我也可以组合各种方法。可以编写一个应用程序,用一个 Streamlined 模板(带站点范围内的编程约定)代表多数视图, 然后在我需要打破公共的站点范围约定时,再按照需要覆盖单个视图。这是一种极为强大的开发范式,可以让人从一个很好的默认实现开始,然后再在站点范围内或单一页面的基础上用定制实现来编辑或完全替换标准实现。





回页首


整体再回顾

Streamlined 是个带支持应用程序生成的富元模型的框架。元模型变成了更高层次的编程语言,提高了效率。在某些方式上,这样一个框架在 Rails 中是不可避免的,因为同样的元编程工具使得搭建可以用于 Rails 框架的所有用户。构建更有效的搭建系统只是个时间早晚的问题。

在 Streamlined 中,得到了这样一个框架,它组合了更高的语言抽象、代码生成和用来扩展的定制挂钩。公共的元模型驱动着每件事。使用这项技术,框架的重点主要放在用户界面上,但是可以看到它的前进已经超越了 Web 页面。Streamlined 已经或很快就会提供以下这些特性:

  • 用于内容联合的 Atom 提要:这会让 Web 上的其他应用程序可以利用由 Streamlined 应用程序生成的并符合公共标准的自动 XML 提要。
  • XML 和 CSV 导出:Streamlined 允许公共数据格式的导出。
  • 查询和过滤器:Streamlined 允许用简单查询过滤内容,然后使用结果。
  • 基于 REST 的 Web 服务:Streamlined 开始时有 Web 服务支持,但是后来清除掉了,因为 Rails 架构师正在重新设计 Web 服务支持,要将其改成基于简单 REST 方式的插件系统(请参阅 参考资料)。

Streamlined 正在努力在不远的将来成为一个元编程框架,允许您创建任意模板,插入现有 Rails 组件、生成器和插件。这个框架将超过外观感受,进而创建一个公共架构,有可能形成一个极为强大的企业应用程序生成器。Relevance, LLC 的 Justin Gehtland 说过: “Streamlined 的下几个发行版,意在巩固它作为元框架的位置。它将包含和重用标准 的 Rails 生成器、切实可用的插件和其他开源框架,从而迅速装配起整个应用程序,超出目前提供的 CRUD 支持。我们将可以用与以前同样的方法进行配置:依赖良好约定,但是提供用于修改默认项的声明式 DSL。”

带有优秀定制的更高抽象层次是任何语言(包括 Java 语言)的神圣目标。在下期的 跨越边界 文章中,我要研究延迟绑定的好处。在那之前,请继续寻找通过提升抽象来增强实力的方法,并请继续跨越边界。



参考资料

学习
  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文

  • Java To Ruby: Things Your Manager Should Know (Pragmatic Bookshelf,2006):这是本文作者编著的一本书,讲述何时何处从 Java 编程转变到 Ruby on Rails 以及如何完成这种转变。

  • Beyond Java (O'Reilly,2005):本文作者编写的一本书,讲述 Java 语言的提高和稳定发展,以及在某些方面能够挑战 Java 平台的技术。

  • Simply RESTful:提供了对 Rails REST Web 服务的下一个模型的概观。

  • Book review: Agile Web Development with Rails ”(Darren Torpey,developerWorks,2005 年 5 月):介绍了这样一本书,它可以加深读者对 Rails 及敏捷开发方式原理的理解。

  • Streamlined:访问 Streamlined 的 Web 站点和博客。

  • Rails API:Rails 框架文档是从内到外学习搭建和让其工作的元编程技术的最佳途径。参阅 scaffolding.rb 类获得更多细节。

  • Programming Ruby (Dave Thomas 等,Pragmatic Bookshelf,2005):关于 Ruby 编程的一本流行书。

  • Java 技术专区:数百篇 Java 编程各方面的文章。


获得产品和技术
  • Streamlined:下载 Streamlined 应用程序生成器并试用。Streamlined 正在快速发展,所以要下载初始的 alpha 版本。

  • Ruby on Rails:请下载开源的 Ruby on Rails Web 框架。

  • Ruby:从该项目的 Web 站点获得 Ruby。


讨论


关于作者

Bruce Tate 居住在得克萨斯州的首府奥斯汀,他是一位父亲,同时也是山地车手和皮艇手。他是 3 本最畅销 Java 书籍的作者,其中包括荣获 Jolt 大奖的 Better, Faster, Lighter Java 一书,最近又出版了 Spring: A Developer's Notebook 一书。他在 IBM 工作了 13 年,现在是 J2Life, LLC 的创始人兼顾问。他潜心研究基于 Java 和 Ruby 的轻量级开发策略和架构

::...
免责声明:
当前网页内容, 由 大妈 ZoomQuiet 使用工具: ScrapBook :: Firefox Extension 人工从互联网中收集并分享;
内容版权归原作者所有;
本人对内容的有效性/合法性不承担任何强制性责任.
若有不妥, 欢迎评注提醒:

或是邮件反馈可也:
askdama[AT]googlegroups.com


订阅 substack 体验古早写作:


点击注册~> 获得 100$ 体验券: DigitalOcean Referral Badge

关注公众号, 持续获得相关各种嗯哼:
zoomquiet


自怼圈/年度番新

DU22.4
关于 ~ DebugUself with DAMA ;-)
粤ICP备18025058号-1
公安备案号: 44049002000656 ...::