<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>genki</title>
    <description></description>
    <link>http://genki.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>最近想找些关于全文检索的资料</title>
        <author>genki</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://genki.javaeye.com">genki</a>&nbsp;
          链接：<a href="http://genki.javaeye.com/blog/162926" style="color:red;">http://genki.javaeye.com/blog/162926</a>&nbsp;
          发表时间: 2008年02月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          这几天想找找跟全文检索相关的资料。看了Robbin几个人的文章发现都只是点到为止。提到了几种方法solr，sphinx ，ferret 。具体请看<a href="http://www.javaeye.com/topic/127968" target="_blank">这里</a> robbin 的文章。其中讲到了全文检索中中文分词的一些难题。因为还是初学者，很多东西不是很了。找了些国外的资料发现很多国外的网站也开始从ferret转移到用sphinx。<br />以下是一些对ferret的一些评论：<br /><div class="quote_title">引用</div><div class="quote_div">In past projects I’ve had some limited success, and spectacular failure, with Ferret, a Ruby port of the Java based Lucene search. It was slow, leaked memory and generally caused more pain than it should.</div><br /><div class="quote_title">引用</div><div class="quote_div">Ferret is unstable in production. Segfaults, corrupted indexes  <br />> galore. We've switched around 40 clients form ferret to sphinx and  <br />> solved their problems this way. I will never use ferret again after  <br />> all the problems I have seen it cause peoples production apps.</div><br />虽然这些评论并非都很可观，但也说明了ferret确实存在某些问题。所以决定先学学sphinx。<br /><br />但是sphinx相关文档少的可怜（可能是我不太会找）。在官方网站上发现了这篇<a href="http://dev.cgfinal.com/sphinx/sphinx.html" target="_blank">sphinx速成指南</a>.<br />还有几篇rails结合sphinx使用的文章。<a href="http://www.slashdotdash.net/articles/2007/08/06/rails-searching-with-sphinx" target="_blank"> Rails searching with Sphinx</a><br /><br /><a href="http://kpumuk.info/ror-plugins/using-sphinx-search-engine-in-ruby-on-rails/" target="_blank">Using Sphinx search engine in Ruby on Rails</a><br /><br />还有一篇也是robbin的<a href="http://robbin.javaeye.com/blog/122696" target="_blank">JavaEye3.0开发手记之三 － 狮身人面</a><br />过几天有时间在来研究下中文分词。
          <br/>
          <span style="color:red;">
            <a href="http://genki.javaeye.com/blog/162926#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/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 18 Feb 2008 12:08:08 +0800</pubDate>
        <link>http://genki.javaeye.com/blog/162926</link>
        <guid>http://genki.javaeye.com/blog/162926</guid>
      </item>
      <item>
        <title>Rolling with Rails 2.0 - Part 2</title>
        <author>genki</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://genki.javaeye.com">genki</a>&nbsp;
          链接：<a href="http://genki.javaeye.com/blog/162656" style="color:red;">http://genki.javaeye.com/blog/162656</a>&nbsp;
          发表时间: 2008年02月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          这里继续<a href="http://www.javaeye.com/topic/162536">Part 1</a>的内容。<br /><h2>其他一些好东西</h2>&nbsp;<p>所以，基本上我们已经完成了:一个功能齐全的博客系统，带有对管理任务的验证支持。都是些很棒的东西！现在让我们从头走一遍谈谈其他在rails2.0里面的好东西，其中的一些东西对用户是&ldquo;不可见的&rdquo;。</p><p>&nbsp;</p><h3>查询缓存</h3><p>在ActiveRecord里我最喜欢的一项改变是查询缓存。概念非常简单：当在处理一个查询的时候，你可能会重复同一个Sql查询超过一次，有时候你只是这样做:</p><p>&nbsp;</p><pre name="code" class="ruby">@results ||= Posts.find(:all)  </pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p><p>意味着你正在手动&rdquo;缓存&ldquo;。但有时候你有非常复杂的查询条件，怎么去缓存他就变得不是很明了。我们可能会看到这样的查询:</p><br /><pre name="code" class="ruby"># app/controllers/posts_controller.rb
class PostsController &lt; ApplicationController
  def index
    @posts = Post.find(:all)
    @posts2 = Post.find(:all)
  ...
  end
  ...
end</pre>&nbsp;<p>&nbsp;</p><p>如果我们访问http://localhost:3000/posts然后看看log/development.log,这是我们会看到的一个片段：</p><pre name="code" class="ruby">Parameters: {&quot;action&quot;=&gt;&quot;index&quot;, &quot;controller&quot;=&gt;&quot;posts&quot;}
Post Load (0.000357)   SELECT * FROM `posts` 
CACHE (0.000000)   SELECT * FROM `posts` </pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p>


<p>第一个查找发起了一个普通的对数据库查询。但第二个查找，因为和前面一个一模一样，因此将不会再一次访问数据库，他会从内部缓存中得到结果！真是聪明。</p><p>这不是一个带有全部特性的数据库缓存。也不是Memcached的替代方案。</p><p>这只是对RAILS工具集的一个小小增加，也是我们无论如何都会手动去做的。</p><p>&nbsp;</p><h3>Assets</h3><p>说道性能，还有另个困挠着网络管理者。假设我们要给我们的blog加上些Ajax.首先我们要做的是添加必要的</p><p>Scriptaculous 和 Prototype 库:&nbsp;</p><p>&nbsp;</p><pre name="code" class="ruby">&lt;!-- app/views/layouts/application.html.erb --&gt;
...
&lt;%= javascript_include_tag :defaults %&gt;
...</pre>&nbsp;<p>如果我们刷新浏览器并且查看生成的HTML源码，这是我们会得到的：</p><p>&nbsp;</p><pre name="code" class="ruby">&lt;script src=&quot;/javascripts/prototype.js?1197463288&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;/javascripts/effects.js?1197463288&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;/javascripts/dragdrop.js?1197463288&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;/javascripts/controls.js?1197463288&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;/javascripts/application.js?1197463288&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</pre>&nbsp;<p>&nbsp;</p><pre></pre><p>现在，非常的杂乱不堪。他还能更糟糕，因为很可能会我们根据所创建的接口的复杂性去增加更多的库。</p><p>问题是：这里的每一行都将发起一次HTTP请求给服务器。我们将会至少访问服务器5次去建立单单一个页面。</p><p>让我们稍作修改：</p><pre name="code" class="ruby">&lt;!-- app/views/layouts/application.html.erb --&gt;
...
&lt;%= javascript_include_tag :defaults, :cache =&gt; true %&gt;
...</pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>注意到在 javascript_include_tag里的 :cache选项了吗。为了让他生效我们必需重启服务器到生产模式下。作为提醒这里我们并没有创建任何表，所以首先我们必须迁移目前我们所做的一切。</p><pre name="code" class="ruby">rake db:migrate RAILS_ENV=production
./script/server -e production</pre>&nbsp;<p>&nbsp;</p>




	<p>&nbsp;</p><p>现在刷新下你的浏览器再看看HTML源码：</p><p>&nbsp;</p><pre name="code" class="html">&lt;script src=&quot;/javascripts/all.js?1197470157&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</pre>&nbsp;&nbsp;<br />&nbsp; <p>漂亮多了！所有那5个或更多的 载入javascript的单独HTTP请求 都被缩减到了一个。rails2.0封装了所有带有:cache选项的javascript到单独的一个javascript里。根据你网站的规模这可是意味着对于用户浏览器更短的载入时间。现在，还有一些其他类似方案如字符串压缩和这两种的组合使用，但这都是些不再rails2.0内核里的东西但又是值得去拥有的特性。</p><p>别忘了退出在生产模式下的服务器并且重启他到开发模式。</p><p>&nbsp;</p><h3>Ajax Helper&nbsp;</h3>


	<p>现在，说道Ajax，还有些新的helper.其中之一是让我们更简单的对浏览器DOM里的单一元素进行标识。像这样:</p><p>&nbsp;</p><pre name="code" class="ruby">&lt;!-- app/views/posts/index.html.erb --&gt;
