textmate开发一个blog

TextMate 开发一个简单的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“图标安装此bundle
这样,Ruby on Rails 2.0 的bundle就安装到 TextMate里面了
 
下面可以开始开发一个新的应用了
 
在终端里输入
 
Ruby代码  复制代码
  1. rails blogcd blogmate .  
 
 
增加一个模型
 
Ruby代码  复制代码
  1. ruby script/generate model Post subject:string body:text  
 
 
生成了一个新的迁移 001_create_posts.rb,包含一个方法 Create_Table
 
Ruby代码  复制代码
  1. create_table :posts do |t|  t.string :subject  t.text :body  t.timestampsend  
 
 
Sexy Migration的支持
 
把光标放在 t.text :body下面一行,输入 t. 然后按下 ⇥( Tab键),选择“ Create boolean column” 或者按下 0 ,创建一个布尔字段,然后输入 published,如果按下Tab键没有反应,看看是否选择对了Ruby on Rails的bundle(在底下状态栏里)
 
请注意这个时候下面还有一行t. 如果需要继续输入其他字段,就可以再按下Tab键直接输入。
 
我们现在不需要别的更多的字段了,就按下 ⌃   ⇧  K( Control+Shift+K)把这一行删除。然后保存文件 ⌘  S
 
然后运行迁移,可以在命令行输入
 
 
Ruby代码  复制代码
  1. rake db:migrate  
 
 
或者直接在编辑器里按下 ⌃ | (Control+| ),选择“Migrate to Current” 。
 
Post Fixtures
 
修改一下test/fixtures/post.yml
 
 
Ruby代码  复制代码
  1. published:  subject: Some article  body: A test article  published: truenonpublished:  body: Still writing this one  
 
 
在Rails2.0里,fixtures不再使用id了,后面我们可以看到怎么使用foxy fixtures来自动关联。
 
 
Public blog controller
 
创建一个控制器,可以通过命令行
 
Ruby代码  复制代码
  1. ruby script/generate controller blog  
 
 
或者直接在编辑器里按下 ⌃  |( Control+|),选择“ Call Generate Script”,再选择“Controller”,给它起个名字叫做blog,action里不要设置任何内容。
 
现在打开blog_controller_test.rb ,要快速找到文件,可以按下  T ,然后输入 bct 就可以找到了。
 
可以看到,功能性的测试是非常清楚和简单的。
 
让我们开始 TDD 吧,先删除   test_truth  方法
 
然后创建一个测试来显示 blog文章列表
输入deftg
然后按下 Tab
自动生成了以下代码
 
Ruby代码  复制代码
  1. def test_should_get_action  @model = models(:fixture_name)  get :action:id => @model.to_param  assert_response :successend  
 
 
光标停留在action上,用index代替它,然后按下Tab键,光标选择了方法的第一行,删除它,然后按三次Tab键,删除 :id => @model.to_param  部分。再次按下Tab键,光标跳到方法的最后一行。现在的代码应该是这样的:
 
 
Ruby代码  复制代码
  1. def test_should_get_index  get :index  assert_response :successend  
 
 
键入 asg ,然后按下Tab键,然后键入posts,然后按下Tab键。
 
下面,键入ass ,然后按下Tab键,输入div#posts,按下Tab键,删除余下的部分。然后连续按Tab键两次,把光标放在assert_select里面,现在代码如下
 
 
Ruby代码  复制代码
  1. assert_select 'div#posts' doend  
 
 
键入ass,按下Tab键,键入div.post,然后按下Tab两次,键入count,取代掉text。
现在再按下Tab键,键入post.size,最后一次按下Tab键,删除被选中的部分。
 
好了,现在我们的测试方法完成了
 
 
Ruby代码  复制代码
  1. 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 代码片段来创建一个POST 方法的测试。很容易记住的,deftg 代表define test get, deftp 代表define test post
 
Controller actions
要导航到blog_controller.rb文件,有三种选择:
1,按下 ⇧   ⌥   ⌘   ↓( Shift+option+command+下箭头),然后在弹出的菜单中选择“Controller”,
2,按下⌥  ⌘  ↓ (option+command+ 下箭头),然后直接去controller 文件(来回切换)
3,按下 ⌘  T,然后键入bc,找到那个文件。
 
增加一个index方法
 
Ruby代码  复制代码
  1. def index  @posts = Post.find_all_by_published(true)end  
 
 
Action Views
 
