<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>vivijie (Neil)</title>
    <link>https://ruby-china.org/vivijie</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>关于 Rails Validations 的几点疑问。</title>
      <description>&lt;p&gt;再次吐槽 Ruby China 对 Markdown 的支持：
排版的版本 &lt;a href="http://vivijie.github.io/blog/2013/12/08/about-rails-validations/" rel="nofollow" target="_blank"&gt;http://vivijie.github.io/blog/2013/12/08/about-rails-validations/&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;今天做 Kevin gotealeaf 课后作业，有一个关于 validations 的题目：&lt;/p&gt;

&lt;p&gt;** How exactly do Rails validations get triggered? Where are the errors saved? Why we need the ‘render’ to show the validation messages on the user interface? **&lt;/p&gt;

&lt;p&gt;下面是我自己的理解，欢迎高手拍砖。&lt;/p&gt;

&lt;p&gt;假设我们有一个 Category 类，变量是 name，在 model 文件里设置了 validates。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p247&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;01&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="no"&gt;Category&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;created_at: &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;updated_at: &lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;model/category.rb&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt;      &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;presence: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;** 1. How exactly do Rails validations get triggered? **&lt;/p&gt;

&lt;p&gt;查了 Rails 的文档，ActiveRecord::Base 中包括有：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ActiveRecord::AutosaveAssociation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ActiveRecord::Validations. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ActiveRecord::AutosaveAssociation 中有如下描述： &lt;/p&gt;

&lt;p&gt;If validations for any of the associations fail, their error messages will be applied to the parent.&lt;/p&gt;

&lt;p&gt;所以，如果没有对 name 进行赋值，save 的时候 ActiveRecord::AutosaveAssociation 发现 validations fail 了，产生了 error messages。&lt;/p&gt;

&lt;p&gt;** 2. Where are the errors saved? **&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p247&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;023&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;#&amp;lt;ActiveModel::Errors:0x007fec3dbda728 @base=#&amp;lt;Category id: nil, name: "zoe", created_at: nil, updated_at: nil&amp;gt;, @messages={}&amp;gt; &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现 errors 是保存在&lt;a href="/messages" class="user-mention" title="@messages"&gt;&lt;i&gt;@&lt;/i&gt;messages&lt;/a&gt;里面的。&lt;/p&gt;

&lt;p&gt;下面看一下 messages 的内容是怎么产生的。 &lt;/p&gt;

&lt;p&gt;参照文档，自定义了 model 中的 validates。要求 name 首字母以 z 开头的时候，报错。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt; 
  &lt;span class="n"&gt;validates_each&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kp"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt; &lt;span class="kp"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'starts with z.'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'z'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 console 中查看：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p247&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;013&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'zoe'&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"zoe"&lt;/span&gt;
&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p247&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;014&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valid?&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p247&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;015&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"starts with z."&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p247&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;016&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;errors messages 修改成功。推断&lt;code&gt;validates      :name, presence: true&lt;/code&gt; 会生成默认的 messages：name:["can't be blank”]。我们可以自己写 validations 方法。&lt;/p&gt;

&lt;p&gt;** 3.  Why we need the ‘render’ to show the validation messages on the user interface? **&lt;/p&gt;

&lt;p&gt;查了下 rails controller 的生命周期：&lt;/p&gt;

&lt;p&gt;When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action.&lt;/p&gt;

&lt;p&gt;在下一个页面请求的时候，controller 的生命周期结束。&lt;a href="/messages" class="user-mention" title="@messages"&gt;&lt;i&gt;@&lt;/i&gt;messages&lt;/a&gt;是&lt;a href="/controller" class="user-mention" title="@controller"&gt;&lt;i&gt;@&lt;/i&gt;controller&lt;/a&gt;的实例变量，会随着&lt;a href="/controller" class="user-mention" title="@controller"&gt;&lt;i&gt;@&lt;/i&gt;controller&lt;/a&gt;的销毁而销毁。所以要用 render 来 hold 住页面，让页面不刷新。&lt;/p&gt;

&lt;p&gt;** 不理解的地方： **&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If validations for any of the associations fail, their error messages will be applied to the parent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;这里的 parent 指的是 errors 还是&lt;a href="/messages" class="user-mention" title="@messages"&gt;&lt;i&gt;@&lt;/i&gt;messages&lt;/a&gt;呢？&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;下面这块代码怎么称呼&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;ActiveModel&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mh"&gt;0x007fec3dbda728&lt;/span&gt; &lt;span class="vi"&gt;@base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Category id: nil, name: "zoe", created_at: nil, updated_at: nil&amp;gt;, @messages={}&amp;gt; messages={}&amp;gt; &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>vivijie</author>
      <pubDate>Sun, 08 Dec 2013 17:43:04 +0800</pubDate>
      <link>https://ruby-china.org/topics/16067</link>
      <guid>https://ruby-china.org/topics/16067</guid>
    </item>
  </channel>
</rss>