...
&lt;% for post in @posts %&gt;
  &lt;% div_for(post) do %&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;%=h post.title %&gt;&lt;/td&gt;
    &lt;td&gt;&lt;%=h post.body %&gt;&lt;/td&gt;
    &lt;td&gt;&lt;%= link_to 'Show', post %&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;% end %&gt;
&lt;% end %&gt;
...</pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>注意看div_for 这个 helper.我们传给他一个Post实例，然后当我们在http://localhost:3000/posts里刷新下浏览器，这是我们得到的HTML源码:</p><p>&nbsp;</p><p>&nbsp;</p><pre name="code" class="ruby">...
&lt;div class=&quot;post&quot; id=&quot;post_1&quot;&gt;
&lt;tr&gt;
  &lt;td&gt;Hello Brazil!&lt;/td&gt;
  &lt;td&gt;Yeah!&lt;/td&gt;
  &lt;td&gt;&lt;a href=&quot;/posts/1&quot;&gt;Show&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/div&gt;
...</pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p><p>看到了吗？你得到了一个div标签，该标签带有已经设置有默认的class和id。现在我们可以用Prototype去获取单个元素，像这样:</p><p>&nbsp;</p><pre name="code" class="ruby">item = $('post_1')</pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;还有很多很多。对于Ajax来说这真是福音。更多的&rdquo;不要重复你自己&ldquo;。&nbsp;</p>


	<p><strong>声明：</strong> &nbsp;我知道这不是WEB标准!我的意思是：决不要在两个 div 标签中放入&nbsp;&amp;lttr&gt; 标签。首先，非必要的时候不要使用表格。而我这里这样做的原因是：更快速的切入主题，因为脚手架已经帮我们生成了表格，我只是不想扯到一些枝节上。在有限的标签里不要这样做。</p><h3>Atom Feeds</h3><p>&nbsp;&nbsp;</p><p>现在假设我们的网站需要Atom feeds!显然，每个博客都需要feed.那么我们该怎么做呢？很幸运的是，rails已经能辨识.atom格式。以下是我们所要做的：</p><pre name="code" class="ruby">class PostsController &lt; ApplicationController
  def index
    @posts = Post.find(:all)

    respond_to do |format|
      ...
      format.atom # index.atom.builder
    end
  end
  ...
end</pre><p>&nbsp;</p><p>只要增加一个format.atom调用，在他后面不需要任何的块，他就会自动的调用index.atom.builder.那么让我们创建一个:</p><p>&nbsp;</p><pre name="code" class="ruby"># app/views/posts/index.atom.builder
atom_feed do |feed|
  feed.title &quot;My Great Blog!!&quot;
  feed.updated((@posts.first.created_at))
  
  for post in @posts
    feed.entry(post) do |entry|
      entry.title(post.title)
      entry.content(post.body, :type =&gt; 'html')
      entry.author do |author|
        author.name(&quot;Fabio Akita&quot;)
      end
    end
  end
end</pre>&nbsp;<p>&nbsp;&nbsp;</p><p>这里，我们使用Atom Feed Help,这是新加入到rails2.0的辅助方法。这是我们已经有的XMLBuilder的简化版。它是利用成熟的DSL去创建的Atom feeds。现在给你解释下，你很可能已经注意到我们的模板不再有rhtml扩展名。脚手架生成'html.erb'扩展名。这是因为新的rails惯例是:</p><p>&nbsp;</p>


	<pre>engine]</pre>


	<p>所以，index.html.erb意思是，index这个action的模板返回的是'html'的内容并且使用erb引擎去渲染模板。那么，index.atom.builder的意思就是，另一个index的action的模板返回Atom的内容并且使用'builder'引擎去渲染它。rails2.0<span style="font-weight: bold">暂时</span>还认得出旧的文件名，但你最好现在就开始使用这种新的格式。</p><p>所以，当这些都设置好了后，我们可以访问 http://localhost:3000/posts.atom(注意到URL里的扩展名)</p><p>&nbsp;</p><p><img src="http://www.akitaonrails.com/assets/2007/12/12/Picture_8_1.png" height="298" alt="" width="400" /></p><p>&nbsp;</p><p>Safari知道它是一个Atom feed并且很好的了修饰它。其他Web客户端的行为也许会有些不一样，而这是那个Atom Feed Help所生成的:</p><p>&nbsp;</p><pre name="code" class="xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;feed xml:lang=&quot;en-US&quot; xmlns=&quot;http://www.w3.org/2005/Atom&quot;&gt;
  &lt;id&gt;tag:::posts&lt;/id&gt;
  &lt;link type=&quot;text/html&quot; rel=&quot;alternate&quot; href=&quot;http://:&quot;/&gt;
  &lt;title&gt;My Great Blog!!&lt;/title&gt;
  &lt;updated&gt;2007-12-09T20:54:15-02:00&lt;/updated&gt;
  &lt;entry&gt;
    &lt;id&gt;tag:::Post1&lt;/id&gt;
    &lt;published&gt;2007-12-09T20:54:15-02:00&lt;/published&gt;
    &lt;updated&gt;2007-12-09T20:55:31-02:00&lt;/updated&gt;
    &lt;link type=&quot;text/html&quot; rel=&quot;alternate&quot; href=&quot;http://:/posts/1&quot;/&gt;
    &lt;title&gt;Hello Brazil!&lt;/title&gt;
    &lt;content type=&quot;html&quot;&gt;Yeah!&lt;/content&gt;
    &lt;author&gt;
      &lt;name&gt;Fabio Akita&lt;/name&gt;
    &lt;/author&gt;
  &lt;/entry&gt;
  ...
&lt;/feed&gt;</pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p><p>漂亮，我们的博客有Feed了，但先别停下脚步。</p>


	<h3>支持iPhone</h3>


	<p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>让我们做些更炫的事。当iPhone的用户访问我们博客的时候，要是我想要拥有一个不同的主页该怎么办呢？我们可以为他创建一个用户定制的 Mime-Type。首先，让我们配置下 mime type:</p><p>&nbsp;</p><pre name="code" class="ruby"># config/initializers/mime-types.rb
Mime::Type.register_alias &quot;text/html&quot;, :iphone</pre>&nbsp;<p>&nbsp;&nbsp;</p><p>像以前我解释的那样，这是一个负责mime type部分的模块化了的环境配置文件。上面的那一行将会注册用户定制的:iphone类型使它成为html。我们必须重启服务器让他生效。</p><p>接着，我们必须修改Posts控制器使其能够识别iPhone.</p><p>&nbsp;</p><pre name="code" class="ruby"># app/controllers/posts_controller.rb
class PostsController &lt; ApplicationController
  before_filter :adjust_format_for_iphone

  def index
    @posts ||= Post.find(:all)

    respond_to do |format|
      ...
      format.iphone # index.iphone.erb
    end
  end

  ...
  def adjust_format_for_iphone
    if request.env['HTTP_USER_AGENT'] &amp;&amp;
      request.env['HTTP_USER_AGENT'][/iPhone/]
      request.format = :iphone
    end
  end
end</pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p><p><strong>声明：</strong>似乎Apple公司建议去寻找 &quot;Mobile Safari &quot;字符串而不是如我上面代码片段所做的使用&quot;iphone&quot;字符串.</p><p>在这个特殊的例子里我们分析从浏览器返回到服务器的HTTP头。这些数据可以从'request'这个hash里得到。所以，当客户端提到iPhone,我们可以恰当的将默认的request.format从:html变换到:iphone.在&lsquo;index&rsquo;action里或是其他任何action里我们希望有不同的行为，这里只是一个回应不同格式的问题。正像我们之前解释的那样，这会提交index.iphone.erb模板。所以让我们为他新建一个简单的模板：</p><p>&nbsp;</p><p>&nbsp;</p><pre name="code" class="java">&lt;!-- app/views/posts/index.iphone.erb --&gt;
&lt;h1&gt;For iPhones&lt;/h1&gt;

