【文章內(nèi)容簡介】
探究竟。depot ruby script/performance/profiler 39。(secret,salt)39。Loading Rails...Using the standard Ruby profiler.% cumulative self self totaltimesecondssecondscallsms/callms/callname1Integertimes1000000112Symbolto_sym...1Hashupdate這很奇怪:大半的時間似乎都被耗在times()和sin()方法上了。我們來看看源代碼:def (password, salt) { (1)} string_to_hash = password + salt Digest::(string_to_hash)end哎喲!頂上的那個循環(huán)是我在進行某個手工測試時加上的,以便讓程序運行得慢一點,然后我就忘了在部署之前把它去掉。我一定是忘了給自己留下一張小紙條……最后,別忘了日志文件。如果你需要了解與時間相關(guān)的信息,它們能夠提供大量寶貴的信息。 使用Mock對象Using Mock Objects未來的某個時候,我們肯定需要在Depot應用中加上一些代碼,以便真正收到來自顧客的付款。所以,假設(shè)我們已經(jīng)搞定了所有那些文案工作,并可以把那些信用卡數(shù)字變成我們銀行賬戶上實實在在的錢了。然后,我們創(chuàng)建了一個PaymentGateWay類(位于lib/),它可以和負責處理信用卡的網(wǎng)關(guān)交互。最后,我們在StoreController的save_order()這個action中添加下列代碼,就可以讓Depot應用處理信用卡了。gateway = response = (:login = 39。username39。 , :password = 39。password39。 , :amount = @, :card_number = @, :expiration = @, :name = @)當collect()方法被調(diào)用時,這些信息會通過網(wǎng)絡(luò)發(fā)送給后端的信用卡處理系統(tǒng)。這對于我們的錢包有好處,但對于功能測試就不那么好了,因為這樣一來,StoreController就必須能夠連接到真正的信用卡處理系統(tǒng)才行。而且,即便網(wǎng)絡(luò)連接和信用卡系統(tǒng)都不成問題,我們也不能每次運行功能測試就提交一堆信用卡交易事務。所以,我們并不想真的用PaymentGateWay對象進行測試,而是想用一個mock對象來替換它。在mock的幫助下,測試就不必依賴于網(wǎng)絡(luò)連接,從而確保結(jié)果的一致性。還好,Rails讓對象的模擬替換也易如反掌。為了在測試環(huán)境模擬collect()方法,我們只需要在test/mocks/。下面就來看看這個名字中的奧妙吧。首先,文件名必須與試圖替換的文件名相同。我們可以模擬模型、控制器或者庫文件:唯一的要求就是文件名必須相同。第二,看看占位文件的路徑,我們將它放在了test/mocks目錄的test子目錄下,這個子目錄用于存放所有用于測試環(huán)境的占位文件。如果我們希望在開發(fā)環(huán)境中替換某些文件,就應該將占位文件放在test/mocks/development目錄下?,F(xiàn)在,看看占位文件本身。require 39。lib/payment_gateway39。class PaymentGateway I39。m a stubbed out method def collect(request) true endend請注意,占位文件實際上加載了原來的PaymentGatway類(通過調(diào)用require()方法),然后對其進行了修改,覆蓋了其中的collect()方法。也就是說,我們不必模擬出PaymentGateway的所有方法,只需要修改那些運行測試時有必要修改的方法即可。在這里,修改后的collect()方法直接返回一個偽造的應答信息。有了這個文件以后,StoreController就會試用我們模擬出來的PaymentGateway類。之所以如此,是因為Rails把mock路徑放在整個搜索路徑的最前面,因此它會加載test/mocks/test/。這就是全部了。使用占位程序,我們可以集中注意力來測試最重要的東西,而Rails則讓這一切變得無比輕松。Stub vs.Mock也許你已經(jīng)注意到了,在前面我們一直用占位(stub)這個詞來稱呼那些用于模擬真實的類和方法,但Rails卻把它們放在test/mocks子目錄下。在這一點上,Rails確實有些不太注意用詞,這里所說的mock實際上只是占位程序:它們是偽造出的代碼塊,用在測試中以避免用到某些資源。不過,要是你真的想使用mock對象——用于檢查“程序如何使用某些對象”的對象——Rails也能夠滿足你。,Rails包含了Flex Mock —Jim Weirich的Rubymock對象庫。在所有測試中都可以使用這個庫,不過你需要明確導入它:require flexmock我們做了了什么What We Just Did我們?yōu)镈epot應用寫了一些測試,但并沒有測試到所有東西。然而,我們現(xiàn)在知道,確實可以測試所有東西。實際上,Rails對測試驅(qū)動開發(fā)提供了絕佳的支持,可以幫助你寫出更好的測試。盡早、盡可能頻繁地進行測試——你可以在投入正式運行之前找到bug.你的設(shè)計會得到改善,你的Rails應用會因此而感謝你。第3部分 Rails框架Part III The Rails Framework第15章深入RailsRails in Depth Depot項目已經(jīng)搞定了,現(xiàn)在正是深入研究Rails的好時機。在本書的剩下部分里,我們將一個主題一個主題地——或者說,一個模塊一個模塊地——探索Rails。本章將為讀者拉開大幕。在這一章里,我們將從較高的層面向讀者介紹一些必要的指示:目錄結(jié)構(gòu)、配置、環(huán)境、支撐類,以及調(diào)試提示。不過首先,我們必須回答幾個重要的問題…… Rails在哪兒So,Where39。sRails?Rails的一個有趣之處在于它的組成方式。從開發(fā)者的角度,你所有的時間都是在跟ActiveRecord或是ActionView這些高層的東西打交道。這里確實有一個名叫Rails的組件,不過它位于所有其他組件之下,默默地安排協(xié)調(diào)著它們的工作。要是沒有Rails組件,就不會有前面這個成功的Depot應用。但與此同時,Rails的底層基礎(chǔ)設(shè)施只有很少一部分與開發(fā)者的日常工作相關(guān),在本章的余下篇幅里,我們就要介紹這些部分。 目錄結(jié)構(gòu)DirectoryStructureRails要求一個特定的運行時目錄結(jié)構(gòu)。 my_app命令生成的頂級目錄結(jié)構(gòu)。我們看看每個目錄中都放了什么東西(不過不一定按順序來)。config和db這兩個目錄值得花一點時間來介紹,所以我們?yōu)樗鼈兎謩e準備了一小節(jié)的篇幅。頂層目錄下還有一個Rakefile文件,你可以使用它來運行測試、創(chuàng)建文檔、生成數(shù)據(jù)庫結(jié)構(gòu),等等。在命令行輸入rake tasks就可以看到完整的任務列表。 rails my_app命令的結(jié)果app/和test/我們的工作大多在app和test這兩個目錄中進行。應用程序的主要代碼都位于app目錄()。在稍后詳細討論ActiveRecord、ActionController和ActionView的時候,我們還會深入介紹app目錄的內(nèi)部結(jié)構(gòu)。此外,在第14章“任務T:測試”(第177頁)中,我們已經(jīng)介紹過test目錄了。 app/目錄doc/doc目錄是用來存放文檔——RDoc自動生成的文檔的。如果你運行rake doc:app命令,在doc/app目錄下就會有HTML格式的文檔。你可以編輯doc/README_FOR_APP文件,為自己的文檔定制一個首頁。“在線商店”應用的文檔首頁。lib/lib目錄用于存放那些不屬于模型、視圖和控制器的應用代碼。譬如說,你可能編寫了一個庫用于創(chuàng)建PDF格式的收據(jù)以便顧客下載1 我們在新版的Pragmatic Programmer在線商店中就是這樣做的。,控制器會直接用send_data()方法將收據(jù)發(fā)送給瀏覽器。此時,用于創(chuàng)建PDF收據(jù)的代碼就很自然地位于lib目錄下。lib目錄還很適合放置模型、視圖和控制器之間共享的代碼。譬如說你可能需要用一個庫來檢查信用卡號的校驗和,或是執(zhí)行某些財務計算,或是計算復活節(jié)的日期* 譯者注:復活節(jié)是3月21日或其后月滿之后的第一個星期天,因此是需要計算的。簡而言之,任何不直接屬于模型、視圖或者控制器的代碼都應該放在lib目錄下。也許你會想,只能把一大堆文件直接放在lib目錄下嗎?不必擔心。大部分Rails開發(fā)者都會在lib目錄下創(chuàng)建子目錄,以便將不同功能的代碼分組存放。譬如在Pragmatic Programmer在線商店中,用于生成收據(jù)、報關(guān)文件和其他PDF文檔的代碼都放在lib/pdf_stuff目錄下。將文件放到lib目錄下之后,你就可以在應用程序的任何地方使用它們。如果文件中包含了類或者模塊的定義,并且文件名是類名或者模塊名的小寫格式,那么Rails就會自動裝載這個文件。譬如說生成PDF收據(jù)的代碼位于lib/pdf_stuff/,那么只要這個類名叫PdfStuff::Receipt,Rails就能夠找到并自動裝載它。如果庫的命名不符合這些自動裝載的條件,也可以通過Ruby的require機制引用它們。如果要引用1ib目錄下的文件,只需要直接引用文件名。譬如說,假設(shè)計算復活節(jié)日期的庫位于lib/,我們就可以在任何模型、視圖或者控制器中使用下列代碼引用它:require easter如果庫位于lib目錄中的子目錄,別忘了在require語句中包含目錄名。譬如說如果要在控制器中引用“計算航空郵件費用”的庫,就需要使用下列代碼:require shipping/airmailRake任務RakeTasks在lib目錄下還有一個空的tasks目錄,你可以在其中編寫自己的Rake任務,用于給自己的項目實現(xiàn)自動化功能。這本書不是關(guān)于Rake的,所以我們不打算深入這部分內(nèi)容,只給大家看一個簡單的例子好了。Rails 提供了一個Rake任務用來告訴我們最后執(zhí)行的是哪個遷移任務。但如果能夠看到被執(zhí)行過的遷移任務的列表可能更好。But it may be helpful to see a list of all the migrations that have been performed.我們可以編寫一個Rake任務來打印schema_migration表中的版本清單。Rake任務完全用Ruby代碼寫出。Download depot_r/lib/tasks/namespace :db do desc Prints the migrated versions task :schema_migrations = :environment do puts ActiveRecord::( 39。select version from schema_migrations order by version39。 ) endend然后就可以在命令行執(zhí)行這個任務,就像執(zhí)行其他Rake任務一樣。depot rake db:schema_migrations(in /Users/rubys/Work/...)20080601000001200806010000022008060100000320080601000004200806010000052008060100000620080601000007更多關(guān)于“如何編寫Rake任務”的信息,請參閱Rake文檔:。log/在Rails運行的過程中,它會生成很多有用的日志信息,這些信息(默認情況下)被保存在log目錄下。在這里我們可以找到三個主要的日志文件,、。這些日志文件不僅包含簡單的日志跟蹤信息,還包含時間統(tǒng)計信息、緩存信息和實際執(zhí)行的數(shù)據(jù)庫語句等。日志記入哪個文件,取決于應用程序運行于哪個環(huán)境——關(guān)于運行環(huán)境,我們在介紹config目錄時還會深入討論。public/public目錄是應用程序的“臉面”:web服務器會把這個目錄作為應用程序的根目錄。大部分部署配置的工作都在這里進行,所以我們將在第27章“部署與生產(chǎn)”(第615頁)中介紹它。scripts/scripts目錄中存放了一些有用的工具程序。不加參數(shù)運行這些腳本就可以看到它們各自的用法。about顯示應用程序所使用的Ruby和Rails各組件的版本號,以及別的配置信息。dbconsole可以讓你通過命令行和數(shù)據(jù)庫直接交互。console讓你可以用irb與Rails應用中的方法交互。destroy刪除用generate工具生成的文件。generate代碼生成器,內(nèi)建支持創(chuàng)建控制器、郵件發(fā)送器(mailer)、模型、腳手架和web service。你也可以從Rails網(wǎng)站 下載其他的代碼生成模塊。不帶參數(shù)運行g(shù)enerate可以得到相關(guān)生成器的用法說明。譬如: