2006 年 10 月 30 日
Ruby on Rails 是一種基于 Ruby 編程語言的高效的 Web 開發環境。Streamlined 是基于 Ruby on Rails 的一個快速發展的開放源碼框架。Streamlined 綜合了 Ajax、元編程、代碼生成以及 Ruby on Rails 的強大功能,把 Rails 的生產力帶到了一個新層次。
我生平首次參加馬拉松培訓。馬拉松培訓最有趣的方面——實際上,也是惟一的方面——就是提高不斷疊加所帶來的影響。有時,我為了提高效率而進行專門為了改進身體條件而設計的長短跑。有時,在跑步過程中,我學習避免小的錯誤,避免重復多余的姿勢(多余的姿勢對單個步幅沒有太大影響,但卻會在整個 26.2 英里的跑步過程中浪費能量或傷害到我)。我每周都有提高,可每周之間的區別并不顯著。但是一個訓練計劃周期過后,我會從最初只能跑 4 英里提高到能跑 26.2 英里。軟件開發也與此類似。如果持續進行小的改進,消除多余的重復,您就會不斷地累積提高,從而在今后的每個項目中都會做得更好。
 |
關于本系列
在 跨越邊界 系列中,作者 Bruce Tate 提出這樣一個觀點:當今的 Java 程序員通過學習其他方法和語言會得到其他思路。自從 Java 明顯成為所有開發項目的最佳選擇以來,編程前景已經改變。其他的框架正在影響搭建 Java 框架的方式,從其他語言學到的概念可以影響您的 Java 編程。您編寫的 Python(Ruby、Smalltalk……)代碼可以改變您處理 Java 編碼的方式。
本系列介紹了與 Java 開發有根本不同但是卻直接適用的編程概念和技術。在某些情況下,需要集成這些技術以利用它。在其他情況下,則可以直接應用這些概念。比起其他語言和框架能夠影響 Java 社區的開發人員、框架甚至基本方法這一概念,單獨的工具不是那么重要。
|
|
在這篇包含兩部分的文章中,我把重點放在 Ruby on Rails 搭建上,這是一個能夠在早期開發階段削減重復的 Rails 特性。第 1 部分介紹 Rails 搭建的限制和 Streamlined,Streamlined 是個代碼生成器,它高效地應用了元編程技術來消除更高層次上的重復。第 2 部分將進一步深入 Streamlined 的元編程模型及其定制特性。
低級重復與高級重復
在整個 跨越邊界 系列中,我介紹了通過降低重復和提高效率實現反復改進的語言和框架:
- 具備諸如 duck typing 這類特性的編程語言,通過使用更少的類型定義、減少純粹為了支持編譯器所需要的代碼數,能夠提高靈活性和減少重復。
- 框架試圖通過處理核心任務(例如持久性或事務)來提高效率和消除重復,這樣就不必為每個新的應用程序編寫代碼。
- Ruby on Rails 通過利用公共規范消除重復配置,允許框架推斷您的意圖,而不是強迫您配置應用程序特性(例如應用程序中特定的數據庫表名稱和列名稱)。
就像所有高效的語言和框架必須做的那樣,這些措施都把重點放在每個步驟上,或低級重復上。但是一旦搭建了一個有效的基礎,就可以把目標放得更高。Rails 的搭建特性試圖通過公共應用程序類型(數據庫支持的 Web 應用程序)消除重復。
 |
削減贅負
Rails 所做的削減數量驚人。重復配置、代碼中重新闡述的規范,以及其他框架中的無效理念,在這個框架中都消失了。但是仍然存在大量的重復。對于所有的框架來說都是如此。請記住,日本汽車制造商不是靠造一輛車就威脅了奔馳和寶馬公司的,而是通過不懈的改進。如果您為了搭建傳統 Web 應用程序而在框架中尋找改進,那么仍然會找到大量可以削減的內容。
|
|
多數數據庫支持的 Web 應用程序,幾乎要為系統中每個主要的表都提供執行 CRUD 操作(創建、讀取、更新和刪除)的用戶界面。 搭建這些用戶界面應當自動進行,而不應當重復。 Rails 通過 搭建開始消除這種重復,搭建是一個特性,可以根據數據庫表集合的內容構建默認的 CRUD 界面。使用 Rails,只用幾個簡單步驟,就可以從頭開始構建一個搭建完整的應用程序。如果一直跟隨 跨越邊界 系列,那么以前就看過這些步驟。這次,我再把這些步驟簡要介紹一下:
- 輸入
rails trails 生成編排山地摩托車賽道的 Rails 應用程序。
- 用選中的數據庫引擎創建叫作
trails_development 的數據庫,并修改 trails/config/database.yml,以反映選中的配置。
- 切換到 trails 目錄,,生成模型和控制器:輸入
ruby script/generate model Trail (如果在 UNIX 上運行,可以省略 ruby ) 生成叫作 Trail 的模型,輸入 script/generate controller Trails 生成叫作 TrailsController 的控制器。
- 把文件 db/migrate/001_create_trail.rb 編輯成清單 1 那樣:
清單 1. 初始遷移
class CreateTrails < ActiveRecord::Migration
def self.up
create_table :trails do |t|
t.column :name, :string
t.column :difficulty, :string
t.column :description, :text
end
end
def self.down
drop_table :trails
end
end
|
- 把文件 app/controllers/trails_controller.rb 編輯成像清單 2 一樣:
清單 2. TrailsController 中的搭建
class TrailsController < ApplicationController
scaffold :trail
end
|
- 輸入
rake migrate ,運行遷移。
- 用命令
script/server 啟動服務器,并把瀏覽器指到 localhost:3000/trails/list。
現在就已經得到了一個簡單的能夠工作的帶有數據庫支持的 Web 應用程序,可以進行基于 CRUD 的每個操作,如圖 1 所示??梢钥吹街髌聊涣谐隽嗣總€項目和相關的圖片,提供了 Ajax 窗口用來創建、讀取、更新和刪除項目。
圖 1. 簡單的 Rails 應用程序
到現在,只付出了很少努力,就到達了一個可以把應用程序開發帶到更高檔次的地步。Rails 演示人員總會展示搭建功能,因為它是如此之炫,而且對于調試和在匆忙之間為客戶做演示,都極為有用。可以通過代碼生成器生成搭建 —— 在這個示例中輸入了 script/generate scaffold Trail Trails —— 或者在控制器中指定 scaffold 元編程標記。每種方式都有自己的用途。
添加關系
搭建確實有一些明顯限制:它不處理關系,也沒有利用優秀的 Rails Web 服務或 Ajax 支持。為了說明這些限制,要創建帶有模型、視圖和控制器的 Location 。Location 與 Trail 之間存在一對多關系。搭建并不能協助該關系的管理。
創建 location 的模型(script/generate model Location )和控制器(script/generate controller Location Locations )。就像對 TrailsController 所做的那樣,把 scaffold :location 添加到 location_controller.rb。要把 Location 和 Trail 編織在一起,兩者間需要多對一關系,所以把 belongs_to :location 添加到 Trail ,把 has_many :trails 添加到 Location ,如清單 3 所示:
清單 3. trail.rb 和 location.rb 間的關系
class Trail < ActiveRecord::Base
belongs_to :location
end
class Location < ActiveRecord::Base
has_many :trails
end
|
把 db/migrate/002_create_locations.rb 編輯成清單 4 那樣:
清單 4. locations 表的遷移
class CreateLocations < ActiveRecord::Migration
def self.up
create_table :locations do |t|
t.column :city, :string
t.column :state, :string
end
add_column "trails", "location_id", :integer
end
def self.down
drop_table :locations
remove_column "trails", "location_id"
end
end
|
輸入 rake migrate 運行遷移。(要查看關于遷移的更多內容,請參閱 跨越邊界: Rails 遷移。)
一下子就有了這么多設置?,F在可以深吸一口氣,總結以下到目前為止構建的內容:
- 有了一個針對賽道的數據庫表和另一個針對地點的數據庫表。
- 有了 Ruby 模型對象,對象之間還有 Rails 關系。
- 模型現在在賽道和地點之間有多對一關系。
- 有了處理模式中的變化的策略,也可以收回目前為止兩個主要模式變化中的任何一個。
- 有了原始用戶界面。
雖然可能想添加一些驗證,但模型對象是適合生產應用的第一級 Rails 對象。許多 Rails 模型對象之所以簡單,是因為屬性都是用元編程動態添加的。為了演示現在的關系,通過控制臺添加一些數據。輸入 script/console 啟動控制臺,并輸入清單 5 中的命令:
清單 5. 把數據添加到賽道和地點
>> trail = Trail.new
=> #<Trail:0x2446168 @attributes={"name"=>nil, "location_id"=>nil,
"description"=>nil, "difficulty"=>nil}, @new_record=true>
>> trail.name = "Hermosa Creek"
=> "Hermosa Creek"
>> trail.difficulty = "easy"
=> "easy"
>> trail.description = "22 miles of mostly downhill singletrack."
=> "22 miles of mostly downhill singletrack."
>> trail.save
=> true
>> location = Location.new
=> #<Location:0x240d1c4 @attributes={"city"=>nil, "state"=>nil}, @new_record=true>
>> location.city = "Durango"
=> "Durango"
>> location.state = "Co"
=> "Co"
>> location.trails << trail
=> [#<Trail:0x2446168 @errors=#<ActiveRecord::Errors:0x2411c9c @errors={},
@base=#<Trail:0x2446168 ...>>, @attributes={"name"=>"Hermosa Creek", "id"=>1,
"location_id"=>nil, "description"=>"22 miles of mostly downhill singletrack.",
"difficulty"=>"easy"}, @new_record=false>]
>> location.save
=> true
>> hc = Trail.find 1
=> #<Trail:0x147c588 @attributes={"name"=>"Hermosa Creek", "location_id"=>"1",
"id"=>"1", "description"=>"22 miles of mostly downhill singletrack.",
"difficulty"=>"easy"}>
>> hc.location
=> #<Location:0x6cc2f8 @attributes={"city"=>"Durango", "id"=>"1", "state"=>"Co"}
|
清單 5 向數據庫添加了一條賽道和一個地點,由從 trails 中的 location_id 列指向 locations 中的 id 列的外鍵管理。模型對象足夠健壯,可以作為應用程序的構建基礎。但是,視圖就是另一回事了。
關系問題
把瀏覽器指向 http://localhost:3000/trails/show/1,看到圖 2 所示的屏幕:
圖 2. Rails 搭建沒有關系字段
在這里看不出 trail 和 location 之間的關系。還會注意到,搭建非常原始:它沒有圖片、沒有 Ajax、沒有公共標頭或側欄,也沒有任何現代 Web 頁面中常見的修飾。但重要的是通過 搭建,只花了幾分鐘就得到了一個相對復雜的應用程序。您可能并不指望這個簡單特性能夠生成健壯的代碼,但是現在您可以把您的期望值抬高一點。
雖然搭建代表著對多數 Web 開發框架技術水平的顯著提高,可它仍然有提高的余地,也應當如此。但是如果在此基礎上構建,您會發現獲益極多。這就像是從 13 英里開始馬拉松訓練,而不是從 4 英里開始。
搭建,像許多元編程技術一樣,就是個運行時代碼生成器。Rails 社區中的有些人認為搭建是有局限的,認為搭建還沒有豐富到可以處理多數應用程序。其他人則認為搭建很好用,搭建的質量才是基本問題。這完全取決于應用程序的性質。如果正在構建一個重復的模式,那么會從構成搭建基礎的元編程技術得到巨大收獲。如果模板是充分可調整、充分豐富的,那么在框架中就能在更高層次上減少重復?,F在開始介紹 Streamlined。
Streamlined:強化的搭建
自從 Rails 出現以來,各種形式的和各種大小的 Rails 插件一直在提升所有應用程序開發的抽象程度。像登錄生成器這樣的組件允許生成安全性。其他插件使得在 Rails 中處理 Web 服務更容易。Streamlined 以其產品級質量的應用程序生成器超越了搭建。與使用搭建時一樣,您可能需要擴展生成的代碼,但初始的應用程序從它本身來說,其功能性令人驚訝。
 |