要创建或者导航到一个视图,可以按下 ⇧   ⌥   ⌘   ↓( shift+option+command+下箭头),然后选择  “View”(就像上面那样),或者按下 ⌥   ⌘   ↓  ( option+command+下箭头)来切换控制器的方法和对应的视图。
因为没有对应的 app/views/blog/index*文件,所以会提示要求创建一个空白视图,默认的它会猜测文件名称为 index.html.erb 因为方法的名字叫做 index,但是当然您可以改为其他名字。
如果您得到一个提示: blog_controller.rb does not have a view,请注意先要保存控制器文件,然后在切换到视图。同时请注意,光标要在控制器方法的范围内,才能起作用。
 
好了,我们转到刚创建的index.html.erb
 
键入div,然后按两次Tab,把id改为post,然后再按Tab,应该是这样:
 
<div id=“posts“>
 
</div>
 
在div标签中,键入for,按下Tab,生成一个for循环,在光标处替换list为@posts,按下Tab,然后键入post,替换掉item,再按下Tab,现在光标应该在for循环里面了。
键入div,然后Tab,生成了div标签,删除id,键入class='post',再按Tab,进入了div标签里面。
 
用control+>创建一个<%= %>, 如果再按一下control+>,就会生成<% %>,再按一下,会生成<%- -%>,再按一下就会生成<%# %>,如此循环往复。
 
键入post.body
 
实 际上,我们还需要显示标题,所以在<%= post.body %>这一行上面(按下command+return,上箭头),键入h3,然后按下control+<,生成<h3>标签。键入 control+>,键入post.subject
 
在下面的 <% else %><% end %> 中间,创建一个简单的标记<p></p>(使用control+<),键入There are no posts available to read. All y'all come back soon, yer hear.  呵呵,纯粹好玩儿。
 
好了,我们的视图现在是这样子了:
 
 
Ruby代码  复制代码
  1. <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>  
 
 
现在可以运行单元测试了,应该可以通过。可以在命令行中输入:rake test:functionals   或者直接在编辑器里键入control+\然后选择2: Test Functionals
 
我们现在还不能让用户留言。
 
 
Foxy Fixtures
 
创建一个comment模型
ruby script/generate model Comment body:text name:string post:references
 
请注意,这里post:references的作用和以前的post_id:integer是一样的。在生成的迁移文件中,它创建了 t.reference :post
下面是生成的第二个迁移文件
 
Ruby代码  复制代码
  1. create_table :comments do |t|  t.text :body  t.string :name  t.references :post  t.timestampsend  
 
 
运行这个迁移。命令行里键入 rake db:migrate , 或直接在编辑器按下control+| 然后在弹出的菜单重选择 Migrate to Current
 
下面,我们创建几个comment fixtures ,以便我们看看Foxy Fixtures 怎么运行的。
打开test/fixtures/comments.yml:
 
 
Ruby代码  复制代码
  1. one:  body: MyText  name: MyString  post:two:  body: MyText  name: MyString  post:  
 
 
字段post替代了rails1.2中post_id的位置。
上面我们有了published和nonpublished,或许很难记住,我们有一个快捷键。
把光标放在post: 后面,然后按下 option+Esc , 就可以选择了。
 
关联
 
为了能够用到Foxy Fixtures,我们必须增加两个模型类之间的关联。
现在你已经可以快速切换到模型类的文件了,(shift+option+command+下箭头)在comment.rb 模型类文件中,键入bt然后按下Tab键,键入post
 
Ruby代码  复制代码
  1. belongs_to :post:class_name => “Post“, :foreign_key => “post_id“  
 
 
现在:class_name和:foreign_key都已经一起自动的把object改为post了,当然,您继续按下Tab键,还可以继续改。不过我们这里就不需要了,直接删除后面的就行了。
 
现在它是这样的:
 
Ruby代码  复制代码
  1. class Comment < ActiveRecord::Base  belongs_to :postend  
 
 
然后去看看post类,打开Post.rb
 
键入hm,然后Tab。 同样的我们不需要那些option,删除。
 
 
Ruby代码  复制代码
  1. class Post < ActiveRecord::Base  has_many :commentsend  
 
如果您需要has_many :through ,可以键入hmt然后tab,就行了。
 
最后我们测试一下。(control+\)
 
 
 
路由
 
打开路由文件routes.rb
 
修改一下:
 
Ruby代码  复制代码
  1. ActionController::Routing::Routes.draw do |map|  map.resources :posts  map.connect ':controller/:action/:id'  map.connect ':controller/:action/:id.:format'end  
 
 
 
 
创建文章
 
从Post模型文件post.rb,您可以快速导航到同名的控制器文件,可以支持单数形式的控制器名或者复数形式的控制器名称,但是默认都是复数形式的,以更加符合REST风格。
 
要 创建一个PostsController,可以使用Go To...的快捷键shift+option+command+下箭头,然后选择Controller,因为没有post_controller.rb或 者posts_controller.rb,所以会问您是否需要创建一个,确认后,就得到我们自己的posts_controller.rb文件了。
 
