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的规范,请转到TextMate的preferences,选择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.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)。然后测试运行,所有的测试都应该通过。
这篇文章的原文在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,如需转载请自行联系原作者

textmate开发一个blog相关推荐

  1. 快速开发一个PHP扩展

    快速开发一个PHP扩展 作者:heiyeluren 时间:2008-12-5 博客:http://blog.csdn.net/heiyeshuwu 本文通过非常快速的方式讲解了如何制作一个PHP 5. ...

  2. Silverlight C# 游戏开发:Flyer01开发一个有趣的游戏

    前面扯了很多理论,虽然很无聊但是对于开发游戏来说非常的有用,在早年的开发环境,没有这么多可视的工具,一切靠的是对画面的理解以及游戏感觉Coding代码,然后不厌其烦的测试修改测试修改. 在未来的一段时 ...

  3. 如何开发一个用户脚本系列(3)——脚本一:百度首页和搜索页面添加 Google 搜索框...

    2019独角兽企业重金招聘Python工程师标准>>> 在本系列的前两篇文章中,我们对用户脚本以及开发脚本前应该掌握的基础知识进行了介绍.从这篇文章开始,将以已发布可用的脚本为基础, ...

  4. Nginx开发一个简单的HTTP过滤模块

    本文将学些开发一个简单的HTTP过滤模块,它能够对Content-Type为text/plain的包体前加上前缀字符串prefix. <一> 过滤模块的调用顺序 过滤模块可以叠加,也就是说 ...

  5. Python开发一个股票类库

    前言 使用Python开发一个股票项目.  项目地址:  https://github.com/pythonstock/stock  相关资料:  http://blog.csdn.net/freew ...

  6. 用Java开发一个停车场系统

    作者:亰 blog.csdn.net/weixin_43951778/article/details/109643951 现开发一个停车系统,用户根据车位停车 1)车主查看现在有无车位(最多100个车 ...

  7. Step by Step 使用HTML5开发一个星际大战游戏(1)

    本系列博文翻译自以下文章 http://blog.sklambert.com/html5-canvas-game-panning-a-background/ Languages: HTML5, Jav ...

  8. ASP.NET MVC+LINQ开发一个图书销售站点

    园子里已经有很多人讲解了其理论知识,网上也有不少示例,但是很多示例都是Prieview1的,写这篇blog的目的是想用ASP.NET MVC+LINQ做一个示例项目,主要是为了演示ASP.NET MV ...

  9. 稳扎稳打Silverlight(18) - 2.0视频之详解MediaElement, 开发一个简易版的全功能播放器...

    [索引页] [×××] 稳扎稳打Silverlight(18) - 2.0视频之详解MediaElement, 开发一个简易版的全功能播放器 作者:webabcd 介绍 Silverlight 2.0 ...

最新文章

  1. 用人工神经网络控制真实大脑,MIT的科学家做到了
  2. scipy minimize当目标函数需要参数、当约束条件特别特别多时
  3. VTK:几何对象之OpenVRCone
  4. JVMTM Tool Interface
  5. 课时109.外边距合并现象(掌握)
  6. 循证e刊 安慰剂的前世今生
  7. good archtchre article
  8. 巨蟒python全栈开发linux之centos1
  9. 基于C语言的双人贪吃蛇游戏程序设计
  10. hbase命令snapshot快照使用
  11. 飘动图片广告html代码,基于JavaScript代码实现随机漂浮图片广告
  12. 一、用于数据分析的Excel技巧
  13. ## 第一节课第一串代码
  14. 网络安全知识竞赛选择题(91-120题)
  15. java 前置系统报文通讯方式_基于Geronimo的银行通讯前置系统的设计
  16. Infer静态分析-内存泄漏分析
  17. zcmu-5066: 黑暗长廊
  18. java 字节流 字符流 的详细介绍
  19. 2021年质量员-市政方向-通用基础(质量员)考试题及质量员-市政方向-通用基础(质量员)考试试卷
  20. day11-random模块-随机

热门文章

  1. qt初学者 第一个小程序 小界面
  2. python双轴折线图是什么意思_操作-简易图形-折线图
  3. python 拆分excel工作表_Python将一个Excel拆分为多个Excel
  4. 荣耀手环5没有篮球鸿蒙,荣耀手环5和篮球版区别
  5. 【PPT 下载】神策 2017 数据驱动大会干货限时分享!
  6. IC攻城狮求职宝典 01 2018年IC设计企业 笔试题 01 英伟达(Nvidia)
  7. [C#] C# 知识回顾 - 装箱与拆箱
  8. 人生第一次删好友,删的就是你!连路飞都怒了!
  9. 转:http2.0时代即将到来~~~~~
  10. php 中 fastcgi