<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>ShiningRay</title>
    <description>我就是一个代码民工
http://shiningray.cn</description>
    <link>http://shiningray.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>Slapp: 简易聊天墙的Merb教程</title>
        <author>ShiningRay</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://shiningray.javaeye.com">ShiningRay</a>&nbsp;
          链接：<a href="http://shiningray.javaeye.com/blog/210800" style="color:red;">http://shiningray.javaeye.com/blog/210800</a>&nbsp;
          发表时间: 2008年07月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="entry-content">
<p>有问题或评价，请联系: <a href="https://mail.google.com/mail?view=cm&amp;tf=0&amp;ui=1&amp;to=socialface@gmail.com" target="_blank">socialface@gmail.com</a>
</p>
<p>程序截图: <a href="http://www.socialface.com/slapp/screenshot.jpg">http://www.socialface.com/slapp/screenshot.jpg</a>
</p>
<h2>简介</h2>
<p>欢迎来到Slapp的教程。本文的主要目标是通过构建一个简易的聊天墙应用来介绍一下Merb微框架的主要组件。</p>
<p>本文其次的目标是成为最好的Merb开放教程并能不断更新。同时，我们希望本教程可以逐渐变得丰富来展现Merb框架的所有方面和开发方式。</p>
<h2>许可</h2>
<p>This tutorial is Copyright 2008 Social Corp. and is licensed under a Creative<br />
Commons Attribution-Noncommercial 3.0 United States License, available at:</p>
<ul>
<li><a href="http://creativecommons.org/licenses/by-nc/3.0/us/">http://creativecommons.org/licenses/by-nc/3.0/us/</a>
</li>
</ul>
<p>相关的源代码以MIT形式的开发源代码许可:</p>
<ul>
<li><a href="http://www.socialface.com/slapp/source">http://www.socialface.com/slapp/source</a>
</li>
</ul>
<p>直接浏览代码：</p>
<ul>
<li><a href="http://gitorious.org/projects/slapp/repos/mainline/trees/master">http://gitorious.org/projects/slapp/repos/mainline/trees/master</a>
</li>
</ul>
<p>也可以直接下载：</p>
<ul>
<li><a href="http://www.socialface.com/slapp/binary">http://www.socialface.com/slapp/binary</a>
</li>
</ul>
<h2>参与人员名单</h2>
<p>本教程最初由来自#merb的Slurry撰写。Slurry在撰写本文时主要参照了Merb的官方文档[^a]以及#merb IRC频道的内容。</p>
<p>在学习Merb的RSpec的过程中，还咨询了John Hornbeck的Blerb[^b]，参考了Tim Connor的&ldquo;Isolate
controller and view testing in
merb&rdquo;一文[^c]&mdash;&mdash;虽然帮助很大，真正掌握Merb/Rspec还是得益于来自#merb的benburkert。</p>
<p>中文版由<a href="http://shiningray.cn/">ShiningRay</a>
翻译</p>
<h2>前期决定</h2>
<p>设计上来说，Merb是一个ORM无关的框架。当然，这仅仅是表示有不同的模型层可以选择。对于Merb v0.9.2来说，包括：DataMapper、Sequel和ActiveRecord（来自Rails）。</p>
<p>尽管DataMapper和Sequel都是很好的选择，但现在关注Merb的主流人群基本上都是来自Rails背景；因此，我们在本教程中会继续使用ActiveRecord。</p>
<h2>1.) 创建模型</h2>
<p>在像Merb这样快速成长的社区中，一个问题可能只需更新或者重装Merb相关的包(gem)就能简单解决，所以让我们先花点时间这样准备一下：</p>
<pre><code># gem sources -a http://merbivore.com
# gem install merb activerecord merb_activerecord merb_helpers rspec merb_rspec

$ merb-gen app slapp
$ cd slapp
</code>
</pre>
<p>完成了前两行命令后，Merb、ActiveRecord以及Rspec应该已经安装好或已经更新了，同时后两行命令则应该创建了一个初始的空Merb应用。</p>
<p>从这里开始，我们就要告诉Merb：我们要怎样用ActiveRecord，要使用哪个测试框架，以及需要载入哪些确切的merb_helpers包（如表单相关的东西）</p>
<p>编辑：<code>slapp/config/init.rb</code>
 并取消第<code>27</code>
和<code>42</code>
的注释，同时将：<code>dependency &quot;merb_helpers&quot;</code>
 添加在<br />
<code>Merb::BootLoader.after_app_loads do</code>
 这一行之前:</p>
<pre><code>use_orm :activerecord
...
use_test :rspec
...
dependency &quot;merb_helpers&quot;
...
Merb::BootLoader.after_app_loads do
  ### Add dependencies here that must load after the application loads:

  # dependency &quot;magic_admin&quot; # this gem uses the app's model classes
end
</code>
</pre>
<p>现在我们已经告诉Merb要使用ActiveRecord： <code>use_orm :activerecord</code>
, 让我们来生成第一个模型：</p>
<pre><code>$ merb-gen model Post
</code>
</pre>
<p>靠，居然没效。不过等等，你应该还有一个全新的<code>slapp/config/database.yml.sample</code>
 文件等待配置，对吧？[^1]</p>
<p>好，现在你要做的就是将这个文件名字改成<code>database.yml</code>
并在其中插入合适的数据库链接信息，像这样：</p>
<pre><code>:development: &amp;defaults
  :adapter: mysql
  :database: slapp_development
  :username: slapp
  :password: SL@rrYin08
  :host: localhost
  :socket: /tmp/mysql.sock
  :encoding: utf8

:test:
  &lt;&lt;: *defaults
  :database: slapp_test