注意,我们通常也可以使用脚手架来自动生成控制器文件,包括路由和测试文件。
 
在posts_controller里面,我们先要创建一个控制器类。
如果posts_controller.rb里面有内容,清空全部的内容,然后键入cla,按下Tab,选择 Create controller class ,在光标处键入 Posts ,然后 tab ,然后键入 post ,然后 tab ,最后键入 Post ,然后 tab ,光标回到了类的中间。
 
 
Ruby代码  复制代码
  1. class PostsController < ApplicationController  before_filter :find_post  private  def find_post    @post = Post.find(params[:id]) if params[:id]  endend  
 
 
TDD测试PostsController
 
当前是没有一个PostsController的功能测试的,我们可以继续使用GoTo...的快捷键:shift+option+command+下箭头,然后选择Functional Test,就会创建一个控制器测试文件
 
然后分别为index, new, edit三个方法建立测试,使用deftg+Tab的方式。
 
要建立create方法的测试,请键入deftp,然后按下Tab,键入create,然后tab,键入post,然后tab,按下delete键,再tab,再delete,再Tab,光标就转移到{}中,然后修改成下面这样:
 
Ruby代码  复制代码
  1. def test_should_post_create  post :create:post => { :subject => 'Test':body => 'Some body':published => '1' }  assert_response :redirectend  
 
 
在assert_response这一行后面,我们可以测试我们想跳转重定向的位置。
如果您键入art,就会创建一个:assert_redirected_to :action => “index“   代码片段
现在增加了好几个assert_redirected_to的代码片段,使用到了资源化路由( resourceful route)
 
  • 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)
 
键入artpp,然后按下Tab,然后键入post,这样生成的断言就会要求create动作重定向到index页面
 
最后的test_should_post_create方法应该是这样的:
 
 
Ruby代码  复制代码
  1. def test_should_post_create  post :create:post => { :subject => 'Test':body => 'Some body':published => '1' }  assert_response :redirect  assert_redirected_to posts_pathend  
 
 
然后运行测试,然后可以看到这些新创建的测试都没通过
 
 
视图
 
 
现在回到posts_controller.rb 文件(option+command+ 下箭头)
我们要创建三个新的方法:index, new, edit。新方法可以这样来创建: 键入   def ,  ⇥ ,  index ,  ⇥ ,  @posts = Post.fina ,  ⇥ ,  ⌫ .
 
现在这三个方法分别需要三个视图模板来对应。
 
把光标放在 index方法中,呼叫出Goto...菜单(shift+option+command+下箭头),选择View,会创建一个新的index.html.erb模板,
 
让我们来创建一个表格来显示Posts吧
 
键入table,按下control+<,生成table标签。
然后在table标签中生成tbody标签
 
在tbody里,我们希望可以枚举 @posts ,每一行一条记录。
 
按三次control+>, 创建<%- -%>,在里面键入  @posts.each do |post|
 
转到下一行(command+enter),键入end, 按下Tab,生成<% end -%>,这样我们在这个erb模板中就有了一个ruby的代码块了。
 
在这个代码块中,生成tr和td,我们把post的标题放在这个单元格里。
 
键入 post.subject,选中,然后按下control+>,生成 <%= post.subject %> .
 
最后形成这样子:
 
Ruby代码  复制代码
  1. <table>  <tbody>    <%- @posts.each do |post| -%>      <tr>        <td><%= post.subject %></td>      </tr>    <% end -%>  </tbody></table>  
 
 
 
Forms
 
把光标放在Controller的new方法中,按下shift+option+command+下箭头,叫出Goto菜单,选择View,生成new.html.erb
 
在新的new.html.erb文件中,键入ffe,然后tab,键入post,然后tab两次
 
有两个snippet可以生成form_for: ff和ffe,唯一的区别就是ff生成的代码没有error_messages_for那部分内容
 
然后我们需要为subject属性增加一个标签和文本框:
 
首先生成一个p标签(control+<,然后tab),键入f. 然后tab,选择Label,把默认的改为subject,然后tab, 删除剩下的。
接着创建一个<br>标签(control+entern),键入f.然后tab,选择TextField,键入subject
 
现在重复创建body和published两个文本域
 
请注意,published这个属性的标签最好用published yet?,你可以tab进默认的字符串替换掉。
最后用f. snippet增加一个submit按钮
 
启动script/server,然后访问[url]http://localhost:3000/posts/new[/url]
 
最后的form代码如下:
 
Ruby代码  复制代码
  1. <%= 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标签,但不是<br />,而你希望更加符合xhtml的规范,请转到 TextMatepreferences ,选择 advanced 标签,选中 “Shell Variablees” ,增加一个变量叫做 TM_XHTML, 值是 /
 
 