&lt;% for post in @posts %&gt;
&lt;h3&gt;&lt;%=h post.title %&gt;&lt;/h3&gt;
&lt;p&gt;&lt;%=h post.body %&gt;&lt;/p&gt;
&lt;% end %&gt;</pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p><p>简单多了。为了测试他我们可以使用针对多种浏览器的各种插件，这些插件会传递一个正确的用户代理。我们可以使用真实的机器（iphone）或者如果你使用的是Mac，下载一个<a href="http://www.marketcircle.com/iphoney/">iPhoney</a>仿真器。以下是我们将会得到的:</p><p>&nbsp;<img src="http://www.akitaonrails.com/assets/2007/12/12/Picture_9_1.png" height="730" alt="" width="400" /></p><p>&nbsp;</p><p>很棒，我们的博客瞬间变得越来越强大了，不止是拥有feeds并且还支持iphone设备。这就是我们处理其他种类的设备或者文件格式如PDFs，CSVs等等的方式。</p>


	<h3>调试</h3>


	<p>旧的rails有一个叫做breakPoiter的东西。但事实上基本没人用过它，因为它没有被很好的实现。几个月前另一个伙伴加入了他就是<a href="http://www.datanoise.com/ruby-debug/">ruby-debug</a>.很棒的是rails现在也将他引进了。首先，你必须安装它的gem:</p><pre>sudo gem install ruby-debug</pre><p>现在我们必须用一个新的选项去启动服务器：</p><pre>
./script/server -u</pre><p>最后，我们可以在任何时候加上'debugger'命令。例如，让我们在这里这样做：</p><p>&nbsp;</p><p>&nbsp;</p><pre name="code" class="ruby"># app/controllers/posts_controller.rb
class PostsController &lt; ApplicationController
  # GET /posts
  # GET /posts.xml
  def index
    @posts = Post.find(:all)
    debugger
    ...
  end
  ...
