textmate开发一个blog
生活随笔
收集整理的這篇文章主要介紹了
textmate开发一个blog
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
TextMate開發(fā)一個簡單的Blog吧。 1、首先安裝git,下載源碼編譯或者到這里下載package:[url]http://metastatic.org/text/Concern/2007/09/15/new-git-package-for-os-x/[/url] 2、在Terminal中鍵入如下命令: cd ~/Desktop git clone git://github.com/drnic/ruby-on-rails-tmbundle.git “Ruby on Rails.tmbundle“ cd “Ruby on Rails.tmbundle“ git checkout --track -b two_point_ooh origin/two_point_ooh git pull 3、雙擊桌面上的“Ruby on Rails.tmbundle“圖標(biāo)安裝此bundle 這樣,Ruby on Rails 2.0 的bundle就安裝到TextMate里面了 下面可以開始開發(fā)一個新的應(yīng)用了 在終端里輸入 Ruby代碼? rails?blogcd?blogmate?.?? 增加一個模型 Ruby代碼? ruby?script/generate?model?Post?subject:string?body:text?? 生成了一個新的遷移 001_create_posts.rb,包含一個方法 Create_Table Ruby代碼? create_table?:posts?do?|t|??t.string?:subject??t.text?:body??t.timestampsend?? Sexy Migration的支持 把光標(biāo)放在 t.text :body下面一行,輸入 t. 然后按下?(Tab鍵),選擇“Create boolean column”?或者按下0?,創(chuàng)建一個布爾字段,然后輸入 published,如果按下Tab鍵沒有反應(yīng),看看是否選擇對了Ruby on Rails的bundle(在底下狀態(tài)欄里) 請注意這個時候下面還有一行t. 如果需要繼續(xù)輸入其他字段,就可以再按下Tab鍵直接輸入。 我們現(xiàn)在不需要別的更多的字段了,就按下??K(Control+Shift+K)把這一行刪除。然后保存文件?S 然后運(yùn)行遷移,可以在命令行輸入 Ruby代碼? rake?db:migrate?? 或者直接在編輯器里按下??|(Control+|),選擇“Migrate to Current”。 Post Fixtures 修改一下test/fixtures/post.yml Ruby代碼? published:??subject:?Some?article??body:?A?test?article??published:?truenonpublished:??body:?Still?writing?this?one?? 在Rails2.0里,fixtures不再使用id了,后面我們可以看到怎么使用foxy fixtures來自動關(guān)聯(lián)。 Public blog controller 創(chuàng)建一個控制器,可以通過命令行 Ruby代碼? ruby?script/generate?controller?blog?? 或者直接在編輯器里按下?|(Control+|),選擇“Call Generate Script”,再選擇“Controller”,給它起個名字叫做blog,action里不要設(shè)置任何內(nèi)容。 現(xiàn)在打開blog_controller_test.rb,要快速找到文件,可以按下??T,然后輸入bct就可以找到了。 可以看到,功能性的測試是非常清楚和簡單的。 讓我們開始TDD吧,先刪除?test_truth?方法 然后創(chuàng)建一個測試來顯示blog文章列表 輸入deftg 然后按下Tab鍵 自動生成了以下代碼 Ruby代碼? def?test_should_get_action??@model?=?models(:fixture_name)??get?:action,?:id?=>?@model.to_param??assert_response?:successend?? 光標(biāo)停留在action上,用index代替它,然后按下Tab鍵,光標(biāo)選擇了方法的第一行,刪除它,然后按三次Tab鍵,刪除:id => @model.to_param?部分。再次按下Tab鍵,光標(biāo)跳到方法的最后一行。現(xiàn)在的代碼應(yīng)該是這樣的: Ruby代碼? def?test_should_get_index??get?:index??assert_response?:successend?? 鍵入 asg ,然后按下Tab鍵,然后鍵入posts,然后按下Tab鍵。 下面,鍵入ass ,然后按下Tab鍵,輸入div#posts,按下Tab鍵,刪除余下的部分。然后連續(xù)按Tab鍵兩次,把光標(biāo)放在assert_select里面,現(xiàn)在代碼如下 Ruby代碼? assert_select?'div#posts'?doend?? 鍵入ass,按下Tab鍵,鍵入div.post,然后按下Tab兩次,鍵入count,取代掉text。 現(xiàn)在再按下Tab鍵,鍵入post.size,最后一次按下Tab鍵,刪除被選中的部分。 好了,現(xiàn)在我們的測試方法完成了 Ruby代碼? def?test_should_get_index??get?:index??assert_response?:success??assert(posts?=?assigns(:posts),?“Cannot?find?@posts“)??assert_select?'div#posts'?do????assert_select?'div.post',?:count?=>?posts.size??endend?? 還有一個deftp代碼片段來創(chuàng)建一個POST方法的測試。很容易記住的,deftg代表define test get, deftp代表define test post Controller actions 要導(dǎo)航到blog_controller.rb文件,有三種選擇: 1,按下???↓(Shift+option+command+下箭頭),然后在彈出的菜單中選擇“Controller”, 2,按下??↓(option+command+下箭頭),然后直接去controller文件(來回切換) 3,按下?T,然后鍵入bc,找到那個文件。 增加一個index方法 Ruby代碼? def?index??@posts?=?Post.find_all_by_published(true)end?? Action Views 要創(chuàng)建或者導(dǎo)航到一個視圖,可以按下???↓(shift+option+command+下箭頭),然后選擇“View”(就像上面那樣),或者按下??↓(option+command+下箭頭)來切換控制器的方法和對應(yīng)的視圖。 因?yàn)闆]有對應(yīng)的app/views/blog/index*文件,所以會提示要求創(chuàng)建一個空白視圖,默認(rèn)的它會猜測文件名稱為index.html.erb,?因?yàn)榉椒ǖ拿纸凶?span style="font-family:Monaco;font-size:12px;line-height:normal;">index,但是當(dāng)然您可以改為其他名字。 如果您得到一個提示:blog_controller.rb does not have a view,請注意先要保存控制器文件,然后在切換到視圖。同時請注意,光標(biāo)要在控制器方法的范圍內(nèi),才能起作用。 好了,我們轉(zhuǎn)到剛創(chuàng)建的index.html.erb 鍵入div,然后按兩次Tab,把id改為post,然后再按Tab,應(yīng)該是這樣: <div id=“posts“> </div> 在div標(biāo)簽中,鍵入for,按下Tab,生成一個for循環(huán),在光標(biāo)處替換list為@posts,按下Tab,然后鍵入post,替換掉item,再按下Tab,現(xiàn)在光標(biāo)應(yīng)該在for循環(huán)里面了。 鍵入div,然后Tab,生成了div標(biāo)簽,刪除id,鍵入class='post',再按Tab,進(jìn)入了div標(biāo)簽里面。 用control+>創(chuàng)建一個<%= %>, 如果再按一下control+>,就會生成<% %>,再按一下,會生成<%- -%>,再按一下就會生成<%# %>,如此循環(huán)往復(fù)。 鍵入post.body 實(shí) 際上,我們還需要顯示標(biāo)題,所以在<%= post.body %>這一行上面(按下command+return,上箭頭),鍵入h3,然后按下control+<,生成<h3>標(biāo)簽。鍵入 control+>,鍵入post.subject 在下面的 <% else %><% end %> 中間,創(chuàng)建一個簡單的標(biāo)記<p></p>(使用control+<),鍵入There are no posts available to read. All y'all come back soon, yer hear.? 呵呵,純粹好玩兒。 好了,我們的視圖現(xiàn)在是這樣子了: Ruby代碼? <div?id=“posts“>??<%?if?!@posts.blank??%>????<%?for?post?in?@posts?%>??????<div?class=“post“>????????<h3><%=?post.subject?%></h3>????????<%=?post.body?%>??????</div>????<%?end?%>??<%?else?%>????<p>There?are?no?posts?available?to?read.?All?y'all?come?back?soon,?yer?hear.</p>??<%?end?%></div>?? 現(xiàn)在可以運(yùn)行單元測試了,應(yīng)該可以通過。可以在命令行中輸入:rake test:functionals ? 或者直接在編輯器里鍵入control+\然后選擇2:Test Functionals 我們現(xiàn)在還不能讓用戶留言。 Foxy Fixtures 創(chuàng)建一個comment模型 ruby script/generate model Comment body:text name:string post:references 請注意,這里post:references的作用和以前的post_id:integer是一樣的。在生成的遷移文件中,它創(chuàng)建了t.reference :post, 下面是生成的第二個遷移文件 Ruby代碼? create_table?:comments?do?|t|??t.text?:body??t.string?:name??t.references?:post??t.timestampsend?? 運(yùn)行這個遷移。命令行里鍵入 rake db:migrate , 或直接在編輯器按下control+| 然后在彈出的菜單重選擇Migrate to Current 下面,我們創(chuàng)建幾個comment fixtures,以便我們看看Foxy Fixtures怎么運(yùn)行的。 打開test/fixtures/comments.yml: Ruby代碼? one:??body:?MyText??name:?MyString??post:two:??body:?MyText??name:?MyString??post:?? 字段post替代了rails1.2中post_id的位置。 上面我們有了published和nonpublished,或許很難記住,我們有一個快捷鍵。 把光標(biāo)放在post: 后面,然后按下 option+Esc , 就可以選擇了。 關(guān)聯(lián) 為了能夠用到Foxy Fixtures,我們必須增加兩個模型類之間的關(guān)聯(lián)。 現(xiàn)在你已經(jīng)可以快速切換到模型類的文件了,(shift+option+command+下箭頭)在comment.rb 模型類文件中,鍵入bt然后按下Tab鍵,鍵入post Ruby代碼? belongs_to?:post,?:class_name?=>?“Post“,?:foreign_key?=>?“post_id“?? 現(xiàn)在:class_name和:foreign_key都已經(jīng)一起自動的把object改為post了,當(dāng)然,您繼續(xù)按下Tab鍵,還可以繼續(xù)改。不過我們這里就不需要了,直接刪除后面的就行了。 現(xiàn)在它是這樣的: Ruby代碼? class?Comment?<?ActiveRecord::Base??belongs_to?:postend?? 然后去看看post類,打開Post.rb 鍵入hm,然后Tab。 同樣的我們不需要那些option,刪除。 Ruby代碼? class?Post?<?ActiveRecord::Base??has_many?:commentsend?? 如果您需要has_many :through ,可以鍵入hmt然后tab,就行了。 最后我們測試一下。(control+\) 路由 打開路由文件routes.rb 修改一下: Ruby代碼? ActionController::Routing::Routes.draw?do?|map|??map.resources?:posts??map.connect?':controller/:action/:id'??map.connect?':controller/:action/:id.:format'end?? 創(chuàng)建文章 從Post模型文件post.rb,您可以快速導(dǎo)航到同名的控制器文件,可以支持單數(shù)形式的控制器名或者復(fù)數(shù)形式的控制器名稱,但是默認(rèn)都是復(fù)數(shù)形式的,以更加符合REST風(fēng)格。 要 創(chuàng)建一個PostsController,可以使用Go To...的快捷鍵shift+option+command+下箭頭,然后選擇Controller,因?yàn)闆]有post_controller.rb或 者posts_controller.rb,所以會問您是否需要創(chuàng)建一個,確認(rèn)后,就得到我們自己的posts_controller.rb文件了。 注意,我們通常也可以使用腳手架來自動生成控制器文件,包括路由和測試文件。 在posts_controller里面,我們先要創(chuàng)建一個控制器類。 如果posts_controller.rb里面有內(nèi)容,清空全部的內(nèi)容,然后鍵入cla,按下Tab,選擇Create controller class,在光標(biāo)處鍵入Posts,然后tab,然后鍵入post,然后tab,最后鍵入Post,然后tab,光標(biāo)回到了類的中間。 Ruby代碼? class?PostsController?<?ApplicationController??before_filter?:find_post??private??def?find_post????@post?=?Post.find(params[:id])?if?params[:id]??endend?? TDD測試PostsController 當(dāng)前是沒有一個PostsController的功能測試的,我們可以繼續(xù)使用GoTo...的快捷鍵:shift+option+command+下箭頭,然后選擇Functional Test,就會創(chuàng)建一個控制器測試文件 然后分別為index, new, edit三個方法建立測試,使用deftg+Tab的方式。 要建立create方法的測試,請鍵入deftp,然后按下Tab,鍵入create,然后tab,鍵入post,然后tab,按下delete鍵,再tab,再delete,再Tab,光標(biāo)就轉(zhuǎn)移到{}中,然后修改成下面這樣: Ruby代碼? def?test_should_post_create??post?:create,?:post?=>?{?:subject?=>?'Test',?:body?=>?'Some?body',?:published?=>?'1'?}??assert_response?:redirectend?? 在assert_response這一行后面,我們可以測試我們想跳轉(zhuǎn)重定向的位置。 如果您鍵入art,就會創(chuàng)建一個:assert_redirected_to :action => “index“??代碼片段 現(xiàn)在增加了好幾個assert_redirected_to的代碼片段,使用到了資源化路由( resourceful route) def?test_should_post_create??post?:create,?:post?=>?{?:subject?=>?'Test',?:body?=>?'Some?body',?:published?=>?'1'?}??assert_response?:redirect??assert_redirected_to?posts_pathend?? 然后運(yùn)行測試,然后可以看到這些新創(chuàng)建的測試都沒通過 視圖 現(xiàn)在回到posts_controller.rb文件(option+command+下箭頭) 我們要創(chuàng)建三個新的方法:index, new, edit。新方法可以這樣來創(chuàng)建:鍵入def,??,?index,??,?@posts = Post.fina,??,??. 現(xiàn)在這三個方法分別需要三個視圖模板來對應(yīng)。 把光標(biāo)放在index方法中,呼叫出Goto...菜單(shift+option+command+下箭頭),選擇View,會創(chuàng)建一個新的index.html.erb模板, 讓我們來創(chuàng)建一個表格來顯示Posts吧 鍵入table,按下control+<,生成table標(biāo)簽。 然后在table標(biāo)簽中生成tbody標(biāo)簽 在tbody里,我們希望可以枚舉 @posts ,每一行一條記錄。 按三次control+>, 創(chuàng)建<%- -%>,在里面鍵入?@posts.each do |post| 轉(zhuǎn)到下一行(command+enter),鍵入end, 按下Tab,生成<% end -%>,這樣我們在這個erb模板中就有了一個ruby的代碼塊了。 在這個代碼塊中,生成tr和td,我們把post的標(biāo)題放在這個單元格里。 鍵入 post.subject,選中,然后按下control+>,生成<%= post.subject %>. 最后形成這樣子: Ruby代碼? <table>??<tbody>????<%-?@posts.each?do?|post|?-%>??????<tr>????????<td><%=?post.subject?%></td>??????</tr>????<%?end?-%>??</tbody></table>?? Forms 把光標(biāo)放在Controller的new方法中,按下shift+option+command+下箭頭,叫出Goto菜單,選擇View,生成new.html.erb 在新的new.html.erb文件中,鍵入ffe,然后tab,鍵入post,然后tab兩次 有兩個snippet可以生成form_for: ff和ffe,唯一的區(qū)別就是ff生成的代碼沒有error_messages_for那部分內(nèi)容 然后我們需要為subject屬性增加一個標(biāo)簽和文本框: 首先生成一個p標(biāo)簽(control+<,然后tab),鍵入f. 然后tab,選擇Label,把默認(rèn)的改為subject,然后tab, 刪除剩下的。 接著創(chuàng)建一個<br>標(biāo)簽(control+entern),鍵入f.然后tab,選擇TextField,鍵入subject 現(xiàn)在重復(fù)創(chuàng)建body和published兩個文本域 請注意,published這個屬性的標(biāo)簽最好用published yet?,你可以tab進(jìn)默認(rèn)的字符串替換掉。 最后用f. snippet增加一個submit按鈕 啟動script/server,然后訪問[url]http://localhost:3000/posts/new[/url] 最后的form代碼如下: Ruby代碼? <%=?error_messages_for?:post?%><%?form_for?@post?do?|f|?-%>??<p>????<%=?f.label?:subject?%><br?/>????<%=?f.text_field?:subject?%>??</p>??<p>????<%=?f.label?:body?%><br?/>????<%=?f.text_area?:body?%>??</p>??<p>????<%=?f.label?:published,?“Published?yet?“?%><br?/>????<%=?f.check_box?:published?%>??</p>??<p>????<%=?f.submit?“Submit“?%>??</p><%?end?-%>?? 值得說明的是:如果您按下control+enter生成了br標(biāo)簽,但不是<br />,而你希望更加符合xhtml的規(guī)范,請轉(zhuǎn)到TextMate的preferences,選擇advanced標(biāo)簽,選中“Shell Variablees”,增加一個變量叫做TM_XHTML,值是 / Partials 剛才我們創(chuàng)建的這個form和edit.html.erb模板所需要的form是一模一樣的,除了復(fù)制/粘帖的方法以外,我們還可以創(chuàng)建一個局部模板,減少不必要的重復(fù)。 全部選中form(command+A),然后按下control+shift+H,會出來一個對話框,我們給他一個名字叫做form,然后確定。 可以看到自動生成了一個名叫_form.html.erb的文件,剛才的form都在這個_form.html.erb文件中,而new.html.erb的內(nèi)容被一個語句代替: <%= render :partial => 'form' %> 然后我們創(chuàng)建edit.html.erb:? 回到控制器文件,把光標(biāo)放在edit方法上,按下shift+option+command+下箭頭, 就自動生成了edit.html.erb,把剛才上面的語句粘帖到空白文件中。 Link Helpers 在new.html.erb底下我們還想創(chuàng)建一個新的返回全部文章列表的鏈接。在post控制器,而不是在blog控制器噢。其實(shí)就是調(diào)用index方法,通過resources rout: posts_path 下面這些link_to是支持resources route的: def?create??@post?=?Post.new(params[:post])??if?@post.save??else??endend?? 把光標(biāo)放在else上面,鍵入repp,然后tab2次。用post代替選中的文字。 redirect_to也有很多變化 respond_to?do?|wants|??wants.html?{??}end?? 然后按兩次tab,光標(biāo)置入wants.html{}里面,鍵入ra,然后tab,然后鍵入new。最終代碼如下: Ruby代碼? respond_to?do?|wants|??wants.html?{?render?:action?=>?“new“?}end?? 另一個,可以使用“升級”了的快捷鍵(shift+command+H),可以把選中的代碼轉(zhuǎn)換到respond_to代碼塊里。 選中if語句條件為真部分的respond_to整行(shift+command+L)然后按shift+command+H,光標(biāo)停在js上,刪除這一行(control+shift+K),最終代碼變成: Ruby代碼? def?create??@post?=?Post.new(params[:post])??if?@post.save????respond_to?do?|wants|??????wants.html?do????????redirect_to(posts_path)??????end????end??else????respond_to?do?|wants|??????wants.html?{?render?:action?=>?“new“?}????end??endend??
現(xiàn)在我們可以在[url]http://localhost:3000/posts/new[/url] 添加新的文章,并且在[url]http://loclahost:3000/blog[/url] 查看文章了。 還有一些遷移工作 我們還有一些附加工作, 把comments表的name列改名為author 在comments表中增加一個author_url字段 在comments表中的post_id字段增加一個索引 讓我們嘗試在一個遷移文件中做這些事情。 啟動快速遷移(shift+control+M),給我們的遷移起個名字叫做ModifyComments,自動生成了003_Modify_comments.rb并打開,光標(biāo)放在了mtab后面,刪除它,鍵入mcol,然后tab,選擇Rename / Rename Column?(3),鍵入comments???name???author????. 同樣我們再次鍵入mcol,然后tab,選擇Add / Remove Column(1),鍵入comments?author_url,然后?兩次。 現(xiàn)在鍵入mind,然后?,選擇Choose?Add / Remove Index(1),鍵入comments?post_id. ? 最終的代碼如下: class ModifyComments < ActiveRecord::Migration def self.up rename_column :comments, :name, :author add_column :comments, :author_url, :string add_index :comments, :post_id end def self.down remove_index :comments, :post_id remove_column :comments, :author_url rename_column :comments, :author, :name end end 請注意,down方法的順序是和up方法的順序相反的。 保存并執(zhí)行這個遷移 要記住修改comments的fixture文件。先打開文件(command+T,鍵入cy,回車),把name字段改名為author字段,并在每一節(jié)增加author_url,然后測試,應(yīng)該都順利通過。 此外,我們希望當(dāng)一篇文章發(fā)表時我們可以被通知到,為了做到這一點(diǎn),我們需要做以下的修改: 當(dāng)文章發(fā)布時,有一個時間日期published date可以跟蹤; 移除published這個字段,因?yàn)槭欠褚呀?jīng)發(fā)布我們可以看published date。 開始快速遷移(shift+control+M),起個名字叫做?AddPublishedAtForPosts,?一個新的遷移文件004_add_published_at_for_posts.rb,光標(biāo)還是在mtab后面,一樣,我們把mtab刪除,鍵入mcol,然后tab,在彈出來的菜單上選擇Add / Remove Column?(1),?鍵入posts??published_at???datetime???和enter。 再次鍵入mcol,tab,然后選擇Remove / Add Column(5),鍵入posts???published,然后tab兩次。 最終代碼如下: class AddPublishedAtForPosts < ActiveRecord::Migration def self.up add_column :posts, :published_at, :datetime remove_column :posts, :published end def self.down add_column :posts, :published, :boolean remove_column :posts, :published_at end end Remove/Add Column命令自動在down方法中決定published字段為布爾字段,這取決于當(dāng)前數(shù)據(jù)庫db/schema.rb文件的狀態(tài)。 然后保存并執(zhí)行遷移。 現(xiàn)在我們要修改posts fixture文件。選擇posts.yml文件(command+T, 鍵入pyml,選擇posts.yml)。用published_at: 2008-01-01代替published: true。?修改posts功能測試,首先先打開測試文件( shift+option+command+下箭頭,選擇Go to functional Test),用:published_at => Date.new(2008, 1, 1)?替換?:published => '1'. 修改post模型,打開posts.rb(shift+option+command+下箭頭,選擇Go to Model), 黏貼下面的代碼: class Post < ActiveRecord::Base has_many :comments def published !self.published_at.nil? end def published=(publish) if publish self.published_at = DateTime.now if self.published_at.nil? else self.published_at = nil end end end 修改blog_controller.rb,用Post.find(:all, :conditions => “published_at IS NOT NULL“)代替Post.find_all_by_published(true)。然后測試運(yùn)行,所有的測試都應(yīng)該通過。 這篇文章的原文在ruby on rails bundle的幫助里,在TextMate里面打開rails項(xiàng)目的一個rb文件,使用快捷鍵Control+H,然后選擇2,就可以看到這篇文章了。最新的ror bundle更新于上個月底,還是很具有參考價值的,對于如何快速使用TextMate進(jìn)行rails開發(fā)是一個不錯的教程。
不過還有一些沒完成。不是特別完整。但是基本的快捷鍵都包含在里面了。
---------英文原版教程
Step-by-step demonstration
Get Version
1.90.0??
In this demo we’ll create a blog; because that’s what blogs are for: being demonstrations of web frameworks.
The demonstration uses new features of Rails 2.0 and the snippets in this bundle.
A New App
rails blog
cd blog
mate .
Add some models
ruby script/generate model Post subject:string body:text
This creates a 001_create_posts migration with a create_table:
create_table :posts do |t|
? t.string :subject
? t.text :body
? t.timestamps
end
Sexy Migration support
If you put the cursor on the line after t.text :body, type t. and press ?. Select “Create boolean column” (by pressing 0), and type “published” into the template field. If nothing happened when you pressed ?, check that when you opened the migrations file you’ve selected the bundle “Ruby on Rails”.
Note that another t. was created on the next line! Press ? and the cursor will be placed after it. You can now press ? again to create another column, or delete this line.
Here, delete the extraneous t. line (? ? K). And save the file (? S).
Run the migrations, either from the prompt:
rake db:migrate
or directly from the editor with ? | (Ctrl-Pipeline), and choosing option “Migrate to Current”.
Post fixtures
Update the test/fixtures/posts.yml file as:
published:
? subject: Some article
? body: A test article
? published: true
nonpublished:
? body: Still writing this one
Note, in Rails 2.0 fixtures no longer have explicit ids. Later on we’ll look at snippets for using Foxy Fixtures with auto-completion for associations.
Public blog controller
Create a controller for our blog, either via the command prompt:
ruby script/generate controller blog
or directly from the editor with ? |, and choosing option “Call Generate Script”, choose “Controller”, give it the name “blog”, and empty the list of actions.
Now open blog_controller_test.rb. To find this file quickly press ? T, type bct, and select the file.
Note how much cleaner functional tests are now via ActionController::TestCase.
Let’s do some TDD. First, delete the test_truth dummy method.
To create a test to show a list of blog articles:
deftg
and ? gives:
def test_should_get_action
? @model = models(:fixture_name)
? get :action, :id => @model.to_param
? assert_response :success
end
Type index to replace action. Press ?, and then ? to remove the first line, then press ? three times and then ? to remove the :id => @model.to_param part. The press ? again to go to the end of the method. Now we have:
def test_should_get_index
? get :index
? assert_response :success
end
Now type asg, press ?, and type posts, and press ? again. This creates an instance variable lookup within an assertion:
assert(posts = assigns(:posts), "Cannot find @posts")
Now, let’s assert the HTML format.
Type ass and press ?. Type div#posts, press ? and ?, then ? twice to place the cursor within the assert_select block:
assert_select 'div#posts' do
end
Now we’ll check that the @posts objects are represented in the div#posts element.
With the cursor inside the assert_select:
Type ass, press ?, type div.post, press ? twice, and type count (to replace the text). Now press ? again, and type posts.size. Press ? a final time (it will highlight the do...end block), and press ?.
Our test method is now finished:
def test_should_get_index
? get :index
? assert_response :success
? assert(posts = assigns(:posts), "Cannot find @posts")
? assert_select 'div#posts' do
??? assert_select 'div.post', :count => posts.size
? end
end
NOTE: there is also a deftp snippet for functional tests to create a POST test stub. To memorize: deftg stands for define test get and deftp stands for define test post
Controller actions
To navigate to blog_controller.rb there are three options:
press ? ? ? ↓, and select “Controller” from the drop-down list
press ? ? ↓ and you’ll go directly to the controller (toggles between the two files)
press ? T, type bc, choose the file, press ?.
Add the index action method:
def index
? @posts = Post.find_all_by_published(true)
end
Action views
To create/navigate to the view, press ? ? ? ↓ and select “View” (like above). Or press ? ? ↓ to toggle between a controller method and it’s view.
As there are no app/views/blog/index* files, it will prompt you to create a blank view file. By default it guesses index.html.erb (because the method was named index), but of course you can change that in the dialog box.
If instead you got the message “blog_controller.rb does not have a view”, note that you first need to save the controller file before hitting ? ? ? ↓ or ? ? ↓. Also note that the cursor must be within the scope of a method for ? ? ? ↓ or ? ? ↓ to work.
Press enter to accept index.html.erb. You are taken to the new file.
Let’s create HTML to match the earlier tests.
Type div and press ? twice, then type posts and press ?:
<div id="posts">
</div>
Inside the div element, type for and press ?. This expands into a large ERb-enabled for-loop. Type @posts, press ?, type post and press ?. The cursor is now inside the for-loop.
Inside the for-loop, type: div and press ?. Press ?, and type? class='post' and press ? to enter the div element.
Create a <%=? %> element (? >). If you press ? > again, it toggles to <%? %>, and then again and it becomes <%-? -%>, and again and it becomes <%#? %> (a Ruby comment). Pressing ? > again starts at <%=? %> again.
Enter post.body within the ERb template field.
Actually, we’ll need to show the subject too, so above the <%= post.body %> line (press ↑ followed by ? ?) type ‘h3’, and press ? < (LessThan), then ? > (GreatherThan), and post.subject.
The resulting line is: <h3><%= post.subject %></h3>
Move the cursor down between <% else %> and <% end %>.
Create a simple element <p></p> (? ? W or ? <). You can change the element type here. Just press ? to go inside the element. Type There are no posts available to read. All y'all come back soon, yer hear. because its funny.
Our index.html.erb template is now:
<div id="posts">
? <% if !@posts.blank? %>
??? <% for post in @posts %>
????? <div class="post">
??????? <h3><%= post.subject %></h3>
??????? <%= post.body %>
????? </div>
??? <% end %>
? <% else %>
??? <p>There are no posts available to read. All y'all come back soon, yer hear.</p>
? <% end %>
</div>
If we run our functional tests they now pass: run either from the command prompt with rake test:functionals or directly from the editor by pressing ? \ and press 2 for “Test Functionals”
As yet, we have no way for users to leave comments.
Foxy Fixtures
Create a comment model:
ruby script/generate model Comment body:text name:string post:references
Note: here post:references is effectively the same as post_id:integer. Within the generated migration it creates t.reference :post. There is also a t. and tcr snippet for references, as for other standard datatypes, which helps setup polymorphic associations.
The generated create_table in 002_create_comments.rb is:
create_table :comments do |t|
? t.text :body
? t.string :name
? t.references :post
? t.timestamps
end
Run rake db:migrate, or directly from the editor with ? | and choose option “Migrate to Current”.
Now create some comment fixtures so we can look at Foxy Fixtures. Open text/fixtures/comments.yml (? T, type cy, press ?).
By default, the generated comments.yml starts like:
one:
? body: MyText
? name: MyString
? post:
two:
? body: MyText
? name: MyString
? post:
The post fields replace the rails1.2 post_id fields. Now, we can specify the post.yml labels for a post. From above we have published and unpublished. It can be hard to remember what fixtures we have, so there is a key-combo helper.
Put the cursor after post: and press ? ?. A drop-down box appears with the names of the posts.yml fixtures. Select published and press ?. Repeat for the 2nd fixture. This gives us:
one:
? body: MyText
? name: MyString
? post: published
two:
? body: MyText
? name: MyString
? post: published
Associations
To enable the Foxy Fixtures, we need to add associations to the model classes.
You can now quickly go from a fixtures file (we’re in comments.yml) to the model file (? ? ? ↓).
Within comment.rb model, create a new line within the class, and type bt and press ?. Type post. This creates a snippet:
belongs_to :post, :class_name => "Post", :foreign_key => "post_id"
The class name and foreign key are now generated from the association name. You can change them by tabbing across. But, we only need the default, so we can delete these options.
Press ? and ? to remove the :class_name and :foreign_key options. The Comment class is now:
class Comment < ActiveRecord::Base
? belongs_to :post
end
Now go to the Post class. Press ? T and type post and select the model file, and press ?.
Create a new line within the Post class (? ?). Type hm and press ? to generate a has_many association. Type comment, and the resulting snippet is:
has_many :comments, :class_name => "comment", :foreign_key => "class_name_id"
We don’t need the options. So press ? once and then ?.
class Post < ActiveRecord::Base
? has_many :comments
end
Note: there is also a has_many :through snippet. Type hmt and ? to activate it.
Finally, we can run our tests since adding the Comment model + fixtures (? \).
rake test
Routes
Open the routes file (? T, type routes and press ?).
Change the routes file to:
ActionController::Routing::Routes.draw do |map|
? map.resources :posts
? map.connect ':controller/:action/:id'
? map.connect ':controller/:action/:id.:format'
end
Creating Posts
From the Post model class (post.rb) you can now quickly navigate to a controller of the same name. It supports either singular or plural controller names, but will default to the plural name, which is the REST/resources preferred name.
To create a PostsController, use the ‘Go To’ hot key (as above) ? ? ? ↓ and select ‘Controller’. As there is no post_controller.rb nor posts_controller.rb it will create a posts_controller.rb controller file; which is what we want here.
Note; at this stage you could use the Rails 2.0 scaffold generator to create the posts_controller.rb (and tests and routes).
In the blank file, we need to create a controller class.
Type cla and ?, and select “Create controller class”. Type Posts and ?, post and ?, and finally, Post and ?. This leaves the cursor in the middle of the generated class:
class PostsController < ApplicationController
? before_filter :find_post
? private
? def find_post
??? @post = Post.find(params[:id]) if params[:id]
? end
end
TDD for Posts controller
Currently there is not a functional test for our posts_controller.rb. To create it, use the ‘Go To’ hot key (? ? ? ↓) and select ‘Functional Test’. This will create a blank file.
Type cla and ?, and select “Create functional test class”. Type Posts and ?. (The functional test class name should match the controller class, with Test suffixed to it).
The functional test class snippet gives you a deft stub. If you press ? now, it creates a generic test method snippet:
def test_case_name
end
Instead, we will use the deftg (GET request) and deftp (POST request) snippets.
Create a test for the index, new and edit actions. For index and new, we can delete the @model = models(:fixture_name), etc parts.
To test for the create action, type deftp and ?. Type create and ?, type post and ?, type ? and ?, and again ? and ?. Now enter in a hash of the values to pass in for the test, say :subject => 'Test', :body => 'Some body', :published => '1'. The result should look like:
def test_should_post_create
? post :create, :post => { :subject => 'Test', :body => 'Some body', :published => '1' }
? assert_response :redirect
end
On the line after the assert_response expression, we’ll test for where we want to be redirected to.
If you type art you create an old-style assert_redirected_to :action => "index" snippet.
In addition there are now various assert_redirected_to snippets that use resourceful routes:
artp – assert_redirected_to model_path(@model)
artpp – assert_redirected_to models_path
artnp – assert_redirected_to parent_child_path(@parent, @child)
artnpp – assert_redirected_to parent_child_path(@parent)
As we’ll see later, this naming scheme is used for other snippets that use resourceful routes, like link_to and redirect_to.
Type artpp and ?, and type post, to assert that the create action must redirect to the index page.
The final test_should_post_create method is:
def test_should_post_create
? post :create, :post => { :subject => 'Test', :body => 'Some body', :published => '1' }
? assert_response :redirect
? assert_redirected_to posts_path
end
Running our tests (rake test:functionals or ? \) shows all these new tests failing.
Views
Go back to the posts_controller.rb file (? ? ↓).
Now add three actions – index, new and edit. New methods can be created with the def snippet:
class PostsController < ApplicationController
? before_filter :find_post
? def index
??? @posts = Post.find(:all)
? end
? def new
??? @post = Post.new
? end
? def edit
? end
? private
? def find_post
??? @post = Post.find(params[:id]) if params[:id]
? end
end
Note: the index method could be created by typing def, ?, index, ?,? @posts = Post.fina, ?, ?.
Now we need templates for the index, new and edit actions.
Place the cursor inside the index method, and use the ‘Go To’ hot key (? ? ? ↓) and select ‘View’. A dialog box will pop up asking for the name of the new template (as there are no app/views/posts/index* files). By default, the suffix is now .html.erb rather than the old .rhtml. Press ?, to accept index.html.erb as your template name.
Let’s just create a simple table showing the Posts.
Type table and ? < to generate <table></table>, and press ? to put the tags on separate lines.
Do the same to create a <tbody></tbody> element.
Inside the <tbody></tbody> we want to iterate over the @posts, one per <tr></tr> row.
Press ? >, three times, to create a <%- -%> tag. Inside it type @posts.each do |post|.
On the next line (? ?), type end and ?, to create <% end -%>. We now have a Ruby block within this ERb template.
Inside the block, create a <tr></tr> element, and within it create a <td></td> element. We’ll skip over anything fancy here, and just put the post’s subject here.
Type post.subject and select it. Now press ? > to wrap the selected text inside <%= post.subject %>.
The resulting index.html.erb is:
<table>
? <tbody>
??? <%- @posts.each do |post| -%>
????? <tr>
??????? <td><%= post.subject %></td>
????? </tr>
??? <% end -%>
? </tbody>
</table>
Forms
Place the cursor inside the new method, and use the ‘Go To’ hot key (? ? ? ↓) and select ‘View’. Press ? to accept new.html.erb.
Inside the blank new.html.erb file, type ffe and press ?, and type post and press ? twice:
<%= error_messages_for :post %>
<% form_for @post do |f| -%>
<% end -%>
form_for is the Rails 2.0 preferred helper for managing forms, and there are now snippets for common form_for helpers. There are ff and ffe snippets; the former does not have the error messages section.
To create a label and text field for the subject attribute:
Create a <p></p> block (Press ? <, then ?, then ?). Type f. and ?, and select “Label”. Type subject, press ? and press ?. Create a <br /> (? ?). Type f. and ?, and select “Text Field”. Type subject.
This gives us:
<%= error_messages_for :post %>
<% form_for @post do |f| -%>
? <p>
??? <%= f.label :subject %><br />
??? <%= f.text_field :subject %>
? </p>
<% end -%>
Now repeat for body and published fields.
Note, for published, you might change the label to Published yet? by tabbing into the default string file.
Finally, add a “Submit” button using the f. snippet tab completion.
Start script/server from the prompt and you can now view this form at [url]http://localhost:3000/posts/new[/url]
The final form is:
<%= error_messages_for :post %>
<% form_for @post do |f| -%>
? <p>
??? <%= f.label :subject %><br />
??? <%= f.text_field :subject %>
? </p>
? <p>
??? <%= f.label :body %><br />
??? <%= f.text_area :body %>
? </p>
? <p>
??? <%= f.label :published, "Published yet?" %><br />
??? <%= f.check_box :published %>
? </p>
? <p>
??? <%= f.submit "Submit" %>
? </p>
<% end -%>
Note: if you got <br> when hitting ? ? instead of <br /> then you might want to go to the preferences of TextMate (? ,), choose tab “Advanced”, choose “Shell Variables”, click the + sign to add a new shell variable, and give it the name TM_XHTML and a value of? /
Partials
The form we just created is exactly the same as the form required for the edit.html.erb template.
Instead of copy+pasting it into the edit.html.erb file, we’ll create a partial template.
Select the entire form (? A), and press ? ? H and a dialog box appears. Type in form and press ?.
You’ll notice a new file _form.html.erb has appeared which contains the code you had selected, while the code in the file new.html.erb has been replaced by:
<%= render :partial => 'form' %>
Now copy and paste this into the edit.html.erb file. To create this file, return to the controller (from the new.html.erb file, press ? ? ↓), go to the edit action, and use ? ? ↓ again to create the edit.html.erb template file.
Link helpers
At the bottom of the new.html.erb we want a link back to the list of all posts (within the posts controller, not the public blog controller). This will be the index action, and will be accessible via the resources route posts_path.
There are several link_to snippets that support the resources routes:
lip – <%= link_to "link text...", model_path(@model) %>
lipp – <%= link_to "link text...", models_path %>
linp – <%= link_to "link text...", parent_child_path(@parent, @child) %>
linpp – <%= link_to "link text...", parent_child_path(@parent) %>
lim – <%= link_to model.name, model_path(model) %>
The tab stop points are in useful places.
So, to create our link to the posts page, type lipp and ?, type Show all posts, press ? twice and type post.
Controllers: respond_to and redirect_to
Now we’ll add a create action to the posts_controller.rb. Let’s go there (? ? ↓).
Below the edit method type def and ?, and type create and ?. Now fill out the create action like:
def create
? @post = Post.new(params[:post])
? if @post.save
? else
? end
end
Place the cursor in the true section of the if statement. Type repp and ? to create a redirect_to expression. Press ? again and replace the selected text with post.
Like the various link_to snippets, there are matching redirect_to snippets.
rep – redirect_to(model_path(@model))
repp – redirect_to(models_path)
renp – redirect_to(parent_child_path(@parent, @child))
renpp – redirect_to(parent_child_path(@parent))
There are tab stops in useful places.
In the false section of the if expression, we’ll demonstrate the respond_to block. There are two ways to generate a respond_to block.
Type rest and ?, and you get a standard empty block you can work with:
respond_to do |wants|
? wants.html {? }
end
Press ? twice to get inside the wants.html block, type ra, press ?, then type new. The final block is:
respond_to do |wants|
? wants.html { render :action => "new" }
end
Alternately, there is the “upgrade” hot key (? ? H), where you can convert some existing selected code, into a respond_to block.
Select the whole line containing the redirect_to expression from the true section of the if statement (? ? L).
Press ? ? H and the line is replaced with:
respond_to do |wants|
? wants.html do
??? redirect_to(posts_path)
? end
? wants.js {? }
end
The js is the first tab stop. The point of this hot key is to instantly refactor your existing html respond code, and support a second response format.
For now remove the line with wants.js (? ? K).
The completed create action is:
def create
? @post = Post.new(params[:post])
? if @post.save
??? respond_to do |wants|
????? wants.html do
??????? redirect_to(posts_path)
????? end
??? end
? else
??? respond_to do |wants|
????? wants.html { render :action => "new" }
??? end
? end
end
Yes you’d probably only have one respond_to block, but this is a demo so I am taking the scenic route.
Our application so far
In the browser, we can create posts via [url]http://localhost:3000/posts/new[/url] and then view them as a blog visitor at [url]http://localhost:3000/blog.[/url]
Some more migrations
We’re looking for the following additions:
rename the column name of table comments to author
add a new column author_url to table comments
add an index to the column post_id of the comments table
Let’s try to do this all in one migrations file. Start Quick Migration (? ? M). Let’s name it ModifyComments. A new migrations file 003_modify_comments.rb is created and opened, and the cursor is placed behind the mtab trigger. For now delete mtab and instead enter mcol and press ?. Choose Rename / Rename Column (3). Type comments ? name ? author ? ?.
Again type mcol and ?. This time choose Add / Remove Column (1). Type comments ? author_url, then ? twice, and press ?.
Now type mind and ?. Choose Add / Remove Index (1). Type comments ? post_id.
The end result looks like this:
class ModifyComments < ActiveRecord::Migration
? def self.up
??? rename_column :comments, :name, :author
??? add_column :comments, :author_url, :string
??? add_index :comments, :post_id
? end
? def self.down
??? remove_index :comments, :post_id
??? remove_column :comments, :author_url
??? rename_column :comments, :author, :name
? end
end
Notice how the down method calls are in reversed order of the up method calls.
Save the file (? S) and migrate to current (? |).
Be sure to modify the comments fixture file. Go there (? T, press cy, choose comments.yml). Rename name to author and add a row for author_url for each comment. Check your tests again (? \, choose option 1). All tests should pass.
Futhermore we’d like to know when a post was published. To do this we’ll want the following modifications:
keep track of the datetime when a post was published.
remove the column published from the posts table because it can be determined if a post is published by looking at whether or not a value is present for the published date.
Start Quick Migration (? ? M). Let’s name it AddPublishedAtForPosts. A new migrations file 004_add_published_at_for_posts.rb is created and opened, and the cursor is placed behind the mtab trigger. Again delete mtab and instead enter mcol and press ?. Choose Add / Remove Column (1). Type posts ? published_at ? datetime ? and ?.
Again type mcol and ?. Choose Remove / Add Column (5). Type posts ? published and press ? twice.
The end result looks like this:
class AddPublishedAtForPosts < ActiveRecord::Migration
? def self.up
??? add_column :posts, :published_at, :datetime
??? remove_column :posts, :published
? end
? def self.down
??? add_column :posts, :published, :boolean
??? remove_column :posts, :published_at
? end
end
Notice how the Remove / Add Column command automagically determined in the down method the column type of column published to be a boolean. It determines this by looking at the current state of your db/schema.rb file.
Save the file (? S) and migrate to current (? |).
Now we need to modify the posts fixtures file. Go there (? T, type pyml, choose posts.yml). Replace the line published: true by published_at: 2008-1-1.
Modify the posts functional test, first go there (? ? ? ↓, choose “Go to Functional Test”). Replace :published => '1' by :published_at => Date.new(2008, 1, 1).
Modify the post model, first go there (? ? ? ↓, choose “Go to Model”). Have the code look like:
class Post < ActiveRecord::Base
? has_many :comments
? def published
??? !self.published_at.nil?
? end
? def published=(publish)
??? if publish
????? self.published_at = DateTime.now if self.published_at.nil?
??? else
????? self.published_at = nil
??? end
? end
end
Modify the blog_controller.rb file. Replace Post.find_all_by_published(true) by Post.find(:all, :conditions => "published_at IS NOT NULL").
Finally, check your tests again (? \). All tests should pass.
本文轉(zhuǎn)自 fsjoy1983 51CTO博客,原文鏈接:http://blog.51cto.com/fsjoy/73182,如需轉(zhuǎn)載請自行聯(lián)系原作者
- artp –?assert_redirected_to model_path(@model)
- artpp –?assert_redirected_to models_path
- artnp –?assert_redirected_to parent_child_path(@parent, @child)
- artnpp –?assert_redirected_to parent_child_path(@parent)
- lip –?<%= link_to “l(fā)ink text...“, model_path(@model) %>
- lipp –?<%= link_to “l(fā)ink text...“, models_path %>
- linp –?<%= link_to “l(fā)ink text...“, parent_child_path(@parent, @child) %>
- linpp –?<%= link_to “l(fā)ink text...“, parent_child_path(@parent) %>
- lim –?<%= link_to model.name, model_path(model) %>
- rep –?redirect_to(model_path(@model))
- repp –?redirect_to(models_path)
- renp –?redirect_to(parent_child_path(@parent, @child))
- renpp –?redirect_to(parent_child_path(@parent))
現(xiàn)在我們可以在[url]http://localhost:3000/posts/new[/url] 添加新的文章,并且在[url]http://loclahost:3000/blog[/url] 查看文章了。 還有一些遷移工作 我們還有一些附加工作, 把comments表的name列改名為author 在comments表中增加一個author_url字段 在comments表中的post_id字段增加一個索引 讓我們嘗試在一個遷移文件中做這些事情。 啟動快速遷移(shift+control+M),給我們的遷移起個名字叫做ModifyComments,自動生成了003_Modify_comments.rb并打開,光標(biāo)放在了mtab后面,刪除它,鍵入mcol,然后tab,選擇Rename / Rename Column?(3),鍵入comments???name???author????. 同樣我們再次鍵入mcol,然后tab,選擇Add / Remove Column(1),鍵入comments?author_url,然后?兩次。 現(xiàn)在鍵入mind,然后?,選擇Choose?Add / Remove Index(1),鍵入comments?post_id. ? 最終的代碼如下: class ModifyComments < ActiveRecord::Migration def self.up rename_column :comments, :name, :author add_column :comments, :author_url, :string add_index :comments, :post_id end def self.down remove_index :comments, :post_id remove_column :comments, :author_url rename_column :comments, :author, :name end end 請注意,down方法的順序是和up方法的順序相反的。 保存并執(zhí)行這個遷移 要記住修改comments的fixture文件。先打開文件(command+T,鍵入cy,回車),把name字段改名為author字段,并在每一節(jié)增加author_url,然后測試,應(yīng)該都順利通過。 此外,我們希望當(dāng)一篇文章發(fā)表時我們可以被通知到,為了做到這一點(diǎn),我們需要做以下的修改: 當(dāng)文章發(fā)布時,有一個時間日期published date可以跟蹤; 移除published這個字段,因?yàn)槭欠褚呀?jīng)發(fā)布我們可以看published date。 開始快速遷移(shift+control+M),起個名字叫做?AddPublishedAtForPosts,?一個新的遷移文件004_add_published_at_for_posts.rb,光標(biāo)還是在mtab后面,一樣,我們把mtab刪除,鍵入mcol,然后tab,在彈出來的菜單上選擇Add / Remove Column?(1),?鍵入posts??published_at???datetime???和enter。 再次鍵入mcol,tab,然后選擇Remove / Add Column(5),鍵入posts???published,然后tab兩次。 最終代碼如下: class AddPublishedAtForPosts < ActiveRecord::Migration def self.up add_column :posts, :published_at, :datetime remove_column :posts, :published end def self.down add_column :posts, :published, :boolean remove_column :posts, :published_at end end Remove/Add Column命令自動在down方法中決定published字段為布爾字段,這取決于當(dāng)前數(shù)據(jù)庫db/schema.rb文件的狀態(tài)。 然后保存并執(zhí)行遷移。 現(xiàn)在我們要修改posts fixture文件。選擇posts.yml文件(command+T, 鍵入pyml,選擇posts.yml)。用published_at: 2008-01-01代替published: true。?修改posts功能測試,首先先打開測試文件( shift+option+command+下箭頭,選擇Go to functional Test),用:published_at => Date.new(2008, 1, 1)?替換?:published => '1'. 修改post模型,打開posts.rb(shift+option+command+下箭頭,選擇Go to Model), 黏貼下面的代碼: class Post < ActiveRecord::Base has_many :comments def published !self.published_at.nil? end def published=(publish) if publish self.published_at = DateTime.now if self.published_at.nil? else self.published_at = nil end end end 修改blog_controller.rb,用Post.find(:all, :conditions => “published_at IS NOT NULL“)代替Post.find_all_by_published(true)。然后測試運(yùn)行,所有的測試都應(yīng)該通過。 這篇文章的原文在ruby on rails bundle的幫助里,在TextMate里面打開rails項(xiàng)目的一個rb文件,使用快捷鍵Control+H,然后選擇2,就可以看到這篇文章了。最新的ror bundle更新于上個月底,還是很具有參考價值的,對于如何快速使用TextMate進(jìn)行rails開發(fā)是一個不錯的教程。
不過還有一些沒完成。不是特別完整。但是基本的快捷鍵都包含在里面了。
---------英文原版教程
Step-by-step demonstration
Get Version
1.90.0??
In this demo we’ll create a blog; because that’s what blogs are for: being demonstrations of web frameworks.
The demonstration uses new features of Rails 2.0 and the snippets in this bundle.
A New App
rails blog
cd blog
mate .
Add some models
ruby script/generate model Post subject:string body:text
This creates a 001_create_posts migration with a create_table:
create_table :posts do |t|
? t.string :subject
? t.text :body
? t.timestamps
end
Sexy Migration support
If you put the cursor on the line after t.text :body, type t. and press ?. Select “Create boolean column” (by pressing 0), and type “published” into the template field. If nothing happened when you pressed ?, check that when you opened the migrations file you’ve selected the bundle “Ruby on Rails”.
Note that another t. was created on the next line! Press ? and the cursor will be placed after it. You can now press ? again to create another column, or delete this line.
Here, delete the extraneous t. line (? ? K). And save the file (? S).
Run the migrations, either from the prompt:
rake db:migrate
or directly from the editor with ? | (Ctrl-Pipeline), and choosing option “Migrate to Current”.
Post fixtures
Update the test/fixtures/posts.yml file as:
published:
? subject: Some article
? body: A test article
? published: true
nonpublished:
? body: Still writing this one
Note, in Rails 2.0 fixtures no longer have explicit ids. Later on we’ll look at snippets for using Foxy Fixtures with auto-completion for associations.
Public blog controller
Create a controller for our blog, either via the command prompt:
ruby script/generate controller blog
or directly from the editor with ? |, and choosing option “Call Generate Script”, choose “Controller”, give it the name “blog”, and empty the list of actions.
Now open blog_controller_test.rb. To find this file quickly press ? T, type bct, and select the file.
Note how much cleaner functional tests are now via ActionController::TestCase.
Let’s do some TDD. First, delete the test_truth dummy method.
To create a test to show a list of blog articles:
deftg
and ? gives:
def test_should_get_action
? @model = models(:fixture_name)
? get :action, :id => @model.to_param
? assert_response :success
end
Type index to replace action. Press ?, and then ? to remove the first line, then press ? three times and then ? to remove the :id => @model.to_param part. The press ? again to go to the end of the method. Now we have:
def test_should_get_index
? get :index
? assert_response :success
end
Now type asg, press ?, and type posts, and press ? again. This creates an instance variable lookup within an assertion:
assert(posts = assigns(:posts), "Cannot find @posts")
Now, let’s assert the HTML format.
Type ass and press ?. Type div#posts, press ? and ?, then ? twice to place the cursor within the assert_select block:
assert_select 'div#posts' do
end
Now we’ll check that the @posts objects are represented in the div#posts element.
With the cursor inside the assert_select:
Type ass, press ?, type div.post, press ? twice, and type count (to replace the text). Now press ? again, and type posts.size. Press ? a final time (it will highlight the do...end block), and press ?.
Our test method is now finished:
def test_should_get_index
? get :index
? assert_response :success
? assert(posts = assigns(:posts), "Cannot find @posts")
? assert_select 'div#posts' do
??? assert_select 'div.post', :count => posts.size
? end
end
NOTE: there is also a deftp snippet for functional tests to create a POST test stub. To memorize: deftg stands for define test get and deftp stands for define test post
Controller actions
To navigate to blog_controller.rb there are three options:
press ? ? ? ↓, and select “Controller” from the drop-down list
press ? ? ↓ and you’ll go directly to the controller (toggles between the two files)
press ? T, type bc, choose the file, press ?.
Add the index action method:
def index
? @posts = Post.find_all_by_published(true)
end
Action views
To create/navigate to the view, press ? ? ? ↓ and select “View” (like above). Or press ? ? ↓ to toggle between a controller method and it’s view.
As there are no app/views/blog/index* files, it will prompt you to create a blank view file. By default it guesses index.html.erb (because the method was named index), but of course you can change that in the dialog box.
If instead you got the message “blog_controller.rb does not have a view”, note that you first need to save the controller file before hitting ? ? ? ↓ or ? ? ↓. Also note that the cursor must be within the scope of a method for ? ? ? ↓ or ? ? ↓ to work.
Press enter to accept index.html.erb. You are taken to the new file.
Let’s create HTML to match the earlier tests.
Type div and press ? twice, then type posts and press ?:
<div id="posts">
</div>
Inside the div element, type for and press ?. This expands into a large ERb-enabled for-loop. Type @posts, press ?, type post and press ?. The cursor is now inside the for-loop.
Inside the for-loop, type: div and press ?. Press ?, and type? class='post' and press ? to enter the div element.
Create a <%=? %> element (? >). If you press ? > again, it toggles to <%? %>, and then again and it becomes <%-? -%>, and again and it becomes <%#? %> (a Ruby comment). Pressing ? > again starts at <%=? %> again.
Enter post.body within the ERb template field.
Actually, we’ll need to show the subject too, so above the <%= post.body %> line (press ↑ followed by ? ?) type ‘h3’, and press ? < (LessThan), then ? > (GreatherThan), and post.subject.
The resulting line is: <h3><%= post.subject %></h3>
Move the cursor down between <% else %> and <% end %>.
Create a simple element <p></p> (? ? W or ? <). You can change the element type here. Just press ? to go inside the element. Type There are no posts available to read. All y'all come back soon, yer hear. because its funny.
Our index.html.erb template is now:
<div id="posts">
? <% if !@posts.blank? %>
??? <% for post in @posts %>
????? <div class="post">
??????? <h3><%= post.subject %></h3>
??????? <%= post.body %>
????? </div>
??? <% end %>
? <% else %>
??? <p>There are no posts available to read. All y'all come back soon, yer hear.</p>
? <% end %>
</div>
If we run our functional tests they now pass: run either from the command prompt with rake test:functionals or directly from the editor by pressing ? \ and press 2 for “Test Functionals”
As yet, we have no way for users to leave comments.
Foxy Fixtures
Create a comment model:
ruby script/generate model Comment body:text name:string post:references
Note: here post:references is effectively the same as post_id:integer. Within the generated migration it creates t.reference :post. There is also a t. and tcr snippet for references, as for other standard datatypes, which helps setup polymorphic associations.
The generated create_table in 002_create_comments.rb is:
create_table :comments do |t|
? t.text :body
? t.string :name
? t.references :post
? t.timestamps
end
Run rake db:migrate, or directly from the editor with ? | and choose option “Migrate to Current”.
Now create some comment fixtures so we can look at Foxy Fixtures. Open text/fixtures/comments.yml (? T, type cy, press ?).
By default, the generated comments.yml starts like:
one:
? body: MyText
? name: MyString
? post:
two:
? body: MyText
? name: MyString
? post:
The post fields replace the rails1.2 post_id fields. Now, we can specify the post.yml labels for a post. From above we have published and unpublished. It can be hard to remember what fixtures we have, so there is a key-combo helper.
Put the cursor after post: and press ? ?. A drop-down box appears with the names of the posts.yml fixtures. Select published and press ?. Repeat for the 2nd fixture. This gives us:
one:
? body: MyText
? name: MyString
? post: published
two:
? body: MyText
? name: MyString
? post: published
Associations
To enable the Foxy Fixtures, we need to add associations to the model classes.
You can now quickly go from a fixtures file (we’re in comments.yml) to the model file (? ? ? ↓).
Within comment.rb model, create a new line within the class, and type bt and press ?. Type post. This creates a snippet:
belongs_to :post, :class_name => "Post", :foreign_key => "post_id"
The class name and foreign key are now generated from the association name. You can change them by tabbing across. But, we only need the default, so we can delete these options.
Press ? and ? to remove the :class_name and :foreign_key options. The Comment class is now:
class Comment < ActiveRecord::Base
? belongs_to :post
end
Now go to the Post class. Press ? T and type post and select the model file, and press ?.
Create a new line within the Post class (? ?). Type hm and press ? to generate a has_many association. Type comment, and the resulting snippet is:
has_many :comments, :class_name => "comment", :foreign_key => "class_name_id"
We don’t need the options. So press ? once and then ?.
class Post < ActiveRecord::Base
? has_many :comments
end
Note: there is also a has_many :through snippet. Type hmt and ? to activate it.
Finally, we can run our tests since adding the Comment model + fixtures (? \).
rake test
Routes
Open the routes file (? T, type routes and press ?).
Change the routes file to:
ActionController::Routing::Routes.draw do |map|
? map.resources :posts
? map.connect ':controller/:action/:id'
? map.connect ':controller/:action/:id.:format'
end
Creating Posts
From the Post model class (post.rb) you can now quickly navigate to a controller of the same name. It supports either singular or plural controller names, but will default to the plural name, which is the REST/resources preferred name.
To create a PostsController, use the ‘Go To’ hot key (as above) ? ? ? ↓ and select ‘Controller’. As there is no post_controller.rb nor posts_controller.rb it will create a posts_controller.rb controller file; which is what we want here.
Note; at this stage you could use the Rails 2.0 scaffold generator to create the posts_controller.rb (and tests and routes).
In the blank file, we need to create a controller class.
Type cla and ?, and select “Create controller class”. Type Posts and ?, post and ?, and finally, Post and ?. This leaves the cursor in the middle of the generated class:
class PostsController < ApplicationController
? before_filter :find_post
? private
? def find_post
??? @post = Post.find(params[:id]) if params[:id]
? end
end
TDD for Posts controller
Currently there is not a functional test for our posts_controller.rb. To create it, use the ‘Go To’ hot key (? ? ? ↓) and select ‘Functional Test’. This will create a blank file.
Type cla and ?, and select “Create functional test class”. Type Posts and ?. (The functional test class name should match the controller class, with Test suffixed to it).
The functional test class snippet gives you a deft stub. If you press ? now, it creates a generic test method snippet:
def test_case_name
end
Instead, we will use the deftg (GET request) and deftp (POST request) snippets.
Create a test for the index, new and edit actions. For index and new, we can delete the @model = models(:fixture_name), etc parts.
To test for the create action, type deftp and ?. Type create and ?, type post and ?, type ? and ?, and again ? and ?. Now enter in a hash of the values to pass in for the test, say :subject => 'Test', :body => 'Some body', :published => '1'. The result should look like:
def test_should_post_create
? post :create, :post => { :subject => 'Test', :body => 'Some body', :published => '1' }
? assert_response :redirect
end
On the line after the assert_response expression, we’ll test for where we want to be redirected to.
If you type art you create an old-style assert_redirected_to :action => "index" snippet.
In addition there are now various assert_redirected_to snippets that use resourceful routes:
artp – assert_redirected_to model_path(@model)
artpp – assert_redirected_to models_path
artnp – assert_redirected_to parent_child_path(@parent, @child)
artnpp – assert_redirected_to parent_child_path(@parent)
As we’ll see later, this naming scheme is used for other snippets that use resourceful routes, like link_to and redirect_to.
Type artpp and ?, and type post, to assert that the create action must redirect to the index page.
The final test_should_post_create method is:
def test_should_post_create
? post :create, :post => { :subject => 'Test', :body => 'Some body', :published => '1' }
? assert_response :redirect
? assert_redirected_to posts_path
end
Running our tests (rake test:functionals or ? \) shows all these new tests failing.
Views
Go back to the posts_controller.rb file (? ? ↓).
Now add three actions – index, new and edit. New methods can be created with the def snippet:
class PostsController < ApplicationController
? before_filter :find_post
? def index
??? @posts = Post.find(:all)
? end
? def new
??? @post = Post.new
? end
? def edit
? end
? private
? def find_post
??? @post = Post.find(params[:id]) if params[:id]
? end
end
Note: the index method could be created by typing def, ?, index, ?,? @posts = Post.fina, ?, ?.
Now we need templates for the index, new and edit actions.
Place the cursor inside the index method, and use the ‘Go To’ hot key (? ? ? ↓) and select ‘View’. A dialog box will pop up asking for the name of the new template (as there are no app/views/posts/index* files). By default, the suffix is now .html.erb rather than the old .rhtml. Press ?, to accept index.html.erb as your template name.
Let’s just create a simple table showing the Posts.
Type table and ? < to generate <table></table>, and press ? to put the tags on separate lines.
Do the same to create a <tbody></tbody> element.
Inside the <tbody></tbody> we want to iterate over the @posts, one per <tr></tr> row.
Press ? >, three times, to create a <%- -%> tag. Inside it type @posts.each do |post|.
On the next line (? ?), type end and ?, to create <% end -%>. We now have a Ruby block within this ERb template.
Inside the block, create a <tr></tr> element, and within it create a <td></td> element. We’ll skip over anything fancy here, and just put the post’s subject here.
Type post.subject and select it. Now press ? > to wrap the selected text inside <%= post.subject %>.
The resulting index.html.erb is:
<table>
? <tbody>
??? <%- @posts.each do |post| -%>
????? <tr>
??????? <td><%= post.subject %></td>
????? </tr>
??? <% end -%>
? </tbody>
</table>
Forms
Place the cursor inside the new method, and use the ‘Go To’ hot key (? ? ? ↓) and select ‘View’. Press ? to accept new.html.erb.
Inside the blank new.html.erb file, type ffe and press ?, and type post and press ? twice:
<%= error_messages_for :post %>
<% form_for @post do |f| -%>
<% end -%>
form_for is the Rails 2.0 preferred helper for managing forms, and there are now snippets for common form_for helpers. There are ff and ffe snippets; the former does not have the error messages section.
To create a label and text field for the subject attribute:
Create a <p></p> block (Press ? <, then ?, then ?). Type f. and ?, and select “Label”. Type subject, press ? and press ?. Create a <br /> (? ?). Type f. and ?, and select “Text Field”. Type subject.
This gives us:
<%= error_messages_for :post %>
<% form_for @post do |f| -%>
? <p>
??? <%= f.label :subject %><br />
??? <%= f.text_field :subject %>
? </p>
<% end -%>
Now repeat for body and published fields.
Note, for published, you might change the label to Published yet? by tabbing into the default string file.
Finally, add a “Submit” button using the f. snippet tab completion.
Start script/server from the prompt and you can now view this form at [url]http://localhost:3000/posts/new[/url]
The final form is:
<%= error_messages_for :post %>
<% form_for @post do |f| -%>
? <p>
??? <%= f.label :subject %><br />
??? <%= f.text_field :subject %>
? </p>
? <p>
??? <%= f.label :body %><br />
??? <%= f.text_area :body %>
? </p>
? <p>
??? <%= f.label :published, "Published yet?" %><br />
??? <%= f.check_box :published %>
? </p>
? <p>
??? <%= f.submit "Submit" %>
? </p>
<% end -%>
Note: if you got <br> when hitting ? ? instead of <br /> then you might want to go to the preferences of TextMate (? ,), choose tab “Advanced”, choose “Shell Variables”, click the + sign to add a new shell variable, and give it the name TM_XHTML and a value of? /
Partials
The form we just created is exactly the same as the form required for the edit.html.erb template.
Instead of copy+pasting it into the edit.html.erb file, we’ll create a partial template.
Select the entire form (? A), and press ? ? H and a dialog box appears. Type in form and press ?.
You’ll notice a new file _form.html.erb has appeared which contains the code you had selected, while the code in the file new.html.erb has been replaced by:
<%= render :partial => 'form' %>
Now copy and paste this into the edit.html.erb file. To create this file, return to the controller (from the new.html.erb file, press ? ? ↓), go to the edit action, and use ? ? ↓ again to create the edit.html.erb template file.
Link helpers
At the bottom of the new.html.erb we want a link back to the list of all posts (within the posts controller, not the public blog controller). This will be the index action, and will be accessible via the resources route posts_path.
There are several link_to snippets that support the resources routes:
lip – <%= link_to "link text...", model_path(@model) %>
lipp – <%= link_to "link text...", models_path %>
linp – <%= link_to "link text...", parent_child_path(@parent, @child) %>
linpp – <%= link_to "link text...", parent_child_path(@parent) %>
lim – <%= link_to model.name, model_path(model) %>
The tab stop points are in useful places.
So, to create our link to the posts page, type lipp and ?, type Show all posts, press ? twice and type post.
Controllers: respond_to and redirect_to
Now we’ll add a create action to the posts_controller.rb. Let’s go there (? ? ↓).
Below the edit method type def and ?, and type create and ?. Now fill out the create action like:
def create
? @post = Post.new(params[:post])
? if @post.save
? else
? end
end
Place the cursor in the true section of the if statement. Type repp and ? to create a redirect_to expression. Press ? again and replace the selected text with post.
Like the various link_to snippets, there are matching redirect_to snippets.
rep – redirect_to(model_path(@model))
repp – redirect_to(models_path)
renp – redirect_to(parent_child_path(@parent, @child))
renpp – redirect_to(parent_child_path(@parent))
There are tab stops in useful places.
In the false section of the if expression, we’ll demonstrate the respond_to block. There are two ways to generate a respond_to block.
Type rest and ?, and you get a standard empty block you can work with:
respond_to do |wants|
? wants.html {? }
end
Press ? twice to get inside the wants.html block, type ra, press ?, then type new. The final block is:
respond_to do |wants|
? wants.html { render :action => "new" }
end
Alternately, there is the “upgrade” hot key (? ? H), where you can convert some existing selected code, into a respond_to block.
Select the whole line containing the redirect_to expression from the true section of the if statement (? ? L).
Press ? ? H and the line is replaced with:
respond_to do |wants|
? wants.html do
??? redirect_to(posts_path)
? end
? wants.js {? }
end
The js is the first tab stop. The point of this hot key is to instantly refactor your existing html respond code, and support a second response format.
For now remove the line with wants.js (? ? K).
The completed create action is:
def create
? @post = Post.new(params[:post])
? if @post.save
??? respond_to do |wants|
????? wants.html do
??????? redirect_to(posts_path)
????? end
??? end
? else
??? respond_to do |wants|
????? wants.html { render :action => "new" }
??? end
? end
end
Yes you’d probably only have one respond_to block, but this is a demo so I am taking the scenic route.
Our application so far
In the browser, we can create posts via [url]http://localhost:3000/posts/new[/url] and then view them as a blog visitor at [url]http://localhost:3000/blog.[/url]
Some more migrations
We’re looking for the following additions:
rename the column name of table comments to author
add a new column author_url to table comments
add an index to the column post_id of the comments table
Let’s try to do this all in one migrations file. Start Quick Migration (? ? M). Let’s name it ModifyComments. A new migrations file 003_modify_comments.rb is created and opened, and the cursor is placed behind the mtab trigger. For now delete mtab and instead enter mcol and press ?. Choose Rename / Rename Column (3). Type comments ? name ? author ? ?.
Again type mcol and ?. This time choose Add / Remove Column (1). Type comments ? author_url, then ? twice, and press ?.
Now type mind and ?. Choose Add / Remove Index (1). Type comments ? post_id.
The end result looks like this:
class ModifyComments < ActiveRecord::Migration
? def self.up
??? rename_column :comments, :name, :author
??? add_column :comments, :author_url, :string
??? add_index :comments, :post_id
? end
? def self.down
??? remove_index :comments, :post_id
??? remove_column :comments, :author_url
??? rename_column :comments, :author, :name
? end
end
Notice how the down method calls are in reversed order of the up method calls.
Save the file (? S) and migrate to current (? |).
Be sure to modify the comments fixture file. Go there (? T, press cy, choose comments.yml). Rename name to author and add a row for author_url for each comment. Check your tests again (? \, choose option 1). All tests should pass.
Futhermore we’d like to know when a post was published. To do this we’ll want the following modifications:
keep track of the datetime when a post was published.
remove the column published from the posts table because it can be determined if a post is published by looking at whether or not a value is present for the published date.
Start Quick Migration (? ? M). Let’s name it AddPublishedAtForPosts. A new migrations file 004_add_published_at_for_posts.rb is created and opened, and the cursor is placed behind the mtab trigger. Again delete mtab and instead enter mcol and press ?. Choose Add / Remove Column (1). Type posts ? published_at ? datetime ? and ?.
Again type mcol and ?. Choose Remove / Add Column (5). Type posts ? published and press ? twice.
The end result looks like this:
class AddPublishedAtForPosts < ActiveRecord::Migration
? def self.up
??? add_column :posts, :published_at, :datetime
??? remove_column :posts, :published
? end
? def self.down
??? add_column :posts, :published, :boolean
??? remove_column :posts, :published_at
? end
end
Notice how the Remove / Add Column command automagically determined in the down method the column type of column published to be a boolean. It determines this by looking at the current state of your db/schema.rb file.
Save the file (? S) and migrate to current (? |).
Now we need to modify the posts fixtures file. Go there (? T, type pyml, choose posts.yml). Replace the line published: true by published_at: 2008-1-1.
Modify the posts functional test, first go there (? ? ? ↓, choose “Go to Functional Test”). Replace :published => '1' by :published_at => Date.new(2008, 1, 1).
Modify the post model, first go there (? ? ? ↓, choose “Go to Model”). Have the code look like:
class Post < ActiveRecord::Base
? has_many :comments
? def published
??? !self.published_at.nil?
? end
? def published=(publish)
??? if publish
????? self.published_at = DateTime.now if self.published_at.nil?
??? else
????? self.published_at = nil
??? end
? end
end
Modify the blog_controller.rb file. Replace Post.find_all_by_published(true) by Post.find(:all, :conditions => "published_at IS NOT NULL").
Finally, check your tests again (? \). All tests should pass.
本文轉(zhuǎn)自 fsjoy1983 51CTO博客,原文鏈接:http://blog.51cto.com/fsjoy/73182,如需轉(zhuǎn)載請自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的textmate开发一个blog的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET MVC导出excel(数
- 下一篇: js_调试_01_14 个你可能不知道的