| Mike Perham (developerworks@perham.net), 高級軟件工程師, IBM
2007 年 7 月 09 日 節省點(diǎn)時(shí)間,少一些頭痛,使用 Ruby on Rails ActiveScaffold 插件可以創(chuàng )建更容易維護的頁(yè)面。ActiveScaffold 可以處理用戶(hù)接口所需的所有 CRUD(創(chuàng )建、讀取、更新和刪除)操作,這樣可以為您節省更多時(shí)間來(lái)重點(diǎn)關(guān)注更有挑戰(也更有趣的)問(wèn)題。 為復雜應用程序編寫(xiě)基于 Web 的數據輸入 UI 永遠都不是件快樂(lè )的事,通常都是非常單調乏味的。良好用戶(hù)界面的一個(gè)關(guān)鍵屬性是一致性,但是這需要一個(gè)博學(xué)勤勉的開(kāi)發(fā)團隊才能設計符合這種設計標準的 Web 頁(yè)面。與其他 Web 應用程序框架類(lèi)似,Ruby on Rails 也有相同的問(wèn)題。不過(guò),Ruby 語(yǔ)言的動(dòng)態(tài)特性提供了一個(gè)解決方案:ActiveScaffold。ActiveScaffold 是 Ruby on Rails (也稱(chēng)為 Rails)的一個(gè)插件,它可以動(dòng)態(tài)地生成基于模型的視圖。ActiveScaffold 不需要手工創(chuàng )建頁(yè)面來(lái)顯示模型,而是可以從內部審視 ActiveRecord 模型,并動(dòng)態(tài)地生成一個(gè) CRUD(創(chuàng )建、讀取、更新、刪除)用戶(hù)界面來(lái)管理這些對象。 本文是基于 ActiveScaffold、Ruby 和 Rails 的當前(撰寫(xiě)本文之時(shí))可用的最新版本來(lái)撰寫(xiě)的(鏈接和版本號請參看 參考資料)。另外,本文假設您非常熟悉 Ruby on Rails,并且正在使用 Linux? 或 Mac OS X 系統。Windows? 用戶(hù)應該修改本文中給出的命令來(lái)適合自己的環(huán)境(例如,將 ‘ruby’ 添加到腳本命令最前面)。 安裝 ActiveScaffold 由于 ActiveScaffold 是一個(gè) Rails 插件,可以從一個(gè)遠程 Web 或者 Subversion 服務(wù)器上安裝。下面的命令將從 ActiveScaffold Subversion 服務(wù)器中獲取 ActiveScaffold。 清單 1. 安裝 ActiveScaffold 插件 script/plugin install http://activescaffold.googlecode.com/svn/tags/active_scaffold | 注意這將獲取 ActiveScaffold 的當前發(fā)行版(即最新發(fā)行版)。撰寫(xiě)本文時(shí)使用的是 1.0 發(fā)行版,但是也可以使用將來(lái)的發(fā)行版:ActiveScaffold 開(kāi)發(fā)人員迄今為止一直很好地關(guān)注著(zhù)兼容性問(wèn)題。
模型 最現代的 Web 應用程序框架都基于 MVC(模型、視圖、控制器)模式,Rails 也不例外。模型表示數據庫中存儲的數據,每個(gè)表在 Ruby 中都有一個(gè)對應的 ActiveRecord 模型類(lèi)。在本文中,我們創(chuàng )建了一個(gè)簡(jiǎn)單的項目跟蹤應用程序,其中,組織擁有很多用戶(hù)和很多項目。下面的代碼顯示了 ActiveRecord 向應用程序和對應模型類(lèi)上遷移的過(guò)程。注意模型類(lèi)要比 Java 中相同的類(lèi)簡(jiǎn)單很多。這是 Rails 的 DRY(不要重復自己)原則的基本例子。由于遷移早已包含了列,為什么還要在模型類(lèi)中再次將它們列出來(lái)呢? 清單 2. 遷移 class AddOrganizations < ActiveRecord::Migration def self.up create_table :organizations do |t| t.column :name, :string, :limit => 50, :null => false end end def self.down drop_table :organizations end end class AddUsers < ActiveRecord::Migration def self.up create_table :users do |t| t.column :first_name, :string, :limit => 50, :null => false t.column :last_name, :string, :limit => 50, :null => false t.column :email, :string, :limit => 100, :null => false t.column :password_hash, :string, :limit => 64, :null => false t.column :organization_id, :integer, :null => false end add_index :users, :email, :unique => true end def self.down drop_table :users end end class AddProjects < ActiveRecord::Migration def self.up create_table :projects do |t| t.column :name, :string, :limit => 50, :null => false t.column :organization_id, :integer, :null => false end end def self.down drop_table :projects end end class AddProjectsUsers < ActiveRecord::Migration def self.up create_table :projects_users do |t| t.column :project_id, :integer, :null => false t.column :user_id, :integer, :null => false t.column :role_type, :integer, :null => false end end def self.down drop_table :projects_users end end |
清單 3. 模型 class User < ActiveRecord::Base belongs_to :organization end class Organization < ActiveRecord::Base has_many :projects has_many :users end class Project < ActiveRecord::Base belongs_to :organization has_many :projects_users has_many :administrators, :through => :projects_users, :source => :user, :conditions => "projects_users.role_type = 3" has_many :managers, :through => :projects_users, :source => :user, :conditions => "projects_users.role_type = 2" has_many :workers, :through => :projects_users, :source => :user, :conditions => "projects_users.role_type = 1" end class ProjectsUser < ActiveRecord::Base belongs_to :project belongs_to :user end | | | 插件和生成器的對比 Ruby on Rails 可以支持兩種 “開(kāi)發(fā)助手”。生成器 是靜態(tài)的 — 它們只會(huì )在生成代碼時(shí)運行一次。插件 是動(dòng)態(tài)的 — 它們可以作為應用程序運行時(shí)的一部分運行。例如,標準的 Rails scaffold 生成器就是運行一次來(lái)基于模型中的當前字段創(chuàng )建一個(gè)靜態(tài) HTML 模板。如果希望添加一列,就必須重新生成視圖(丟失所做的更改),或者將字段手工添加到視圖中。這將給模型更改增加不必要的復雜性。 插件會(huì )在運行時(shí)生成這些視圖,因此更改模型就不困難了。與生成器相比,插件并沒(méi)有什么實(shí)際的劣勢,但是可能會(huì )比使用生成器稍微復雜一點(diǎn)。 | | User、Organization 和 Project 表都代表域中的傳統實(shí)體,而 ProjectsUsers 表則會(huì )在 Project 和 User 實(shí)體之間增加一個(gè)多對多的關(guān)系。在本例中,它會(huì )添加一個(gè) role_type 屬性,它代表用戶(hù)在項目中所扮演的角色。用戶(hù)可能是工人、經(jīng)理和/或管理員。 在模型上創(chuàng )建任何用戶(hù)界面所需要的關(guān)鍵信息都是要理解模型之間的關(guān)系。通過(guò)在模型中聲明 has_many 和 belongs_to,就在它們之間定義了一種特定類(lèi)型的關(guān)系。一旦 ActiveScaffold 知道了這些關(guān)系,就可以提供一個(gè)用戶(hù)界面以一種用戶(hù)可以理解的方式對這些對象進(jìn)行操作。在這種情況下,ActiveScaffold 就可以確定某個(gè) Project 是由某個(gè) Organization 所有的,因此可以相應地調整用戶(hù)界面。如果您更改了這種關(guān)系,則用戶(hù)界面就可以相應地變化,無(wú)需開(kāi)發(fā)人員更改 UI。 邊注:由于 Rails 遷移框架中存在某種限制,使清單 2 中的遷移無(wú)法使用外鍵。為了確保數據一致性,強烈推薦使用這些外鍵。Redhill Consulting 提供了一個(gè)很好的 foreign_key_migrations 插件,它增加了在 Rail 數據庫遷移框架中對外鍵的支持;有關(guān)更多信息,請參看 參考資料 中的鏈接。
Rails scaffold 現在我們已經(jīng)充實(shí)了模型,接下來(lái)可以在上面放一個(gè) Web 界面。Rail 提供了一個(gè) “scaffold” 生成器,它可以為某個(gè)給定模型生成一組基本的 CRUD 頁(yè)面。下面的命令用來(lái)為這個(gè)模型創(chuàng )建標準的 Ruby scaffold:即一個(gè)具有一組 CRUD 方法和一組對應的模型 HTML 視圖的控件。 清單 4. 生成標準的 Rails scaffold script/generate scaffold user script/generate scaffold organization | scaffold 生成器有幾個(gè)重要的限制: - 沒(méi)有關(guān)系支持:創(chuàng )建模型實(shí)例就意味著(zhù)只能編輯實(shí)例的基本屬性。如果模型需要定義一個(gè)關(guān)系(例如,Project 需要選中所有的 Organization),就需要手工修改頁(yè)面將這個(gè)域添加到窗體上。
- 不能往返:對于模型反復進(jìn)行的更改不支持 “往返” 操作,這是因為所生成的代碼是靜態(tài)的。一旦代碼被修改之后,就不能在不丟失所做更改的情況下重新生成 scaffold 了。
- 缺少樣式支持:所生成的頁(yè)面都是最基本的黑色和白色,只有最少量的 CSS 支持。不對基本的 HTML 標記使用樣式就不支持通過(guò) CSS 使用皮膚功能。
同時(shí)具有前兩個(gè)限制說(shuō)明 scaffold 實(shí)際上更像一個(gè)玩具,而不像一個(gè)有用的工具。圖 1 給出了 Rails 提供的默認 scaffold。 圖 1. 標準的 Rails scaffold Rails 還包括了 dynamic scaffold,它實(shí)際上提供了相同的代碼支持,而不需要提前生成控件代碼。這并沒(méi)有給您帶來(lái)太多好處 — 因為大部分代碼都位于 HTML 視圖中,而且仍然需要視圖代碼。通過(guò)將 scaffold 方法添加到控件類(lèi)中可以啟用動(dòng)態(tài) scaffold。 清單 5. 添加 Rails 的標準 scaffold class UsersController < ApplicationController scaffold :user end | 小心! 如果標準的 Rails scaffold 代碼與 ActiveScaffold 一起使用,就可能會(huì )出現問(wèn)題。在切換到 ActiveScaffold 之前,請確保您已經(jīng)清除了所有的 scaffold 控件和視圖代碼。
ActiveScaffold 默認顯示 ActiveScaffold 為模型提供了一個(gè)更加有用的 UI。scaffold 的上述 3 個(gè)問(wèn)題都已解決。首先,我們需要修改控件來(lái)使用 ActiveScaffold scaffold: 清單 6. 添加 ActiveScaffold scaffold class UsersController < ApplicationController active_scaffold :user layout "activescaffold" end | 然后為所有 ActiveScaffold 頁(yè)面添加標準布局(將下面的代碼放入 app/views/layouts/activescaffold.rhtml 中): 清單 7. ActiveScaffold 標準布局 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>My Application</title> <%= javascript_include_tag :defaults %> <%= active_scaffold_includes %> </head> <body> <%= yield %> </body> </html> | 現在用戶(hù)清單看起來(lái)就好多了: 圖 2. 標準的 ActiveScaffold scaffold 這個(gè)默認顯示可以很好地用于快速構建原型或開(kāi)發(fā)。然而,與所有默認情況類(lèi)似,稍加定制就可以使它更加適合于您的特定需求。
定制視圖 ActiveScaffold 有幾個(gè)鉤子函數可以讓您定制如何顯示模型??梢越o active_scaffold 方法傳遞一個(gè)用來(lái)配置 scaffold 的可選配置塊。 全局配置 ActiveScaffold 的全局配置允許對所有控件進(jìn)行定制: 清單 8. 全局配置 class ApplicationController < ActionController::Base AjaxScaffold.set_defaults do |conf| conf.list.results_per_page = 20 end end | 這個(gè)例子配置系統中所有的 ActiveScaffold scaffold 以便在顯示記錄時(shí)每頁(yè)顯示 20 個(gè)結果。 本地配置 每個(gè)控件 scaffold 都可以使用自己特有的 ActiveScaffold 配置。 清單 9. 特定控件的本地配置 class UsersController < ApplicationController active_scaffold :user do |conf| conf.modules.exclude :update conf.list.label = ‘People‘ conf.list.sorting = [{:last_name => :ASC}, {:first_name => :ASC}] conf.list.columns.exclude :password_hash end end | 這個(gè)例子就不能更新模型實(shí)例、更改列表標題和定制默認用戶(hù)列表排序了。sorting 讓您可以控制如何從數據庫中返回記錄,并期望得到一個(gè) {column => direction} 散列數組。還配置 ActiveScaffold 不顯示用戶(hù)不需要查看的特定列;在本例中,password_hash 列不需要在 UI 中顯示,因此就將其排除了。 顯示 ActiveRecord 對象 to_label 方法讓您可以定制模型實(shí)例在頁(yè)面中的顯示方式。默認情況下,ActiveScaffold 會(huì )查找模型中的一組方法: to_label name label title to_s 最后一個(gè)方法是由 ActiveRecord 提供的,會(huì )顯示成 “#:<Address:0xFFFFFF:>” 的形式,這對于用戶(hù)來(lái)說(shuō)不夠友好。下面是一個(gè)更好的方法: 清單 10. 定制模型的顯示 class User < ActiveRecord::Base belongs_to :organization def to_label first_name << ‘ ‘ << last_name end end | 例如,用戶(hù)的 to_label 可能是 John Doe。 定制屬性顯示 ActiveScaffold 允許開(kāi)發(fā)人員完全控制模型屬性的實(shí)際顯示方式。默認情況下,ActiveScaffold 只會(huì )對一些簡(jiǎn)單的屬性值調用 to_s,從而確定它們到 HTML 的順序。要對此進(jìn)行定制,只需要在 app/helpers/<model>_helper.rb 中將一個(gè)列顯示幫助方法添加到相應的幫助類(lèi)中即可。 清單 11. 定制屬性顯示 def birthdate_column(record) record.birthdate.strftime("%d %B %Y") end | 在上面的幫助方法中,您擁有記錄的全部訪(fǎng)問(wèn)權限。在本例中,這個(gè)幫助并不是很智能,因為它并不能說(shuō)明用戶(hù)所請求的現場(chǎng),確定日期格式就需要用到該現場(chǎng)。 對于 has_many 和 has_and_belongs_to_many 關(guān)聯(lián)來(lái)說(shuō),ActiveScaffold 會(huì )通過(guò)使用上面提到的 to_label 邏輯來(lái)渲染它們,從而顯示前 3 個(gè)條目。這 3 個(gè)條目會(huì )鏈接在一起,這樣在點(diǎn)擊時(shí),整個(gè)關(guān)聯(lián)就可以顯示出來(lái)了。這可以防止用戶(hù)界面被大型關(guān)聯(lián)集所覆蓋的情況。 窗體顯示 ActiveScaffold 也可以基于 Rails 的 ActiveRecord 和 ActiveView 庫為模型創(chuàng )建一個(gè)窗體。varchar 列會(huì )變成文本輸入,boolean 型變量會(huì )映射成 HTML 的復選框等等。 有一點(diǎn)需要注意:虛擬屬性(在模型中作為屬性定義,但卻不真正保存在數據庫中的屬性)的 HTML 渲染方式可能與普通的模型屬性不同。任何名字中包含 “password” 的普通模型屬性在 HTML 都會(huì )渲染為一個(gè)密碼輸入。不過(guò)對于虛擬屬性來(lái)說(shuō)卻并非如此,在使用虛擬屬性作為密碼窗體輸入時(shí),很容易發(fā)現這一點(diǎn)。在這種情況下,我們將使用虛擬屬性來(lái)捕獲窗體輸入,并在保存時(shí)將這些值映射到 password_hash 列中,這樣用戶(hù)的純文本輸入就可以作為一個(gè) SHA256 散列安全地保存到數據庫中。 清單 12. 在用戶(hù)模型中創(chuàng )建虛擬屬性 require ‘digest/sha2‘ class User < ActiveRecord::Base attr_accessor :password, :password_confirmation validates_presence_of :password, :password_confirmation def validate errors.add(‘password‘, ‘a(chǎn)nd confirmation do not match‘) unless password_confirmation == password end def before_save self.password_hash = Digest::SHA256.hexdigest(password) if password end end | 我們添加了兩個(gè) form_column 幫助方法將它們作為密碼輸入正確地進(jìn)行渲染。ActiveScaffold 期望使用 field_name 參數中給定的名稱(chēng)對輸入進(jìn)行 POST 處理。 清單 13. 定制虛擬屬性的窗體顯示 def password_form_column(record, field_name) password_field_tag field_name, record.password end def password_confirmation_form_column(record, field_name) password_field_tag field_name, record.password_confirmation end |
關(guān)系 到現在為止,我們只考慮了基本的模型操作,例如顯示或編輯簡(jiǎn)單的列值。ActiveScaffold 中最復雜的部分是確定模型之間的關(guān)系以及它們如何影響應用程序的用戶(hù)界面。要正確實(shí)現操作就幾乎無(wú)法避開(kāi)這一部分;本節將介紹如何配置 ActiveScaffold 來(lái)正確使用模型。 列表顯示 為了在模型之間進(jìn)行導航,ActiveScaffold 會(huì )在一個(gè)列表視圖中顯示關(guān)系鏈接。舉例來(lái)說(shuō),在查看一個(gè)組織列表時(shí),會(huì )看到一個(gè) Users 鏈接來(lái)顯示一個(gè)頁(yè)面,其中每個(gè) Users 都對應一個(gè)給定的 Organization。要定制此鏈接,需要為該列定義一個(gè)幫助方法: 清單 14. 定制關(guān)聯(lián)的顯示 def users_column(record) name = "user" name = "users" if record.users.size > 1 "<a href="/user/list?user_id=#{record.id}">#{record.users.size} #{name}</a>" end | 窗體顯示 ActiveScaffold 還提供了基于所定義的關(guān)系在模型之間進(jìn)行導航的功能。以 belongs_to 關(guān)系為例。在上面的例子中,User belongs_to 一個(gè)組織。這就意味著(zhù)一個(gè) User 在創(chuàng )建時(shí)必須具有一個(gè)相關(guān)的 Organization(如果 Organization 是可選的,那么您就應該使用一個(gè)可以為空值的 has_one 關(guān)系)。 ActiveScaffold 可以理解這種關(guān)系,并可以使用 “select” 語(yǔ)句從數據庫中顯示一個(gè) Organizations 列表,這樣用戶(hù)可以選出與正在創(chuàng )建的 User 關(guān)聯(lián)在一起的 Organization。 這對于只有 10 到 20 個(gè) Organization 的小型數據集而言可以很好地工作,但是卻不能擴展到具有大量的 Organization 的情況。您可以使用窗體列渲染程序來(lái)重寫(xiě)對列的渲染。下面給出了一個(gè)簡(jiǎn)單的例子,其中,您可以在開(kāi)發(fā)時(shí)獲悉可能的值: 清單 15. 使用靜態(tài)選擇列表 def organization_form_column(record, field_name) # simple example that just hard codes two possible values select_tag field_name, options_for_select(‘IBM‘ => ‘1‘, ‘Lenovo‘ => ‘2‘) end |
搜索記錄 ActiveScaffold 提供了一些有用的搜索功能來(lái)查找大型表中的記錄。默認情況下,scaffold 在上面的目錄表中有一個(gè) “Search” 鏈接,使用該鏈接可以打開(kāi)一個(gè)文本框,用戶(hù)可以在這個(gè)文本框中輸入搜索條件。ActiveScaffold 會(huì )創(chuàng )建一條 SQL 語(yǔ)句為模型搜索所有的 varchar 列,這樣輸入諸如 “ham” 之類(lèi)的條件就可以找到基于姓氏的用戶(hù)記錄了。與其他地方類(lèi)似,這里也有幾個(gè)配置選項。 實(shí)時(shí)搜索 當用戶(hù)按下 Return 時(shí),就會(huì )執行默認搜索。ActiveScaffold 可以通過(guò)啟用 “實(shí)時(shí)搜索” 選項來(lái)進(jìn)行實(shí)時(shí)搜索。這會(huì )基于用戶(hù)當前輸入每秒生成一個(gè) Ajax 調用。記住,實(shí)時(shí)搜索可能是數據庫密集型的。正如下面解釋的一樣,在使用這個(gè)特性之前,要確保您已經(jīng)配置了要搜索的列,并且已經(jīng)正確地創(chuàng )建了表索引。 清單 16. 在實(shí)時(shí)搜索和默認搜索之間進(jìn)行切換 ActiveScaffold.set_defaults do |conf| conf.actions.exclude :search conf.actions.add :live_search end | 調節可用性 清單 17. 調節 scaffold 的搜索配置 active_scaffold :user do |conf| conf.live_search.columns = [:last_name, :first_name] conf.live_search.full_text_search = false end | 這段代碼告訴 ActiveScaffold 要限制對這個(gè) scaffold 的搜索 —— 只允許使用用戶(hù)的姓和名進(jìn)行搜索,禁用全文搜索。后一個(gè)選項是用于大型表的伸縮性調節選項。如果用戶(hù)搜索 “ham”,默認情況下,ActiveScaffold 會(huì )生成一條 SQL 語(yǔ)句,其中包含以下內容:lower(column_name) LIKE "%ham%",無(wú)法為它編制索引。通過(guò)禁用全文搜索,告訴它使用 “以...開(kāi)始” 的語(yǔ)義:lower(column_name) LIKE "ham%"。這固然限制了搜索的靈活性,但得到了更好的伸縮性。
定制操作 除了標準的 CRUD 操作之外,ActiveScaffold 還可以讓您定義自己的控件操作。數據庫應用程序經(jīng)常需要將數據導出為 PDF、Excel、CSV 或 XML。添加此功能非常容易,首先我們可以為具有對應操作方法的控件增加一個(gè) “操作鏈接”: 清單 18. 定義定制操作 class UsersController < ApplicationController active_scaffold :user do |conf| conf.action_links.add ‘export_csv‘, :label => ‘Export to Excel‘, :page => true end def export_csv # find_page is how the List module gets its data. see Actions::List#do_list. records = find_page().items return if records.size == 0 # Note this code is very generic. We could move this method and the # action_link configuration into the ApplicationController and reuse it # for all our models. data = "" cls = records[0].class data << cls.csv_header << "\r\n" records.each do |inst| data << inst.to_csv << "\r\n" end send_data data, :type => ‘text/csv‘, :filename => cls.name.pluralize + ‘.csv‘ end end | 您可以通過(guò)將實(shí)際的模型知識封裝到模型中來(lái)保證代碼是面向對象的。 清單 19. 定制操作的對應模型方法 class User < ActiveRecord::Base ... # The header line lists the attribute names. ID is quoted to work # around an issue with Excel and CSV files that start with "ID". def self.csv_header ""ID",Last Name,First Name,Email,Birthdate" end # Emit our attribute values as a line of CSVs def to_csv id.to_s << "," << last_name << "," << first_name << "," << email << "," << birthdate.to_s end end |
本地化 軟件要想在全球得到廣泛使用,一個(gè)關(guān)鍵特性是它需要能夠以用戶(hù)的本地語(yǔ)言進(jìn)行操作。Ruby 和 Rails 并沒(méi)有提供標準的 API 來(lái)處理 locale,因此它的集成比其他方法的集成更加困難(比如 Java 集成)。ActiveScaffold 小組決定將所有的本地化工作推遲給應用程序執行,這是通過(guò)一個(gè)簡(jiǎn)單的查找鉤子 Object::as_ 方法實(shí)現的,它會(huì )嵌入到您喜歡的 Ruby 本地化插件中。在本例中,代碼顯示了如何將方法參數傳遞給 Globalize 插件(請參看 參考資料 中的鏈接)所提供的 _ 方法(是的,這個(gè)方法的名字就是 “_”)。 清單 20. 對 ActiveScaffold 進(jìn)行本地化 # Put this at the bottom of your app/controllers/application.rb file class Object def as_(string, *args) # Use Globalize‘s _ method to provide the actual lookup of the string. _(string, ` *args) end end | Globalize 現在可以為傳入這個(gè)方法的所有字符串提供本地化翻譯。
定制 ActiveScaffold 的樣式 ActiveScaffold 提供了一組非常豐富的 CSS 樣式,可以對標準的 UI 進(jìn)行調整以便提供定制的外觀(guān)。您可以通過(guò)創(chuàng )建并重寫(xiě) CSS 文件并將其包含到標準的 CSS 包含文件之后,從而重寫(xiě)默認樣式來(lái)匹配站點(diǎn)的顏色方案、字體等設置。在本例中,我們包含了一個(gè)名為 public/stylesheets/as_overrides.css 的文件: 清單 21. 重寫(xiě)默認的 ActiveScaffold 樣式 <head> <title>My Application</title> <%= javascript_include_tag :defaults %> <%= active_scaffold_includes %> <%= stylesheet_include_tag "as_overrides" %> </head> | 標準的 ActiveScaffold 樣式表位于 vendor/plugins/active_scaffold/frontends/default/stylesheets/stylesheet.css 中。
安全性 ActiveScaffold 提供了一個(gè)身份驗證 API 來(lái)確保數據安全性。第一級是對控件進(jìn)行的粗粒度的安全性控制,這并不是記錄特有的。在控件上,可以定義 #{action}_authorized? 方法,其中 #{action} 是一個(gè) ActiveScaffold 操作:create、list、 search、 show、 update 或 delete。 清單 22. 基于控件的安全性 class ProjectsController < ApplicationController active_scaffold :project do |conf| # Needed to inject the current_user method into the model config.security.current_user_method = :current_user end protected # only authenticated admin users are authorized to create projects def create_authorized? user = current_user !user.nil? && user.is_admin? end def current_user @session[:user_id] ? User.find(@session[:user_id]) : nil end end | 第二級安全性讓您可以創(chuàng )建更加復雜的數據特有的邏輯。舉例來(lái)說(shuō),由于 Projects belongs_to Organizations,因此對項目的編輯進(jìn)行限制,使得只有擁有項目的組織的管理員才能執行編輯操作,這是合理的。為此,將方法添加到模型(比如 authorized_for_#{crud_action})中,其中 #{crud_action} 是 create、read、 update 或 destroy 之一。 清單 23. 基于模型的安全性 class Project < ActiveRecord::Base belongs_to :organization # Since projects are owned by an organization, allow only administrators # of that organization to edit the project def authorized_for_update? organization.is_admin? current_user end end | 注意 current_user 方法是可用的,因為 ActiveScaffold 根據對應控件的 current_user_method 配置將其插入了模型中。
結束語(yǔ) 諸如 Ruby 之類(lèi)的動(dòng)態(tài)語(yǔ)言中啟用了諸如 Java? 語(yǔ)言和 PHP 之類(lèi)的靜態(tài)語(yǔ)言中所沒(méi)有的一些功能。ActiveScaffold 是眾多基于模型的 “智能” UI 系統中的一種,它可以極大地簡(jiǎn)化數據輸入頁(yè)面的創(chuàng )建和維護(有關(guān)這些問(wèn)題的信息,請參看下面 參考資料 中的內容)。 對于本文有什么評論或問(wèn)題嗎? 請在我的 blog 上發(fā)表評論,并讓我知道您的想法!
|