end</pre>&nbsp;<p>&nbsp;</p><p>如果我们在 http://localhost:3000/posts里刷新下浏览器，你会发现浏览器停住不动了，而当我们看看终端，这是我们将会得到的</p><p>&nbsp;</p><pre name="code" class="ruby">/Users/akitaonrails/tmp/blog_demo/app/controllers/posts_controller.rb:7 respond_to do |format|
(rdb:5) @posts
[#, #]

(rdb:5) @posts &lt;&lt; Post.new(:title =&gt; &quot;Post from Console&quot;)
[#, #, #]

(rdb:5) list
[2, 11] in /Users/akitaonrails/tmp/blog_demo/app/controllers/posts_controller.rb
   2    # GET /posts
   3    # GET /posts.xml
   4    def index
   5      @posts = Post.find(:all)
   6      debugger
=&gt; 7      respond_to do |format|
   8        format.html # index.html.erb
   9        format.xml  { render :xml =&gt; @posts }
   10      end
   11    end

(rdb:5) c</pre>&nbsp;<p>&nbsp;</p><p>&nbsp;</p><p>所以，我们有了一个script/console 和ruby-debug混合的shell.然后我们就可以实时的控制我们的Rails对象，修改他们。我们可以用's'键(step)一步步的执行代码或者用'c'键(continue)来继续执行代码。list命令可以帮助我们了解我们在源代码的哪里。ruby-debug有几十个选项和命令，这些都是非常值得掌握的。</p><p>&nbsp;</p><p>没有什么能够代替一个好的测试套件。决不要用调试去替代测试。这两个是用来协同工作的。说道测试，Fixtures也得到了关注。rails引进了一个叫 <a href="http://m.onkey.org/2007/10/26/fixtures-go-foxy">Foxy Fixtures</a>的东东。</p><p>&nbsp;</p><p>&nbsp;</p><h3>Foxy Fixtures</h3><p>&nbsp;</p><p>有一件非常痛苦的事，那就是在一个什么都是硬编码的fixtures里跟踪那些主键和外键。如果你有一个简单的fixture,那倒不是什么大问题。但是如果你有几十个的fixture,每个fixture又有几十行和许许多多的关联，特别是多对多关联，那么它就变得相当难以维护。</p><p>现在让我们看看Rails2.0里的Fixture长什么样：</p><pre name="code" class="ruby"># test/fixtures/posts.yml
DEFAULTS: &amp;DEFAULTS
  created_at: &lt;%= Time.now %&gt;
  updated_at: &lt;%= Time.now %&gt;
  
post_one:
  title: MyString
  body: MyText
  &lt;&lt;: *DEFAULTS

post_two:
  title: MyString
  body: MyText
  &lt;&lt;: *DEFAULTS

</pre>&nbsp;<p>&nbsp;</p><pre name="code" class="ruby"># test/fixtures/comments.yml
DEFAULTS: &amp;DEFAULTS
  created_at: &lt;%= Time.now %&gt;
  updated_at: &lt;%= Time.now %&gt;

comment_one:
  post: post_one
  body: MyText
  &lt;&lt;: *DEFAULTS

comment_two:
  post: post_two
  body: MyText
  &lt;&lt;: *DEFAULTS</pre><br /><br /><p>&nbsp;</p><p>David曾经说过，给一行fixture命名是一种艺术，而现在这更加重要了。给 fixture的行一个有意义的，一个让他更容易地被看出哪个测试将会包含他的名字。</p><p>&nbsp;</p><p>这里有个新东西就是我们可以强制的手动设置主键(在id字段内)，另个东西就是我们可以用名字进行关联而不是id.看看我们是怎样将comment关联到post的：利用POST的名字。</p><p>&nbsp;</p><p>当我们有许多行在相同的字段里都有同样的值时，我们可以分离他们并在所有这些需要的行进行重用。在上面的例子里，我分离了每行都用到的时间字段。这听起来有点华而不实，但并非如此：它会让测试上到一个全新的台阶。开发者通常不喜欢写测试，所以让他变得简单点能激励他们做做测试。</p><p>&nbsp;</p><h3>Cookie 存储</h3>


	<p>&nbsp;</p><p>Web应用控制请求间的状态一个默认的方式是去保存一个session，然后将它的ID通过cookie传送给用户的浏览器而后又回传回来。这虽然行得通但是有个问题：要在哪里存储这些session?直到1.2版本，rails将所有session按照单个文件存储'tmp'文件夹里。你也可以将它转换成 SQL session Store(将Session存储到数据库表里)&nbsp;</p>


	<p>&nbsp;</p><p>第一个选择的优势是能够快速的初始化，但是最后还是会有些问题，因为你并没有将&rdquo;一切共享&ldquo;：文件只会在一台服务器里如果你有很多box，你就必须去处理那些类似NFS的恶心的东西.数据库更容易去管理，但你却要为每个用户请求都要访问数据库而付出代价。文件存储session还有点坏处就是如果你忘了时不时清空tmp文件夹，当你开始拥有成百上千的小文件时，文件系统就会很容易的变得臃肿。</p><p>&nbsp;</p><p>所以，Rails 2.0带来了漂亮简洁的解决方案叫做 &quot;Cookie Session Store&quot;.这是默认选择。一切都是在config/environment.rb文件里发起，你可以在那里找到像这样的东西:</p>


	<pre name="code" class="ruby">config.action_controller.session = {
  :session_key =&gt; '_blog_demo_session',
  :secret      =&gt; '2f60d1e...3ebe3b7'
}</pre>&nbsp;它将会利用很强的密码去生成密钥(初始的beta版本是不安全的，但是发布版本的安全性会非常强)<br /><p>如果你分析正在被传输的HTTP包你会发现这个HTTP头:</p>


	<p>&nbsp;</p><p>&nbsp;</p><pre name="code" class="html">
Set-Cookie: _blog_demo_session=BAh7BiI...R7AA%253D%253D--f92a00...2dc27c; path=/

</pre>&nbsp;我在这里把字符串缩短了：他们相当长。这里的概念就是：不要将session存储在服务器里，将它传回给浏览器。这都是些用户不会去关心的数据。被传递的数据是一个被Hash保护的 Base64 的blob用来防止被篡改。<p>&nbsp;</p>




	<p>这里是遵循 <strong>不要将大的对象存储在SESSION </strong>和&nbsp;<strong>不要将秘密和重要数据存储在SESSION</strong> 这两个原则.</p>


	<p>如果你将一个带有很复杂的数据结构的集合存在了缓存，你很可能干了件错事。一个典型的Rails session不会存储超过一个用户ID又或者是一个Flash消息。所以他非常的小。跟网页里其他的文件比起来他简直微不足道。</p><p>&nbsp;</p><p>好处就是你可以省去维护服务端的session文件的麻烦，你也不用遭受因为访问数据库而带来的性能问题。这是一个很棒的选择你也应该使用它除非你有非常特别的要求。如果你刚开始使用Rails 2.0也不去关心这些，你将会自动的使用Cookie存储。这比使用基于文件存储又忘记清理session文件夹而最终有一天被他害惨了要来的好得多。</p><p>&nbsp;</p>


	<p>&nbsp;</p><p>&nbsp;</p><h2>在rails2.0被遗弃的</h2>


	<p>所以，增加了很多东西。还有更多的东西我在这个教程里面没有提到。如果你运行在rails1.2.6,看看你的日志：他会展示那些你正在使用的并且在rails2.0将不复存在的东西。</p><p>&nbsp;</p><p>一些你很可能还在使用的东西：</p>


	<p>&nbsp;</p>


	<ul><li>在控制器里，不要使用 @params, @session, @flash, @request or @env. 你现在拥有他们各自同样的方法名params, session,
flash, request and env. 用他们来代替以前的那些.</li></ul>


	<ul><li>在模型里，不要使用 find_one or find_all, 用 find(:first) 和 find(:all)</li></ul>


	<p>一些主要的组件被删除了:</p>


	<ul><li>acts_as_list</li><li>acts_as_tree</li><li>acts_as_nested_set</li><li>All database adapters, except MySQL, SQLite, PostgreSQL</li><li>&lsquo;Classic&rsquo; Pagination</li><li>ActionWebService</li></ul>


	<p>&nbsp;acts_as插件现在是可选的，你还是可以拥有他们。他们都在这里http://svn.rubyonrails.org/rails/plugins/</p>


	<p>旧的 pagination还在Err the Blog被维护着,所以你只要:</p>


<pre>
./script/plugin install \
svn://errtheblog.com/svn/plugins/classic_pagination
</pre>

	<p>但是当然，不要再用它了，我们用<a href="http://errtheblog.com/posts/56-im-paginating-again">will_paginate</a>.</p><p>&nbsp;</p>


	<p>数据库适配器现在也是分离的gems了.这样做非常好因为现在他们各自的升级都不再和rails的发行有关系了。他们可以更频繁的发布而你也可以用或不用他们。默认的安装方式是：</p>


<pre>
sudo gem install activerecord-[database]-adapter
</pre>

	<p>分离这些东西对rails带来的另个好处是它使rails的内核更加苗条，而不用去加载那些大部分人都不会去用的东西。</p><p>&nbsp;</p><h2><span class="caps">SOAP</span> vs <span class="caps">REST</span></h2>


	<p>最后一个被遗弃的东西也是我想讲的是，多亏有了ActiveResource总算可以去除ActionWebService了。在rails里创建<span class="caps">SOAP</span> APIs 并不困难但也并不好玩。让我给你展示下用ActiveResource你可以做些什么</p>


	<p>&nbsp;</p><p>让我们保持刚刚创建的blog处在运行状态。从另一个终端shell里，让我们创建另个rails项目：</p><p>&nbsp;</p><pre name="code" class="ruby">rails blog_remote
cd blog_remote
./script/console

</pre>&nbsp;就是他了，一个骨瘦如柴的rails应用。在这个例子里rails控制台可以单独的执行。在里面输入如下：&nbsp;<p>&nbsp;</p><br /><pre name="code" class="ruby">
class Post &lt; ActiveResource::Base
  self.site = 'http://akita:akita@localhost:3000/admin'
end</pre><p>&nbsp;</p><p>现在，准备大吃一惊吧!从这个同样的控制台里输入如下：</p><p>&nbsp;</p><pre name="code" class="ruby">Post.find(:all)</pre><br /><p>是的!记得吗：在这个新的rails项目里我们并没有设置数据库或是创建任何模型。这都是我们之前的调用所产生的例子：</p><pre name="code" class="ruby">from (irb):4&gt;&gt; Post.find(:all)
=&gt; [#&lt;Post:0x1254ea4 @prefix_options={}, @attributes={
&quot;updated_at&quot;=&gt;Sun Dec 09 22:55:31 UTC 2007, &quot;body&quot;=&gt;&quot;Yeah!&quot;,
 &quot;title&quot;=&gt;&quot;Hello Brazil!&quot;, &quot;id&quot;=&gt;1, &quot;created_at&quot;=&gt;Sun Dec 09 22:54:15 UTC 2007}&gt;, 
#&lt;Post:0x1254e90 @prefix_options={}, @attributes={
&quot;updated_at&quot;=&gt;Sun Dec 09 23:05:32 UTC 2007, &quot;body&quot;=&gt;&quot;Rio on Rails event!!&quot;, 
&quot;title&quot;=&gt;&quot;Hello from Rio on Rails&quot;, &quot;id&quot;=&gt;2, 
&quot;created_at&quot;=&gt;Sun Dec 09 23:05:32 UTC 2007}&gt;]</pre><p>&nbsp;我们甚至可以创建一个新的post,像这样:</p><pre name="code" class="ruby">&gt;&gt; p = Post.create(:title =&gt; &quot;Created Remotely&quot;, 
:body =&gt; &quot;Hello from console again!&quot;)
=&gt; #&lt;Post:0x122df48 @prefix_options={}, @attributes={
&quot;updated_at&quot;=&gt;Wed Dec 12 15:13:53 UTC 2007, 
&quot;body&quot;=&gt;&quot;Hello from console again!&quot;, &quot;title&quot;=&gt;&quot;Created Remotely&quot;, 
&quot;id&quot;=&gt;3, &quot;created_at&quot;=&gt;Wed Dec 12 15:13:53 UTC 2007}&gt;</pre><p>现在，回到你的浏览器并且刷新 http://localhost:3000/posts</p><pre name="code" class="ruby">http://www.akitaonrails.com/assets/2007/12/12/Picture_7_1.png</pre>&nbsp;<br /><p>懂了吗？我们现在拥有了两个 rails 应用程序，而其中的一个能通过基于RESTful的HTTP调用来和另个交谈！</p><p>还不止这些：当我们创建Post ActiveResource，我们把用户名和密码给予了之前做的基于HTTP的认证。这也是其中的一个优点：他使以后的API调用更加容易。</p><p>&nbsp;</p><p>所以说，我们<span style="font-size: x-small"><span style="font-weight: bold">连一行代码都没改</span></span>就已经能够创建远程聚合(remote integration)。所有的这一切都要感谢利用REstful方式去创建 Rails应用：你拥有每个你所创建的 RESTful 资源的全部API.跟着惯例走使一切都变得容易。</p><p>&nbsp;</p><h2>结论</h2>


	<p>所以，Rails包含了这些好东西，有许多优化的东西使你不必重新学习他们，这是非常棒的。但如果说你已经有一个1.2版本的rails应用并且正在运行，那将它盲目的升级到2.0有意义吗？</p><p>并不总是这样。在这里我们必须运用我们的常识：</p><br /><ul><li>有什么2.0里的东西你想在你的应用程序里面使用吗？ </li><li>你的应用拥有能胜任的测试组件了吗？</li><li>你使用的第三方插件已经能够在rails2.0底下工作了吗</li></ul>


	<p>在没有回答这些问题之前你不应该更新。这就是为什么你可以锁定旧的gems到你的应用程序: </p><p>&nbsp;</p><p>&nbsp;</p><pre name="code" class="ruby">rake rails:freeze:gems -v1.2.6

</pre><p>&nbsp;你可以同时安装 rails1.2 和 2.0。要使用旧的rails命令去创建1.2项目你要这样做：</p><p>&nbsp;</p><pre name="code" class="ruby">rails _1.2.6_ [project_name]

</pre><p>&nbsp;确保你已经覆盖了所有东西。1.2.6会在你的日志里面给出一些警告。</p><p>根据这些警告去调整你的应用程序使之能够运行在Rails2.0下</p><p>&nbsp;</p><p>如果你是新手，正在学习rails，你可能还找不到太多的材料去学习：直到目前为止的书籍都只涵盖1.2的内容。但这不是什么问题：先学1.2，然后你可以等待全新的2.0书籍发行（我在明年初的时候会发行一本），根据我的教程你将能够学习2.0，只是几天的时间，又或者只是几个小时。</p><p>Rails2.0绝不是一场革命，它是一个非常受欢迎的演进。Rails原本的简洁精致已经相当不错了。这相当棒。</p><p>我希望我的教程能够帮助大家快速掌握rails2.0的新特性。</p><p>如果你想要得到我们刚刚做的博客的全部的源码，从这里<a href="http://www.akitaonrails.com/assets/2007/12/12/blog_demo.zip" target="_blank">下载</a>。</p><p>&nbsp;Have fun!</p><p>&nbsp;</p><p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://genki.javaeye.com/blog/162656#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</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>Sat, 16 Feb 2008 16:58:15 +0800</pubDate>
        <link>http://genki.javaeye.com/blog/162656</link>
        <guid>http://genki.javaeye.com/blog/162656</guid>
      </item>
      <item>
        <title>Rolling with Rails 2.0 - The First Full Tutorial </title>
        <author>genki</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://genki.javaeye.com">genki</a>&nbsp;
          链接：<a href="http://genki.javaeye.com/blog/162536" style="color:red;">http://genki.javaeye.com/blog/162536</a>&nbsp;
          发表时间: 2008年02月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <em>以下这篇文章翻译自<a href="http://www.akitaonrails.com/2007/12/12/rolling-with-rails-2-0-the-first-full-tutorial" target="_blank">这里</a> ,还是新人翻译不好请大家多多指教.</em><br /><br />我很高兴的看到 我的<a href="http://www.akitaonrails.com/2007/12/10/the-first-rails-2-0-screencast-english" target="_blank">rails2.0视频教程</a>被广泛接受。超过1500人次看过它，做他的目的是想简单快速地介绍下rails2.0,展示下那些可以在30分钟以内做到的事。<br />  现在，我将要把那段视频分成几个主要的部分，并且建立一个包含一些新特性的逐步讲解的rails2.0教程.<br />像其他教程一样，他并不能涵盖rails2.0 100%的特性，详细的我推荐你去看看<br /> <a href="http://www.peepcode.com/" target="_blank">Peepcode’s Rails2 PDF</a> 和Ryan Bates <a href="http://railscasts.com/" target="_blank">Railscasts.com .</a><br />这是一个两部分教程，第二部分请点击<a href="http://www.javaeye.com/topic/162656" target="_blank">这里</a> 关于这个教程的全部源代码在<a href="http://www.akitaonrails.com/assets/2007/12/12/blog_demo.zip" target="_blank">这里</a>。<br />让我们开始把。<br /><span style="font-size: x-large">Recognizing the Environment</span><br /><br /><br /><br />这个教程是面向那些已经掌握了一些rails1.2知识的人，请参考互联网上许多优秀的关于  rails1.2的教程。<br />一开始你所要做的事更新你的gems:<br /><pre name="code" class="ruby">sudo gem install rails --include-dependencies</pre><br /><br />你可能还想要更新的你的 RubyGems:<br /><br /><pre name="code" class="ruby"> sudo gem update --system</pre><br /><br />首先，我们创建一个rails应用程序：<br /><pre name="code" class="ruby">rails blog</pre><br /><br />这将会创建我们常见的rails 文档结构。首先注意到的是开发环境。这是我们现在拥有的主要结构:<br />    * config/environment.rb<br />    * config/initializers/inflections.rb<br />    * config/initializers/mime_types.rb<br /><br />所有在  config/initializers  文件夹里的东西将会和environment.rb 同时被载入。那是因为当你的项目使用了许多不同的插件和gems的时候，environment.rb会变得越来越臃肿 并且难以维护。现在我们有了一个更简单的方式去模块化管理我们的配置。<br /><br /><span style="font-size: x-large">Database</span><br /><br />第二个要配置的东西是我们的数据库。这和以前要干的事没两样，都是在 config/database.yml:<br /><br /><pre name="code" class="ruby">development:
  adapter: mysql
  encoding: utf8
  database: blog_development
  username: root
  password: root
  socket: /opt/local/var/run/mysql5/mysqld.sock

test:
  adapter: mysql
  encoding: utf8
  database: blog_test
  username: root
  password: root
  socket: /opt/local/var/run/mysql5/mysqld.sock

production:
  adapter: mysql
  encoding: utf8
  database: blog_production
  username: root
  password: root
  socket: /opt/local/var/run/mysql5/mysqld.sock</pre><br /> 注意到现在你有一个‘encoding’  选项默认设置成 utf-8. rails 应用程序也是默认读取 KCODE=true, 意味着他一开始就应经支持了UNICODE了，这是相当棒的。但"encoding"配置还有另个用途：每个连接到数据库的rails都会被告之使用encoding的设置。就像是设置了 '"SET NAMES UTF8"<br /><br />一个小技巧用来对 database.yml实现 DRY 原则的是:<br /><pre name="code" class="ruby">defaults: &defaults
  adapter: mysql
  encoding: utf8
  username: root
  password: root
  socket: /opt/local/var/run/mysql5/mysqld.sock

development:
  database: blog_development
  &lt;&lt;: *defaults

test:
  database: blog_test 
  &lt;&lt;: *defaults

production:
  database: blog_production
  &lt;&lt;: *defaults</pre><br /><br />好多了，我们有个新的rask任务。并且其中的一些和数据库有关:<br />db:charset 	Retrieves the charset for the current environment’s database<br />db:collation 	Retrieves the collation for the current environment’s database<br />db:create 	Create the database defined in config/database.yml for the current RAILS_ENV<br />db:create:all 	Create all the local databases defined in config/database.yml<br />db:drop 	Drops the database for the current RAILS_ENV<br />db:drop:all 	Drops all the local databases defined in config/database.yml<br />db:reset 	Drops and recreates the database from db/schema.rb for the current environment.<br />db:rollback 	Rolls the schema back to the previous version. Specify the number of steps with STEP=n<br />db:version 	Retrieves the current schema version number<br /><br /><br />我们有更好的数据库管理支持。在以前,我们得登入到数据库控制台然后手动创建这些数据库，现在我们只要简单地:<br />rake db:create:all<br />如果我们现在想要重头做起，我们可以 db:drop:all. 如果是在开发当中我们可以利用 db:rollback 回退到上个版本。<br /><br /><span style="font-size: x-large">Sexyness(性感)</span><br /><br />在数据库的设置好了下面我们就可以创建我们的第一个资源。记得吧，现在 rails2.0是默认 RESTful 的。（for brazilians: 我也另外写了篇关于restful 的教程）。<br />.<br />/script/generate scaffold Post title:string body:text<br /><br />这里唯一不同的是'scaffold'的用处就和我们以前用的'scaffold_resource'一样，和那个旧的并不是 restful 的 scaffold 也已经没了。你现在也没有 ActionController类方法'scaffold'来动态创建一个带有默认action的空的控制器了。所以任何我们所scaffold 的东西都变成 RESTful的了。<br />他会创建那些常见的东西：Controller, Helper, Model, Migration, Unit Test, Functional Test.<br />主要的不同是在迁移文件：<br /><br /><pre name="code" class="ruby"># db/migrate/001_create_posts.rb
class CreatePosts &lt; ActiveRecord::Migration
  def self.up
    create_table :posts do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end

  def self.down
    drop_table :posts
  end
end</pre><br /><br /><br />这叫做 性感迁移(Sexy Migrations),首先发明自 “Err the Blog” 作为一个插件随之进入到了rails内核中。了解他们有何异同最好的方式就是去看看他在rails1.2中长什么样:<br /><br /><pre name="code" class="ruby">class CreatePosts &lt; ActiveRecord::Migration
  def self.up
    create_table :posts do |t|
      t.column :title, :string
      t.column :body, :text
      t.column :created_at, :datetime
      t.column :updated_at, :datetime
    end
  end

  def self.down
    drop_table :posts
  end