</code>
</pre>
<p>让我们重新运行上面的merb-gen命令，不过这次我们还要再加上一些Post的默认属性：</p>
<pre><code>$ merb-gen model Post body:string created_at:datetime
</code>
</pre>
<p>如果一切正常，那么第一个模型类就有了。让我们使用<code>rake</code>
对数据库进行迁移，来加上表：</p>
<pre><code>$ rake db:migrate
</code>
</pre>
<p>现在，Slapp应该有一个有效可用的Post模型了。为了以防万一，我们将介绍RSpec[^2].</p>
<h2>2.) RSpec</h2>
<p>RSpec&mdash;&mdash;你可能已经知道了&mdash;&mdash;是Test::Unit的另一种替代测试方案。与RSpec交互主要通过rake任务和<code>spec</code>
命令：</p>
<pre><code>$ rake spec
</code>
</pre>
<p>或者，更加详细的：</p>
<pre><code>$ spec spec/* --format specdoc -c
</code>
</pre>
<p><em>(重要：这里使用的rake任务来自Merb前沿代码，应该很快会发布为标准包。在那之前，可以使用以下同等任务：<br />
<code>rake specs</code>
 以及 <code>rake spec TASK=controllers/pages</code>
)</em>
</p>
<p>两个任务会运行<code>slapp/spec/*</code>
中的任何代码，简洁起见，我们这里会使用rake。</p>
<p>让我们再次运行测试：</p>
<pre><code>$ rake spec
</code>
</pre>
<p>如你所见，Merb已经为我们创建了一个默认的测试，但是他还不能通过： </p>
<pre><code>Post
- should have specs (PENDING: Not Yet Implemented)
...
</code>
</pre>
<p>打开: <code>slapp/spec/models/post_spec.rb</code>
 看看这个测试在哪里：</p>
<pre><code>describe Post do

  it &quot;should have specs&quot;

end
</code>
</pre>
<p>将该spec改成实用的代码，比如：</p>
<pre><code>describe Post do

  it &quot;should be valid when new&quot; do
      post = Post.new
      post.should be_valid
  end

end
</code>
</pre>
<p>该测试看上去好像不多，但是它确实可以验证Merb、RSpec、ActiveRecord和我们的数据库都已经安装成功并工作正常。</p>
<pre><code>$ rake spec
</code>
</pre>
<p>同时，如果我们在此运行新的spec，数据库又出现一个问题。确切地说，我们忘记了创建<code>slapp_test</code>
 数据库并将<code>slapp_development</code>
的结构复制过去。</p>
<pre><code>$ rake db:create:all
$ rake db:test:clone
</code>
</pre>
<p>重新运行spec:</p>
<pre><code>$ rake spec
...
Post
- should be valid
...
1 example, 0 failures
</code>
</pre>
<p>这时成功在向我们问候：<code>1 example, 0 failures</code>
，这一行表示所有的spec都通过了。更重要的是，我们遇到并克服了实际使用RSpec的第一个问题。</p>
<h2>3.) 控制器</h2>
<p>虽然现在从技术上说我们没有控制器也能启动Merb，但是基本上作不了什么。事实上就是什么都作不了，我们还是先来创建一个控制器：</p>
<pre><code>$ merb-gen controller Posts
</code>
</pre>
<p>如你所见，Merb的控制器的命名方式是模型名（或者资源等）的名字复数化，且不使用<code>Controller</code>
后缀。</p>
<p>看一下<code>slapp/app/controllers/posts.rb</code>
，你应该看到我们新的Posts控制器里有一个默认的<code>#index</code>
动作。另外，Merb还应该创建了一个新的spec: <code>slapp/spec/controllers/posts_spec.rb</code>
 ，里面的内容应该类似于：</p>
<pre><code>describe Posts, &quot;index action&quot; do
  before(:each) do
      dispatch_to(Posts, :index)
  end
end
</code>
</pre>
<p>让我们编辑：<code>slapp/spec/controllers/posts_spec.rb</code>
，更改内容为：</p>
<pre><code>require File.join(File.dirname(__FILE__), &quot;..&quot;, 'spec_helper.rb')

describe Posts, &quot;#index&quot; do

  it &quot;should respond correctly&quot; do
      dispatch_to(Posts, :index).should respond_successfully
  end

end
</code>
</pre>
<p>指定控制器是比较直观的，在上面的例子中，我们描述了Posts控制器（从<code>dispatch_to</code>
中返回的）有一个<code>#index</code>
动作并且它能被外部世界成功调用（即，它返回一个HTTP 20x 代码）[^3].</p>
<p>我们想再次运行spec，但是由于我们没有添加任何模型或视图测试，所以让我们执行更加明确的&ldquo;仅控制器&rdquo;的rake任务：</p>
<pre><code>$ rake spec:controller
</code>
</pre>
<p>成功的测试结果：</p>
<pre><code>Posts index action
- should respond correctly
...
1 example, 0 failures
</code>
</pre>
<p>漂亮。现在我们已经有了一个可以工作的控制器和一个有效的模型&mdash;&mdash;我们只缺一些好的视图了。</p>
<h2>4.) The View from Above</h2>
<p>前面当我们创建Posts控制器时，Merb同时也为<code>#index</code>
动作创建了一个草图。位于：<code>slapp/app/views/posts/index.html.erb</code>
，你也许会看看里面有什么，然而，让我们先暂时忽略这个视图，并回到控制器。</p>
<p>在 <code>slapp/app/controllers/posts.rb</code>
 中，我们执行一个简单的 ActiveRecord <code>#find</code>
<br />
并在<code>#index</code>
动作中将结果存储为一个实例变量：</p>
<pre><code>class Posts &lt; Application

  def index
      @posts = Post.find(:all, :order =&gt; &quot;created_at DESC&quot;)
      render
  end

end
</code>
</pre>
<p>然后我们确认该动作仍然是可以调用的：</p>
<pre><code>$ rake spec:controller
...
1 example, 0 failures
</code>
</pre>
<p>就和Rails（或者其他Web框架）中一样，在控制器中创建的实例变量可以在对应的视图中调用。</p>
<p>在本案中，我们可以回到：<code>slapp/app/views/posts/index.html.erb</code>
 并将临时文本替换成显示<code>@posts</code>
变量内容的代码。</p>
<p>为了能保持模块化，我们将使用Merb <code>#partial</code>
[^4] 功能来达到这个目的。</p>
<p>将 <code>slapp/app/views/posts/index.html.erb</code>
 的内容替换成：</p>
<pre><code>&lt;h1&gt;Welcome to Slapp&lt;/h1&gt;
&lt;h2&gt;A simple chat wall&lt;/h2&gt;

&lt;p&gt;Recent Posts:&lt;/p&gt;
&lt;div id=&quot;posts&quot; class=&quot;container&quot;&gt;
    &lt;%= partial(&quot;/shared/post&quot;, :with =&gt; @posts) %&gt;
&lt;/div&gt;
</code>
</pre>
<p>不看HTML，调用<code>#partial</code>
应该还是比较容易理解的&mdash;&mdash;<br />
我们想多次渲染<code>slapp/app/views/shared/_post.html.erb</code>
 视图来呈现<code>@posts</code>
的内容。</p>
<p>现在创建: <code>shared/</code>
 目录以及: <code>_post.html.erb</code>
 文件:</p>
<pre><code>$ mkdir app/views/shared
$ touch app/views/shared/_post.html.erb
</code>
</pre>
<p>并编辑 <code>slapp/app/views/shared/_post.html.erb</code>
 的内容为：</p>
<pre><code>&lt;div id=&quot;post-&lt;%= post.id %&gt;&quot; class=&quot;post&quot;&gt;
    &lt;p class=&quot;body&quot;&gt;&lt;%= h(post.body) %&gt;&lt;/p&gt;
    &lt;p class=&quot;created&quot;&gt;&lt;%= relative_date(post.created_at) %&gt;&lt;/p&gt;
&lt;/div&gt;
</code>
</pre>
<p>上面调用: <code>partial(&quot;/shared/post&quot;, :with =&gt; @posts)</code>
 会反复传递一个单个<code>post</code>
对象给视图<code>_post.html.erb</code>
并渲染。</p>
<h2>5.) 启动Merb</h2>
<p>现在我们有了模型、控制器、以及一个视图，让我们启动Merb：</p>
<pre><code>$ merb
$ curl http://localhost:4000/
</code>
</pre>
<p>你应该看到了一个普通的欢迎页，再转到：</p>
<pre><code>$ curl http://localhost:4000/posts/index
</code>
</pre>
<p>这时你应该看到<code>slapp/app/views/posts/index.html.erb</code>
的内容了。当然，由于我们还未创建任何帖子，所以应该看不到任何东西。</p>
<p>让我们使用交互Merb会话（其实就是一个在Merb应用的内容中启动的IRB）修正上面的问题：</p>
<pre><code>$ merb -i
~ Loaded DEVELOPMENT Environment...
...
&gt;&gt; Post.create(:body =&gt; &quot;Memp went down&quot;)
</code>
</pre>
<p>使用典型的 ActiveRecord <code>#create</code>
 方法，我们现在创建了一个Post。重新载入 <br />
<code>Posts#index</code>
 页面:</p>
<pre><code>$ curl http://localhost:4000/posts/index
</code>
</pre>
<p>我们应该成功地看到了新的帖子。</p>
<h2>6.) 特殊的视图</h2>
<p>现在我们已经实现了视图，<strong>也许</strong>
他们不会经常更改，让我们先描述他们。</p>
<p>首先，创建目录和spec文件：</p>
<pre><code>$ mkdir -p spec/views/posts/
$ touch spec/views/posts/index_spec.rb
</code>
</pre>
<p>然后将一下代码放入<code>slapp/spec/views/posts/index_spec.rb</code>
</p>
<pre><code>require File.join(File.dirname(__FILE__), &quot;..&quot;, &quot;..&quot;, &quot;spec_helper.rb&quot;)

describe &quot;posts/index&quot; do

  before(:each) do
      @controller = Posts.new(fake_request)
      @posts = [Post.create(:body =&gt; &quot;Merb&quot;, :created_at =&gt; Time.now), Post.create(:body =&gt; &quot;Rocks!&quot;, :created_at =&gt; Time.now)]
      @controller.instance_variable_set(:@posts, @posts)
      @body = @controller.render(:index)
  end

  it &quot;should have a containing div for the posts&quot; do
      @body.should have_selector(&quot;div#posts.container&quot;)
  end

  it &quot;should have a div for each individual post&quot; do
      @posts.each do |post|
        @body.should have_selector(&quot;div#posts.container div#post-#{ post.id }.post&quot;)
      end
  end

  it &quot;should have the contents of each post inside a div with an id and class&quot; do
      @posts.each do |post|
        @body.should match_tag(:div, :id =&gt; &quot;post-#{ post.id }&quot;, :class =&gt; &quot;post&quot;, :content =&gt; post.body)
      end
  end

  after(:each) do
      Post.destroy_all
  end

end
</code>
</pre>
<p>当使用RSpec描述对象时，常常需要在运行测试的前后维护测试特定的内容。毫不奇怪，RSpec向我们提供了<code>#before</code>
和<code>#after</code>
块来实现这个目的。</p>
<p>回到代码中：我们在<code>#before</code>
块中首先做的是从我们的 <code>Posts</code>
 创建<code>@controller</code>
实例。下面我们使用<code>fake_request</code>
助手[^5]来模拟HTTP请求。</p>
<p>回想一下我们的视图：</p>
<pre><code>...
&lt;p&gt;Recent Posts:&lt;/p&gt;
&lt;div id=&quot;posts&quot; class=&quot;container&quot;&gt;
    &lt;%= partial(&quot;/shared/post&quot;, :with =&gt; @posts) %&gt;
&lt;/div&gt;
</code>
</pre>
<p>我们知道我们还需要一组帖子来进行测试，碰巧，这就是<code>#before</code>
块的第二和第三行所做的事情：</p>
<pre><code>...
@posts = [Post.create(:body =&gt; &quot;Merb&quot;, :created_at =&gt; Time.now), Post.create(:body =&gt; &quot;Rocks!&quot;, :created_at =&gt; Time.now)]
</code>
</pre>
<p>@controller.instance<em>variable</em>
set(:@posts, @posts)<br />
    &hellip;</p>
<p>这里，我们仅仅将插入了一组记录并保存为<code>@controller</code>
的<code>@posts</code>
实例变量。记住，调用Posts控制器的<code>#index</code>
动作和我们之前所做的没有什么区别，除了我们使用了一个ActiveRecord的<code>#find</code>
，而不是手工使用<code>#new</code>
创建帖子：</p>
<p>前面的Posts控制器Our Posts controller from earlier:</p>
<pre><code>class Posts &lt; Application

  def index
      @posts = Post.find(:all, :order =&gt; &quot;created_at DESC&quot;)
      render
  end

end
</code>
</pre>
<p>最后，<code>#before</code>
的第四和最后一行渲染了视图并将响应的主体（本案中是HTML）放入了<code>@body</code>
 实例变量。</p>
<pre><code>...
@body = @controller.render(:index)
...
</code>
</pre>
<p>(本质上来说，我们是使用fake_request来&ldquo;查看&rdquo;Posts#index动作。)</p>
<p>现在，我们的控制器已经设置好了，同时视图也渲染了，我们就开始列出我们对视图中应该有什么的预期。[^6]</p>
<p>首先，我们描述HTML里应该最外面有一个div来放每一个单独的帖子的div：</p>
<pre><code>...
it &quot;should have a containing div for the posts&quot; do
    @body.should have_selector(&quot;div#posts.container&quot;)
end
...
</code>
</pre>
<p>下面，我们断定容器div确实包含着帖子的div：</p>
<pre><code>...
it &quot;should have a div for each individual post&quot; do
  @posts.each do |post|
      @body.should have_selector(&quot;div#posts.container div#post-#{ post.id }.post&quot;)
  end
end
...
</code>
</pre>
<p>然后，我们进入每个帖子的div来验证内容准确地匹配对应的Post：</p>
<pre><code>...
it &quot;should have the contents of each post inside a div with an id and class&quot; do
  @posts.each do |post|
      @body.should match_tag(:div, :id =&gt; &quot;post-#{ post.id }&quot;, :class =&gt; &quot;post&quot;, :content =&gt; post.body)
  end
end
...
</code>
</pre>
<p>最后，我们使用<code>#after</code>
块来删除在#before块中创建的帖子：</p>
<pre><code>...
after(:each) do
  Post.destroy_all
end
...
</code>
</pre>
<p>尽管我们还没真正完成，我们先来验证一下整个应用：</p>
<pre><code>$ rake spec
...
Posts#index
- should respond correctly

Post
- should be valid

posts/index.html.erb
- should have a containing div for the posts
- should have a div with an id and class for each individual post
- should have the contents of each post inside a div with an id and class

Finished in 0.226266 seconds

5 examples, 0 failures
</code>
</pre>
<h2>7.) 表单创建</h2>
<p>有了浏览帖子的能力之后，现在就可以实现同样重要的创建帖子的功能了。</p>
<p>打开<code>slapp/app/views/posts/index.html.erb</code>
并在帖子列表下面添加该表单[^7]：</p>
<pre><code>...
&lt;p&gt;Post Something:&lt;/p&gt;
&lt;% form_tag(:action =&gt; url( :controller =&gt; &quot;posts&quot;, :action =&gt; &quot;create&quot;) ) do %&gt;
    &lt;%= text_field(:name =&gt; &quot;body&quot;, :size =&gt; 40) %&gt;
    &lt;%= submit_button(&quot;Post Message!&quot;) %&gt;
&lt;% end %&gt;
</code>
</pre>
<p>几乎不言自明，我们是要构建一个简单的表单，有一个文本输入框和一个提交按钮。</p>
<p>你应该已经注意到我们已经将表单设置为递交到<code>Posts</code>
控制器的<code>#create</code>
动作。我们需要实现这个动作，不过在我们继续之前，我们先快速描述一下这个表单。</p>
<p>编辑: <code>slapp/spec/views/posts/index_spec.rb</code>
并在<code>#after</code>
 块上面添加一下内容:</p>
<pre><code>...
it &quot;should have a form to create new posts with a single input and submit button&quot; do
  @body.should match_selector(&quot;form[@action=/posts/create]&quot;)
  @body.should match_selector(&quot;form[@action=/posts/create] input[@name=body]&quot;)
  @body.should match_selector(&quot;form[@action=/posts/create] button[@type=submit]&quot;)
end
</code>
</pre>
<p>和前面一样，我们使用<code>#match_selector</code>
 来断言表单、正文输入框以及提交按钮的存在。唯一不同的是我们使用了一个基于HTML属性的选择器[^8]<code>form[@action=/posts/create]</code>
</p>
<p>Run the specs:</p>
<pre><code>$ rake spec:view
...
4 examples, 0 failures
</code>
</pre>
<p>我们可以再次启动Merb来亲眼检验一下新的表单了。不过，因为我们已经使用了RSpec，这步不是非常必须的，我们可以立刻继续往下。</p>
<p>说道RSpec，这次，当我们在要去完成<code>#create</code>
动作的时候，我们应该在写代码之前先写出该步骤的spec。</p>
<p>将以下内容复制到 <code>slapp/spec/controllers/posts_spec.rb</code>
：</p>
<pre><code>require File.join(File.dirname(__FILE__), &quot;..&quot;, 'spec_helper.rb')

describe Posts, &quot;#index&quot; do

  it &quot;should respond correctly&quot; do
      dispatch_to(Posts, :index).should respond_successfully
  end

end

describe Posts, &quot;#create&quot; do

  before(:each) do
      @params = { :body =&gt; &quot;It was a good game though&quot; }
  end

  it &quot;should redirect to #index after successfully creating a Post&quot; do
      lambda {
        dispatch_to(Posts, :create, @params).should redirect_to(&quot;/posts/index&quot;)
      }.should change(Post, :count)
  end

end
</code>
</pre>
<p>这里，在<code>#before</code>
块中，我们准备了一个只有一个<code>:body</code>
键的<code>@params</code>
表以便开始描述<code>#create</code>
动作 。下面，我们列出了第一个预期：create动作在它成功创建一个帖子之后应该重定向到<code>#index</code>
动作。</p>
<p>(这就是如果一个浏览器通过表单提交了某些信息，在我们的应用中能看到的情况。)</p>
<p>我们用一个lambda表达式检验一个帖子是不是被创建了，其中RSpec会调用两次：先执行一次，然后等相关的<code>{...}</code>
块执行完之后再执行另一次。</p>
<p>这两次中，RSpec都会调用<code>Post.count</code>
，如果两次调用返回不同的值，那么我们就能确信Post被创建了，那么这个块（该动作）就是成功的。</p>
<p>因为我们还没有编码<code>#create</code>
，所以spec显然会失败：</p>
<pre><code>$ rake spec:controller
...
Posts#create
- should redirect to #index after successfully creating a Post (FAILED - 1)

1)
'Posts#create should redirect to #index after successfully creating a Post' FAILED
count should have changed, but is still 0
...
</code>
</pre>
<p>切换到: <code>slapp/app/controllers/posts.rb</code>
并再次复制以下内容：</p>
<pre><code>class Posts &lt; Application

  def index
      @posts = Post.find(:all, :order =&gt; &quot;created_at DESC&quot;)
      render
  end

  def create
      Post.create!(:body =&gt; params[:body])
      redirect url(:action =&gt; &quot;index&quot;)
  end

end
</code>
</pre>
<p>现在我们定义了<code>#create</code>
，现在回到spec文件，应该就可以通过了：</p>
<pre><code>$ rake spec:controller
...
2 examples, 0 failures
</code>
</pre>
<p>通过了这些spec，我们就有了一个可以正常工作的聊天墙了。</p>
<p>听起来是个好消息，我们也几乎就要完成了。我们还需要做得就是检验创建一个新的帖子需要一定的文本，否则则会出现一个异常。</p>
<h2>8.) 收尾</h2>
<p>现在，任何都可以点击 &ldquo;Post Message!&rdquo; 然后创建一个新的帖子。因为我们并不想让一堆空白的帖子占据聊天墙，所以我们应该在创建新帖子之前校验至少有一些文本被提交了。</p>
<p>因为本文是一个教程，我们不打算将所有东西都仔细进行合适的处理，所以这里我们直接使用ActiveRecord的<code>validates_length_of</code>
过滤器。 ;-)</p>
<p>打开: <code>slapp/spec/models/post_spec.rb</code>
 并观察我们现有的spec：</p>
<pre><code>...
it &quot;should be valid when new&quot; do
  post = Post.new
  post.should be_valid
end
</code>
</pre>
<p>因为我们要校验正文文本的存在，这个spe已经不再有效，我们需要如下的内容来替代：</p>
<pre><code>describe Post do

  it &quot;should NOT be valid when new&quot; do
      post = Post.new
      post.should_not be_valid
  end

  it &quot;should require at least two body characters to be valid&quot; do
      post = Post.new
      post.should_not be_valid
      post.errors.on(:body).should include(&quot;is too short (minimum is 2 characters)&quot;)
  end

end
</code>
</pre>
<p>和平时一样，我们首先运行失败的spec来建立我们对于特定行为的预期：</p>
<pre><code>...
Post
- should NOT be valid when new (FAILED - 1)
- should require at least two body characters to be valid (FAILED - 2)

1)
'Post should NOT be valid when new' FAILED
expected valid? to return false, got true
./spec/models/post_spec.rb:7:

2)
'Post should require at least two body characters to be valid' FAILED
expected valid? to return false, got true
./spec/models/post_spec.rb:12:
...
</code>
</pre>
<p>然后实现上面提出的修正，在本案中，则是在<code>slapp/app/models/post.rb</code>
中加入一样:</p>
<pre><code>class Post &lt; ActiveRecord::Base

  validates_length_of :body, :minimum =&gt; 2

end
</code>
</pre>
<p>再次运行spec来检验我们的修正是否有效：</p>
<pre><code>$ rake spec:model
...
2 examples, 0 failures
</code>
</pre>
<p>这时我们的模型完成了。让我们继续给Posts控制器添加一个spec&mdash;&mdash;<br />
我们需要描写当提交一个帖子没有包含正文的时候，我们没有真正去处理这个错误而已直接返回由我们的ORM抛出的异常。</p>
<p>编辑<code>slapp/spec/controllers/posts_spec.rb</code>
并加入下面这个spec：</p>
<pre><code>...
it &quot;should raise an exception when insufficient body text is submitted&quot; do
  lambda {
      dispatch_to(Posts, :create).should redirect_to(&quot;/posts/index&quot;)
  }.should raise_error(ActiveRecord::RecordInvalid)
end
</code>
</pre>
<p>如你所见，我们仅仅是不带<code>@params</code>
表来调用<code>#create</code>
。这创建一个没有正文的空Post，这样就会导致ActiveRecord的校验失败，并抛出我们预期的<code>RecordInvalid</code>
异常。</p>
<p>有了这个，我们的第一个聊天墙的版本就完成了。就和前面一样，你可以通过启动Merb并浏览<code>Posts#index</code>
动作来试试程序：</p>
<pre><code>$ merb
$ curl http://localhost:4000/posts/index
</code>
</pre>
<h2>最后的思考</h2>
<p>从这里开始，你可能还有很多东西想添加，例如：分页、动态发布/更新、SPAM过滤器、文本格式化等等。</p>
<p>这些对于任何优秀的聊天程序都是很基本的，你都可以利用Merb来实现。</p>
<p>同时，别忘了浏览官方的项目首页看看有没有最新的更新、看看别人的版本甚至创建属于你自己的：</p>
<ul>
<li><a href="http://www.socialface.com/slapp/source">http://www.socialface.com/slapp/source</a>
</li>
</ul>
<p>&hellip;</p>
<p>有任何问题/评价，请致电<a href="https://mail.google.com/mail?view=cm&amp;tf=0&amp;ui=1&amp;to=socialface@gmail.com" target="_blank">socialface@gmail.com</a>
或者在#merb找 Slurry</p>
<h2>脚注</h2>
<p>[^1]: The observant may have also just found their first &ldquo;Merb&rdquo; bug.. the<br />
generator claimed to have made a &ldquo;database.sample.yml&rdquo; file, although the file<br />
is really named &ldquo;database.yml.sample&rdquo;. :-)</p>
<p>[^2]: RSpec links in order of approximate handyness to the beginner:</p>
<ul>
<li><a href="http://rspec.info/documentation/expectations.html">http://rspec.info/documentation/expectations.html</a>
</li>
<li><a href="http://rspec.info/documentation/test_unit.html">http://rspec.info/documentation/test_unit.html</a>
</li>
<li><a href="http://rspec.info/documentation/before_and_after.html">http://rspec.info/documentation/before_and_after.html</a>
</li>
<li><a href="http://rspec.info/rdoc/classes/Spec/Matchers.html">http://rspec.info/rdoc/classes/Spec/Matchers.html</a>
</li>
<li><a href="http://rspec.info/rdoc/index.html">http://rspec.info/rdoc/index.html</a>
</li>
</ul>
<p>[^3]: Merb RSpec controller matchers: </p>
<ul>
<li><a href="http://www.merbivore.com/documentation/merb-core/0.9.2/index.html?a=C00000089&amp;name=Rspec#">http://www.merbivore.com/documentation/merb-core/0.9.2/index.html?a=C00000089&amp;name=Rspec#</a>
</li>
</ul>
<p>[^4]: Merb partials: </p>
<ul>
<li><a href="http://www.merbivore.com/documentation/merb-core/0.9.2/index.html?a=M000328&amp;name=partial">http://www.merbivore.com/documentation/merb-core/0.9.2/index.html?a=M000328&amp;name=partial</a>
</li>
</ul>
<p>[^5]: Merb fake_request helper: </p>
<ul>
<li><a href="http://merbivore.com/documentation/merb-core/0.9.2/index.html?a=C00000116&amp;name=FakeRequest">http://merbivore.com/documentation/merb-core/0.9.2/index.html?a=C00000116&amp;name=FakeRequest</a>
</li>
</ul>
<p>[^6]: Merb RSpec view matchers: </p>
<ul>
<li><a href="http://merbivore.com/documentation/merb-core/0.9.2/index.html?a=C00000089&amp;name=Rspec#">http://merbivore.com/documentation/merb-core/0.9.2/index.html?a=C00000089&amp;name=Rspec#</a>
</li>
</ul>
<p>[^7]: Merb Form Helpers: </p>
<ul>
<li><a href="http://merbivore.com/documentation/merb-plugins/head/merb_helpers/index.html?a=M000046&amp;name=form_tag">http://merbivore.com/documentation/merb-plugins/head/merb_helpers/index.html?a=M000046&amp;name=form_tag</a>
</li>
</ul>
<p>[^8]: Hpricot CSS Selectors: </p>
<ul>
<li><a href="http://code.whytheluckystiff.net/hpricot/wiki/SupportedCssSelectors">http://code.whytheluckystiff.net/hpricot/wiki/SupportedCssSelectors</a>
</li>
</ul>
</div>
          <br/>
          <span style="color:red;">
            <a href="http://shiningray.javaeye.com/blog/210800#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 02 Jul 2008 22:24:53 +0800</pubDate>
        <link>http://shiningray.javaeye.com/blog/210800</link>
        <guid>http://shiningray.javaeye.com/blog/210800</guid>
      </item>
      <item>
        <title>所有的网站都在屏蔽删除这个消息，更加说明事情的可疑，希望看到的朋友能够转帖，也希望政府能够给我们一个</title>
        <author>ShiningRay</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://shiningray.javaeye.com">ShiningRay</a>&nbsp;
          链接：<a href="http://shiningray.javaeye.com/blog/210743" style="color:red;">http://shiningray.javaeye.com/blog/210743</a>&nbsp;
          发表时间: 2008年07月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h3><a href="http://xuliangyong.javaeye.com/blog/209955"><br />
</a>
</h3>
<div class="blog_content">
    所有的网站都在屏蔽删除这个消息，更加说明事情的可疑，希望看到的朋友能够转帖，也希望政府能够给我们一个真相
<br />
<br />
贵州省瓮安副县长惨无人道 6-28事件[转自ALIBABA]
<br />
<br />
瓮安四十多万人民不满政府焚烧事件的真相！
<br />
贵州省瓮安县三中一名读初二的学生,16岁的李素芬，因期末考试没给同班的一个女生抄答案而被杀害！报案后法医前去检验尸体时，居然说是吃药自
杀！但药却还在吼管里根本没下去！又说被淹死，但身体里根本没有水，死者脖子多处伤痕！显然被掐死的。凶手被抓不到24小时就被放了出来！原因是：元凶是
县委书记的亲侄女，另2个男生因和派出所所长有亲戚关系！当天死者家属为讨个说法和亲戚来到当地（瓮安）派出所！随知道他们不仅不说理还把死者家属打成重
伤！叔叔，爷爷，奶奶被打住院抢救！妈妈说话含糊，已失去理智，婶婶被剪去头发关押在派出所！死者死后被放10天，公安部门，还有各个领导都没处理！还强
行抢走尸体！公安局曾多次发生硬抢死尸而破坏现场企图掩盖事实的事情！人们感到同情而纷纷捐钱让他们告上中央时，公安人员前来阻止，谁捐就抓谁！当含冤的
死者亲属向政府讨公道时。政府官员居然避而不见！于是他们又去找公安局讨公道。为了隐瞒事实。警察居然把明白事理。能说会道的死者幺爸打进医院。还出手打
了学生和老师，终于。人们被激怒了！！！当天下午。愤怒的群众们烧了公安局。政府大楼。。可他们又丢了催泪弹威胁群众，用警棍乱打申冤的学生们！！次日。
居然又在网上发布虚假信息。。还断了我们这的网络，。通信。。就是因为想封锁一切有关瓮安的真相！！企图把这一大冤案事瞒天过海。！！！
<br />
公安局。政府被含冤的人们烧后。。所有的。所有的。的瓮安所有的群众都高声欢呼！。。奋力鼓掌！，，大叫&lsquo;烧的好。这些腐败官员太无法无天了&rsquo;&ldquo;怎么对的起&lsquo;为人民服务&rsquo;这几个字？&rdquo;&ldquo;让腐败的贪官污吏们下台&rdquo;！！！
<br />
人民群众永远都是正义的！！！！！！
<br />
四十多万的瓮安人民永远都站在正义的一边。。！！！！
<br />
抵制腐败！！！还百姓一个公道！！！
<br />
还人民一个光明！！！！！！
<br />
&mdash;&mdash;只要你是一个有正义感。。有血性的中国人。
<br />
请把这条消息转发。。越多越好。。让全国人民都知道真相。。看清他们的丑恶嘴脸！！
<br />
<br />
太阳下的贵州省瓮安县无法无天
<br />
<br />
事件死者：
<br />
1、李淑芬（音），15岁，瓮安县三中初二学生，成绩优秀。
<br />
2、李淑芬的叔叔，玉华中学老师。
<br />
3、目前尚不知姓名，6月29日上午被警察开车撞死。
<br />
直接凶手：
<br />
1、王娇娇（音），女；
<br />
2、王娇娇的两个表哥（搞铝合金装修）。
<br />
3、包庇凶手的警察
<br />
死者李淑芬与王娇娇关系：
<br />
同学，并且同在一个房东家租住上下楼。
<br />
王娇娇及其表哥与公安、政府等部门实权人物关系（版本不一）：
<br />
1、据说是公安局局长的亲戚；
<br />
2、据说与县委书记王勤是叔侄关系。
<br />
<br />
事件发生已有七八天，2008年6月21日晚，王娇娇（音）约同其两个搞铝合金装修的表哥将李淑芬（音）叫出去，将李淑芬轮奸并杀害，抛尸瓮安西
门河。晚上11点过钟，李淑芬的哥哥报警，警方到现场后，称没有工具不予打捞。李淑芬的叔叔（瓮安县玉华中学老师）闻讯赶到，在受害者房东家借到楼梯等工
具将其遗体捞出。据知情人说，奸杀现场发现卫生纸、避孕套、血迹满地。凶手被抓到后，因为凶手和公安局及政府等部门权力人物的关系一天后即释放。
<br />
事后县公安局将受害者的叔叔带走，并要求其承认受害者是由消防队或者警察打捞上来的，其叔叔不同意，双方理论不成，公安局人员将其暴打一顿后赶
出来，随后，受害者的叔叔在离公安局不远的地方被社会上流氓用铁棍、秤砣等暴打成重伤（据传是公安局叫社会上的流氓所为），送到医院靠输氧维持至6月29
日凌晨三点死亡。李淑芬的姑妈去公安局理论时，被公安局内人员打伤并将其头发剪烂，还不准她给幼儿喂奶。
<br />
事情传到受害者的叔叔所在的学校，引发了广大学生的愤怒，他们始进行示威游行，组织到政府讨公道，政府不理，遭到警察驱赶和毒打，将四五个学生
打成脑震荡、断腿、断手等重伤。公安局甚至放言，谁敢站出来说话就抓谁，即使把胡锦涛喊来都是这样子。政府部门包庇凶手的这种凶横态度终于激怒了学生，他
们于6月
28日下午将公安局一楼烧光，砸烂七八辆警车，学生的行动得到了全城百姓的支持和声援，一时间政府门前聚集了上万人，随着全城人民的愤怒不断高涨，晚上学
生们将县政府大楼点燃。示威的学生声援的百姓与政府机构的冲突进入对峙状态，6月29日凌晨四五点钟，聚集在县政府前的人们逐渐散去，只还有一小部分学生
的时候，警察又开始逞凶，据政府附近居民所见，他们将数十名学生抓来跪在地上死命毒打（据说有人被打死，警察旋即将其拖走），6月29日上午警察封锁通往
县政府的所有路口，在封锁过程中开车将一学生撞死。
<br />
从6月21日晚上事发到6月29日这几天中，受害者家属在将李淑芬遗体打捞上来后，一直在河边守护，期间，李淑芬的父亲上省政府告状，目前已经
失去任何消息，其母亲已经疯掉。公安局和县政府多次施压要求立即将受害者埋葬，目的没有达到。多次有数十人去抢尸、偷尸，也因守护人员奋力守护没有得逞。
愤怒的学生、百姓要求上告的签名上万，捐款达七八万元，但被警察抢劫并烧掉。据传州政府曾前去调查，但被县里面半路拦回，当地有记者想曝光此事遭到警察毒
打。6月
28日县里面还切断了所有通讯，全城无法和外界取得联系。据传省里面有关机构和人员在事发不久即知道这回事，但因省公安厅一副厅长和凶手有亲戚关系，一直
将此事压下不准调查。随着事情越来越恶化到几乎无法控制，政府机构调集了各周边县市警察、防暴队、武警（据说还有昆明军区一个师的部队）前往进行*；省里
开始有人下去，新华社等媒体开始介入采访。但目前的瓮安县在光天化日之下，依然一片黑暗，6月29日上午政府在公告中对此事的定性为非法集会、&ldquo;*烧&rdquo;事
件。以此定性，为要求正义、惩治凶手的学生、百姓很多将被冠以反国家、反社会等罪行，一生被打击迫害，学校老师将被冠以教唆不明真相的学生和百姓冲击国家
机关等罪行&hellip;&hellip;可怜那些被打死、打伤、打残的学生，他们都是中小学生，十一二岁的大好年华，政府就是用这种行动来为他们树立榜样、认识社会的吗？法律何
在？公平正义何在？天理何在？ </div>
          <br/>
          <span style="color:red;">
            <a href="http://shiningray.javaeye.com/blog/210743#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 02 Jul 2008 19:09:02 +0800</pubDate>
        <link>http://shiningray.javaeye.com/blog/210743</link>
        <guid>http://shiningray.javaeye.com/blog/210743</guid>
      </item>
      <item>
        <title>近期帖子一览</title>
        <author>ShiningRay</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://shiningray.javaeye.com">ShiningRay</a>&nbsp;
          链接：<a href="http://shiningray.javaeye.com/blog/208858" style="color:red;">http://shiningray.javaeye.com/blog/208858</a>&nbsp;
          发表时间: 2008年06月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><a href="http://shiningray.cn/chu-dao-shang-hai-gong-zuo-de-sheng-huo-zhi-nan.html" title="Permalink to 初到上海工作的生活指南" rel="bookmark">初到上海工作的生活指南</a>
</p>
<div><a href="http://shiningray.cn/two-months-at-uuzone.html" title="Permalink to 我在UUZone的两个月" rel="bookmark">我在UUZone的两个月</a>
<br />
<a href="http://shiningray.cn/how-to-determine-a-good-employer.html" title="Permalink to 毕业生和实习生如何挑一个好公司" rel="bookmark">毕业生和实习生如何挑一个好公司</a>
</div>
<div><a href="http://shiningray.cn/earthquake-and-resignation.html" title="Permalink to 地震和辞职" rel="bookmark">地震和辞职</a>
<br />
<a href="http://shiningray.cn/ruby-on-rails-deployment-schemes.html" title="Permalink to 浅析Ruby on Rails部署方案" rel="bookmark">浅析Ruby on Rails部署方案</a>
</div>
<div><a href="http://shiningray.cn/passenger-mod_rails-introduction.html" title="Permalink to Passenger (mod_rails)介绍" rel="bookmark">Passenger (mod_rails)介绍</a>
</div>
<div><a href="http://shiningray.cn/proxypass-directive-in-apache.html" title="Permalink to Apache的ProxyPass指令详解" rel="bookmark">Apache的ProxyPass指令详解</a>
</div>
<p>
    
<a href="http://shiningray.cn/the-red-button-on-dell.html" title="Permalink to Dell电源上价格不菲的红色开关" rel="bookmark">Dell电源上价格不菲的红色开关</a>
<br />
<a href="http://shiningray.cn/mysql-chu-ji-xing-neng-diao-zheng-jiao-ben.html" title="Permalink to MySQL初级性能调整脚本" rel="bookmark">MySQL初级性能调整脚本</a>
<br />
<a href="http://shiningray.cn/myisam-vs-innodb.html" title="Permalink to MyISAM vs InnoDB" rel="bookmark">MyISAM vs InnoDB</a>
<br />
 <a href="http://shiningray.cn/windows-shang-pei-zhi-codeblocks-wxwidgets.html" title="Permalink to Windows上配置Code::Blocks + wxWidgets" rel="bookmark">Windows上配置Code::Blocks + wxWidgets</a></p>
<div>
<a href="http://shiningray.cn/li-yong-ssh-tong-dao-jia-mi-wang-ye-lan.html" title="Permalink to 利用SSH通道加密网页浏览" rel="bookmark">利用SSH通道加密网页浏览</a>
</div>
<div>
<a href="http://shiningray.cn/coding-de-han-yi.html" title="Permalink to Coding的含义" rel="bookmark">Coding的含义</a>
</div>
<div>
<a href="http://shiningray.cn/rails-zhong-qu-xiao-asset-id.html" title="Permalink to Rails中取消asset id" rel="bookmark">Rails中取消asset id</a>
</div>
<div>
<a href="http://shiningray.cn/ni-sou-wo-sou.html" title="Permalink to 你搜我搜" rel="bookmark">你搜我搜</a>
</div>
<div>
<a href="http://shiningray.cn/php-de-er-jin-zhi-wei-yi-cao-zuo.html" title="Permalink to PHP的二进制位移操作" rel="bookmark">PHP的二进制位移操作</a>
</div>
<div>
<a href="http://shiningray.cn/ying-pan-sun-huai-gao-ke-yong-xing-you-gan.html" title="Permalink to 硬盘损坏+高可用性有感" rel="bookmark">硬盘损坏＋高可用性有感</a>
</div>
<div>
<a href="http://shiningray.cn/redmine.html" title="Permalink to Redmine" rel="bookmark">Redmine</a>
</div>
<p>
    
<a href="http://shiningray.cn/hui-xiang-gao-zhong-shi-de-yi-ci-bei-cai-fang-jing-li.html" title="Permalink to 回想高中时的一次被采访经历" rel="bookmark">回想高中时的一次被采访经历</a></p>
<div>
<div>
<a href="http://shiningray.cn/sheng-ji-dao-rubygems-10.html" title="Permalink to 升级到RubyGems 1.0" rel="bookmark">升级到RubyGems 1.0</a>
</div>
<div>
<a href="http://shiningray.cn/functional_%20_programming.html" title="Permalink to 函数式JavaScript编程指南" rel="bookmark">函数式JavaScript编程指南</a>
</div>
<div>
<a href="http://shiningray.cn/tcmalloc-thread-caching-malloc.html" title="Permalink to TCMalloc:线程缓存的Malloc" rel="bookmark">TCMalloc：线程缓存的Malloc</a>
</div>
<div>
<a href="http://shiningray.cn/farewell-2007.html" title="Permalink to Farewell, 2007" rel="bookmark">Farewell, 2007</a>
</div>
<div>
<a href="http://shiningray.cn/linux-shang-pei-zhi-nginx-mongrel-cluster.html" title="Permalink to Linux 上配置 Nginx + Mongrel cluster" rel="bookmark">Linux 上配置 Nginx + Mongrel cluster</a>
</div>
<div>
<a href="http://shiningray.cn/64.html" title="Permalink to Ruby 1.9.0 发布" rel="bookmark">Ruby 1.9.0 发布</a>
</div>
<div>
<a href="http://shiningray.cn/multiple_tags_separator.html" title="Permalink to WordPress标签分隔符插件" rel="bookmark">WordPress标签分隔符插件</a>
</div>
<div>
<a href="http://shiningray.cn/nginx-de-wordpress-pei-zhi.html" title="Permalink to Nginx的WordPress配置" rel="bookmark">Nginx的WordPress配置</a>
</div>
<div>
<a href="http://shiningray.cn/linux-shang-pei-zhi-nginx-php5-fastcgi.html" title="Permalink to Linux 上配置 Nginx + PHP5 FastCGI" rel="bookmark">Linux 上配置 Nginx + PHP5 FastCGI</a>
</div>
<div>
<a href="http://shiningray.cn/rao-kai-wan-wang-fang-huo-qiang.html" title="Permalink to 绕开万网防火墙" rel="bookmark">绕开万网防火墙</a>
</div>
</div>
          <br/>
          <span style="color:red;">
            <a href="http://shiningray.javaeye.com/blog/208858#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 28 Jun 2008 01:35:18 +0800</pubDate>
        <link>http://shiningray.javaeye.com/blog/208858</link>
        <guid>http://shiningray.javaeye.com/blog/208858</guid>
      </item>
      <item>
        <title>请访问我的BLOG</title>
        <author>ShiningRay</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://shiningray.javaeye.com">ShiningRay</a>&nbsp;
          链接：<a href="http://shiningray.javaeye.com/blog/163254" style="color:red;">http://shiningray.javaeye.com/blog/163254</a>&nbsp;
          发表时间: 2008年02月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://shiningray.cn" target="_blank">http://shiningray.cn</a>
          <br/>
          <span style="color:red;">
            <a href="http://shiningray.javaeye.com/blog/163254#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 19 Feb 2008 10:12:20 +0800</pubDate>
        <link>http://shiningray.javaeye.com/blog/163254</link>
        <guid>http://shiningray.javaeye.com/blog/163254</guid>
      </item>
      <item>
        <title>Nirvana Studio</title>
        <author>ShiningRay</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://shiningray.javaeye.com">ShiningRay</a>&nbsp;
          链接：<a href="http://shiningray.javaeye.com/blog/24847" style="color:red;">http://shiningray.javaeye.com/blog/24847</a>&nbsp;
          发表时间: 2006年09月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          请访问 <a href="http://www.nirvanastudio.org" target="_blank">http://www.nirvanastudio.org</a>
          <br/>
          <span style="color:red;">
            <a href="http://shiningray.javaeye.com/blog/24847#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 22 Sep 2006 17:11:05 +0800</pubDate>
        <link>http://shiningray.javaeye.com/blog/24847</link>
        <guid>http://shiningray.javaeye.com/blog/24847</guid>
      </item>
  </channel>
</rss>