Partials
 
刚才我们创建的这个form和edit.html.erb模板所需要的form是一模一样的,除了复制/粘帖的方法以外,我们还可以创建一个局部模板,减少不必要的重复。
全部选中form(command+A),然后按下control+shift+H,会出来一个对话框,我们给他一个名字叫做form,然后确定。
可以看到自动生成了一个名叫_form.html.erb的文件,刚才的form都在这个_form.html.erb文件中,而new.html.erb的内容被一个语句代替:
<%= render :partial => 'form' %>
 
然后我们创建edit.html.erb:  回到控制器文件,把光标放在edit方法上,按下shift+option+command+下箭头, 就自动生成了edit.html.erb,把刚才上面的语句粘帖到空白文件中。
 
 
 
Link Helpers
 
在new.html.erb底下我们还想创建一个新的返回全部文章列表的链接。在post控制器,而不是在blog控制器噢。其实就是调用index方法,通过resources rout: posts_path
 
下面这些link_to是支持resources route的:
 
  • 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) %>
 
 
键入lipp,然后tab,键入Show all posts,然后tab两次,键入post
 
(经过测试,lipp好像有些问题,不能生成相应的代码,可以在Bundle Editor里面选择Edit Snippets... ,把lipp的snippet改为:
${TM_TEMPLATE_START_RUBY_EXPR}link_to ${1:“${2:link text...}“}, ${10:model}s_path${TM_TEMPLATE_END_RUBY_EXPR}
 
这样就好了)
 
Controllers: response_to 和 redirect_to
 
现在我们在posts_controller.rb里面加上 create方法。先去访问文件(option+command+下箭头)
 
在edit方法下面,键入def,然后tab,然后键入create,然后tab。然后在create方法中填充下面的内容
 
 
Ruby代码  复制代码
  1. def create  @post = Post.new(params[:post])  if @post.save  else  endend  
 
把光标放在else上面,键入repp,然后tab2次。用post代替选中的文字。
 
redirect_to也有很多变化
 
  • 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))
 
 
在if语句条件为假的部分,我们演示一下respond_to 代码块。有两个方法生成respond_to代码块:
 
键入rest,然后tab,生成
 
 
Ruby代码  复制代码
  1. respond_to do |wants|  wants.html {  }end  
 
 
然后按两次tab,光标置入wants.html{}里面,键入ra,然后tab,然后键入new。最终代码如下:
 
 
Ruby代码  复制代码
  1. respond_to do |wants|  wants.html { render :action => “new“ }end  
 
 
另一个,可以使用“升级”了的快捷键(shift+command+H),可以把选中的代码转换到respond_to代码块里。
 
选中if语句条件为真部分的respond_to整行(shift+command+L)然后按shift+command+H,光标停在js上,删除这一行(control+shift+K),最终代码变成:
 
Ruby代码  复制代码
  1. 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  

 
现在我们可以在[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并打开,光标放在了mtab后面,删除它,键入mcol,然后tab,选择 Rename / Rename Column  (3) ,键入 comments     name     author     .
同样我们再次键入mcol,然后tab,选择 Add / Remove Column  (1),键入 comments ⇥author_url,然后 ⇥两次。
现在键入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方法的顺序相反的。
保存并执行这个迁移
 
要记住修改comments的fixture文件。先打开文件(command+T,键入cy,回车),把name字段改名为author字段,并在每一节增加author_url,然后测试,应该都顺利通过。
 
此外,我们希望当一篇文章发表时我们可以被通知到,为了做到这一点,我们需要做以下的修改:
当文章发布时,有一个时间日期published date可以跟踪;
移除published这个字段,因为是否已经发布我们可以看published date。
 
开始快速迁移(shift+control+M),起个名字叫做 AddPublishedAtForPosts   一个新的迁移文件004_add_published_at_for_posts.rb, 光标还是在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字段为布尔字段,这取决于当前数据库db/schema.rb文件的状态。
 
然后保存并执行迁移。
 
 
 
现在我们要修改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.rbshift+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) 。然后测试运行,所有的测试都应该通过。
这篇文章的原文在ruby on rails bundle的帮助里,在 TextMate里面打开rails项目的一个rb文件,使用快捷键Control+H,然后选择2,就可以看到这篇文章了。最新的ror bundle更新于上个月底,还是很具有参考价值的,对于如何快速使用 TextMate进行rails开发是一个不错的教程。

不过还有一些没完成。不是特别完整。但是基本的快捷键都包含在里面了。
---------英文原版教程
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.




本文转自 fsjoy1983 51CTO博客,原文链接:http://blog.51cto.com/fsjoy/73182,如需转载请自行联系原作者
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值