end</pre><br />他去除了重复的't.column',现在使用‘t.column_type’的格式并且自动加上的时间戳被浓缩成了一行语句‘t.timestamps’.并没有改变什么行为，只是让代码更加"性感"些<br />现在，像以前一样运行一个迁移任务<br /><br />rake db:migrate<br /><br />在以前，如果我要回退到某一个数据迁移我需要这样做:<br /><br />rake db:migrate VERSION=xxx<br /><br />'xxx'是代表我们想要回退到的版本号,现在我们仅仅需要:<br /><br />rake db:rollback<br /><br />更加的简洁优雅，这是肯定的。一切都设置好了，现在我们可以像以前一样启动服务器，看看生成的页面。<br /><br />./script/server<br /><br /><img src="http://www.akitaonrails.com/assets/2007/12/12/Picture_1_1.png" /><br /><br />他会自动载入不管是在 Mongrel, Webrick 或 Lightpd 在  3000 端口。我们和以前一样拥有一个根页面，为 index.html.有一个小点我在视频里面没有提到的是:<br /><pre name="code" class="ruby"># config/routes.rb
ActionController::Routing::Routes.draw do |map|
  map.root :controller => 'posts'
  map.resources :posts
end
</pre><br />这里有一个新的语句‘map.root’，该语句和 “map.connect ’’, :controller =&gt; ‘posts’.有同样的效果。只是一点小小的花招并没有做什么大动作只是让路由文件看起来更优美些。一旦设置了它，别忘了了删除 public/index.html 文件。 根URL就总会指向POSTS控制器。<br /><img src="http://www.akitaonrails.com/assets/2007/12/12/Picture_2_1.png" /><br />正如你所看到的，一切都和以前一样。所有的脚手架模板都还是一样。你可以浏览一下，创建一些新行等等。<br /><br /><span style="font-size: x-large">嵌套路由</span><br /><br />让我们来创建一些和POST有关的COMMENT。他会完成创建我们 blog的资源：<br /><pre name="code" class="ruby">
./script/generate scaffold Comment post:references body:text
rake db:migrate</pre><br /><br />和上面一样：scaffold一下资源，在命令行中设置组名和数据类型然后迁移文件就会自动设置好。注意到另外一个新增的东西：关键字”references“ 正如 许多朋友提醒我的，这会使数据迁移更性感。<br />比较起来，这是以前做这件事用的方法：<br /><br /><pre name="code" class="ruby">./script/generate scaffold Comment post:references body:text</pre><br />外键只是一个无关紧要的实现细节，看看新的数据迁移文件:<br /><br /><pre name="code" class="ruby">def self.up
  create_table :comments do |t|
    t.references :post
    t.text :body

    t.timestamps
  end
end</pre><br /><br />看这里关于新的关键字” references“的细节。所以，运行 db:migrate 将在数据库中创建表。<br />然后我们设置 ActiveRecord模型让他们和彼此互相关联，像这样：<br /><pre name="code" class="ruby"># app/models/post.rb
class Post &lt; ActiveRecord::Base
  has_many :comments
end

# app/models/comment.rb
class Comment &lt; ActiveRecord::Base
  belongs_to :post
end</pre><br />好了，这里没有什么新东西了，我们已经知道怎么处理 ActiveRecord关联了 ,但我们依旧工作在RESTFUL资源环境下。在新的RAILS方式里，我们可以拥有像这样的url:<br /><br />http://localhost:3000/posts/1/comments<br />http://localhost:3000/posts/1/comments/new<br />http://localhost:3000/posts/1/comments/3<br /><br /><br />意思是 从这个具体的POST里取出相关的comment，脚手架生成器只准备了如下这种url:<br /><br />http://localhost:3000/posts/1<br />http://localhost:3000/comments/new<br />http://localhost:3000/comments/3<br /><br /><br />那是因为在config/routes.rb 中:<br /><pre name="code" class="ruby">
# config/routes.rb
ActionController::Routing::Routes.draw do |map|
  map.resources :comments

  map.root :controller => 'posts'
  map.resources :posts
end</pre><br /><br />让我们稍微调整一下，就像在model中，我们可以这样调用一个嵌套路由：<br /><br /><pre name="code" class="ruby"># config/routes.rb
ActionController::Routing::Routes.draw do |map|
  map.root :controller => 'posts'
  map.resources :posts, :has_many => :comments
end</pre><br /><br />就像这样！ 现在我们可以想上面这样嵌套url. 首先要理解的是当我打入这样的URL:http://localhost:3000/posts/1/comments<br />rails 会将它解析成这样:<br /><br />    * 载入控制器<br />    * 设置 params[:post_id] = 1<br />    * 在这种情况下，调用' index ' 的action.<br /><br />我们必须准备让CommentsController被嵌套。所以这是我们接下来要去修改的：<br /><br /><pre name="code" class="ruby">class CommentsController &lt; ApplicationController
  before_filter :load_post
  ...
  def load_post
    @post = Post.find(params[:post_id])
  end
end</pre><br /><br />这是的 @post已经在所有的术语comments控制器中的action中设置好了。现在我们必须做这些修改：<br /><br />之前 	                     修改后<br />Comment.find 	              @post.comments.find<br />Comment.new 	              @post.comments.build<br />redirect_to(@comment)    	redirect_to([@post, @comment])<br />redirect_to(comments_url) 	redirect_to(post_comments_url(@post))<br /><br />这是的comment 控制器准备好了。现在让我们改变一下4个在  app/views/comments中的视图。<br />如果你打开不管是  new.html.erb 或者是 edit.html.erb你将会注意到如下这些新的特性：<br /><br /><pre name="code" class="ruby"># new edit.html.erb and new.html.erb
form_for(@comment) do |f|
  ...
end</pre><br /><br />这是新的定义方式：<br /><pre name="code" class="ruby"># old new.rhtml
form_for(:comment, :url => comments_url) do |f|
  ...
end</pre><br /><br /><pre name="code" class="ruby"># old edit.rhtml
form_for(:comment, :url => comment_url(@comment), 
  :html => { :method => :put }) do |f|
  ...
end</pre><br /><br />注意到同样的 form_for 语句是怎样适应 'new' 和 'edit' 的情况。<br />这是因为Rails可以根据 @comment模型实例的类名去推断出做些什么,但是现在<br />对于嵌套路由来说，comment是依赖于post,所以我们必须这样做:<br /><br /><pre name="code" class="ruby"># new edit.html.erb and new.html.erb
form_for([@post, @comment]) do |f|
 ...
end</pre><br /><br />Rails会变得足够聪明去辨识这个数组是表示一个嵌套路由，它会去检查 routes.rb 并且找出<br />他是post_comment_url(@post, @comment)的具名路由<br /> <br />让我们先来解释下具名路由。当我们在routes.rb设置资源路由时，我们可以得到下列具名路由：<br /><br />oute 	HTTP verb 	Controller Action<br />comments 	GET 	index<br />comments 	POST 	create<br />comment(:id) 	GET 	show<br />comment(:id) 	PUT 	update<br />comment(:id) 	DELETE 	destroy<br />new_comment 	GET 	new<br />edit_comment(:id) 	GET 	edit<br /><br />“七个 Action足以对付一切 …” :-)<br />你可以给他们加上 'path'或是'url'的后缀，不同在：<br /><br />comments_url 	http://localhost:3000/comments<br />comments_path 	/comments<br /><br />最后你可以给他们加上 'formatted'的前缀，给你：<br /><br />comments_url 	http://localhost:3000/comments<br />comments_path 	/comments<br /><br />现在，当comment已经嵌套在post里面，我们必须加上 'post'前缀，在 rails1.2里，这个前缀是可选择的，<br />他将能够依据传到具名路由helper里的的数字或者参数区分它们。但这会带来许多歧义性，所以现在必须强制加上前缀，像这样：<br /><br />route 	HTTP verb 	URL<br />post_comments(@post) 	GET 	/posts/:post_id/comments<br />post_comments(@post) 	POST 	/posts/:post_id/comments<br />post_comment(@post, :id) 	GET 	/posts/:post_id/comments/:id<br />post_comment(@post, :id) 	PUT 	/posts/:post_id/comments/:id<br />post_comment(@post, :id) 	DELETE 	/posts/:post_id/comments/:id<br />new_post_comment(@post) 	GET 	/posts/:post_id/comments/new<br />edit_post_comment(@post, :id) 	GET 	/posts/:post_id/comments/edit<br /><br />所以，总结起来，我们必须让 comments 视图的行为更像是嵌套在一个POST里一样。<br />所以我们还必须对从默认脚手架生成的代码到内嵌的表格中的具名路由做些改变:<br /><br /><pre name="code" class="ruby">&lt;!-- app/views/comments/_comment.html.erb -->
&lt;% form_for([@post, @comment]) do |f| %>
  &lt;p>
    &lt;b>Body&lt;/b>&lt;br />
    &lt;%= f.text_area :body %>
  &lt;/p>

  &lt;p>
    &lt;%= f.submit button_name %>
  &lt;/p>