Streamlined 由 Relevance LLC 創建,脫胎于進行自動培訓(叫作 Code Site )的一組開源工具,而且每次 Relevance 構建商業 Rails 應用程序時,它都會得到改進。最終,此團隊生成了通用的 Streamlined 框架,可以解決跨越眾多項目的公共問題。
|
|
請下載初始 alpha 版本的 Streamlined .gem 文件(參閱 參考資料)。切換到保存 .gem 的目錄,并輸入 gem install streamlined 。所需要的所有內容都會自動安裝。如果出現問題,可以通過 streamlined 的博客得到優秀的支持,也有商業支持可選。
現在是把 Streamlined 投入實踐的時候了。首先,輸入 script generate streamlined location trail ,運行 Streamlined 生成器。當提示是否替換 locations 和 trails 控制器時,回答 y 。
把瀏覽器指向 http://localhost:3000/locations/list 查看圖 3 中的結果:
圖 3. 默認的 Streamlined 應用程序
可以立即讓 Streamlined 生成一個更完整的應用程序。把 Streamlined 列表與 圖 1 中的列表比較。區別是驚人的:
- 默認應用程序處理關系,單擊其中一個 Edit 鏈接就可以看到。在下一節會看到更多關于關系的內容,在這篇文章的第 2 部分中甚至會更多。
- 應用程序更好地運用樣式表,并生成更復雜的樣式表。 Streamlined 運用各種技術,例如在表格周圍使用
<div>s ,使得每個頁面元素更容易進行樣式處理。Streamlined 的 alpha 版本的樣式處理有限,但是預期未來的版本會突破這個限制。
- 應用程序在左側有默認的導航側欄,在頂部有菜單和標頭。這些菜單有更完整的默認行為,而且能夠定制。
- 表格每一行都有代表編輯、顯示或刪除行的圖片,應用程序還有額外的圖片代表創建新條目、導出 CSV 以及把整個表導出為 XML。
這個頁面看起來更像默認應用程序,而不太像不完整的搭建。這正是 Streamlined 的亮點。在深入之前,先對 Streamlined 的工作方式做個簡單描述。
先決條件
要使用 Streamlined,先要有一個可以工作的數據庫模式、一個使用經典 Rails 工具和規范(在這篇文章中已經見到)的模型。然后,用 script/generate streamlined model1, model2, 等等命令生成 Streamlined 界面。Streamlined 觀察 Rails 的命名規范,并在處于開發模式時頻繁地重新裝入應用程序對象,這樣只要刷新瀏覽器,就可以看到最新的代碼變化。
像 Rails 搭建一樣,Streamlined 是個元編程框架,用元數據構建默認應用程序,構建的程序可以用各種方式定制??蚣懿樵儍蓚€元數據源:活動記錄模型和每個模型對象的定制元數據文件。默認情況下,Rails 從活動記錄內捕獲到足夠的元數據,構建復雜的用戶界面?;顒佑涗洸樵償祿毂?,獲得表中數據之外的信息,并且維護您所提供的其他信息,例如主鍵、關系、字段、字段類型、字段大小。Streamlined 利用所有這些信息來提供默認應用程序,但是要調整應用程序,框架還需要更多數據。Streamlined 提供了額外的元數據來源。
快速查看 trails/app 下的目錄,可以看到 Rails 的常見目錄:models、controllers、views 和 helpers。但是還有第五個目錄可用:streamlined。就是在這里指定額外的元數據。streamlined 目錄中四個文件快速列表說明了問題:
- location.rb 和 trail.rb 包含同名模型的詳細定制信息。
- streamlined_relationships.rb 包含活動記錄中指定的關系的更多信息,例如 Streamlined 要如何呈現關系。
- streamlined_ui.rb 包含全局用戶界面問題的配置信息,例如是否創建頭、尾以及左側導航欄。
Streamlined 立刻組合了代碼生成(它生成可以修改的代碼)和真正的元編程(它使用 Ruby 語言在運行時把代碼動態地添加到應用程序)開始工作。Streamlined 生成日后可能要修改的靜態內容和頁面。例如,生成器直接把樣式表和圖片復制到您的項目??梢杂谜嬲脑幊袒虼a生成來創建視圖,視圖可能需要修改,也可能不需要修改。
特性
通過操作這個默認應用程序,可以對它提供了多少特性有些感覺。左側的導航側欄擁有針對每個所指定模型的鏈接——針對本文的模型就是賽道和地點。單擊鏈接,會進入每個模型的主頁面。標頭有管理域內對象的一套默認鏈接,有上下文敏感幫助,還有關于頁面。
在進入表格數據區時,會看到更為復雜的功能。有充當記錄過濾器的文本框。要查看它的工作方式,請單擊 + 鏈接添加新賽道,并輸入一些數據。然后,在主窗口輸入 Her 。將看到列表被調整成只有字段中包含指定文本的條目。也可以單擊任意一個列名,根據這一列對列進行排序。
繼續操作下去,肯定會注意到優秀的 Ajax 功能。在這里的 CRUD 設置中使用 Ajax 的最大好處是在一個主屏幕上就能提供管理表所需要的全部內容,只有很少的彈出框(用來編輯、顯示和刪除)。Ajax 支持更豐富的用戶體驗、更簡潔的應用程序路徑和更好的用戶反饋。
最后看看關系管理。請單擊左側側欄上的 Locations 鏈接。然后單擊 + 圖片,添加新地點(試著添加 Moab,Utah)。單擊賽道下的 Edit,并選擇應當屬于這個地點的賽道。請注意 Streamlined 默認記錄了屬于每個地點的賽道的數量。這個默認行為已經非常豐富了,但是我在第 2 部分還要用更復雜的優化對它進行定制。
與 Java 框架比較
目前為止,最流行的 Java? 框架都不生成 搭建,更不用說應用程序了。部分原因是在這個領域在根本上缺少驅動創新的競爭。Ruby on Rails 正在改變這種局面。而且,可以假設,在 Web 框架發展了八年之后,應當有人已經構建出了類似的東西。
應用程序生成器在 Java 環境中一直沒有成功。它們有一個重要的問題:過多地依賴代碼生成器,但在元模型上,卻缺乏能夠對代碼生成進行補充的堅實的元編程框架。這類框架可以提供短期的生產力提升,但是不能在長時間內持續改進。生成的代碼通常太脆弱和復雜。除非有足夠的能力在每次代碼生成之間定制代碼,否則時間一長就會失去生產力。Streamlined 確實支持代碼生成,但只支持應用程序中不變的那些部分,或者應用程序中簡單的可變部分——例如視圖和樣式表,而這些內容開發人員可以容易地修改和維護。
有兩個看起來想正確地混合代碼生成和元數據的 Java 框架,它們是 RIFE 和 JMatter(請參閱 參考資料)。我在這個系列中已經多次討論過 RIFE,但是 JMatter 是新的。JMatter 框架擁有開源許可,也有商業報價。JMatter 基于 Hibernate 和 Swing,它允許根據元編程模型迅速地開發非常復雜的應用程序。Eitan Suez 這位 Java 圈中著名的發言人構建了 JMatter,以幫助快速地啟動一項針對醫療實踐的兩層客戶/服務器應用程序的 Java 開發。在將近兩年的特化之后,JMatter 驚人地強壯,而且它的特性很容易與 Rails 和 Streamlined 對抗。如果 Jmatter 中的變化步伐能趕上 Ruby 社區的技術水平,那么它今后還會存在。
結束語
在這篇文章中,我介紹了 Rails 搭建、它的限制以及稱作 Streamlined 的替代品。Streamlined 搭建得更完整,但到目前為止,它仍然還是搭建。在第 2 部分中,您將獲得圍繞 Streamlined 的元編程模型的更詳細討論,還將學習如何定制應用程序的關鍵部分。在這之前,您可以放飛思維、大量實踐,繼續跨越邊界。
參考資料
學習
獲得產品和技術
- Streamlined:下載 Streamlined 應用程序生成器并試用。Streamlined 正在快速發展,所以要下載初始的 alpha 版本。
- RIFE:基于 Java 的元編程框架,它通過強大的元模型為基于 CRUD 的應用程序提供了非常好的搭建。
- JMatter:下載新的 JMatter 框架,這是本文作者所看到的針對基于 Swing 和 Hibernate 的兩層的應用程序的最佳框架。
- 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 的輕量級開發策略和架構。
|
|