这些天一直在学习TextMate,正好看到里面的DemoHelp,觉得很有用,就把它简单的翻译了一下,放在这里给需要的朋友。有什么问题请指正。
==============
关于TextMate的传说已经流传很久了,这款MacOSX所缺失的文字编辑器在熟练的高手手里是开发Rails的最佳工具。一起来看看怎么用TextMate开发一个简单的Blog吧。
1、首先安装git,下载源码编译或者到这里下载package:http://metastatic.org/text/Concern/2007/09/15/new-git-package-for-os-x/
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里面了
下面可以开始开发一个新的应用了
在终端里输入
rails blogcd blogmate .
增加一个模型
ruby script/generate model Post subject:string body:text
生成了一个新的迁移 001_create_posts.rb,包含一个方法 Create_Table
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
然后运行迁移,可以在命令行输入
rake db:migrate
或者直接在编辑器里按下⌃ |(Control+|),选择“Migrate to Current”。
Post Fixtures
修改一下test/fixtures/post.yml
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 script/generate controller blog
或者直接在编辑器里按下⌃ |(Control+|),选择“Call Generate Script”,再选择“Controller”,给它起个名字叫做blog,action里不要设置任何内容。
现在打开blog_controller_test.rb,要快速找到文件,可以按下⌘ T,然后输入bct就可以找到了。
可以看到,功能性的测试是非常清楚和简单的。
让我们开始TDD吧,先删除 test_truth 方法
然后创建一个测试来显示blog文章列表
输入deftg
然后按下Tab键
自动生成了以下代码
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键,光标跳到方法的最后一行。现在的代码应该是这样的:
def test_should_get_index get :index assert_response :successend
键入 asg ,然后按下Tab键,然后键入posts,然后按下Tab键。
下面,键入ass ,然后按下Tab键,输入div#posts,按下Tab键,删除余下的部分。然后连续按Tab键两次,把光标放在assert_select里面,现在代码如下
assert_select 'div#posts' doend
键入ass,按下Tab键,键入div.post,然后按下Tab两次,键入count,取代掉text。
现在再按下Tab键,键入post.size,最后一次按下Tab键,删除被选中的部分。
好了,现在我们的测试方法完成了
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方法
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标签中,键入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+<,生成
标签。键入control+>,键入post.subject
在下面的 <% else %><% end %> 中间,创建一个简单的标记
(使用control+<),键入There are no posts available to read. All y'all come back soon, yer hear. 呵呵,纯粹好玩儿。
好了,我们的视图现在是这样子了:
<% if [email protected]? %> <% for post in @posts %><% end %> <% else %><%= post.subject %>
<%= post.body %>There are no posts available to read. All y'all come back soon, yer hear.
<% end %>
现在可以运行单元测试了,应该可以通过。可以在命令行中输入: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,
下面是生成的第二个迁移文件
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:
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
belongs_to :post, :class_name => “Post“, :foreign_key => “post_id“
现在:class_name和:foreign_key都已经一起自动的把object改为post了,当然,您继续按下Tab键,还可以继续改。不过我们这里就不需要了,直接删除后面的就行了。
现在它是这样的:
class Comment < ActiveRecord::Base belongs_to :postend
然后去看看post类,打开Post.rb
键入hm,然后Tab。 同样的我们不需要那些option,删除。
class Post < ActiveRecord::Base has_many :commentsend
如果您需要has_many :through ,可以键入hmt然后tab,就行了。
最后我们测试一下。(control+\)
路由
打开路由文件routes.rb
修改一下:
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,光标回到了类的中间。
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,光标就转移到{}中,然后修改成下面这样:
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方法应该是这样的:
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 %>.
最后形成这样子:
<%= post.subject %> |
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, 删除剩下的。
接着创建一个
标签(control+entern),键入f.然后tab,选择TextField,键入subject
现在重复创建body和published两个文本域
请注意,published这个属性的标签最好用published yet?,你可以tab进默认的字符串替换掉。
最后用f. snippet增加一个submit按钮
启动script/server,然后访问http://localhost:3000/posts/new
最后的form代码如下:
<%= error_messages_for :post %><% form_for @post do |f| -%><%= f.label :subject %>
<%= f.text_field :subject %><%= f.label :body %>
<%= f.text_area :body %><%= f.label :published, “Published yet?“ %>
<%= f.check_box :published %><%= f.submit “Submit“ %>
<% end -%>
值得说明的是:如果您按下control+enter生成了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方法中填充下面的内容
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,生成
respond_to do |wants| wants.html { }end
然后按两次tab,光标置入wants.html{}里面,键入ra,然后tab,然后键入new。最终代码如下:
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),最终代码变成:
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
现在我们可以在http://localhost:3000/posts/new 添加新的文章,并且在http://loclahost:3000/blog 查看文章了。