&lt;% end %></pre><br /><br /><pre name="code" class="ruby">&lt;!-- app/views/comments/edit.html.erb -->
&lt;h1>Editing comment&lt;/h1>

&lt;%= error_messages_for :comment %>

&lt;%= render :partial => @comment, 
  :locals => { :button_name => "Update"} %>

&lt;%= link_to 'Show', [@post, @comment] %> |
&lt;%= link_to 'Back', post_comments_path(@post) %></pre><br /><br /><pre name="code" class="ruby">&lt;!-- app/views/comments/new.html.erb -->
&lt;h1>New comment&lt;/h1>

&lt;%= error_messages_for :comment %>

&lt;%= render :partial => @comment, 
  :locals => { :button_name => "Create"} %>

&lt;%= link_to 'Back', post_comments_path(@post) %>
</pre><br /><pre name="code" class="ruby">
&lt;!-- app/views/comments/show.html.erb -->
&lt;p>
  &lt;b>Body:&lt;/b>
  &lt;%=h @comment.body %>
&lt;/p>


&lt;%= link_to 'Edit', [:edit, @post, @comment] %> |
&lt;%= link_to 'Back', post_comments_path(@post) %>
</pre><br /><pre name="code" class="ruby">
&lt;!-- app/views/comments/index.html.erb -->
&lt;h1>Listing comments&lt;/h1>

&lt;table>
  &lt;tr>
    &lt;th>Post&lt;/th>
    &lt;th>Body&lt;/th>
  &lt;/tr>

&lt;% for comment in @comments %>
  &lt;tr>
    &lt;td>&lt;%=h comment.post_id %>&lt;/td>
    &lt;td>&lt;%=h comment.body %>&lt;/td>
    &lt;td>&lt;%= link_to 'Show', [@post, comment] %>&lt;/td>
    &lt;td>&lt;%= link_to 'Edit', [:edit, @post, comment] %>&lt;/td>
    &lt;td>&lt;%= link_to 'Destroy', [@post, comment], 
      :confirm => 'Are you sure?', :method => :delete %>&lt;/td>
  &lt;/tr>
&lt;% end %>
&lt;/table>

&lt;br />

&lt;%= link_to 'New comment', 
  new_post_comment_path(@post) %></pre><br /><br />提几点：<br />注意到我创建了一个局部模板去 DRY（不要重复你自己） new 和 edit中表格。但请注意，<br />并非使用 :partial=> 'comment' 而使用 ：partial=>@comment.<br />然后再一次他能够从类名推断出局部模板的名字。如果我们传进一个集合他会将其转换成旧的语句 ':partial,:collection'<br /><br />我可以使用post_comment_path(@post, @comment),或是更简单的 [@post, @comment]<br /><br />请注意不要忘记背后的那些具名路由。<br />最后，最好将comment列表页的链接放到POST视图里，我们这样做：<br /> <br /><pre name="code" class="ruby">&lt;!-- app/views/posts/show.html.erb -->
&lt;%= link_to 'Comments', post_comments_path(@post) %>
&lt;%= link_to 'Edit', edit_post_path(@post) %> |
&lt;%= link_to 'Back', posts_path %></pre><br /><br />我只是加了个链接，让我们瞧瞧他看起来怎么样:<br /><img src="http://www.akitaonrails.com/assets/2007/12/12/Picture_3_1.png" /><br /><img src="http://www.akitaonrails.com/assets/2007/12/12/Picture_4_1.png" /><br /><img src="http://www.akitaonrails.com/assets/2007/12/12/Picture_5_1.png" /><br /><br /><span style="font-size: x-large">完成视图</span><br /><br />好了，看起来还不错。但这并不像 一个blog的行为! POST的show 视图应该有些comment罗列在那里，并且新建评论的表格应该也在那里。所以让我们做些小改变。这里没有什么新的，只是传统的rails。让我们从视图开始：<br /><br /> <pre name="code" class="ruby">&lt;!-- app/views/posts/show.html.erb -->
&lt;p>
  &lt;b>Title:&lt;/b>
  &lt;%=h @post.title %>
&lt;/p>

&lt;p>
  &lt;b>Body:&lt;/b>
  &lt;%=h @post.body %>
&lt;/p>

&lt;!-- #1 -->
&lt;% unless @post.comments.empty? %>
  &lt;h3>Comments&lt;/h3>
  &lt;% @post.comments.each do |comment| %>
  &lt;p>&lt;%= h comment.body %>&lt;/p>
  &lt;% end %>
&lt;% end %>

&lt;!-- #2 -->
&lt;h3>New Comment&lt;/h3>
&lt;%= render :partial => @comment = Comment.new, 
   :locals => { :button_name => 'Create'}%>

&lt;%= link_to 'Comments', post_comments_path(@post) %>
&lt;%= link_to 'Edit', edit_post_path(@post) %> |
&lt;%= link_to 'Back', posts_path %></pre><br /><br />再提几点:<br />1.迭代中并没有什么新的东西，只是列出一些评论<br /><br />2.再一次，我们传进@comment给 partial语句<br /><br />最后一个小调整，任何时候我们创建一个新COMMENT（译者注：原文为POST,估计是作者笔误,多谢woody_420420兄提醒.），我们会想回到同样的POST中的show视图，所以我们改变CommentController的行为像这样:<br /><br /><pre name="code" class="ruby"># app/controllers/comments_controller.rb
# old redirect:
redirect_to([@post, @comment])
# new redirect:
redirect_to(@post)
</pre><br /><br /><span style="font-size: x-large">命名空间路由</span><br /><br />好了，现在我们有一个骨瘦如柴的迷你博客像是模仿经典david在2005念做的一段15分钟创建一个BLOG的视频。现在我们更进一步：Post不应该让所有的人都可以去编辑他，我们的网站需要一个管理部分。让我们为它创建一个控制器：<br /><br />./script/generate controller Admin::Posts<br /><br />rails2.0 现在支持命名空间。他会创建一个子目录叫 app/controllers/admin.<br />我们所要作的是：<br />1.创建一个新的路由<br />2.把所有在旧的 posts控制器中的 action复制到新的 Admin::posts中<br />3.复制所有旧的posts视图到app/views/admin* ，在旧的posts 控制器中只留下 ‘index’和‘show’ 这两个action, <br />这意味着也要删除new 和 edit.<br />4.修改 我们刚刚复制的actions和views，让他能够知道他是在admin控制器中<br /><br />首先，我们再次编辑 config/routes.rb:<br /><br /><pre name="code" class="ruby">map.namespace :admin do |admin|
  admin.resources :posts
end</pre><br /><br /><br />这意味着我们现在有了带着 'admin'前缀的 posts的具名路由。这会使旧的POST路由和新的<br />admin post路由不会想混。像这样：<br /><br />posts_path 	/posts<br />post_path(@post) 	/posts/:post_id<br />admin_posts_path 	/admin/posts<br />admin_post_path(@post) 	/admin/posts/:post_id<br /><br />现在让我们从旧的POST控制器中拷贝ACTION并修改路由地址去适应新的命名空间：<br /><br /><pre name="code" class="ruby"># app/controllers/admin/posts_controller.rb
...
def create
  # old:
  format.html { redirect_to(@post) }
  # new:
  format.html { redirect_to([:admin, @post]) }
end

def update
  # old:
  format.html { redirect_to(@post) }
  # new:
  format.html { redirect_to([:admin, @post]) }
end

def destroy
  # old:
  format.html { redirect_to(posts_url) }
  # new:
  format.html { redirect_to(admin_posts_url) }
end
...</pre><br /><br />不要忘记删除所有在app/controllers/posts_controller.rb中的方法，只要留下 ‘index’ 和‘show’两个方法。<br /><br />现在，让我们拷贝视图（假设你的 shell已经在项目的根文件夹下）：<br />cp app/views/posts/*.erb app/views/admin/posts<br />rm app/views/posts/new.html.erb<br />rm app/views/posts/edit.html.erb<br /><br /> 现在让我们编辑 app/views/admin/posts中的视图：<br /><br /> <pre name="code" class="ruby">&lt;!-- app/views/admin/posts/edit.html.erb -->
&lt;h1>Editing post&lt;/h1>

&lt;%= error_messages_for :post %>

&lt;% form_for([:admin, @post]) do |f| %>
 ...
&lt;% end %>

&lt;%= link_to 'Show', [:admin, @post] %> |
&lt;%= link_to 'Back', admin_posts_path %></pre><br /><br /><br /><pre name="code" class="ruby">
&lt;!-- app/views/admin/posts/new.html.erb -->
&lt;h1>New post&lt;/h1>

&lt;%= error_messages_for :post %>

&lt;% form_for([:admin, @post]) do |f| %>
  ...
&lt;% end %>

&lt;%= link_to 'Back', admin_posts_path %></pre><br /><pre name="code" class="ruby">

&lt;!-- app/views/admin/posts/show.html.erb -->
&lt;p>
  &lt;b>Title:&lt;/b>
  &lt;%=h @post.title %>
&lt;/p>

&lt;p>
  &lt;b>Body:&lt;/b>
  &lt;%=h @post.body %>
&lt;/p>

&lt;%= link_to 'Edit', edit_admin_post_path(@post) %> |
&lt;%= link_to 'Back', admin_posts_path %></pre><br /><br /><pre name="code" class="ruby">
&lt;!-- app/views/admin/posts/index.html.erb -->
...
&lt;% for post in @posts %>
  &lt;tr>
    &lt;td>&lt;%=h post.title %>&lt;/td>
    &lt;td>&lt;%=h post.body %>&lt;/td>
    &lt;td>&lt;%= link_to 'Show', [:admin, post] %>&lt;/td>
    &lt;td>&lt;%= link_to 'Edit', edit_admin_post_path(post) %>&lt;/td>
    &lt;td>&lt;%= link_to 'Destroy', [:admin, post], 
      :confirm => 'Are you sure?', :method => :delete %>&lt;/td>
  &lt;/tr>
&lt;% end %>
&lt;/table>

&lt;br />

&lt;%= link_to 'New post', new_admin_post_path %></pre><br /><br />基本上完成了：如果你测试 http://localhost:3000/admin/posts，他应该能正常的工作。但看起来却很丑陋，那是因为我们没有全局的布局模板。<br /><br />当我们生成第一个脚手架时，rails为post和 comment生成各自相关的布局模板。所以让我们删掉他们并且创建一个通用的：<br /><br />cp app/views/layouts/posts.html.erb \<br />app/views/layouts/application.html.erb<br />rm app/views/layouts/posts.html.erb<br />rm app/views/layouts/comments.html.erb<br /><br />然后让我们改一下标题：<br /><br /><pre name="code" class="java">&lt;!-- app/views/layouts/application.html.erb -->
...
&lt;title>My Great Blog&lt;/title>
...</pre><br /><br />他只剩下旧的在posts控制器里的'index' 和 ‘show’页面，他们仍然拥有我们有链接到我们删除过的方法的链接，所以我们可以删掉他们。<br /><br /><br /><pre name="code" class="ruby">&lt;!-- app/views/posts/index.html.erb -->
&lt;h1>My Great Blog&lt;/h1>

&lt;table>
  &lt;tr>
    &lt;th>Title&lt;/th>
    &lt;th>Body&lt;/th>
  &lt;/tr>

&lt;% for post in @posts %>
  &lt;tr>
    &lt;td>&lt;%=h post.title %>&lt;/td>
    &lt;td>&lt;%=h post.body %>&lt;/td>
    &lt;td>&lt;%= link_to 'Show', post %>&lt;/td>
  &lt;/tr>
&lt;% end %>
&lt;/table></pre><br /><br /><br /><pre name="code" class="ruby">&lt;!-- app/views/posts/show.html.erb -->
&lt;p>
  &lt;b>Title:&lt;/b>
  &lt;%=h @post.title %>
&lt;/p>

&lt;p>
  &lt;b>Body:&lt;/b>
  &lt;%=h @post.body %>
&lt;/p>

&lt;% unless @post.comments.empty? %>
  &lt;h3>Comments&lt;/h3>
  &lt;% @post.comments.each do |comment| %>
  &lt;p>&lt;%= h comment.body %>&lt;/p>
  &lt;% end %>
&lt;% end %>

&lt;h3>New Comment&lt;/h3>

&lt;%= render :partial => @comment = Comment.new, 
   :locals => { :button_name => 'Create'}%>

&lt;%= link_to 'Back', posts_path %></pre><br /><br />我们可以从浏览器中测试任何东西，进入 http://localhost:3000/admin/posts ，就能看到一切都工作的很好。但是，我们依旧少了样东西：一个系统管理部分不应该公开。现在你可以进去编辑任何东西了。我们需要认证。<br /><br /><span style="font-size: x-large">HTTP基本认证</span><br /><br />有许多实现验证和授权的方式。一个用的很广泛的插件是<a href="http://agilewebdevelopment.com/plugins/restful_authentication" target="_blank">restful_authentication.</a><br /><br />但是在这里我们不想做些太炫的东西。而RAILS2.0给了我们一个很好的方式去做验证。这个就是：我们用HTTP已经给我们的东西：HTTP基本认真。<br /><br />缺点是：当在生产环境下时你肯定会想用SSL。当然，你还是会这样做。HTML 表单验证并没有让SSL保护。<br /><br />所以，让我们编辑我们的Admin::Posts控制器，添加验证功能：<br /><br /><pre name="code" class="ruby"> # app/controllers/admin/posts.rb
class Admin::PostsController &lt; ApplicationController
  before_filter :authenticate
  ...
  def authenticate
    authenticate_or_request_with_http_basic do |name, pass|
      #User.authenticate(name, pass)
      name == 'akita' && pass == 'akita'      
    end
  end
end</pre><br /><br />你已经知道'before_filter'是做什么用的了：他在任何 控制器中的action执行前先执行 configured方法。如果你在ApplicationController类中设置他会在任何其他控制器执行前先执行。但在这里我们只想要保护 Admin::Posts.<br /><br />接着我们执行这个方法，但秘密武器是 ‘authenticate_or_request_with_http_basic’方法，该方法让我们设置一个块。他给我们用户在输入中的一个用户名和密码。通常我们需要有一个User模型去验证这些数据，但是对于我们这个超级简单的例子来说我就硬编码这个它，但是你知道你该如何做。<br /><br /> <img src="http://www.akitaonrails.com/assets/2007/12/12/Picture_6_1.png" />
          <br/>
          <span style="color:red;">
            <a href="http://genki.javaeye.com/blog/162536#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</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>Fri, 15 Feb 2008 17:41:28 +0800</pubDate>
        <link>http://genki.javaeye.com/blog/162536</link>
        <guid>http://genki.javaeye.com/blog/162536</guid>
      </item>
  </channel>
</rss>