<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Posts on それはそれとして</title>
		<link>https://sore8sore104te.com/posts/</link>
		<description>Recent content in Posts on それはそれとして</description>
		<generator>Hugo -- gohugo.io</generator>
		<language>ja-JP</language>
		<copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright>
		<lastBuildDate>Mon, 20 Mar 2023 22:51:06 +0900</lastBuildDate>
		<atom:link href="https://sore8sore104te.com/posts/index.xml" rel="self" type="application/rss+xml" />
		
		<item>
			<title>Hugoを最新化した</title>
			<link>https://sore8sore104te.com/upgrade-hugo/</link>
			<pubDate>Mon, 20 Mar 2023 22:51:06 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/upgrade-hugo/</guid>
			<description>放置していたブログ環境を最新化した。Hugo のバージョンは 0.111.3 になった。 ブログ開設当初から見るとかなりバージョンアップを重ねている。すごい。 か</description>
			<content type="html"><![CDATA[<p>放置していたブログ環境を最新化した。Hugo のバージョンは <code>0.111.3</code> になった。</p>
<p>ブログ開設当初から見るとかなりバージョンアップを重ねている。すごい。
かたやブログは放置している。</p>
<p>まあそれはそれとして、Netlify で正常にビルド可能かの確認も兼ねて投稿してみる。</p>
]]></content>
		</item>
		
		<item>
			<title>名称とメインファイル名が異なる gem を gemfile に定義する方法</title>
			<link>https://sore8sore104te.com/gem-tips/</link>
			<pubDate>Fri, 26 Nov 2021 22:22:53 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/gem-tips/</guid>
			<description>やること bundler のドキュメントにも書いてあるが、以下のように require を用いることで .gemspec に定義されている名称とメインファイル名が異なる gem にも対応できる。 gem &amp;#39;hoge-gem&amp;#39;,</description>
			<content type="html"><![CDATA[<p><img src="./ruby-gems.jpeg" alt="image"></p>
<h1 id="やること">やること</h1>
<p><a href="https://bundler.io/gemfile.html">bundler のドキュメント</a>にも書いてあるが、以下のように <code>require</code> を用いることで .gemspec に定義されている名称とメインファイル名が異なる gem にも対応できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;hoge-gem&#39;</span><span class="p">,</span> <span class="ss">:require</span> <span class="o">=&gt;</span> <span class="s1">&#39;hoge&#39;</span>
</span></span></code></pre></div><p>使い所としては、fork した OSS の gem を改修して別名で公開したいが、ファイル名までは変えたくないとき等が考えられる。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第14章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter14/</link>
			<pubDate>Wed, 20 May 2020 20:42:20 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter14/</guid>
			<description>第14章 ユーザーをフォローする 他のユーザーをフォロー/フォロー解除できるソーシャルな仕組みの追加と、フォローしているユーザーと投稿をステータ</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第14章-ユーザーをフォローする">第14章 ユーザーをフォローする</h1>
<ul>
<li>他のユーザーをフォロー/フォロー解除できるソーシャルな仕組みの追加と、フォローしているユーザーと投稿をステータスフィードに表示する機能を追加する</li>
</ul>
<h2 id="141-relationship-モデル">14.1 Relationship モデル</h2>
<ul>
<li><code>has_many</code> の関連付けを用いて「1人のユーザーが複数のユーザーを <code>has_many</code> としてフォローし、1人のユーザーにフォロワーがいることを <code>has_many</code> で表す」という方法で実装はできそうだが、問題が発生する
<ul>
<li><code>has_many through</code> で解決</li>
</ul>
</li>
</ul>
<h3 id="1411-データモデルの問題および解決策">14.1.1 データモデルの問題（および解決策）</h3>
<ul>
<li><code>following</code> と <code>followers</code> の関係性をリレーションシップというモデルで表現する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">rails generate model Relationship follower_id:integer followed_id:integer
</span></span></span></code></pre></div><ul>
<li>今後 <code>follower_id</code> と <code>followed_id</code> で頻繁に検索することになるので、それぞれのカラムにインデックスを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">CreateRelationships</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="o">[</span><span class="mi">5</span><span class="o">.</span><span class="mi">0</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">change</span>
</span></span><span class="line"><span class="cl">    <span class="n">create_table</span> <span class="ss">:relationships</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">t</span><span class="o">.</span><span class="n">integer</span> <span class="ss">:follower_id</span>
</span></span><span class="line"><span class="cl">      <span class="n">t</span><span class="o">.</span><span class="n">integer</span> <span class="ss">:followed_id</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">t</span><span class="o">.</span><span class="n">timestamps</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_index</span> <span class="ss">:relationships</span><span class="p">,</span> <span class="ss">:follower_id</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_index</span> <span class="ss">:relationships</span><span class="p">,</span> <span class="ss">:followed_id</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_index</span> <span class="ss">:relationships</span><span class="p">,</span> <span class="o">[</span><span class="ss">:follower_id</span><span class="p">,</span> <span class="ss">:followed_id</span><span class="o">]</span><span class="p">,</span> <span class="ss">unique</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>follower_id</code> と <code>followed_id</code> の組み合わせが必ずユニークであることを保証する仕組みである複合キーインデックスを使用している
<ul>
<li>ユーザーが同じユーザーを2回以上フォローすることを防ぐ</li>
</ul>
</li>
<li><code>relationship</code> テーブルを作成するためにいつもの通りマイグレーション</li>
</ul>
<h3 id="1412-userrelationship-の関連付け">14.1.2 User/Relationship の関連付け</h3>
<ul>
<li>1人のユーザーには <code>has_many</code> のリレーションシップがあり、このリレーションシップは2人のユーアー間の関係なので、フォローしているユーザーとフォロワーの両方に属する（ <code>belongs_to</code> ）</li>
<li>能動的関係に対して1対多（ <code>has_many</code> ）の関連付けを実装する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:microposts</span><span class="p">,</span> <span class="ss">dependent</span><span class="p">:</span> <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:active_relationships</span><span class="p">,</span> <span class="ss">class_name</span><span class="p">:</span>  <span class="s2">&#34;Relationship&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                  <span class="ss">foreign_key</span><span class="p">:</span> <span class="s2">&#34;follower_id&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                  <span class="ss">dependent</span><span class="p">:</span>   <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>リレーションシップ/フォロワーに対して <code>belongs_to</code> の関連付けを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Relationship</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:follower</span><span class="p">,</span> <span class="ss">class_name</span><span class="p">:</span> <span class="s2">&#34;User&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:followed</span><span class="p">,</span> <span class="ss">class_name</span><span class="p">:</span> <span class="s2">&#34;User&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1413-relationship-のバリデーション">14.1.3 Relationship のバリデーション</h3>
<ul>
<li>Relationship モデルのバリデーションをテストする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">RelationshipTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@relationship</span> <span class="o">=</span> <span class="no">Relationship</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">follower_id</span><span class="p">:</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span><span class="o">.</span><span class="n">id</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                     <span class="ss">followed_id</span><span class="p">:</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should be valid&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="vi">@relationship</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should require a follower_id&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@relationship</span><span class="o">.</span><span class="n">follower_id</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@relationship</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should require a followed_id&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@relationship</span><span class="o">.</span><span class="n">followed_id</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@relationship</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>Relationship モデルに対してバリデーションを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Relationship</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:follower</span><span class="p">,</span> <span class="ss">class_name</span><span class="p">:</span> <span class="s2">&#34;User&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:followed</span><span class="p">,</span> <span class="ss">class_name</span><span class="p">:</span> <span class="s2">&#34;User&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:follower_id</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:followed_id</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>User 用の fixture ファイルと同様に、生成された Relationship 用の fixutre ではマイグレーションで成約させた一意性を満たすことができないため、現時点では生成された Relationship 用の fixutre は空にしておく</li>
</ul>
<h3 id="1414-フォローしているユーザー">14.1.4 フォローしているユーザー</h3>
<ul>
<li><code>has_many through</code> を使って User モデルに <code>following</code> の関連付けを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:microposts</span><span class="p">,</span> <span class="ss">dependent</span><span class="p">:</span> <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:active_relationships</span><span class="p">,</span> <span class="ss">class_name</span><span class="p">:</span>  <span class="s2">&#34;Relationship&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                  <span class="ss">foreign_key</span><span class="p">:</span> <span class="s2">&#34;follower_id&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                  <span class="ss">dependent</span><span class="p">:</span>   <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:following</span><span class="p">,</span> <span class="ss">through</span><span class="p">:</span> <span class="ss">:active_relationships</span><span class="p">,</span> <span class="ss">source</span><span class="p">:</span> <span class="ss">:followed</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>これによりフォローしているユーザーを <code>following</code> という配列のように扱えるようになる</li>
<li>TDD で <code>follow</code> や <code>unfollow</code> というメソッドを作る
<ul>
<li><code>following?</code> メソッドであるユーザーを未フォローであることを確認</li>
<li><code>follow</code> メソッドでそのユーザーをフォロー</li>
<li><code>following?</code> メソッドでフォロー中になったことを確認</li>
<li><code>unfollow</code> メソッドでフォロー解除を確認
<ul>
<li>という手順で following 関連のメソッドをテストする</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should follow and unfollow a user&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">michael</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">archer</span>  <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">michael</span><span class="o">.</span><span class="n">following?</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">michael</span><span class="o">.</span><span class="n">follow</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">michael</span><span class="o">.</span><span class="n">following?</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">michael</span><span class="o">.</span><span class="n">unfollow</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">michael</span><span class="o">.</span><span class="n">following?</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記テストを参考にして following 関連のメソッドを実装する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">feed</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーをフォローする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">follow</span><span class="p">(</span><span class="n">other_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">following</span> <span class="o">&lt;&lt;</span> <span class="n">other_user</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーをフォロー解除する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">unfollow</span><span class="p">(</span><span class="n">other_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">active_relationships</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">followed_id</span><span class="p">:</span> <span class="n">other_user</span><span class="o">.</span><span class="n">id</span><span class="p">)</span><span class="o">.</span><span class="n">destroy</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 現在のユーザーがフォローしてたらtrueを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">following?</span><span class="p">(</span><span class="n">other_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">following</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">other_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>これでテストが通る</li>
</ul>
<h3 id="1415-フォロワー">14.1.5 フォロワー</h3>
<ul>
<li><code>user.following</code> メソッドと対になる <code>user.followers</code> メソッドを追加する
<ul>
<li><code>active_relationship</code> テーブルを再利用し、受動的関係を使って実装する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:microposts</span><span class="p">,</span> <span class="ss">dependent</span><span class="p">:</span> <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:active_relationships</span><span class="p">,</span>  <span class="ss">class_name</span><span class="p">:</span>  <span class="s2">&#34;Relationship&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">foreign_key</span><span class="p">:</span> <span class="s2">&#34;follower_id&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">dependent</span><span class="p">:</span>   <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:passive_relationships</span><span class="p">,</span> <span class="ss">class_name</span><span class="p">:</span>  <span class="s2">&#34;Relationship&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">foreign_key</span><span class="p">:</span> <span class="s2">&#34;followed_id&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">dependent</span><span class="p">:</span>   <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:following</span><span class="p">,</span> <span class="ss">through</span><span class="p">:</span> <span class="ss">:active_relationships</span><span class="p">,</span>  <span class="ss">source</span><span class="p">:</span> <span class="ss">:followed</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:followers</span><span class="p">,</span> <span class="ss">through</span><span class="p">:</span> <span class="ss">:passive_relationships</span><span class="p">,</span> <span class="ss">source</span><span class="p">:</span> <span class="ss">:follower</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>followers.include?</code> メソッドを使って <code>followers</code> に対するテストを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should follow and unfollow a user&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">michael</span>  <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">archer</span>   <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">michael</span><span class="o">.</span><span class="n">following?</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">michael</span><span class="o">.</span><span class="n">follow</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">michael</span><span class="o">.</span><span class="n">following?</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">archer</span><span class="o">.</span><span class="n">followers</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">michael</span><span class="o">.</span><span class="n">unfollow</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">michael</span><span class="o">.</span><span class="n">following?</span><span class="p">(</span><span class="n">archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="142-follow-の-web-インターフェイス">14.2 [Follow] の Web インターフェイス</h2>
<ul>
<li>フォロー/フォロー解除の基本的なインターフェイスを実装し、フォローしているユーザーと、フォロワーにそれぞれ表示用のページを作成する</li>
</ul>
<h3 id="1421-フォローのサンプルデータ">14.2.1 フォローのサンプルデータ</h3>
<ul>
<li>サンプルデータを自動作成する <code>rails db:seed</code> を使って DB にサンプルデータを登録できると便利なので、サンプルデータに following/follower の関係性を追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># ユーザー</span>
</span></span><span class="line"><span class="cl"><span class="no">User</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span>  <span class="s2">&#34;Example User&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;example@railstutorial.org&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;foobar&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;foobar&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="ss">admin</span><span class="p">:</span>     <span class="kp">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="ss">activated</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">             <span class="ss">activated_at</span><span class="p">:</span> <span class="no">Time</span><span class="o">.</span><span class="n">zone</span><span class="o">.</span><span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="mi">99</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="nb">name</span>  <span class="o">=</span> <span class="no">Faker</span><span class="o">::</span><span class="no">Name</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl">  <span class="n">email</span> <span class="o">=</span> <span class="s2">&#34;example-</span><span class="si">#{</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s2">@railstutorial.org&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">password</span> <span class="o">=</span> <span class="s2">&#34;password&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="no">User</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span>  <span class="nb">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="ss">email</span><span class="p">:</span> <span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="ss">password</span><span class="p">:</span>              <span class="n">password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="ss">password_confirmation</span><span class="p">:</span> <span class="n">password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="ss">activated</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="ss">activated_at</span><span class="p">:</span> <span class="no">Time</span><span class="o">.</span><span class="n">zone</span><span class="o">.</span><span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># マイクロポスト</span>
</span></span><span class="line"><span class="cl"><span class="n">users</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="ss">:created_at</span><span class="p">)</span><span class="o">.</span><span class="n">take</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">50</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">content</span> <span class="o">=</span> <span class="no">Faker</span><span class="o">::</span><span class="no">Lorem</span><span class="o">.</span><span class="n">sentence</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">users</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span> <span class="n">user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="ss">content</span><span class="p">:</span> <span class="n">content</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># リレーションシップ</span>
</span></span><span class="line"><span class="cl"><span class="n">users</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">all</span>
</span></span><span class="line"><span class="cl"><span class="n">user</span>  <span class="o">=</span> <span class="n">users</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl"><span class="n">following</span> <span class="o">=</span> <span class="n">users</span><span class="o">[</span><span class="mi">2</span><span class="o">..</span><span class="mi">50</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="n">followers</span> <span class="o">=</span> <span class="n">users</span><span class="o">[</span><span class="mi">3</span><span class="o">..</span><span class="mi">40</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="n">following</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">followed</span><span class="o">|</span> <span class="n">user</span><span class="o">.</span><span class="n">follow</span><span class="p">(</span><span class="n">followed</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">followers</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">follower</span><span class="o">|</span> <span class="n">follower</span><span class="o">.</span><span class="n">follow</span><span class="p">(</span><span class="n">user</span><span class="p">)</span> <span class="p">}</span>
</span></span></code></pre></div><ul>
<li>DB 上のサンプルデータを作り直して完了</li>
</ul>
<h3 id="1422-統計と-follow-フォーム">14.2.2 統計と [Follow] フォーム</h3>
<ul>
<li>サンプルユーザーにフォローしているユーザーとフォロワーができたので、プロフィールページと Home ページを更新してこれを反映する
<ul>
<li>最初にプロフィールページと Home ページにフォローしているユーザーとフォロワーの統計情報表示用のパーシャルを作成する</li>
<li>次にフォロー用とフォロー解除用のフォームを作成する</li>
<li>最後にフォローしているユーザーの一覧とフォロワーの一覧を表示するページを作成する</li>
</ul>
</li>
<li>統計情報の表示はリンクなっており、専用の表示ページに移動できる
<ul>
<li>Users コントローラに <code>following</code> アクションと <code>followers</code> アクションを追加してルーティングを実装する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span>   <span class="s1">&#39;static_pages#home&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/help&#39;</span><span class="p">,</span>    <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#help&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/about&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#about&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/contact&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#contact&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/signup&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;users#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">post</span>   <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#create&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">delete</span> <span class="s1">&#39;/logout&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#destroy&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:users</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">member</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">get</span> <span class="ss">:following</span><span class="p">,</span> <span class="ss">:followers</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:account_activations</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:password_resets</span><span class="p">,</span>     <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:microposts</span><span class="p">,</span>          <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ルーティングを定義したので、フォロワーの統計情報を表示するパーシャルを実装する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% @user ||= current_user %&gt;
&lt;div class=&#34;stats&#34;&gt;
  &lt;a href=&#34;&lt;%= following_user_path(@user) %&gt;&#34;&gt;
    &lt;strong id=&#34;following&#34; class=&#34;stat&#34;&gt;
      &lt;%= @user.following.count %&gt;
    &lt;/strong&gt;
    following
  &lt;/a&gt;
  &lt;a href=&#34;&lt;%= followers_user_path(@user) %&gt;&#34;&gt;
    &lt;strong id=&#34;followers&#34; class=&#34;stat&#34;&gt;
      &lt;%= @user.followers.count %&gt;
    &lt;/strong&gt;
    followers
  &lt;/a&gt;
&lt;/div&gt;
</code></pre><ul>
<li>統計情報パーシャルができあがったので、 Home ページにフォロワーの統計情報を表示する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% if logged_in? %&gt;
  &lt;div class=&#34;row&#34;&gt;
    &lt;aside class=&#34;col-md-4&#34;&gt;
      &lt;section class=&#34;user_info&#34;&gt;
        &lt;%= render &#39;shared/user_info&#39; %&gt;
      &lt;/section&gt;
      &lt;section class=&#34;stats&#34;&gt;
        &lt;%= render &#39;shared/stats&#39; %&gt;
      &lt;/section&gt;
      &lt;section class=&#34;micropost_form&#34;&gt;
        &lt;%= render &#39;shared/micropost_form&#39; %&gt;
      &lt;/section&gt;
    &lt;/aside&gt;
    &lt;div class=&#34;col-md-8&#34;&gt;
      &lt;h3&gt;Micropost Feed&lt;/h3&gt;
      &lt;%= render &#39;shared/feed&#39; %&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;% else %&gt;
  .
  .
  .
&lt;% end %&gt;
</code></pre><ul>
<li>SCSS を追加してスタイルを整えて完成</li>
</ul>
<h3 id="1422-統計と-follow-フォーム-1">14.2.2 統計と [Follow] フォーム</h3>
<ul>
<li>サンプルユーザーにフォローしているユーザーとフォロワーができたので、プロフィールページと Home ページを更新してこれを反映する
<ul>
<li>最初にプロフィールページと Home ページにフォローしているユーザーとフォロワーの統計情報表示用のパーシャルを作成する</li>
<li>次にフォロー用とフォロー解除用のフォームを作成する</li>
<li>最後にフォローしているユーザーの一覧とフォロワーの一覧を表示するページを作成する</li>
</ul>
</li>
<li>統計情報の表示はリンクなっており、専用の表示ページに移動できる
<ul>
<li>Users コントローラに <code>following</code> アクションと <code>followers</code> アクションを追加してルーティングを実装する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span>   <span class="s1">&#39;static_pages#home&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/help&#39;</span><span class="p">,</span>    <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#help&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/about&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#about&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/contact&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#contact&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/signup&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;users#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">post</span>   <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#create&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">delete</span> <span class="s1">&#39;/logout&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#destroy&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:users</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">member</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">get</span> <span class="ss">:following</span><span class="p">,</span> <span class="ss">:followers</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:account_activations</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:password_resets</span><span class="p">,</span>     <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:microposts</span><span class="p">,</span>          <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ルーティングを定義したので、フォロワーの統計情報を表示するパーシャルを実装する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% @user ||= current_user %&gt;
&lt;div class=&#34;stats&#34;&gt;
  &lt;a href=&#34;&lt;%= following_user_path(@user) %&gt;&#34;&gt;
    &lt;strong id=&#34;following&#34; class=&#34;stat&#34;&gt;
      &lt;%= @user.following.count %&gt;
    &lt;/strong&gt;
    following
  &lt;/a&gt;
  &lt;a href=&#34;&lt;%= followers_user_path(@user) %&gt;&#34;&gt;
    &lt;strong id=&#34;followers&#34; class=&#34;stat&#34;&gt;
      &lt;%= @user.followers.count %&gt;
    &lt;/strong&gt;
    followers
  &lt;/a&gt;
&lt;/div&gt;
</code></pre><ul>
<li>統計情報パーシャルができあがったので、 Home ページにフォロワーの統計情報を表示する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% if logged_in? %&gt;
  &lt;div class=&#34;row&#34;&gt;
    &lt;aside class=&#34;col-md-4&#34;&gt;
      &lt;section class=&#34;user_info&#34;&gt;
        &lt;%= render &#39;shared/user_info&#39; %&gt;
      &lt;/section&gt;
      &lt;section class=&#34;stats&#34;&gt;
        &lt;%= render &#39;shared/stats&#39; %&gt;
      &lt;/section&gt;
      &lt;section class=&#34;micropost_form&#34;&gt;
        &lt;%= render &#39;shared/micropost_form&#39; %&gt;
      &lt;/section&gt;
    &lt;/aside&gt;
    &lt;div class=&#34;col-md-8&#34;&gt;
      &lt;h3&gt;Micropost Feed&lt;/h3&gt;
      &lt;%= render &#39;shared/feed&#39; %&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;% else %&gt;
  .
  .
  .
&lt;% end %&gt;
</code></pre><ul>
<li>SCSS を追加してスタイルを整えて完成</li>
<li>フォロー/フォロー解除フォームのパーシャルも作成しておく</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% unless current_user?(@user) %&gt;
  &lt;div id=&#34;follow_form&#34;&gt;
  &lt;% if current_user.following?(@user) %&gt;
    &lt;%= render &#39;unfollow&#39; %&gt;
  &lt;% else %&gt;
    &lt;%= render &#39;follow&#39; %&gt;
  &lt;% end %&gt;
  &lt;/div&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>上記コードは <code>follow</code> と <code>unfollow</code> のパーシャルに作業を振っているだけ</li>
<li>パーシャルでは Relationship リソース用の新しいルーティングが必要</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span>                <span class="s1">&#39;static_pages#home&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;help&#39;</span>    <span class="o">=&gt;</span> <span class="s1">&#39;static_pages#help&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;about&#39;</span>   <span class="o">=&gt;</span> <span class="s1">&#39;static_pages#about&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;contact&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;static_pages#contact&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;signup&#39;</span>  <span class="o">=&gt;</span> <span class="s1">&#39;users#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;login&#39;</span>   <span class="o">=&gt;</span> <span class="s1">&#39;sessions#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">post</span>   <span class="s1">&#39;login&#39;</span>   <span class="o">=&gt;</span> <span class="s1">&#39;sessions#create&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">delete</span> <span class="s1">&#39;logout&#39;</span>  <span class="o">=&gt;</span> <span class="s1">&#39;sessions#destroy&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:users</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">member</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">get</span> <span class="ss">:following</span><span class="p">,</span> <span class="ss">:followers</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:account_activations</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:password_resets</span><span class="p">,</span>     <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:microposts</span><span class="p">,</span>          <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:relationships</span><span class="p">,</span>       <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>フォロー用のパーシャル（ユーザーをフォローするフォーム）</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= form_for(current_user.active_relationships.build) do |f| %&gt;
  &lt;div&gt;&lt;%= hidden_field_tag :followed_id, @user.id %&gt;&lt;/div&gt;
  &lt;%= f.submit &#34;Follow&#34;, class: &#34;btn btn-primary&#34; %&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>フォロー解除用のパーシャル（ユーザーをフォロー解除するフォーム）</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
             html: { method: :delete }) do |f| %&gt;
  &lt;%= f.submit &#34;Unfollow&#34;, class: &#34;btn&#34; %&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>最後にプロフィールページにフォロー用フォームとフォロワーの統計情報を追加する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, @user.name) %&gt;
&lt;div class=&#34;row&#34;&gt;
  &lt;aside class=&#34;col-md-4&#34;&gt;
    &lt;section class=&#34;user_info&#34;&gt;
      &lt;h1&gt;
        &lt;%= gravatar_for @user %&gt;
        &lt;%= @user.name %&gt;
      &lt;/h1&gt;
    &lt;/section&gt;
    &lt;section class=&#34;stats&#34;&gt;
      &lt;%= render &#39;shared/stats&#39; %&gt;
    &lt;/section&gt;
  &lt;/aside&gt;
  &lt;div class=&#34;col-md-8&#34;&gt;
    &lt;%= render &#39;follow_form&#39; if logged_in? %&gt;
    &lt;% if @user.microposts.any? %&gt;
      &lt;h3&gt;Microposts (&lt;%= @user.microposts.count %&gt;)&lt;/h3&gt;
      &lt;ol class=&#34;microposts&#34;&gt;
        &lt;%= render @microposts %&gt;
      &lt;/ol&gt;
      &lt;%= will_paginate @microposts %&gt;
    &lt;% end %&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre><h3 id="1423-following-と-followers-ページ">14.2.3 [Following] と [Followers] ページ</h3>
<ul>
<li>フォローしているユーザーを表示するページとフォロワーを表示するページは、いずれもプロフィールページとユーザー一覧ページを合わせたような作りになるという点で似ている</li>
<li>まずはフォローしているユーザーのリンクとフォロワーのリンクを動くようにする
<ul>
<li>どちらのページでもログインを要求する</li>
<li>まずはテストから</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@other_user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect following when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">following_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect followers when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">followers_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>Users コントローラに2つの新しいアクションを追加する必要がある
<ul>
<li>ルーティングに基づいた <code>following</code> 及び <code>followers</code> アクション</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:index</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="p">,</span> <span class="ss">:destroy</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                        <span class="ss">:following</span><span class="p">,</span> <span class="ss">:followers</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">following</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@title</span> <span class="o">=</span> <span class="s2">&#34;Following&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span>  <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@users</span> <span class="o">=</span> <span class="vi">@user</span><span class="o">.</span><span class="n">following</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">page</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="s1">&#39;show_follow&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">followers</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@title</span> <span class="o">=</span> <span class="s2">&#34;Followers&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span>  <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@users</span> <span class="o">=</span> <span class="vi">@user</span><span class="o">.</span><span class="n">followers</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">page</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="s1">&#39;show_follow&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>フォローしているユーザーとフォロワーの両方を表示する <code>show_follow</code> ビューを作成する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, @title) %&gt;
&lt;div class=&#34;row&#34;&gt;
  &lt;aside class=&#34;col-md-4&#34;&gt;
    &lt;section class=&#34;user_info&#34;&gt;
      &lt;%= gravatar_for @user %&gt;
      &lt;h1&gt;&lt;%= @user.name %&gt;&lt;/h1&gt;
      &lt;span&gt;&lt;%= link_to &#34;view my profile&#34;, @user %&gt;&lt;/span&gt;
      &lt;span&gt;&lt;b&gt;Microposts:&lt;/b&gt; &lt;%= @user.microposts.count %&gt;&lt;/span&gt;
    &lt;/section&gt;
    &lt;section class=&#34;stats&#34;&gt;
      &lt;%= render &#39;shared/stats&#39; %&gt;
      &lt;% if @users.any? %&gt;
        &lt;div class=&#34;user_avatars&#34;&gt;
          &lt;% @users.each do |user| %&gt;
            &lt;%= link_to gravatar_for(user, size: 30), user %&gt;
          &lt;% end %&gt;
        &lt;/div&gt;
      &lt;% end %&gt;
    &lt;/section&gt;
  &lt;/aside&gt;
  &lt;div class=&#34;col-md-8&#34;&gt;
    &lt;h3&gt;&lt;%= @title %&gt;&lt;/h3&gt;
    &lt;% if @users.any? %&gt;
      &lt;ul class=&#34;users follow&#34;&gt;
        &lt;%= render @users %&gt;
      &lt;/ul&gt;
      &lt;%= will_paginate %&gt;
    &lt;% end %&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre><ul>
<li><code>show_follow</code> の描画結果を確認するための統合テストを書いていく</li>
<li>Relationship 用の fixture にテストデータを追加し、following/follower ページをテストする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FollowingTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;following page&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">following_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@user</span><span class="o">.</span><span class="n">following</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="vi">@user</span><span class="o">.</span><span class="n">following</span><span class="o">.</span><span class="n">count</span><span class="o">.</span><span class="n">to_s</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">body</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">following</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert_select</span> <span class="s2">&#34;a[href=?]&#34;</span><span class="p">,</span> <span class="n">user_path</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;followers page&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">followers_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@user</span><span class="o">.</span><span class="n">followers</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="vi">@user</span><span class="o">.</span><span class="n">followers</span><span class="o">.</span><span class="n">count</span><span class="o">.</span><span class="n">to_s</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">body</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">followers</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert_select</span> <span class="s2">&#34;a[href=?]&#34;</span><span class="p">,</span> <span class="n">user_path</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1424-follow-ボタン-基本編">14.2.4 [Follow] ボタン (基本編)</h3>
<ul>
<li>[Follow]/[Unfollow] ボタンを動作させるため、まずは Relationship コントローラを作成する</li>
<li>まずはリレーションシップの基本的なアクセス制御に対するテストを書く</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">RelationshipsControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;create should require logged-in user&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;Relationship.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">relationships_path</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;destroy should require logged-in user&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;Relationship.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">relationship_path</span><span class="p">(</span><span class="n">relationships</span><span class="p">(</span><span class="ss">:one</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>次に <code>logged_in_user</code> フィルターを Relationships コントローラのアクションに対して追加し、リレーションシップのアクセス制御を行う</li>
<li>[Follow]/[Unfollow] ボタンを動作させるには、フォームから送信されたパラメータを使って <code>followed_id</code> に対応するユーザーを見つけてくる必要がある
<ul>
<li>その後見つけてきたユーザーに対して <code>follow</code> / <code>unfollow</code> メソッドを使う</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">RelationshipsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:followed_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">current_user</span><span class="o">.</span><span class="n">follow</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">Relationship</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">followed</span>
</span></span><span class="line"><span class="cl">    <span class="n">current_user</span><span class="o">.</span><span class="n">unfollow</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1425-follow-ボタン-ajax編">14.2.5 [Follow] ボタン (Ajax編)</h3>
<ul>
<li>ユーザーをフォローした後、ページから離れて元のページに戻る仕様が微妙
<ul>
<li>Ajax で解決できる
<ul>
<li>Web ページからサーバーに非同期でページを移動することなくリクエストを送信可能</li>
</ul>
</li>
</ul>
</li>
<li>フォローフォームを Ajax 使用に変更する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= form_for(current_user.active_relationships.build, remote: true) do |f| %&gt;
  &lt;div&gt;&lt;%= hidden_field_tag :followed_id, @user.id %&gt;&lt;/div&gt;
  &lt;%= f.submit &#34;Follow&#34;, class: &#34;btn btn-primary&#34; %&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>フォロー解除フォームを Ajax 使用に変更する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
             html: { method: :delete },
             remote: true) do |f| %&gt;
  &lt;%= f.submit &#34;Unfollow&#34;, class: &#34;btn&#34; %&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>フォームの更新が終わったので、これに対応する Relationship コントローラを Ajax リクエストに対応できるようにする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">RelationshipsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:followed_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">current_user</span><span class="o">.</span><span class="n">follow</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="nb">format</span><span class="o">.</span><span class="n">html</span> <span class="p">{</span> <span class="n">redirect_to</span> <span class="vi">@user</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="nb">format</span><span class="o">.</span><span class="n">js</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">Relationship</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">followed</span>
</span></span><span class="line"><span class="cl">    <span class="n">current_user</span><span class="o">.</span><span class="n">unfollow</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="nb">format</span><span class="o">.</span><span class="n">html</span> <span class="p">{</span> <span class="n">redirect_to</span> <span class="vi">@user</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="nb">format</span><span class="o">.</span><span class="n">js</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ブラウザ側で JavaScript が無効になっていた場合も動作するようにする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="no">File</span><span class="o">.</span><span class="n">expand_path</span><span class="p">(</span><span class="s1">&#39;../boot&#39;</span><span class="p">,</span> <span class="bp">__FILE__</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SampleApp</span>
</span></span><span class="line"><span class="cl">  <span class="k">class</span> <span class="nc">Application</span> <span class="o">&lt;</span> <span class="no">Rails</span><span class="o">::</span><span class="no">Application</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 認証トークンをremoteフォームに埋め込む</span>
</span></span><span class="line"><span class="cl">    <span class="n">config</span><span class="o">.</span><span class="n">action_view</span><span class="o">.</span><span class="n">embed_authenticity_token_in_remote_forms</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>プロフィールページを更新させずにフォローできるようにする
<ul>
<li>JavaScript と埋め込み Ruby を使ってフォローの関係性を作成する</li>
<li><code>create.js.erb</code> と <code>destroy.js.erb</code> を更新</li>
</ul>
</li>
</ul>
<h3 id="1426-フォローをテストする">14.2.6 フォローをテストする</h3>
<ul>
<li>フォローボタンが動くようになったので、テストを書いていく</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">FollowingTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span>  <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@other</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should follow a user the standard way&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;@user.following.count&#39;</span><span class="p">,</span> <span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">relationships_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">followed_id</span><span class="p">:</span> <span class="vi">@other</span><span class="o">.</span><span class="n">id</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should follow a user with Ajax&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;@user.following.count&#39;</span><span class="p">,</span> <span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">relationships_path</span><span class="p">,</span> <span class="ss">xhr</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">followed_id</span><span class="p">:</span> <span class="vi">@other</span><span class="o">.</span><span class="n">id</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should unfollow a user the standard way&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">follow</span><span class="p">(</span><span class="vi">@other</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">relationship</span> <span class="o">=</span> <span class="vi">@user</span><span class="o">.</span><span class="n">active_relationships</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">followed_id</span><span class="p">:</span> <span class="vi">@other</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;@user.following.count&#39;</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">relationship_path</span><span class="p">(</span><span class="n">relationship</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should unfollow a user with Ajax&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">follow</span><span class="p">(</span><span class="vi">@other</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">relationship</span> <span class="o">=</span> <span class="vi">@user</span><span class="o">.</span><span class="n">active_relationships</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">followed_id</span><span class="p">:</span> <span class="vi">@other</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;@user.following.count&#39;</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">relationship_path</span><span class="p">(</span><span class="n">relationship</span><span class="p">),</span> <span class="ss">xhr</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="143-ステータスフィード">14.3 ステータスフィード</h2>
<ul>
<li>現在のユーザーにフォローされているユーザーのマイクロポストの配列を作成し、現在のユーザー自身のマイクロポストと合わせて表示する</li>
</ul>
<h3 id="1431-動機と計画">14.3.1 動機と計画</h3>
<ul>
<li>現在のユーザーによってフォローされているユーザーに対応するユーザー id を持つマイクロポストを取り出し、同時に現在のユーザー自身のマイクロポストも一緒に取り出すことが目的</li>
<li>以下の条件を満たすテストを書いていく
<ul>
<li>フォローしているユーザーのマイクロポストがフィードに含まれていること</li>
<li>自分自身のマイクロポストもフィードに含まれていること</li>
<li>フォローしていないユーザーのマイクロポストがフィードに含まれていないこと</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;feed should have the right posts&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">michael</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">archer</span>  <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">lana</span>    <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:lana</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># フォローしているユーザーの投稿を確認</span>
</span></span><span class="line"><span class="cl">    <span class="n">lana</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">post_following</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">michael</span><span class="o">.</span><span class="n">feed</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">post_following</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 自分自身の投稿を確認</span>
</span></span><span class="line"><span class="cl">    <span class="n">michael</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">post_self</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert</span> <span class="n">michael</span><span class="o">.</span><span class="n">feed</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">post_self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># フォローしていないユーザーの投稿を確認</span>
</span></span><span class="line"><span class="cl">    <span class="n">archer</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">post_unfollowed</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert_not</span> <span class="n">michael</span><span class="o">.</span><span class="n">feed</span><span class="o">.</span><span class="n">include?</span><span class="p">(</span><span class="n">post_unfollowed</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1432-フィードを初めて実装する">14.3.2 フィードを初めて実装する</h3>
<ul>
<li>ここでは <code>microposts</code> テーブルからあるユーザーがフォローしているユーザーに対する id を持つマイクロポストをすべて選択するクエリが必要</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># パスワード再設定の期限が切れている場合はtrueを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">password_reset_expired?</span>
</span></span><span class="line"><span class="cl">    <span class="n">reset_sent_at</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="o">.</span><span class="n">hours</span><span class="o">.</span><span class="n">ago</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーのステータスフィードを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">feed</span>
</span></span><span class="line"><span class="cl">    <span class="no">Micropost</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="s2">&#34;user_id IN (?) OR user_id = ?&#34;</span><span class="p">,</span> <span class="n">following_ids</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーをフォローする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">follow</span><span class="p">(</span><span class="n">other_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">following</span> <span class="o">&lt;&lt;</span> <span class="n">other_user</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1433-サブセレクト">14.3.3 サブセレクト</h3>
<ul>
<li>今のつくりだとユーザーが5000人程度になると Web サービス全体が遅くなる可能性があるので、改善していく</li>
<li><code>where</code> メソッド内の変数にキーと値のペアを使い、DB への問い合わせ数を減らす</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーのステータスフィードを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">feed</span>
</span></span><span class="line"><span class="cl">    <span class="no">Micropost</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="s2">&#34;user_id IN (:following_ids) OR user_id = :user_id&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="ss">following_ids</span><span class="p">:</span> <span class="n">following_ids</span><span class="p">,</span> <span class="ss">user_id</span><span class="p">:</span> <span class="nb">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ここからさらに following_ids を SQLに置き換えて最終的な実装とする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーのステータスフィードを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">feed</span>
</span></span><span class="line"><span class="cl">    <span class="n">following_ids</span> <span class="o">=</span> <span class="s2">&#34;SELECT followed_id FROM relationships
</span></span></span><span class="line"><span class="cl"><span class="s2">                     WHERE follower_id = :user_id&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="no">Micropost</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="s2">&#34;user_id IN (</span><span class="si">#{</span><span class="n">following_ids</span><span class="si">}</span><span class="s2">)
</span></span></span><span class="line"><span class="cl"><span class="s2">                     OR user_id = :user_id&#34;</span><span class="p">,</span> <span class="ss">user_id</span><span class="p">:</span> <span class="nb">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>サンプルアプリケーションの完成！</li>
</ul>
<h2 id="144-最後に">14.4 最後に</h2>
<ul>
<li>完成したよ！！</li>
</ul>
<h1 id="所感">所感</h1>
<p>チマチマと進めていた Rails チュートリアルもようやく終わった。最後はちょっと飽きてきていたものの、とりあえず完走できたので良かった。</p>
<p>Rails に対する悪評はよく耳にしていたが、用法用量を守って Rails の機能を使わないと振り回されてしまう点や、密に結合したMVCのアーキテクチャと継続的に変更を加えていくプロダクト開発との相性が悪そうな点が、その悪評の根底にあるのかなと本チュートリアルを通して認識できた。Rails が悪いのではなく、Rails を正しく使えない自分が悪い、という話なのだと思う。</p>
<p>元々は自社のプロダクトコードをより理解したいという動機で始めた本チュートリアルだったので、これから戦場に飛び込んでみよう。いざ参らん。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第13章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter13/</link>
			<pubDate>Thu, 30 Apr 2020 21:32:57 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter13/</guid>
			<description>第13章 ユーザーのマイクロポスト ユーザーが短いメッセージを投稿できるようにするためのリソースであるマイクロポストを追加していく Micropost データモデル</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第13章-ユーザーのマイクロポスト">第13章 ユーザーのマイクロポスト</h1>
<ul>
<li>ユーザーが短いメッセージを投稿できるようにするためのリソースであるマイクロポストを追加していく
<ul>
<li>Micropost データモデルを作成</li>
<li>User モデルと <code>has_many</code> および <code>belong_to</code> メソッドを使って関連付けを行う</li>
<li>結果を処理し表示するために必要なフォームとその部品を作成する</li>
</ul>
</li>
</ul>
<h2 id="131-micropost-モデル">13.1 Micropost モデル</h2>
<ul>
<li>まずはモデルを作成するところから</li>
</ul>
<h3 id="1311-基本的なモデル">13.1.1 基本的なモデル</h3>
<ul>
<li>マイクロポストの内容を保存する <code>content</code> 属性と、特定のユーザーとマイクロポストを関連付ける <code>user_id</code> 属性の2つの属性だけを持つ Micropost モデルを生成する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">rails generate model Micropost content:text user:references
</span></span></span></code></pre></div><ul>
<li><code>user:reference</code> の引数により、生成されたモデルには <code>belongs_to</code> のコードも追加されている</li>
<li><code>reference</code> 型を使うことにより、自動的にインデックスと外部キー参照付きの <code>user_id</code> カラムが追加され、User と Micropost を関連付ける下準備をしてくれる
<ul>
<li>つよつよでは</li>
</ul>
</li>
<li><code>user_id</code> に関連付けられたすべてのマイクロポストを作成時刻の逆順で取り出しやすくするために、<code>user_id</code> と <code>created_at</code> カラムにインデックスを付与する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">CreateMicroposts</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="o">[</span><span class="mi">5</span><span class="o">.</span><span class="mi">0</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">change</span>
</span></span><span class="line"><span class="cl">    <span class="n">create_table</span> <span class="ss">:microposts</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">t</span><span class="o">.</span><span class="n">text</span> <span class="ss">:content</span>
</span></span><span class="line"><span class="cl">      <span class="n">t</span><span class="o">.</span><span class="n">references</span> <span class="ss">:user</span><span class="p">,</span> <span class="ss">foreign_key</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="n">t</span><span class="o">.</span><span class="n">timestamps</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_index</span> <span class="ss">:microposts</span><span class="p">,</span> <span class="o">[</span><span class="ss">:user_id</span><span class="p">,</span> <span class="ss">:created_at</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>以上をもってマイグレーションを実施</li>
</ul>
<h3 id="1312-micropost-のマイグレーション">13.1.2 Micropost のマイグレーション</h3>
<ul>
<li>まずは <code>Micropost</code> モデル単体を TDD で動くようにしてみる</li>
<li><code>setup</code> で fixture のサンプルユーザーと紐付けた新しいマイクロポストを作成し、その有効性をチェックしする
<ul>
<li>あらゆるマイクロポストはユーザーの id を持っているべきなので、<code>user_id</code> の存在性バリデーションに対するテストも追加する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># このコードは慣習的に正しくない</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span> <span class="o">=</span> <span class="no">Micropost</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">content</span><span class="p">:</span> <span class="s2">&#34;Lorem ipsum&#34;</span><span class="p">,</span> <span class="ss">user_id</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should be valid&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;user id should be present&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span><span class="o">.</span><span class="n">user_id</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>1つ目のテストでは正常な状態かどうかをテストしている</li>
<li>2つ目のテストでは <code>user_id</code> が存在しているかどうかをテストしている
<ul>
<li>パスするために存在性のバリデーションを追加する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Micropost</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:user</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:user_id</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>Rails5 では上記バリデーションを追加せずともテストが通ってしまう
<ul>
<li>慣習的に正しくないコードを書いた場合のみ発生する
<ul>
<li>そんな慣習に依存する仕様はどうなのよ。。。</li>
</ul>
</li>
</ul>
</li>
<li>次にマイクロポストの <code>content</code> 属性に対するバリデーションを追加する
<ul>
<li>存在必然性の制限と、140文字より長くならないようにする制限を加える
<ul>
<li>TDD スタイルで</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span> <span class="o">=</span> <span class="no">Micropost</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="ss">content</span><span class="p">:</span> <span class="s2">&#34;Lorem ipsum&#34;</span><span class="p">,</span> <span class="ss">user_id</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should be valid&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;user id should be present&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span><span class="o">.</span><span class="n">user_id</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;content should be present&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="s2">&#34;   &#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;content should be at most 140 characters&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span><span class="o">.</span><span class="n">content</span> <span class="o">=</span> <span class="s2">&#34;a&#34;</span> <span class="o">*</span> <span class="mi">141</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>アプリケーション側の実装を対応させる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Micropost</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:user</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:user_id</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:content</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">140</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1313-usermicropost-の関連付け">13.1.3 User/Micropost の関連付け</h3>
<ul>
<li>それぞれのマイクロポストは1人のユーザーと関連付けられ、それぞれのユーザーは複数のマイクロポストと関連付けられる</li>
<li>マイクロポストがユーザーに所属するための関連付けはマイグレーションによって自動生成されているが、ユーザーがマイクロポストを複数所有できるようにする関連付けは手動で行う必要がある</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:microposts</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>正しく関連付けができたら、慣習的に正しくマイクロポストを作成する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span> <span class="o">=</span> <span class="vi">@user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">build</span><span class="p">(</span><span class="ss">content</span><span class="p">:</span> <span class="s2">&#34;Lorem ipsum&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should be valid&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;user id should be present&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span><span class="o">.</span><span class="n">user_id</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1314-マイクロポストを改良する">13.1.4 マイクロポストを改良する</h3>
<ul>
<li>User と Micropost の関連付けを改良していく
<ul>
<li>ユーザーのマイクロポストを特定の順序で取得できるようにする</li>
<li>マイクロポストをユーザーに依存させ、ユーザー削除に伴ってマイクロポストも自動削除されるようにする</li>
</ul>
</li>
</ul>
<h4 id="デフォルトのスコープ">デフォルトのスコープ</h4>
<ul>
<li><code>user.microposts</code> メソッドはデフォルトでは読み出しの順序に対して何も保証しないので、最新のものを先頭表示するようにしてみる</li>
<li>例によって TDD で
<ul>
<li>DB 上の最初のマイクロポストが fixture 内のマイクロポストと同じであるか</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;order should be most recent first&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="n">microposts</span><span class="p">(</span><span class="ss">:most_recent</span><span class="p">),</span> <span class="no">Micropost</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記に対応した fixture ファイルも用意しておく</li>
<li>この状態ではテストが失敗するので、<code>default_scope</code> でマイクロポストの順序を変更する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Micropost</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:user</span>
</span></span><span class="line"><span class="cl">  <span class="n">default_scope</span> <span class="o">-&gt;</span> <span class="p">{</span> <span class="n">order</span><span class="p">(</span><span class="ss">created_at</span><span class="p">:</span> <span class="ss">:desc</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:user_id</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:content</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">140</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h4 id="dependent-destroy">Dependent: destroy</h4>
<ul>
<li>サイト管理者はユーザーを廃棄する権限を持つので、ユーザーのは気に伴ってマイクロポストも破棄する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_many</span> <span class="ss">:microposts</span><span class="p">,</span> <span class="ss">dependent</span><span class="p">:</span> <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>dependent: :destroy</code> オプションを使うと、ユーザー削除時にそのユーザーに紐付いたマイクロポストも一緒に削除されるようになる
<ul>
<li>DB に所有者不明のマイクロポストが残ってしまうのを防ぐ
<ul>
<li>こういう不正データ対応は大事よね。。。</li>
</ul>
</li>
</ul>
</li>
<li><code>dependent: :destroy</code> が機能するか User モデルを検証する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Example User&#34;</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;user@example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;foobar&#34;</span><span class="p">,</span> <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;foobar&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;associated microposts should be destroyed&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="ss">content</span><span class="p">:</span> <span class="s2">&#34;Lorem ipsum&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;Micropost.count&#39;</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span><span class="o">.</span><span class="n">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="132-マイクロポストを表示する">13.2 マイクロポストを表示する</h2>
<ul>
<li>ユーザーの <code>show</code> ページで直接マイクロポストを表示させる</li>
</ul>
<h3 id="1321-マイクロポストの描画">13.2.1 マイクロポストの描画</h3>
<ul>
<li>まずは Micropost のコントローラとビューを作成するために、コントローラを生成する</li>
<li>1つのマイクロポストを表示するパーシャルは以下のようになる</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;li id=&#34;micropost-&lt;%= micropost.id %&gt;&#34;&gt;
  &lt;%= link_to gravatar_for(micropost.user, size: 50), micropost.user %&gt;
  &lt;span class=&#34;user&#34;&gt;&lt;%= link_to micropost.user.name, micropost.user %&gt;&lt;/span&gt;
  &lt;span class=&#34;content&#34;&gt;&lt;%= micropost.content %&gt;&lt;/span&gt;
  &lt;span class=&#34;timestamp&#34;&gt;
    Posted &lt;%= time_ago_in_words(micropost.created_at) %&gt; ago.
  &lt;/span&gt;
&lt;/li&gt;
</code></pre><ul>
<li>一度にすべてのマイクロポストが表示されてしまう問題に対処する
<ul>
<li><code>@microposts</code> 変数を Users コントローラの <code>show</code> アクションで明示的に <code>will_paginate</code> に渡す</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">show</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@microposts</span> <span class="o">=</span> <span class="vi">@user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">page</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>最後に投稿数表示を加味してマイクロポストをユーザーの <code>show</code> ページに追加する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, @user.name) %&gt;
&lt;div class=&#34;row&#34;&gt;
  &lt;aside class=&#34;col-md-4&#34;&gt;
    &lt;section class=&#34;user_info&#34;&gt;
      &lt;h1&gt;
        &lt;%= gravatar_for @user %&gt;
        &lt;%= @user.name %&gt;
      &lt;/h1&gt;
    &lt;/section&gt;
  &lt;/aside&gt;
  &lt;div class=&#34;col-md-8&#34;&gt;
    &lt;% if @user.microposts.any? %&gt;
      &lt;h3&gt;Microposts (&lt;%= @user.microposts.count %&gt;)&lt;/h3&gt;
      &lt;ol class=&#34;microposts&#34;&gt;
        &lt;%= render @microposts %&gt;
      &lt;/ol&gt;
      &lt;%= will_paginate @microposts %&gt;
    &lt;% end %&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre><ul>
<li>この時点ではまだマイクロポストがないので何も表示されない</li>
</ul>
<h3 id="1322-マイクロポストのサンプル">13.2.2 マイクロポストのサンプル</h3>
<ul>
<li>サンプルデータにマイクロポストを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="n">users</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">order</span><span class="p">(</span><span class="ss">:created_at</span><span class="p">)</span><span class="o">.</span><span class="n">take</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="mi">50</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">content</span> <span class="o">=</span> <span class="no">Faker</span><span class="o">::</span><span class="no">Lorem</span><span class="o">.</span><span class="n">sentence</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">users</span><span class="o">.</span><span class="n">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span> <span class="n">user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="ss">content</span><span class="p">:</span> <span class="n">content</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>開発環境用の DB で再度サンプルデータを生成する</li>
<li>サンプルデータから生成されたページにはマイクロポスト固有のスタイルが与えられていないので、CSSを適用する</li>
</ul>
<h3 id="1323-プロフィール画面のマイクロポストをテストする">13.2.3 プロフィール画面のマイクロポストをテストする</h3>
<ul>
<li>プロフィール画面で表示されるマイクロポストに対して統合テストを書いていく</li>
<li>プロフィール画面におけるマイクロポストをテストするために、ユーザーと関連付けられたマイクロポストの fixture を追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">orange</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">content</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;I just ate an orange!&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">created_at</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= 10.minutes.ago %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l">michael</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">tau_manifesto</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">content</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Check out the @tauday site by @mhartl: http://tauday.com&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">created_at</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= 3.years.ago %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l">michael</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">cat_video</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">content</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Sad cats are sad: http://youtu.be/PKffm2uI4dk&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">created_at</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= 2.hours.ago %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l">michael</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">most_recent</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">content</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Writing a short test&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">created_at</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= Time.zone.now %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l">michael</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="l">&lt;% 30.times do |n| %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">micropost_&lt;%= n %&gt;</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">content</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= Faker::Lorem.sentence(5) %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">created_at</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= 42.days.ago %&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l">michael</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="l">&lt;% end %&gt;</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>上記データを用いてテストを実装する
<ul>
<li>プロフィール画面にアクセスし</li>
<li>ページタイトルとユーザー名、Gravatar、マイクロポストの投稿数、ページ分割されたマイクロポストという順でテストしていく</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersProfileTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">ApplicationHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;profile display&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/show&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;title&#39;</span><span class="p">,</span> <span class="n">full_title</span><span class="p">(</span><span class="vi">@user</span><span class="o">.</span><span class="n">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;h1&#39;</span><span class="p">,</span> <span class="ss">text</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;h1&gt;img.gravatar&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="vi">@user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">count</span><span class="o">.</span><span class="n">to_s</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">body</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;div.pagination&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">page</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">micropost</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert_match</span> <span class="n">micropost</span><span class="o">.</span><span class="n">content</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">body</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="133-マイクロポストを操作する">13.3 マイクロポストを操作する</h2>
<ul>
<li>データモデリングとマイクロポスト表示テンプレートが完成したので、次は Web 経由でそれらを作成するためのインターフェイスに取り掛かる</li>
<li>Micropost リソースへのインターフェースは、主にプロフィールページと Home ページのコントローラを経由して実行されるので、Micropost コントローラには <code>new</code> や <code>edit</code> のようなアクションは不要になる
<ul>
<li><code>create</code> と <code>destroy</code> があれば十分なので、マイクロポストリソースのルーティングは以下のようになる</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span>   <span class="s1">&#39;static_pages#home&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/help&#39;</span><span class="p">,</span>    <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#help&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/about&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#about&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/contact&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#contact&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/signup&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;users#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">post</span>   <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#create&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">delete</span> <span class="s1">&#39;/logout&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#destroy&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:users</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:account_activations</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:password_resets</span><span class="p">,</span>     <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:microposts</span><span class="p">,</span>          <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1331-マイクロポストのアクセス制御">13.3.1 マイクロポストのアクセス制御</h3>
<ul>
<li>関連付けられたユーザーを通してマイクロポストにアクセスするので、<code>create</code> アクションや <code>destroy</code> アクションを利用するユーザーはログイン済みでなければならない</li>
<li>ログイン済みかどうかを確かめるテストでは、正しいリクエストを各アクションに向けて発行し、マイクロポストの数が変化していないかどうか、またリダイレクトされていないかどうかを確かめればよい</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostsControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span> <span class="o">=</span> <span class="n">microposts</span><span class="p">(</span><span class="ss">:orange</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect create when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;Micropost.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">microposts_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">micropost</span><span class="p">:</span> <span class="p">{</span> <span class="ss">content</span><span class="p">:</span> <span class="s2">&#34;Lorem ipsum&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect destroy when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;Micropost.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">micropost_path</span><span class="p">(</span><span class="vi">@micropost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>テストを通すためにリファクタリングをする</li>
<li><code>logged_in_user</code> メソッドを Application コントローラに移す</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">protect_from_forgery</span> <span class="ss">with</span><span class="p">:</span> <span class="ss">:exception</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">SessionsHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># ユーザーのログインを確認する</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">logged_in_user</span>
</span></span><span class="line"><span class="cl">      <span class="k">unless</span> <span class="n">logged_in?</span>
</span></span><span class="line"><span class="cl">        <span class="n">store_location</span>
</span></span><span class="line"><span class="cl">        <span class="n">flash</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Please log in.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">redirect_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>コードが重複しないように Users コントローラからも <code>logged_in_user</code> を削除する</li>
<li>これで Microposts コントローラからも <code>logged_in_user</code> メソッドを呼び出せるようになったので、<code>create</code> アクションや <code>destroy</code> アクションに対するアクセス制限が before フィルターで簡単に実装できるようになる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1332-マイクロポストを作成する">13.3.2 マイクロポストを作成する</h3>
<ul>
<li>micropost/new ページを使う代わりに、ホーム画面（ルートパス）にフォームを置いてマイクロポストを作成する</li>
<li>マイクロポストの <code>create</code> アクションを作る
<ul>
<li>新しいマイクロポストを <code>build</code> するために User/Micropost 関連付けを使用する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span> <span class="o">=</span> <span class="n">current_user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">build</span><span class="p">(</span><span class="n">micropost_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Micropost created!&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;static_pages/home&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">micropost_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:micropost</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>マイクロポスト作成フォームを構築するために、サイト訪問者がログインしているかどうかに応じて異なる HTML を提供する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% if logged_in? %&gt;
  &lt;div class=&#34;row&#34;&gt;
    &lt;aside class=&#34;col-md-4&#34;&gt;
      &lt;section class=&#34;user_info&#34;&gt;
        &lt;%= render &#39;shared/user_info&#39; %&gt;
      &lt;/section&gt;
      &lt;section class=&#34;micropost_form&#34;&gt;
        &lt;%= render &#39;shared/micropost_form&#39; %&gt;
      &lt;/section&gt;
    &lt;/aside&gt;
  &lt;/div&gt;
&lt;% else %&gt;
  &lt;div class=&#34;center jumbotron&#34;&gt;
    &lt;h1&gt;Welcome to the Sample App&lt;/h1&gt;

    &lt;h2&gt;
      This is the home page for the
      &lt;a href=&#34;https://railstutorial.jp/&#34;&gt;Ruby on Rails Tutorial&lt;/a&gt;
      sample application.
    &lt;/h2&gt;

    &lt;%= link_to &#34;Sign up now!&#34;, signup_path, class: &#34;btn btn-lg btn-primary&#34; %&gt;
  &lt;/div&gt;

  &lt;%= link_to image_tag(&#34;rails.png&#34;, alt: &#34;Rails logo&#34;),
              &#39;http://rubyonrails.org/&#39; %&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>上記コードを動かすためにサイドバーで表示するユーザー情報のパーシャルを作成する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= link_to gravatar_for(current_user, size: 50), current_user %&gt;
&lt;h1&gt;&lt;%= current_user.name %&gt;&lt;/h1&gt;
&lt;span&gt;&lt;%= link_to &#34;view my profile&#34;, current_user %&gt;&lt;/span&gt;
&lt;span&gt;&lt;%= pluralize(current_user.microposts.count, &#34;micropost&#34;) %&gt;&lt;/span&gt;
</code></pre><ul>
<li>マイクロポスト作成フォームも定義する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= form_for(@micropost) do |f| %&gt;
  &lt;%= render &#39;shared/error_messages&#39;, object: f.object %&gt;
  &lt;div class=&#34;field&#34;&gt;
    &lt;%= f.text_area :content, placeholder: &#34;Compose new micropost...&#34; %&gt;
  &lt;/div&gt;
  &lt;%= f.submit &#34;Post&#34;, class: &#34;btn btn-primary&#34; %&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>上記コードを動かすために、<code>home</code> アクションにマイクロポストのインスタンス変数を追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">StaticPagesController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">home</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span> <span class="o">=</span> <span class="n">current_user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">build</span> <span class="k">if</span> <span class="n">logged_in?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">help</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">about</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">contact</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>@micropost</code> 変数はログインしているときのみ定義される</li>
<li>マイクロポスト投稿フォームのパーシャルを動かすために、エラーメッセージのパーシャルを再定義する必要もある
<ul>
<li>User オブジェクト以外でも動作するように error_message パーシャルを更新する</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% if object.errors.any? %&gt;
  &lt;div id=&#34;error_explanation&#34;&gt;
    &lt;div class=&#34;alert alert-danger&#34;&gt;
      The form contains &lt;%= pluralize(object.errors.count, &#34;error&#34;) %&gt;.
    &lt;/div&gt;
    &lt;ul&gt;
    &lt;% object.errors.full_messages.each do |msg| %&gt;
      &lt;li&gt;&lt;%= msg %&gt;&lt;/li&gt;
    &lt;% end %&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>このパーシャルは他の場所でも使用されているので、テストは失敗する
<ul>
<li>ユーザー登録、パスワード再設定、ユーザー編集のそれぞれのビューを更新すればテストは通る</li>
</ul>
</li>
</ul>
<h3 id="1333-フィードの原型">13.3.3 フィードの原型</h3>
<ul>
<li>Home ページにマイクロポストを表示する部分が実装されていないので、まだ投稿内容をすぐに見ることはできない</li>
<li>投稿後にマイクロポストをフィードを表示する</li>
<li>User モデルに <code>feed</code> メソッドを作り、マイクロポストのステータスフィードを実装する準備を行う</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 試作feedの定義</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 完全な実装は次章の「ユーザーをフォローする」を参照</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">feed</span>
</span></span><span class="line"><span class="cl">    <span class="no">Micropost</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="s2">&#34;user_id = ?&#34;</span><span class="p">,</span> <span class="nb">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kp">private</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>フィード機能導入のため、<code>home</code> アクションにフィードのインスタンス変数を追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">StaticPagesController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">home</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">logged_in?</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@micropost</span>  <span class="o">=</span> <span class="n">current_user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">build</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@feed_items</span> <span class="o">=</span> <span class="n">current_user</span><span class="o">.</span><span class="n">feed</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">page</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">help</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">about</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">contact</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>Home ページのフィード用パーシャルは以下</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% if @feed_items.any? %&gt;
  &lt;ol class=&#34;microposts&#34;&gt;
    &lt;%= render @feed_items %&gt;
  &lt;/ol&gt;
  &lt;%= will_paginate @feed_items %&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>Home ページにステータスフィードを追加する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% if logged_in? %&gt;
  &lt;div class=&#34;row&#34;&gt;
    &lt;aside class=&#34;col-md-4&#34;&gt;
      &lt;section class=&#34;user_info&#34;&gt;
        &lt;%= render &#39;shared/user_info&#39; %&gt;
      &lt;/section&gt;
      &lt;section class=&#34;micropost_form&#34;&gt;
        &lt;%= render &#39;shared/micropost_form&#39; %&gt;
      &lt;/section&gt;
    &lt;/aside&gt;
    &lt;div class=&#34;col-md-8&#34;&gt;
      &lt;h3&gt;Micropost Feed&lt;/h3&gt;
      &lt;%= render &#39;shared/feed&#39; %&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;% else %&gt;
  .
  .
  .
&lt;% end %&gt;
</code></pre><ul>
<li>マイクロポスト投稿に失敗すると Home ページが <code>@feed_items</code> インスタンスを保持しているので壊れてしまう
<ul>
<li>空の配列を渡しておけば回避できる</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span> <span class="o">=</span> <span class="n">current_user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">build</span><span class="p">(</span><span class="n">micropost_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Micropost created!&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@feed_items</span> <span class="o">=</span> <span class="o">[]</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;static_pages/home&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">micropost_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:micropost</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1334-マイクロポストを削除する">13.3.4 マイクロポストを削除する</h3>
<ul>
<li>自分が投稿したマイクロポストに対してのみ削除リンクが動作するようにする</li>
<li>マイクロポストのパーシャルに削除リンクを追加する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;li id=&#34;micropost-&lt;%= micropost.id %&gt;&#34;&gt;
  &lt;%= link_to gravatar_for(micropost.user, size: 50), micropost.user %&gt;
  &lt;span class=&#34;user&#34;&gt;&lt;%= link_to micropost.user.name, micropost.user %&gt;&lt;/span&gt;
  &lt;span class=&#34;content&#34;&gt;&lt;%= micropost.content %&gt;&lt;/span&gt;
  &lt;span class=&#34;timestamp&#34;&gt;
    Posted &lt;%= time_ago_in_words(micropost.created_at) %&gt; ago.
    &lt;% if current_user?(micropost.user) %&gt;
      &lt;%= link_to &#34;delete&#34;, micropost, method: :delete,
                                       data: { confirm: &#34;You sure?&#34; } %&gt;
    &lt;% end %&gt;
  &lt;/span&gt;
&lt;/li&gt;
</code></pre><ul>
<li>次に Micropost コントローラの <code>destroy</code> アクションを定義する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:correct_user</span><span class="p">,</span>   <span class="ss">only</span><span class="p">:</span> <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span><span class="o">.</span><span class="n">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Micropost deleted&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span> <span class="n">request</span><span class="o">.</span><span class="n">referrer</span> <span class="o">||</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">micropost_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:micropost</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">correct_user</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@micropost</span> <span class="o">=</span> <span class="n">current_user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span> <span class="k">if</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1335-フィード画面のマイクロポストをテストする">13.3.5 フィード画面のマイクロポストをテストする</h3>
<ul>
<li>マイクロポスト用の fixture に別々のユーザーに紐付けられたマイクロポストを追加していく</li>
<li>次に自分以外のユーザーのマイクロポストを削除しようとすると、適切にリダイレクトされることを確認する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostsControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@micropost</span> <span class="o">=</span> <span class="n">microposts</span><span class="p">(</span><span class="ss">:orange</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect create when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;Micropost.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">microposts_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">micropost</span><span class="p">:</span> <span class="p">{</span> <span class="ss">content</span><span class="p">:</span> <span class="s2">&#34;Lorem ipsum&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect destroy when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;Micropost.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">micropost_path</span><span class="p">(</span><span class="vi">@micropost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect destroy for wrong micropost&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">micropost</span> <span class="o">=</span> <span class="n">microposts</span><span class="p">(</span><span class="ss">:ants</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;Micropost.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">micropost_path</span><span class="p">(</span><span class="n">micropost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>最後にマイクロポストのUIに対する統合テストを書く
<ul>
<li>ログイン</li>
<li>マイクロポストのページ分割の確認</li>
<li>無効なマイクロポストを投稿</li>
<li>有効なマイクロポストを投稿</li>
<li>マイクロポストの削除</li>
<li>他のユーザーのマイクロポストには[delete]リンクが表示されないことを確認</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostsInterfaceTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;micropost interface&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">root_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;div.pagination&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 無効な送信</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;Micropost.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">microposts_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">micropost</span><span class="p">:</span> <span class="p">{</span> <span class="ss">content</span><span class="p">:</span> <span class="s2">&#34;&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;div#error_explanation&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 有効な送信</span>
</span></span><span class="line"><span class="cl">    <span class="n">content</span> <span class="o">=</span> <span class="s2">&#34;This micropost really ties the room together&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;Micropost.count&#39;</span><span class="p">,</span> <span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">microposts_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">micropost</span><span class="p">:</span> <span class="p">{</span> <span class="ss">content</span><span class="p">:</span> <span class="n">content</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="n">follow_redirect!</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="n">content</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">body</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 投稿を削除する</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="ss">text</span><span class="p">:</span> <span class="s1">&#39;delete&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">first_micropost</span> <span class="o">=</span> <span class="vi">@user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">page</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;Micropost.count&#39;</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">micropost_path</span><span class="p">(</span><span class="n">first_micropost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 違うユーザーのプロフィールにアクセス (削除リンクがないことを確認)</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">user_path</span><span class="p">(</span><span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="ss">text</span><span class="p">:</span> <span class="s1">&#39;delete&#39;</span><span class="p">,</span> <span class="ss">count</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="134-マイクロポストの画像投稿">13.4 マイクロポストの画像投稿</h2>
<ul>
<li>画像つきマイクロポストを投稿できるようにする
<ul>
<li>画像アップロード用フォームと投稿された画像そのものという2つの視覚的要素が必要</li>
</ul>
</li>
</ul>
<h3 id="1341-基本的な画像アップロード">13.4.1 基本的な画像アップロード</h3>
<ul>
<li>CarrierWave という画像アップローダーを <code>Gemfile</code> に追加していつものように <code>bundle install</code>
<ul>
<li>失敗するので <code>gem fog</code> を <code>gem fog-aws</code> にしてゴリ押しで進める</li>
</ul>
</li>
<li>CarrierWave により Rails のジェネレーターで画像アップローダーが生成できる</li>
<li>必要となる <code>picture</code> 属性を Micropost モデルに追加するためにマイグレーションファイルを生成して開発環境の DB に適用する</li>
<li>Micropost モデルにアップローダーを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Micropost</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:user</span>
</span></span><span class="line"><span class="cl">  <span class="n">default_scope</span> <span class="o">-&gt;</span> <span class="p">{</span> <span class="n">order</span><span class="p">(</span><span class="ss">created_at</span><span class="p">:</span> <span class="ss">:desc</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">mount_uploader</span> <span class="ss">:picture</span><span class="p">,</span> <span class="no">PictureUploader</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:user_id</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:content</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">140</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>マイクロポスト投稿フォームに画像アップローダーを追加する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= form_for(@micropost) do |f| %&gt;
  &lt;%= render &#39;shared/error_messages&#39;, object: f.object %&gt;
  &lt;div class=&#34;field&#34;&gt;
    &lt;%= f.text_area :content, placeholder: &#34;Compose new micropost...&#34; %&gt;
  &lt;/div&gt;
  &lt;%= f.submit &#34;Post&#34;, class: &#34;btn btn-primary&#34; %&gt;
  &lt;span class=&#34;picture&#34;&gt;
    &lt;%= f.file_field :picture %&gt;
  &lt;/span&gt;
&lt;% end %&gt;
</code></pre><ul>
<li>最後に Web から更新できる許可リストに <code>picture</code> 属性を追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MicropostsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:create</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:correct_user</span><span class="p">,</span>   <span class="ss">only</span><span class="p">:</span> <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">micropost_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:micropost</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:content</span><span class="p">,</span> <span class="ss">:picture</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">correct_user</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@micropost</span> <span class="o">=</span> <span class="n">current_user</span><span class="o">.</span><span class="n">microposts</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span> <span class="k">if</span> <span class="vi">@micropost</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>さらに、マイクロポストの画像表示を追加する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;li id=&#34;micropost-&lt;%= micropost.id %&gt;&#34;&gt;
  &lt;%= link_to gravatar_for(micropost.user, size: 50), micropost.user %&gt;
  &lt;span class=&#34;user&#34;&gt;&lt;%= link_to micropost.user.name, micropost.user %&gt;&lt;/span&gt;
  &lt;span class=&#34;content&#34;&gt;
    &lt;%= micropost.content %&gt;
    &lt;%= image_tag micropost.picture.url if micropost.picture? %&gt;
  &lt;/span&gt;
  &lt;span class=&#34;timestamp&#34;&gt;
    Posted &lt;%= time_ago_in_words(micropost.created_at) %&gt; ago.
    &lt;% if current_user?(micropost.user) %&gt;
      &lt;%= link_to &#34;delete&#34;, micropost, method: :delete,
                                       data: { confirm: &#34;You sure?&#34; } %&gt;
    &lt;% end %&gt;
  &lt;/span&gt;
&lt;/li&gt;
</code></pre><h3 id="1342-画像の検証">13.4.2 画像の検証</h3>
<ul>
<li>画像サイズやフォーマットに対するバリデーションを実装する</li>
<li>画像フォーマットのバリデーションは CarrierWave を修正する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PictureUploader</span> <span class="o">&lt;</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">Uploader</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">storage</span> <span class="ss">:file</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># アップロードファイルの保存先ディレクトリは上書き可能</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 下記はデフォルトの保存先</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">store_dir</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;uploads/</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">underscore</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">mounted_as</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># アップロード可能な拡張子のリスト</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">extension_whitelist</span>
</span></span><span class="line"><span class="cl">    <span class="sx">%w(jpg jpeg gif png)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>画像サイズについては <code>Micropost</code> モデルにバリデーションを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Micropost</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">belongs_to</span> <span class="ss">:user</span>
</span></span><span class="line"><span class="cl">  <span class="n">default_scope</span> <span class="o">-&gt;</span> <span class="p">{</span> <span class="n">order</span><span class="p">(</span><span class="ss">created_at</span><span class="p">:</span> <span class="ss">:desc</span><span class="p">)</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">mount_uploader</span> <span class="ss">:picture</span><span class="p">,</span> <span class="no">PictureUploader</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:user_id</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:content</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">140</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">validate</span>  <span class="ss">:picture_size</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># アップロードされた画像のサイズをバリデーションする</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">picture_size</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">picture</span><span class="o">.</span><span class="n">size</span> <span class="o">&gt;</span> <span class="mi">5</span><span class="o">.</span><span class="n">megabytes</span>
</span></span><span class="line"><span class="cl">        <span class="n">errors</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="ss">:picture</span><span class="p">,</span> <span class="s2">&#34;should be less than 5MB&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>フロント側にもバリデーションを追加する
<ul>
<li>拡張子チェックを追加</li>
<li>ファイルサイズは jQuery でチェックする
<ul>
<li>令和になっても jQuery を使うときが来るとはな&hellip;</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= form_for(@micropost) do |f| %&gt;
  &lt;%= render &#39;shared/error_messages&#39;, object: f.object %&gt;
  &lt;div class=&#34;field&#34;&gt;
    &lt;%= f.text_area :content, placeholder: &#34;Compose new micropost...&#34; %&gt;
  &lt;/div&gt;
  &lt;%= f.submit &#34;Post&#34;, class: &#34;btn btn-primary&#34; %&gt;
  &lt;span class=&#34;picture&#34;&gt;
    &lt;%= f.file_field :picture, accept: &#39;image/jpeg,image/gif,image/png&#39; %&gt;
  &lt;/span&gt;
&lt;% end %&gt;

&lt;script type=&#34;text/javascript&#34;&gt;
  $(&#39;#micropost_picture&#39;).bind(&#39;change&#39;, function() {
    var size_in_megabytes = this.files[0].size/1024/1024;
    if (size_in_megabytes &gt; 5) {
      alert(&#39;Maximum file size is 5MB. Please choose a smaller file.&#39;);
    }
  });
&lt;/script&gt;
</code></pre><ul>
<li>大きすぎるファイルのアップロードは完全には防げないが、ここでは一旦良しとする</li>
</ul>
<h3 id="1343-画像のリサイズ">13.4.3 画像のリサイズ</h3>
<ul>
<li>画像の縦横の長さに対する制限はないので、画像を表示させる前にリサイズする</li>
<li>ImageMagick を入れる
<ul>
<li>ImageMagick かぁ。。。これがデファクトなのかな？</li>
</ul>
</li>
<li>画像をリサイズするために画像アップローダーを修正する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PictureUploader</span> <span class="o">&lt;</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">Uploader</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">MiniMagick</span>
</span></span><span class="line"><span class="cl">  <span class="n">process</span> <span class="ss">resize_to_limit</span><span class="p">:</span> <span class="o">[</span><span class="mi">400</span><span class="p">,</span> <span class="mi">400</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="n">storage</span> <span class="ss">:file</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># アップロードファイルの保存先ディレクトリは上書き可能</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 下記はデフォルトの保存先</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">store_dir</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;uploads/</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">underscore</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">mounted_as</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># アップロード可能な拡張子のリスト</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">extension_whitelist</span>
</span></span><span class="line"><span class="cl">    <span class="sx">%w(jpg jpeg gif png)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1344-本番環境での画像アップロード">13.4.4 本番環境での画像アップロード</h3>
<ul>
<li>本番環境での画像アップロードを調整する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PictureUploader</span> <span class="o">&lt;</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">Uploader</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">CarrierWave</span><span class="o">::</span><span class="no">MiniMagick</span>
</span></span><span class="line"><span class="cl">  <span class="n">process</span> <span class="ss">resize_to_limit</span><span class="p">:</span> <span class="o">[</span><span class="mi">400</span><span class="p">,</span> <span class="mi">400</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">production?</span>
</span></span><span class="line"><span class="cl">    <span class="n">storage</span> <span class="ss">:fog</span>
</span></span><span class="line"><span class="cl">  <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="n">storage</span> <span class="ss">:file</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># アップロードファイルの保存先ディレクトリは上書き可能</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 下記はデフォルトの保存先</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">store_dir</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;uploads/</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">to_s</span><span class="o">.</span><span class="n">underscore</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">mounted_as</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">model</span><span class="o">.</span><span class="n">id</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># アップロード可能な拡張子のリスト</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">extension_whitelist</span>
</span></span><span class="line"><span class="cl">    <span class="sx">%w(jpg jpeg gif png)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>画像アップロード先として S3 のセットアップが必要なのでスキップ</li>
</ul>
<h2 id="135-最後に">13.5 最後に</h2>
<ul>
<li>いつもの通り master にマージして heroku にデプロイして終了</li>
</ul>
<h1 id="所感">所感</h1>
<p>いよいよ Rails チュートリアルも終盤であるが、ここまでくると Web アプリ開発として目新しいことはあまりなく、Rails の機能をただ扱うだけになってきたので正直なところモチベーションの維持が難しくなってきた。（まあ Rails のためのチュートリアルなので何も間違っていないのだが）
次が最後の章なのでなんとか完走したい。。。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第12章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter12/</link>
			<pubDate>Mon, 06 Apr 2020 21:12:46 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter12/</guid>
			<description>第12章 パスワードの再設定 パスワードの再設定はアカウント有効化の内容と似ている パスワード再設定の想定手順 ログインフォームに forgot password リンクを追加 リ</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第12章-パスワードの再設定">第12章 パスワードの再設定</h1>
<ul>
<li>パスワードの再設定はアカウント有効化の内容と似ている</li>
<li>パスワード再設定の想定手順
<ul>
<li>ログインフォームに forgot password リンクを追加
<ul>
<li>リンクを踏むとフォームが表示され、そこにメールアドレスを入力してメールを送信すると、そのメールにパスワード再設定用のリンクが記載されている</li>
</ul>
</li>
</ul>
</li>
<li>パスワード再設定用のメイラーにリソースとデータモデルを追加し、パスワードの再設定を実現していく</li>
<li>PasswordResets リソースを作成し、再設定用のトークンとそれに対応するダイジェストを保存するのが今回の目的</li>
</ul>
<h2 id="121-passwordresets-リソース">12.1 PasswordResets リソース</h2>
<ul>
<li>まずは PasswordResets リソースのモデリングから
<ul>
<li>必要なデータを User モデルに追加していく</li>
</ul>
</li>
<li>PasswordResets もリソースとして扱うので、標準的な RESTful URL を用意する
<ul>
<li>パスワードを再設定するフォームが必要なので、ビューを描画するための <code>new</code> アクションと <code>edit</code> アクションが必要になる</li>
</ul>
</li>
</ul>
<h3 id="1211-pssswordresets-コントローラ">12.1.1 PssswordResets コントローラ</h3>
<ul>
<li>まずはパスワード再設定用のコントローラを作成する
<ul>
<li><code>new</code> アクションと  <code>edit</code> アクションも一緒に</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">rails generate controller PasswordResets new edit --no-test-framework
</span></span></span></code></pre></div><ul>
<li>単体テストをする代わりに結合テストでカバーしていく</li>
<li>新しいパスワードを再設定するためのフォームと、Userモデル内のパスワードを変更するためのフォームが必要になるので、<code>new</code> <code>create</code> <code>edit</code> <code>update</code> のルーティングも用意する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span>   <span class="s1">&#39;static_pages#home&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/help&#39;</span><span class="p">,</span>    <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#help&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/about&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#about&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/contact&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#contact&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/signup&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;users#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">post</span>   <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#create&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">delete</span> <span class="s1">&#39;/logout&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#destroy&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:users</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:account_activations</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:password_resets</span><span class="p">,</span>     <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>パスワード再設定画面へのリンクを追加する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#34;Log in&#34;) %&gt;
&lt;h1&gt;Log in&lt;/h1&gt;

&lt;div class=&#34;row&#34;&gt;
  &lt;div class=&#34;col-md-6 col-md-offset-3&#34;&gt;
    &lt;%= form_for(:session, url: login_path) do |f| %&gt;

      &lt;%= f.label :email %&gt;
      &lt;%= f.email_field :email, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :password %&gt;
      &lt;%= link_to &#34;(forgot password)&#34;, new_password_reset_path %&gt;
      &lt;%= f.password_field :password, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :remember_me, class: &#34;checkbox inline&#34; do %&gt;
        &lt;%= f.check_box :remember_me %&gt;
        &lt;span&gt;Remember me on this computer&lt;/span&gt;
      &lt;% end %&gt;

      &lt;%= f.submit &#34;Log in&#34;, class: &#34;btn btn-primary&#34; %&gt;
    &lt;% end %&gt;

    &lt;p&gt;New user? &lt;%= link_to &#34;Sign up now!&#34;, signup_path %&gt;&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre><h3 id="1212-新しいパスワードの設定">12.1.2 新しいパスワードの設定</h3>
<ul>
<li>パスワードの再設定のデータモデルにおいても、トークン用の仮想的な属性とそれに対応するダイジェストを用意していく</li>
<li>パスワード再設定用のリンクはなるべく短時間で期限切れになるようにしなければならないため、再設定メールの送信時刻も記録する必要がある</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> rails generate migration add_reset_to_users reset_digest:string reset_sent_at:datetime
</span></span></code></pre></div><ul>
<li>パスワード再設定フォームはログインフォームと似ているが、以下の違いがある
<ul>
<li><code>form_for</code> で扱うリソースがと URL が異なっている点</li>
<li>パスワード属性が省略されている点</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#34;Forgot password&#34;) %&gt;
&lt;h1&gt;Forgot password&lt;/h1&gt;

&lt;div class=&#34;row&#34;&gt;
  &lt;div class=&#34;col-md-6 col-md-offset-3&#34;&gt;
    &lt;%= form_for(:password_reset, url: password_resets_path) do |f| %&gt;
      &lt;%= f.label :email %&gt;
      &lt;%= f.email_field :email, class: &#39;form-control&#39; %&gt;

      &lt;%= f.submit &#34;Submit&#34;, class: &#34;btn btn-primary&#34; %&gt;
    &lt;% end %&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre><h3 id="1213-create-アクションでパスワード再設定">12.1.3 create アクションでパスワード再設定</h3>
<ul>
<li>パスワード再設定フォームから送信を行った後、メールアドレスをキーとしてユーザーを DB から見つけ、パスワード再設定用トークンと送信時のタイムスタンプで DB の属性を更新する必要がある</li>
<li>続いてルート URL にリダイレクトし、フラッシュメッセージを表示
<ul>
<li>送信が無効の場合は <code>new</code> ページを出力</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PasswordResetsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:password_reset</span><span class="o">][</span><span class="ss">:email</span><span class="o">].</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span><span class="o">.</span><span class="n">create_reset_digest</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span><span class="o">.</span><span class="n">send_password_reset_email</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:info</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Email sent with password reset instructions&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">.</span><span class="n">now</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Email address not found&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">edit</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>User モデルにはパスワード再設定用のメソッドを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:remember_token</span><span class="p">,</span> <span class="ss">:activation_token</span><span class="p">,</span> <span class="ss">:reset_token</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_save</span>   <span class="ss">:downcase_email</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_create</span> <span class="ss">:create_activation_digest</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># アカウントを有効にする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">activate</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:activated</span><span class="p">,</span>    <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:activated_at</span><span class="p">,</span> <span class="no">Time</span><span class="o">.</span><span class="n">zone</span><span class="o">.</span><span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 有効化用のメールを送信する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">send_activation_email</span>
</span></span><span class="line"><span class="cl">    <span class="no">UserMailer</span><span class="o">.</span><span class="n">account_activation</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span><span class="o">.</span><span class="n">deliver_now</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># パスワード再設定の属性を設定する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create_reset_digest</span>
</span></span><span class="line"><span class="cl">    <span class="nb">self</span><span class="o">.</span><span class="n">reset_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:reset_digest</span><span class="p">,</span>  <span class="no">User</span><span class="o">.</span><span class="n">digest</span><span class="p">(</span><span class="n">reset_token</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:reset_sent_at</span><span class="p">,</span> <span class="no">Time</span><span class="o">.</span><span class="n">zone</span><span class="o">.</span><span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># パスワード再設定のメールを送信する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">send_password_reset_email</span>
</span></span><span class="line"><span class="cl">    <span class="no">UserMailer</span><span class="o">.</span><span class="n">password_reset</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span><span class="o">.</span><span class="n">deliver_now</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># メールアドレスをすべて小文字にする</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">downcase_email</span>
</span></span><span class="line"><span class="cl">      <span class="nb">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span><span class="o">.</span><span class="n">downcase</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 有効化トークンとダイジェストを作成および代入する</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">create_activation_digest</span>
</span></span><span class="line"><span class="cl">      <span class="nb">self</span><span class="o">.</span><span class="n">activation_token</span>  <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">      <span class="nb">self</span><span class="o">.</span><span class="n">activation_digest</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">digest</span><span class="p">(</span><span class="n">activation_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>この時点では無効なメールアドレスを入力した場合に正常に動作する
<ul>
<li>正しいメールアドレスを送信した場合にも正常に動作するためには、パスワード再設定用のメイラーメソッドを定義する必要がある</li>
</ul>
</li>
</ul>
<h2 id="122-パスワード再設定のメール送信">12.2 パスワード再設定のメール送信</h2>
<ul>
<li>残りはパスワード再設定に関するメールを送信するところ</li>
</ul>
<h3 id="1221-パスワード再設定のメールとテンプレート">12.2.1 パスワード再設定のメールとテンプレート</h3>
<ul>
<li>User メイラーのときと同様のリファクタリングをパスワード再設定にも行っていく</li>
<li>まずは User メイラーに <code>password_reset</code> メソッドを追加してパスワード再設定のリンクをメール送信できるようにする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserMailer</span> <span class="o">&lt;</span> <span class="no">ApplicationMailer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">account_activation</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="n">mail</span> <span class="ss">to</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">,</span> <span class="ss">subject</span><span class="p">:</span> <span class="s2">&#34;Account activation&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">password_reset</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="n">mail</span> <span class="ss">to</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">,</span> <span class="ss">subject</span><span class="p">:</span> <span class="s2">&#34;Password reset&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>続いてパスワード再設定メールのテキストテンプレートを定義</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">To reset your password click the link below:

&lt;%= edit_password_reset_url(@user.reset_token, email: @user.email) %&gt;

This link will expire in two hours.

If you did not request your password to be reset, please ignore this email and
your password will stay as it is.
</code></pre><ul>
<li>最後にパスワード再設定メールの HTML テンプレートを定義</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;h1&gt;Password reset&lt;/h1&gt;

&lt;p&gt;To reset your password click the link below:&lt;/p&gt;

&lt;%= link_to &#34;Reset password&#34;, edit_password_reset_url(@user.reset_token,
                                                      email: @user.email) %&gt;

&lt;p&gt;This link will expire in two hours.&lt;/p&gt;

&lt;p&gt;
If you did not request your password to be reset, please ignore this email and
your password will stay as it is.
&lt;/p&gt;
</code></pre><ul>
<li>アカウント有効化メールのときと同様に、Rails のメールプレビュー機能でパスワード再設定メールをプレビューできるようにする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># Preview all emails at http://localhost:3000/rails/mailers/user_mailer</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserMailerPreview</span> <span class="o">&lt;</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">Preview</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Preview this email at</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># http://localhost:3000/rails/mailers/user_mailer/account_activation</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">account_activation</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">activation_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="no">UserMailer</span><span class="o">.</span><span class="n">account_activation</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Preview this email at</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># http://localhost:3000/rails/mailers/user_mailer/password_reset</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">password_reset</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">reset_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="no">UserMailer</span><span class="o">.</span><span class="n">password_reset</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1222-送信メールのテスト">12.2.2 送信メールのテスト</h3>
<ul>
<li>アカウント有効化のテストと同様に、メイラーメソッドのテストを書く</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserMailerTest</span> <span class="o">&lt;</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;account_activation&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">activation_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="n">mail</span> <span class="o">=</span> <span class="no">UserMailer</span><span class="o">.</span><span class="n">account_activation</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="s2">&#34;Account activation&#34;</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">subject</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">to</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="s2">&#34;noreply@example.com&#34;</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">from</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="n">user</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>               <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="n">user</span><span class="o">.</span><span class="n">activation_token</span><span class="p">,</span>   <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="no">CGI</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">),</span>  <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;password_reset&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">reset_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="n">mail</span> <span class="o">=</span> <span class="no">UserMailer</span><span class="o">.</span><span class="n">password_reset</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="s2">&#34;Password reset&#34;</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">subject</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">to</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="s2">&#34;noreply@example.com&#34;</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">from</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="n">user</span><span class="o">.</span><span class="n">reset_token</span><span class="p">,</span>        <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="no">CGI</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">),</span>  <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="123-パスワードを再設定する">12.3 パスワードを再設定する</h2>
<ul>
<li>送信メールを生成できたので、PasswordResets コントローラの <code>edit</code> アクションを実装する</li>
</ul>
<h3 id="1231-edit-アクションで再設定">12.3.1 edit アクションで再設定</h3>
<ul>
<li>パスワード再設定送信メール記載のリンクを機能させるには、パスワード再設定フォームを表示するビューが必要
<ul>
<li>パスワード入力フィールドと確認用フィールドだけでよい</li>
</ul>
</li>
<li>メールアドレスをキーとしてユーザーを検索するためには、<code>edit</code> アクションと <code>update</code> アクションの両方でメールアドレスが必要なため、作業が少し面倒</li>
<li><code>edit</code> アクションでメールアドレスを取り出すことに問題はないが、フォームを一度送信してしまうとこの情報は消えてしまう
<ul>
<li>メールアドレスを保持するため、隠しフィールドとしてページ内に保存する</li>
<li>フォームから送信したときに他の情報と一緒にメールアドレスが送信されるようになる</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#39;Reset password&#39;) %&gt;
&lt;h1&gt;Reset password&lt;/h1&gt;

&lt;div class=&#34;row&#34;&gt;
  &lt;div class=&#34;col-md-6 col-md-offset-3&#34;&gt;
    &lt;%= form_for(@user, url: password_reset_path(params[:id])) do |f| %&gt;
      &lt;%= render &#39;shared/error_messages&#39; %&gt;

      &lt;%= hidden_field_tag :email, @user.email %&gt;

      &lt;%= f.label :password %&gt;
      &lt;%= f.password_field :password, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :password_confirmation, &#34;Confirmation&#34; %&gt;
      &lt;%= f.password_field :password_confirmation, class: &#39;form-control&#39; %&gt;

      &lt;%= f.submit &#34;Update password&#34;, class: &#34;btn btn-primary&#34; %&gt;
    &lt;% end %&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre><ul>
<li>このフォームを描画するために PasswordResets コントローラの <code>edit</code> アクションを更新する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PasswordResetsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:get_user</span><span class="p">,</span>   <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:valid_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">edit</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_user</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:email</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 正しいユーザーかどうか確認する</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">valid_user</span>
</span></span><span class="line"><span class="cl">      <span class="k">unless</span> <span class="p">(</span><span class="vi">@user</span> <span class="o">&amp;&amp;</span> <span class="vi">@user</span><span class="o">.</span><span class="n">activated?</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">              <span class="vi">@user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="ss">:reset</span><span class="p">,</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1232-パスワードを更新する">12.3.2 パスワードを更新する</h3>
<ul>
<li>フォームから新しいパスワードが送信されるので、それに対応する <code>update</code> アクションが必要
<ul>
<li>以下のケースを考慮する
<ul>
<li>パスワード再設定の有効期限が切れていないか</li>
<li>無効なパスワードであれば失敗させる</li>
<li>新しいパスワードが空文字になっていないか</li>
<li>新しいパスワードが正しければ更新する</li>
</ul>
</li>
</ul>
</li>
<li>パスワード再設定の有効期限切れチェックを除くと以下のようになる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PasswordResetsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:get_user</span><span class="p">,</span>         <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:valid_user</span><span class="p">,</span>       <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:check_expiration</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>    <span class="c1"># (1) への対応</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:password_reset</span><span class="o">][</span><span class="ss">:email</span><span class="o">].</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span><span class="o">.</span><span class="n">create_reset_digest</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span><span class="o">.</span><span class="n">send_password_reset_email</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:info</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Email sent with password reset instructions&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">.</span><span class="n">now</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Email address not found&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">edit</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">update</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">params</span><span class="o">[</span><span class="ss">:user</span><span class="o">][</span><span class="ss">:password</span><span class="o">].</span><span class="n">empty?</span>                  <span class="c1"># (3) への対応</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="ss">:password</span><span class="p">,</span> <span class="ss">:blank</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;edit&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">elsif</span> <span class="vi">@user</span><span class="o">.</span><span class="n">update_attributes</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>          <span class="c1"># (4) への対応</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Password has been reset.&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;edit&#39;</span>                                     <span class="c1"># (2) への対応</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:password</span><span class="p">,</span> <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># beforeフィルタ</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">get_user</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:email</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 有効なユーザーかどうか確認する</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">valid_user</span>
</span></span><span class="line"><span class="cl">      <span class="k">unless</span> <span class="p">(</span><span class="vi">@user</span> <span class="o">&amp;&amp;</span> <span class="vi">@user</span><span class="o">.</span><span class="n">activated?</span> <span class="o">&amp;&amp;</span>
</span></span><span class="line"><span class="cl">              <span class="vi">@user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="ss">:reset</span><span class="p">,</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">    <span class="c1"># トークンが期限切れかどうか確認する</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">check_expiration</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">password_reset_expired?</span>
</span></span><span class="line"><span class="cl">        <span class="n">flash</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Password reset has expired.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">redirect_to</span> <span class="n">new_password_reset_url</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>User モデルにパスワード再設定用メソッドを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># パスワード再設定の期限が切れている場合はtrueを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">password_reset_expired?</span>
</span></span><span class="line"><span class="cl">    <span class="n">reset_sent_at</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="o">.</span><span class="n">hours</span><span class="o">.</span><span class="n">ago</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1233-パスワードの再設定をテストする">12.3.3 パスワードの再設定をテストする</h3>
<ul>
<li>パスワード再設定に成功した場合と失敗した場合の統合テストを作成する</li>
<li>最初は forgot password フォームを表示して無効なメールアドレスを送信し、次はそのフォームで有効なメールアドレスを送信する
<ul>
<li>後者ではパスワード再設定用トークンが作成され、再設定用メールが送信される</li>
</ul>
</li>
<li>メールのリンクを開いて無効な情報を送信し、そのリンクから有効な情報を送信する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">PasswordResetsTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="no">ActionMailer</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">deliveries</span><span class="o">.</span><span class="n">clear</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;password resets&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">new_password_reset_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;password_resets/new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># メールアドレスが無効</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="n">password_resets_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">password_reset</span><span class="p">:</span> <span class="p">{</span> <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;password_resets/new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># メールアドレスが有効</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="n">password_resets_path</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">         <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">password_reset</span><span class="p">:</span> <span class="p">{</span> <span class="ss">email</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">email</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not_equal</span> <span class="vi">@user</span><span class="o">.</span><span class="n">reset_digest</span><span class="p">,</span> <span class="vi">@user</span><span class="o">.</span><span class="n">reload</span><span class="o">.</span><span class="n">reset_digest</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="mi">1</span><span class="p">,</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">deliveries</span><span class="o">.</span><span class="n">size</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># パスワード再設定フォームのテスト</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="n">assigns</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># メールアドレスが無効</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_password_reset_path</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">reset_token</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 無効なユーザー</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">toggle!</span><span class="p">(</span><span class="ss">:activated</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_password_reset_path</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">reset_token</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">toggle!</span><span class="p">(</span><span class="ss">:activated</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># メールアドレスが有効で、トークンが無効</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_password_reset_path</span><span class="p">(</span><span class="s1">&#39;wrong token&#39;</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># メールアドレスもトークンも有効</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_password_reset_path</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">reset_token</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;password_resets/edit&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s2">&#34;input[name=email][type=hidden][value=?]&#34;</span><span class="p">,</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 無効なパスワードとパスワード確認</span>
</span></span><span class="line"><span class="cl">    <span class="n">patch</span> <span class="n">password_reset_path</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">reset_token</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">email</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;foobaz&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;barquux&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;div#error_explanation&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># パスワードが空</span>
</span></span><span class="line"><span class="cl">    <span class="n">patch</span> <span class="n">password_reset_path</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">reset_token</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">email</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;div#error_explanation&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 有効なパスワードとパスワード確認</span>
</span></span><span class="line"><span class="cl">    <span class="n">patch</span> <span class="n">password_reset_path</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">reset_token</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">          <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">email</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;foobaz&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                            <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;foobaz&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">is_logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="124-本番環境でのメール送信再掲">12.4 本番環境でのメール送信（再掲）</h2>
<ul>
<li>SendGrid でのメール送信
<ul>
<li>クレジットカード登録が必要なので再度スキップ</li>
</ul>
</li>
</ul>
<h2 id="125-最後に">12.5 最後に</h2>
<ul>
<li>パスワード再設定の実装により、サンプルアプリケーションのユーザー登録・ログイン・ログアウトの仕組みは本物に近いレベルとなった</li>
</ul>
<h1 id="所感">所感</h1>
<p>Webサービスにパスワードリセットの機能はつきものだが、Rails での実装の一例を知ることができた。Rails に従うのが一番と考えると、一例でなくこれが全てなのかもしれない。ダイジェストやトークンを使用しての認証は当たり前のように思えるが、意外と世のサービスがこのように実装されているかどうかは疑わしいのではという気もしてきている。（前職での開発に思いを馳せながら）</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第11章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter11/</link>
			<pubDate>Tue, 24 Mar 2020 22:39:23 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter11/</guid>
			<description>第11章 アカウントの有効化 現時点で新規登録ユーザーは最初からすべての機能にアクセスできるようになっている アカウントを有効化するステップを新規</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第11章-アカウントの有効化">第11章 アカウントの有効化</h1>
<ul>
<li>現時点で新規登録ユーザーは最初からすべての機能にアクセスできるようになっている
<ul>
<li>アカウントを有効化するステップを新規登録の途中に差し込むことで、メールアドレスの有効性を確認できるようにする
<ul>
<li>有効化トークンやダイジェストを関連付けておいた状態で</li>
<li>有効化トークンを含めたリンクをユーザーにメールで送信し</li>
<li>ユーザーがそのリンクをクリックすると有効化できるようにする</li>
</ul>
</li>
</ul>
</li>
<li>アカウントを有効化する段取り
<ul>
<li>ユーザーの初期状態は unactivated にしておく</li>
<li>ユーザー登録されたときに有効化トークンとそれに対応する有効化ダイジェストを生成する</li>
<li>有効化ダイジェストは DB に保存しておき、有効化トークンはメールアドレスと一緒にユーザーに送信する有効化用メールのリンクに仕込んでおく</li>
<li>ユーザーがメールのリンクをクリックしたら、アプリケーションはメールアドレスをキーにしてユーザーを探し、DB 内に保存しておいた有効化ダイジェストと比較することでトークンを認証する</li>
<li>ユーザーを認証できたら、ユーザーのステータスを activated に変更する</li>
</ul>
</li>
</ul>
<h2 id="111-accountactivationsリソース">11.1 AccountActivationsリソース</h2>
<ul>
<li>セッション機能を使って、アカウントの有効化という作業をリソースとしてモデル化する
<ul>
<li>有効化トークンや有効化ステータスなどを User モデルに追加する</li>
</ul>
</li>
</ul>
<h3 id="1111-accountactivations-コントローラ">11.1.1 AccountActivations コントローラ</h3>
<ul>
<li>AccountActivations リソースを作るためにに、まずは AccountActivations コントローラを生成する
<ul>
<li>有効化のメールには次の URL を含めることになる</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code>edit_account_activation_url(activation_token, ...)
</code></pre><ul>
<li>上記は <code>edit</code> アクションへの名前付きルートが必要になるということ
<ul>
<li>名前付きルートを扱えるようにするために、ルーティングにアカウント有効化用の <code>resources</code> 行を追加する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span>   <span class="s1">&#39;static_pages#home&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/help&#39;</span><span class="p">,</span>    <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#help&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/about&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#about&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/contact&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#contact&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/signup&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;users#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">post</span>   <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#create&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">delete</span> <span class="s1">&#39;/logout&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#destroy&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:users</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:account_activations</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1112-accountactivation-のデータモデル">11.1.2 AccountActivation のデータモデル</h3>
<ul>
<li>有効化のメールには一意の有効化トークンが必要
<ul>
<li>仮想的属性を使ってハッシュ化した文字列を DB に保存する</li>
</ul>
</li>
<li>続いて <code>activated</code> 属性を追加して論理値を取るようにする
<ul>
<li>ユーザーが有効であるかどうかをテストできるようになる</li>
</ul>
</li>
<li>マイグレーションを実行してデータモデルを更新する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go"> rails generate migration add_activation_to_users   activation_digest:string activated:boolean activated_at:datetime
</span></span></span></code></pre></div><ul>
<li><code>activated</code> 属性のデフォルト論理値を <code>false</code> にしておいてマイグレーションを実行</li>
</ul>
<h4 id="activation-トークンのコールバック">Activation トークンのコールバック</h4>
<ul>
<li>ユーザーの新規登録時には必ずアカウントの有効化が必要になるので、有効化トークンや有効化ダイジェストはユーザーオブジェクトが作成される前に作成しておく必要がある
<ul>
<li>オブジェクトが作成されたときだけコールバックを呼びたいので <code>before_create</code> を定義する</li>
<li>コールバック内で <code>create_activation_digest</code>  というメソッド参照を定義</li>
<li>Rails は <code>create_activation_digest</code> メソッドを探し、ユーザーを作成する前に実行する</li>
<li>User モデル内でしか使用しないので <code>private</code> にする</li>
<li>コールバックでトークンとそれに対応するダイジェストを割り当てるため、記憶トークンや記憶ダイジェストのために作ったメソッドを使い回す
<ul>
<li><code>User.new</code> で新しいユーザーが定義されると、<code>activation_token</code> 属性や <code>activation_digest</code> 属性が得られるようになる</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:remember_token</span><span class="p">,</span> <span class="ss">:activation_token</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_save</span>   <span class="ss">:downcase_email</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_create</span> <span class="ss">:create_activation_digest</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span>  <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">50</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># メールアドレスをすべて小文字にする</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">downcase_email</span>
</span></span><span class="line"><span class="cl">      <span class="nb">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span><span class="o">.</span><span class="n">downcase</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 有効化トークンとダイジェストを作成および代入する</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">create_activation_digest</span>
</span></span><span class="line"><span class="cl">      <span class="nb">self</span><span class="o">.</span><span class="n">activation_token</span>  <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">      <span class="nb">self</span><span class="o">.</span><span class="n">activation_digest</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">digest</span><span class="p">(</span><span class="n">activation_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h4 id="サンプルユーザーの生成とテスト">サンプルユーザーの生成とテスト</h4>
<ul>
<li>サンプルデータと fixture も更新し、テスト前のサンプルとユーザーを事前に有効化しておく</li>
<li>DB を初期化し、サンプルデータを生成し直して変更を反映させる</li>
</ul>
<h2 id="112-アカウント有効化のメール送信">11.2 アカウント有効化のメール送信</h2>
<ul>
<li>アカウント有効化メールの送信に必要なコードを追加する
<ul>
<li>Action Mailer ライブラリを使って User のメイラーを追加する</li>
<li>Users コントローラの <code>create</code> アクションで有効化リンクをメール送信するために使用する
<ul>
<li>よくあるやつ</li>
</ul>
</li>
<li>メールのテンプレートはビューと同じ要領で定義できる
<ul>
<li>テンプレート内に有効化トークンとメールアドレスのリンクを含め使う</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="1121-送信メールのテンプレート">11.2.1 送信メールのテンプレート</h3>
<ul>
<li>メイラーはモデルやコントローラと同様に <code>rails generate</code> で生成可能</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> rails generate mailer UserMailer account_activation password_reset
</span></span></code></pre></div><ul>
<li><code>account_activation</code> メソッドと、<code>password_reset</code> メソッドが生成される
<ul>
<li>便利だなあ。。。相変わらず裏で何やっているか不明だけど</li>
<li>生成したメイラーごとにビューのテンプレートが2つずつ生成される
<ul>
<li>ひとつはテキストメール用のテンプレート</li>
<li>ひとつは HTML メール用のテンプレート</li>
</ul>
</li>
</ul>
</li>
<li>最初に生成されたテンプレートをカスタマイズして、実際に有効化メールで使えるようにする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ApplicationMailer</span> <span class="o">&lt;</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">default</span> <span class="ss">from</span><span class="p">:</span> <span class="s2">&#34;noreply@example.com&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="n">layout</span> <span class="s1">&#39;mailer&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>次に、ユーザーを含むインスタンス変数を作成してビューで使えるようにし、<code>user.email</code> にメール送信する
<ul>
<li><code>mail</code> に <code>subject</code> として渡している引数はメールの件名</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserMailer</span> <span class="o">&lt;</span> <span class="no">ApplicationMailer</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">account_activation</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="n">mail</span> <span class="ss">to</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">,</span> <span class="ss">subject</span><span class="p">:</span> <span class="s2">&#34;Account activation&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">password_reset</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@greeting</span> <span class="o">=</span> <span class="s2">&#34;Hi&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">mail</span> <span class="ss">to</span><span class="p">:</span> <span class="s2">&#34;to@example.org&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>テンプレートビューは通常のビューと同様に ERB で自由にカスタマイズ可能
<ul>
<li>挨拶文にユーザー名を含め、カスタムの有効化リンクを追加する</li>
<li>リンクにはメールアドレスとトークンの両方を含める必要がある
<ul>
<li>Rails サーバーでユーザーをメールアドレスで検索して有効化トークンを認証できるようにするため</li>
</ul>
</li>
<li>AccountActivations リソースで有効化をモデル化したので、トークン自体は名前付きルートの引数で使われる</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">edit_account_activation_url</span><span class="p">(</span><span class="vi">@user</span><span class="o">.</span><span class="n">activation_token</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
</span></span></code></pre></div><pre tabindex="0"><code>edit_user_url(user)
</code></pre><ul>
<li>上記メソッドは次の形式の URL を生成する</li>
</ul>
<pre tabindex="0"><code>http://www.example.com/users/1/edit
</code></pre><ul>
<li>これに対応するアカウント有効化リンクのベース URL は次のようになる</li>
</ul>
<pre tabindex="0"><code>http://www.example.com/account_activations/q5lt38hQDc_959PVoo6b7A/edit
</code></pre><ul>
<li>上記 URL の <code>q5lt38hQDc_959PVoo6b7A</code> は <code>new_token</code> メソッドで生成されたもので、URLで使えるように Base64 でエンコードされている
<ul>
<li>/user/1/edit の 1 のようなユーザー ID と同じ役割を果たす</li>
<li>特に AccountActivations コントローラの <code>edit</code> アクションでは <code>params</code> ハッシュで <code>params[:id]</code> として参照可能</li>
</ul>
</li>
<li>クエリパラメータを使ってこの URL にメールアドレスも組み込んでみる</li>
</ul>
<pre tabindex="0"><code>account_activations/q5lt38hQDc_959PVoo6b7A/edit?email=foo%40example.com
</code></pre><ul>
<li>メールアドレスの@記号は URL では使用できない文字のためエスケープされている</li>
<li>Rails でクエリパラメータを設定するには、名前付きルートに対して次のようなハッシュを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">edit_account_activation_url</span><span class="p">(</span><span class="vi">@user</span><span class="o">.</span><span class="n">activation_token</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">email</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>名前付きルートでクエリパラメータを定義すると、Rails が特殊な文字を自動的にエスケープしてくれる
<ul>
<li>コントローラで <code>params[:email]</code> からメールアドレスを取り出すときは、自動的にエスケープを解除してくれる
<ul>
<li>いたれりつくせり</li>
</ul>
</li>
</ul>
</li>
<li>以上を組み合わせてアカウント有効化のテキストビューを作成する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">Hi &lt;%= @user.name %&gt;,

Welcome to the Sample App! Click on the link below to activate your account:

&lt;%= edit_account_activation_url(@user.activation_token, email: @user.email) %&gt;
</code></pre><ul>
<li>同様にアカウント有効化の HTML ビューも作成する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;h1&gt;Sample App&lt;/h1&gt;

&lt;p&gt;Hi &lt;%= @user.name %&gt;,&lt;/p&gt;

&lt;p&gt;
Welcome to the Sample App! Click on the link below to activate your account:
&lt;/p&gt;

&lt;%= link_to &#34;Activate&#34;, edit_account_activation_url(@user.activation_token,
                                                    email: @user.email) %&gt;
</code></pre><h3 id="1122-送信メールのプレビュー">11.2.2 送信メールのプレビュー</h3>
<ul>
<li>Rails では特殊な URL にアクセスするとメールのメッセージをプレビューすることができる
<ul>
<li>な、なんだってー</li>
<li>アプリケーションの development 環境の設定を変更する必要がある</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">configure</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">action_mailer</span><span class="o">.</span><span class="n">raise_delivery_errors</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">action_mailer</span><span class="o">.</span><span class="n">delivery_method</span> <span class="o">=</span> <span class="ss">:test</span>
</span></span><span class="line"><span class="cl">  <span class="n">host</span> <span class="o">=</span> <span class="s1">&#39;example.com&#39;</span> <span class="c1"># ここをコピペすると失敗します。自分の環境に合わせてください。</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">action_mailer</span><span class="o">.</span><span class="n">default_url_options</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">host</span><span class="p">:</span> <span class="n">host</span><span class="p">,</span> <span class="ss">protocol</span><span class="p">:</span> <span class="s1">&#39;https&#39;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>自動生成した User メイラーのプレビューファイルの更新も必要</li>
<li>自動生成コードのままでは動作しないので、<code>user</code> 変数が開発用データベースの最初のユーザーになるように定義し、それを <code>UserMailer.account_activation</code> の引数として渡す</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># Preview all emails at http://localhost:3000/rails/mailers/user_mailer</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserMailerPreview</span> <span class="o">&lt;</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">Preview</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Preview this email at</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># http://localhost:3000/rails/mailers/user_mailer/account_activation</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">account_activation</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">activation_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="no">UserMailer</span><span class="o">.</span><span class="n">account_activation</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># Preview this email at</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># http://localhost:3000/rails/mailers/user_mailer/password_reset</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">password_reset</span>
</span></span><span class="line"><span class="cl">    <span class="no">UserMailer</span><span class="o">.</span><span class="n">password_reset</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>以上でプレビュー可能になる
<ul>
<li>摩訶不思議</li>
</ul>
</li>
</ul>
<h3 id="1123-送信メールのテスト">11.2.3 送信メールのテスト</h3>
<ul>
<li>メールプレビューのテストを追加する
<ul>
<li>プレビューのテストに違和感</li>
<li>Rails によってテストは自動生成されている
<ul>
<li>おいおいおい</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserMailerTest</span> <span class="o">&lt;</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;account_activation&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">mail</span> <span class="o">=</span> <span class="no">UserMailer</span><span class="o">.</span><span class="n">account_activation</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="s2">&#34;Account activation&#34;</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">subject</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="s2">&#34;to@example.org&#34;</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">to</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="s2">&#34;from@example.com&#34;</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">from</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="s2">&#34;Hi&#34;</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;password_reset&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">mail</span> <span class="o">=</span> <span class="no">UserMailer</span><span class="o">.</span><span class="n">password_reset</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="s2">&#34;Password reset&#34;</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">subject</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="s2">&#34;to@example.org&#34;</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">to</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="s2">&#34;from@example.com&#34;</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">from</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="s2">&#34;Hi&#34;</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>assert_match</code> という非常に強力なメソッド
<ul>
<li>正規表現で文字列をテストできる
<ul>
<li>つよい</li>
</ul>
</li>
<li>テスト用ユーザーのメールアドレスをエスケープすることも可能</li>
</ul>
</li>
<li>現在のメールの実装をテストする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserMailerTest</span> <span class="o">&lt;</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;account_activation&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">activation_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="n">mail</span> <span class="o">=</span> <span class="no">UserMailer</span><span class="o">.</span><span class="n">account_activation</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="s2">&#34;Account activation&#34;</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">subject</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">to</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="o">[</span><span class="s2">&#34;noreply@example.com&#34;</span><span class="o">]</span><span class="p">,</span> <span class="n">mail</span><span class="o">.</span><span class="n">from</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="n">user</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>               <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="n">user</span><span class="o">.</span><span class="n">activation_token</span><span class="p">,</span>   <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_match</span> <span class="no">CGI</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">),</span>  <span class="n">mail</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">encoded</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>このテストをパスするためには、テストファイル内のドメイン名を正しく設定する必要がある</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">configure</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">action_mailer</span><span class="o">.</span><span class="n">delivery_method</span> <span class="o">=</span> <span class="ss">:test</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">action_mailer</span><span class="o">.</span><span class="n">default_url_options</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">host</span><span class="p">:</span> <span class="s1">&#39;example.com&#39;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1124-ユーザーの-create-アクションを更新">11.2.4 ユーザーの create アクションを更新</h3>
<ul>
<li>ユーザー登録の <code>create</code> アクションに数行追加するだけで、メイラーをアプリケーションで実際に使うことが可能</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">      <span class="no">UserMailer</span><span class="o">.</span><span class="n">account_activation</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span><span class="o">.</span><span class="n">deliver_now</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:info</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Please check your email to activate your account.&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>リダイレクト先をプロフィールページからルート URL に変更し、かつユーザーは以前のようにログインしないようになっている
<ul>
<li>テストが失敗するので該当箇所をひとまずコメントアウト（あとで直す）</li>
</ul>
</li>
<li>ユーザーを実際に登録してみると、メールが生成される（送信はされない）</li>
</ul>
<h2 id="113-アカウントを有効化する">11.3 アカウントを有効化する</h2>
<ul>
<li>メールが生成できたので、次は AccountActivations コントローラの <code>edit</code> アクションを書いていく</li>
</ul>
<h3 id="1131-authenticated-メソッドの抽象化">11.3.1 authenticated? メソッドの抽象化</h3>
<ul>
<li>有効化トークンとメールはそれぞれ <code>params[:id]</code> と <code>params[:email]</code> で参照できるので、次のようなコードでユーザーを検索して認証する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:email</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="ss">:activation</span><span class="p">,</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li><code>authenticated?</code> メソッドは、アカウント有効化のダイジェストと渡されたトークンが一致するかどうかをチェックする
<ul>
<li>以下のメソッドは記憶トークン用なのでまだ正常に動作はしない</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># トークンがダイジェストと一致したらtrueを返す</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">authenticated?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kp">false</span> <span class="k">if</span> <span class="n">remember_digest</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">  <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">remember_digest</span><span class="p">)</span><span class="o">.</span><span class="n">is_password?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>remember_digest</code> は User モデルの属性なので、モデル内では次のように置き換えることができる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">self</span><span class="o">.</span><span class="n">remember_digest</span>
</span></span></code></pre></div><ul>
<li>今回は、上記コードの remember 部分を変数として扱いたい
<ul>
<li>すなわち状況に応じて呼ぶメソッドを切り替えたい</li>
</ul>
</li>
<li><code>authenticated?</code> メソッドでは、受け取ったパラメータに応じて呼び出すメソッドを切り替える
<ul>
<li>メタプログラミング
<ul>
<li>「Rails の一見魔法のような機能（黒魔術とも呼ばれます）の多くは、Ruby のメタプログラミングによって実現されています。」
<ul>
<li>黒魔術（公式）</li>
</ul>
</li>
<li>渡されたオブジェクトに「メッセージを送る」ことによって呼び出すメソッドを動的に決めることができる <code>send</code> メソッドを使う
<ul>
<li>やりすぎでは</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><code>send</code> メソッドの仕組みを利用して、<code>authenticated?</code> メソッドを書き換える</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">authenticated?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">digest</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">&#34;remember_digest&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kp">false</span> <span class="k">if</span> <span class="n">digest</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">  <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">digest</span><span class="p">)</span><span class="o">.</span><span class="n">is_password?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>各引数を一般化し、文字列の式展開も利用する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">authenticated?</span><span class="p">(</span><span class="n">attribute</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">digest</span> <span class="o">=</span> <span class="nb">self</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">attribute</span><span class="si">}</span><span class="s2">_digest&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kp">false</span> <span class="k">if</span> <span class="n">digest</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">  <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">digest</span><span class="p">)</span><span class="o">.</span><span class="n">is_password?</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>モデル内にあるコードなので <code>selef</code> は省略可能</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">authenticated?</span><span class="p">(</span><span class="n">attribute</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">digest</span> <span class="o">=</span> <span class="nb">send</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">attribute</span><span class="si">}</span><span class="s2">_digest&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="kp">false</span> <span class="k">if</span> <span class="n">digest</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">  <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">digest</span><span class="p">)</span><span class="o">.</span><span class="n">is_password?</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>次のようにして <code>authenticated?</code> の従来の振る舞いを再現できる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="ss">:remember</span><span class="p">,</span> <span class="n">remember_token</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>以上を実際の User モデルに適用する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># トークンがダイジェストと一致したらtrueを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">authenticated?</span><span class="p">(</span><span class="n">attribute</span><span class="p">,</span> <span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">digest</span> <span class="o">=</span> <span class="nb">send</span><span class="p">(</span><span class="s2">&#34;</span><span class="si">#{</span><span class="n">attribute</span><span class="si">}</span><span class="s2">_digest&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kp">false</span> <span class="k">if</span> <span class="n">digest</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">    <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">digest</span><span class="p">)</span><span class="o">.</span><span class="n">is_password?</span><span class="p">(</span><span class="n">token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>従来のテストが失敗するので、新しい <code>authenticated?</code> メソッドを使用するように修正する</li>
<li><code>current_user</code> 内の抽象化した <code>authenticated?</code> メソッドを修正</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 現在ログイン中のユーザーを返す (いる場合)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">current_user</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@current_user</span> <span class="o">||=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elsif</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">cookies</span><span class="o">.</span><span class="n">signed</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="ss">:remember</span><span class="p">,</span> <span class="n">cookies</span><span class="o">[</span><span class="ss">:remember_token</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">        <span class="vi">@current_user</span> <span class="o">=</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>User テスト内の抽象化した <code>authenticated?</code> メソッドを修正</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Example User&#34;</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;user@example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;foobar&#34;</span><span class="p">,</span> <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;foobar&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;authenticated? should return false for a user with nil digest&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="ss">:remember</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1132-edit-アクションで有効化">11.3.2 edit アクションで有効化</h3>
<ul>
<li><code>edit</code> アクションでは <code>params</code> ハッシュで渡されたメールアドレスに対応するユーザーを認証する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">AccountActivationsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">edit</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:email</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">user</span><span class="o">.</span><span class="n">activated?</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="ss">:activation</span><span class="p">,</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">user</span><span class="o">.</span><span class="n">update_attribute</span><span class="p">(</span><span class="ss">:activated</span><span class="p">,</span>    <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">user</span><span class="o">.</span><span class="n">update_attribute</span><span class="p">(</span><span class="ss">:activated_at</span><span class="p">,</span> <span class="no">Time</span><span class="o">.</span><span class="n">zone</span><span class="o">.</span><span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Account activated!&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Invalid activation link&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>!user.activated?</code> は既に有効になっているユーザーを誤って再度有効化しないために必要
<ul>
<li>正当であろうとなかろうと、有効化が行われるユーザーはログイン状態になる</li>
</ul>
</li>
<li>この時点ではユーザーのログイン方法を変更していないため、ユーザーの有効化にはまだなんの意味もない
<ul>
<li>ユーザーが有効である場合にのみログインできるように修正する必要がある</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:email</span><span class="o">].</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:password</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">user</span><span class="o">.</span><span class="n">activated?</span>
</span></span><span class="line"><span class="cl">        <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">        <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:remember_me</span><span class="o">]</span> <span class="o">==</span> <span class="s1">&#39;1&#39;</span> <span class="p">?</span> <span class="n">remember</span><span class="p">(</span><span class="n">user</span><span class="p">)</span> <span class="p">:</span> <span class="n">forget</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">redirect_back_or</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">message</span>  <span class="o">=</span> <span class="s2">&#34;Account not activated. &#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">message</span> <span class="o">+=</span> <span class="s2">&#34;Check your email for the activation link.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">flash</span><span class="o">[</span><span class="ss">:warning</span><span class="o">]</span> <span class="o">=</span> <span class="n">message</span>
</span></span><span class="line"><span class="cl">        <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">.</span><span class="n">now</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;Invalid email/password combination&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_out</span> <span class="k">if</span> <span class="n">logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1133-有効化のテストとリファクタリング">11.3.3 有効化のテストとリファクタリング</h3>
<ul>
<li>ユーザー登録のテストにアカウント有効化のテストを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersSignupTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="no">ActionMailer</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">deliveries</span><span class="o">.</span><span class="n">clear</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;invalid signup information&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">signup_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;User.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">users_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="nb">name</span><span class="p">:</span>  <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;user@invalid&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;foo&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;bar&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;div#error_explanation&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;div.field_with_errors&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;valid signup information with account activation&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">signup_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;User.count&#39;</span><span class="p">,</span> <span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">users_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="nb">name</span><span class="p">:</span>  <span class="s2">&#34;Example User&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;user@example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;password&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="mi">1</span><span class="p">,</span> <span class="no">ActionMailer</span><span class="o">::</span><span class="no">Base</span><span class="o">.</span><span class="n">deliveries</span><span class="o">.</span><span class="n">size</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="n">assigns</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">user</span><span class="o">.</span><span class="n">activated?</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 有効化していない状態でログインしてみる</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">is_logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 有効化トークンが不正な場合</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_account_activation_path</span><span class="p">(</span><span class="s2">&#34;invalid token&#34;</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">is_logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># トークンは正しいがメールアドレスが無効な場合</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_account_activation_path</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">activation_token</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="s1">&#39;wrong&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">is_logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 有効化トークンが正しい場合</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_account_activation_path</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">activation_token</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">user</span><span class="o">.</span><span class="n">reload</span><span class="o">.</span><span class="n">activated?</span>
</span></span><span class="line"><span class="cl">    <span class="n">follow_redirect!</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/show&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">is_logged_in?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ユーザー操作の一部をコントローラからモデルに移動するリファクタリングを行う
<ul>
<li><code>activate</code> メソッドを作成してユーザーの有効化属性を更新し、<code>send_activation_email</code> メソッドを作成して有効化メールを送信する</li>
</ul>
</li>
<li>User モデルにユーザー有効化メソッドを追加</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># アカウントを有効にする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">activate</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:activated</span><span class="p">,</span>    <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:activated_at</span><span class="p">,</span> <span class="no">Time</span><span class="o">.</span><span class="n">zone</span><span class="o">.</span><span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 有効化用のメールを送信する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">send_activation_email</span>
</span></span><span class="line"><span class="cl">    <span class="no">UserMailer</span><span class="o">.</span><span class="n">account_activation</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span><span class="o">.</span><span class="n">deliver_now</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ユーザーモデルオブジェクトからメールを送信する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span><span class="o">.</span><span class="n">send_activation_email</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:info</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Please check your email to activate your account.&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ユーザーモデルオブジェクト経由でアカウントを有効化する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">AccountActivationsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">edit</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:email</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">user</span><span class="o">.</span><span class="n">activated?</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="ss">:activation</span><span class="p">,</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">user</span><span class="o">.</span><span class="n">activate</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Account activated!&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Invalid activation link&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="114-本番環境でのメール送信">11.4 本番環境でのメール送信</h2>
<ul>
<li>production 環境で実際にメールを送信できるようにしてみる</li>
<li>SendGrid という Heroku アドオンを利用してアカウントを検証
<ul>
<li>Heroku アカウントにクレジットカードを設定する必要があるので割愛</li>
</ul>
</li>
<li>starter tier というサービスを利用
<ul>
<li>production 環境の SMTP に情報を記入する必要がある</li>
</ul>
</li>
<li>Heroku にデプロイして完了（の体）</li>
</ul>
<h2 id="115-最後に">11.5 最後に</h2>
<ul>
<li>アカウント有効化を実装しったので、サンプルアプリケーションのユーザー登録、ログイン、ログアウトの仕組みがほぼ完成した</li>
<li>最後はパスワード再設定機能
<ul>
<li>アカウント有効かと仕組みは似ている</li>
</ul>
</li>
</ul>
<h1 id="所感">所感</h1>
<p>メールを用いたアカウントの有効化という、Webサービスでよくある実装を行った。複雑な実装をしなければいけないかと思いきや、ここでも Rails に乗っかるだけで簡単に実装できてしまった。裏側はどうなっているのか。。。</p>
<p>この章での醍醐味はやはりメタプログラミングだろう。Ruby のメタプログラミングに初めて触れたのだが、静的型付け言語原理主義者としては、動的にメソッドを切り替えるという言語仕様はなかなか受け入れられるものではない。しかしこの言語仕様が自由度の高いプログラミングを可能にしているのも事実で、Rails の黒魔術を支えているのだと実感できた。コードが壊れやすくなるとは思うが、この自由な世界からプログラミングを始めた人にとっては、静的型付け言語は自由度が低く、毎度ビルドしないといけない非常に扱いづらい言語になるのだろうなあと思えた。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第10章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter10/</link>
			<pubDate>Thu, 12 Mar 2020 11:03:39 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter10/</guid>
			<description>第10章 ユーザーの更新・表示・削除 User リソース用の REST アクションのうち、未実装だった edit update index destroy アクションを加え、REST を完成させる 10.1 ユーザーを更</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第10章-ユーザーの更新表示削除">第10章 ユーザーの更新・表示・削除</h1>
<ul>
<li>User リソース用の REST アクションのうち、未実装だった <code>edit</code> <code>update</code> <code>index</code> <code>destroy</code> アクションを加え、REST を完成させる</li>
</ul>
<h2 id="101-ユーザーを更新する">10.1 ユーザーを更新する</h2>
<ul>
<li>ユーザー情報を編集するには、新規ユーザー用のビューを出力する <code>new</code> アクションと同様に、ユーザーを編集するためのアクションを作成する</li>
<li>同様に、POST リクエストに応答する <code>create</code> の代わりに、PATCH リクエストに応答する <code>update</code> アクションを作成する</li>
</ul>
<h3 id="1011-編集フォーム">10.1.1 編集フォーム</h3>
<ul>
<li>Users コントローラに <code>edit</code> アクションを追加し、それに対応する edit ビューを実装する</li>
<li>ユーザーの id は <code>params[:id]</code> 変数で取り出すことが可能</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">show</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Welcome to the Sample App!&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">edit</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ユーザーの edit ビューを作成</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#34;Edit user&#34;) %&gt;
&lt;h1&gt;Update your profile&lt;/h1&gt;

&lt;div class=&#34;row&#34;&gt;
  &lt;div class=&#34;col-md-6 col-md-offset-3&#34;&gt;
    &lt;%= form_for(@user) do |f| %&gt;
      &lt;%= render &#39;shared/error_messages&#39; %&gt;

      &lt;%= f.label :name %&gt;
      &lt;%= f.text_field :name, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :email %&gt;
      &lt;%= f.email_field :email, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :password %&gt;
      &lt;%= f.password_field :password, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :password_confirmation, &#34;Confirmation&#34; %&gt;
      &lt;%= f.password_field :password_confirmation, class: &#39;form-control&#39; %&gt;

      &lt;%= f.submit &#34;Save changes&#34;, class: &#34;btn btn-primary&#34; %&gt;
    &lt;% end %&gt;

    &lt;div class=&#34;gravatar_edit&#34;&gt;
      &lt;%= gravatar_for @user %&gt;
      &lt;a href=&#34;http://gravatar.com/emails&#34; target=&#34;_blank&#34;&gt;change&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre><ul>
<li>Rails によって <code>@user</code> 変数の属性情報が引き出され、名前やメールアドレスのフィールドに値が自動入力される</li>
<li>Web ブラウザではネイティブで PATCH リクエストを送信できないので、Rails では POST リクエストと隠し <code>input</code> フィールドを利用して PATCH リクエストを偽造している
<ul>
<li>めちゃ微妙。。。</li>
</ul>
</li>
<li>Rails は <code>form_for(@user)</code> を使用してフォームを構成すると、<code>@user.new_record?</code> が <code>true</code> のときには POST を、<code>false</code> のときには PATCH を使う
<ul>
<li>これまた微妙。。。</li>
</ul>
</li>
</ul>
<h3 id="1012-編集の失敗">10.1.2 編集の失敗</h3>
<ul>
<li>ユーザー情報の編集に失敗した場合</li>
<li>ユーザーの <code>update</code> アクションの初期実装</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">show</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Welcome to the Sample App!&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">edit</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">update</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">update_attributes</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># 更新に成功した場合を扱う。</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;edit&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>update_attributes</code> への呼び出しで <code>user_params</code> を使っている
<ul>
<li>Strong Parameters を使ってマスアサインメントの脆弱性を防止している</li>
</ul>
</li>
</ul>
<h3 id="1013-編集失敗時のテスト">10.1.3 編集失敗時のテスト</h3>
<ul>
<li>いつものように統合テストを書いていく
<ul>
<li>まず編集ページにアクセスし、edit ビューが描画されるかをチェック</li>
<li>その後無効な情報を送信し、edit ビューが再描画されるかをチェック</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersEditTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;unsuccessful edit&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/edit&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">patch</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">),</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="nb">name</span><span class="p">:</span>  <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;foo@invalid&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;foo&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;bar&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/edit&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1014-tdd-で編集を成功させる">10.1.4 TDD で編集を成功させる</h3>
<ul>
<li>編集フォームが動作するようにするために統合テストも TDD で実装していく</li>
<li>ユーザー情報を更新する正しい振る舞い
<ul>
<li>flash メッセージが空でないか</li>
<li>プロフィールページにリダイレクトされるか</li>
<li>DB 内のユーザー情報が正しく変更されたか</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersEditTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;successful edit&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/edit&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">name</span>  <span class="o">=</span> <span class="s2">&#34;Foo Bar&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">email</span> <span class="o">=</span> <span class="s2">&#34;foo@bar.com&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">patch</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">),</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="nb">name</span><span class="p">:</span>  <span class="nb">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">email</span><span class="p">:</span> <span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">reload</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="nb">name</span><span class="p">,</span>  <span class="vi">@user</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="n">email</span><span class="p">,</span> <span class="vi">@user</span><span class="o">.</span><span class="n">email</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記テストをパスするためにユーザーの <code>update</code> アクションを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">update</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">update_attributes</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Profile updated&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;edit&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>この段階ではまだテストは通らない
<ul>
<li>パスワードのバリデーションに対して、空だった場合の例外処理を加える必要がある</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:remember_token</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_save</span> <span class="p">{</span> <span class="nb">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span><span class="o">.</span><span class="n">downcase</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">50</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">VALID_EMAIL_REGEX</span> <span class="o">=</span> <span class="sr">/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">255</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">format</span><span class="p">:</span> <span class="p">{</span> <span class="ss">with</span><span class="p">:</span> <span class="no">VALID_EMAIL_REGEX</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="ss">uniqueness</span><span class="p">:</span> <span class="p">{</span> <span class="ss">case_sensitive</span><span class="p">:</span> <span class="kp">false</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_secure_password</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">minimum</span><span class="p">:</span> <span class="mi">6</span> <span class="p">},</span> <span class="ss">allow_nil</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記修正により新規ユーザー登録時にからのパスワードが有効になってしまうのではという危惧
<ul>
<li><code>has_secure_password</code> でオブジェクト生成時に存在性を検証するようになっているため、空のパスワードが新規ユーザー登録時に有効になることはない</li>
</ul>
</li>
</ul>
<h2 id="102-認可">10.2 認可</h2>
<ul>
<li>認証（authentication）
<ul>
<li>サイトのユーザーを識別すること</li>
</ul>
</li>
<li>認可（ahtuorization）
<ul>
<li>そのユーザーが実行可能な操作を管理すること</li>
</ul>
</li>
<li>本章の edit アクションと update アクションにおけるセキュリティホール
<ul>
<li>どのユーザーでもあらゆるアクションにアクセスできるため、ログインしていないユーザーでもユーザー情報を編集できてしまう
<ul>
<li>ユーザーにログインを要求し、かつ自分以外のユーザー情報を変更できないように制御する</li>
</ul>
</li>
</ul>
</li>
<li>ログインしていないユーザーが保護されたページにアクセスしようとしたとき
<ul>
<li>ログインページに転送する
<ul>
<li>よくあるやつ</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="1021-ユーザーにログインを要求する">10.2.1 ユーザーにログインを要求する</h3>
<ul>
<li>Users コントローラの中で before フィルターを使用する
<ul>
<li><code>before_action</code> メソッドで何らかの処理が実行される直前に特定のメソッドを実行する</li>
<li>before フィルターに <code>logged_in_user</code> を追加する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># beforeアクション</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># ログイン済みユーザーかどうか確認</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">logged_in_user</span>
</span></span><span class="line"><span class="cl">      <span class="k">unless</span> <span class="n">logged_in?</span>
</span></span><span class="line"><span class="cl">        <span class="n">flash</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Please log in.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">redirect_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>edit アクションや update アクションでログインを要求するようになっため、テストが失敗する
<ul>
<li>上記アクションをテストする前にログインしておく必要がある</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersEditTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;unsuccessful edit&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;successful edit&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>テストは通るようになったが、セキュリティモデルに関する実装を覗いてもテストが通ってしまう
<ul>
<li>before フィルターをコメントアウトしてセキュリティホールが作られたときにはテストで検出できるようになっているべき</li>
</ul>
</li>
<li>before フィルターは基本的にアクションごとに適用していくので、Users コントローラのテストもアクションごとに書いていく</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect edit when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect update when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">patch</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">),</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="nb">name</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">email</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">email</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1022-正しいユーザーを要求する">10.2.2 正しいユーザーを要求する</h3>
<ul>
<li>ログインを要求するだけでは不十分で、ユーザーが自分の情報だけを編集できるようにする必要がある
<ul>
<li>例によって TDD で</li>
</ul>
</li>
<li>まずはユーザーの情報が互いに編集できないことを確認するために、サンプルユーザーをもう一人追加する</li>
<li>次に <code>log_in_as</code> メソッドを使って、<code>edit</code> アクションと <code>update</code> アクションをテストする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span>       <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@other_user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect edit when logged in as wrong user&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@other_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect update when logged in as wrong user&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@other_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">patch</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">),</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="nb">name</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">email</span><span class="p">:</span> <span class="vi">@user</span><span class="o">.</span><span class="n">email</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>別のユーザーのプロフィールを編集しようとした場合はリダイレクトさせたいので、<code>current_user</code> というメソッドを作成し、 before フィルターからこのメソッドを呼び出せるようにする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:correct_user</span><span class="p">,</span>   <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">edit</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">update</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">update_attributes</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Profile updated&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;edit&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># beforeアクション</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># ログイン済みユーザーかどうか確認</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">logged_in_user</span>
</span></span><span class="line"><span class="cl">      <span class="k">unless</span> <span class="n">logged_in?</span>
</span></span><span class="line"><span class="cl">        <span class="n">flash</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Please log in.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">redirect_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 正しいユーザーかどうか確認</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">correct_user</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span><span class="p">(</span><span class="n">root_url</span><span class="p">)</span> <span class="k">unless</span> <span class="vi">@user</span> <span class="o">==</span> <span class="n">current_user</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>一般的な慣習に倣って <code>current_user?</code> という論理値を返すメソッドを Session ヘルパーに追加し、しれを呼ぶようにリファクタリングを行う
<ul>
<li>だから慣習って何よ。。。</li>
</ul>
</li>
</ul>
<h3 id="1023-フレンドリーフォワーディング">10.2.3 フレンドリーフォワーディング</h3>
<ul>
<li>保護されたページにアクセスしようとすると、問答無用で自分のプロフィールページに移動させられてしまう
<ul>
<li>リダイレクト先はユーザーが開こうとしていたページにしてあげるのが親切</li>
</ul>
</li>
<li>フレンドリーフォワーディングのテストはシンプルに書くことができる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersEditTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;successful edit with friendly forwarding&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">edit_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">edit_user_url</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">name</span>  <span class="o">=</span> <span class="s2">&#34;Foo Bar&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">email</span> <span class="o">=</span> <span class="s2">&#34;foo@bar.com&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">patch</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">),</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="nb">name</span><span class="p">:</span>  <span class="nb">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">email</span><span class="p">:</span> <span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                              <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">reload</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="nb">name</span><span class="p">,</span>  <span class="vi">@user</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="n">email</span><span class="p">,</span> <span class="vi">@user</span><span class="o">.</span><span class="n">email</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ユーザーを希望のページに転送するには、リクエスト時点のページをどこかに保存しておき、その場所にリダイレクトさせる必要がある
<ul>
<li><code>store_location</code> と <code>redirect_back_or</code> の2つのメソッドを使って実現してみる</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 記憶したURL (もしくはデフォルト値) にリダイレクト</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">redirect_back_or</span><span class="p">(</span><span class="n">default</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span><span class="p">(</span><span class="n">session</span><span class="o">[</span><span class="ss">:forwarding_url</span><span class="o">]</span> <span class="o">||</span> <span class="n">default</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="ss">:forwarding_url</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># アクセスしようとしたURLを覚えておく</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">store_location</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">[</span><span class="ss">:forwarding_url</span><span class="o">]</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">original_url</span> <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">get?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>store_location</code> メソッドで before フィルター（ <code>logged_in_user</code> ）を修正してみる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:correct_user</span><span class="p">,</span>   <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">edit</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># beforeアクション</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># ログイン済みユーザーかどうか確認</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">logged_in_user</span>
</span></span><span class="line"><span class="cl">      <span class="k">unless</span> <span class="n">logged_in?</span>
</span></span><span class="line"><span class="cl">        <span class="n">store_location</span>
</span></span><span class="line"><span class="cl">        <span class="n">flash</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Please log in.&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">redirect_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 正しいユーザーかどうか確認</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">correct_user</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span><span class="p">(</span><span class="n">root_url</span><span class="p">)</span> <span class="k">unless</span> <span class="n">current_user?</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>フォワーディング自体を実装するには、<code>redirect_back_or</code> メソッドを使用する
<ul>
<li>リクエストされた URL が存在する場合はそこにリダイレクトし、ない場合は何らかのデフォルトの URL にリダイレクトする</li>
<li>デフォルトの URL は、Session コントローラの <code>create</code> アクションに追加し、サインイン成功後にリダイレクトする</li>
</ul>
</li>
<li><code>session.delete(:forwarding_url)</code> という行を通して転送用の URL を削除している
<ul>
<li>次回ログイン時に保護されたページに転送されるのを防ぐため</li>
<li>転送用の URL を削除する動作は <code>redirect</code> 文の後に置かれていても実行される
<ul>
<li>明示的に <code>return</code> 文やメソッド内の最終行が呼び出されない限り、リダイレクトは発生しない
<ul>
<li>思わぬバグを踏みそうな仕様だ。。。</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:email</span><span class="o">].</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:password</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:remember_me</span><span class="o">]</span> <span class="o">==</span> <span class="s1">&#39;1&#39;</span> <span class="p">?</span> <span class="n">remember</span><span class="p">(</span><span class="n">user</span><span class="p">)</span> <span class="p">:</span> <span class="n">forget</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_back_or</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">.</span><span class="n">now</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;Invalid email/password combination&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>統合テストがパスし、基本ユーザー認証機能とページ保護機能の実装が完了
<ul>
<li>対して実装していないがこの機能が実現できる凄さよ</li>
</ul>
</li>
</ul>
<h2 id="103-すべてのユーザーを表示する">10.3 すべてのユーザーを表示する</h2>
<ul>
<li>すべてのユーザーを一覧表示する <code>index</code> アクションを追加する</li>
</ul>
<h3 id="1031-ユーザーの一覧ページ">10.3.1 ユーザーの一覧ページ</h3>
<ul>
<li>ユーザーの <code>show</code> ページについてはすべてのユーザーから見えるようにしておくが、ユーザーの <code>index</code> ページはログインしたユーザーにしか見せないようにし、未登録ユーザーがデフォルトで表示できるページを制限する</li>
<li>まずは <code>index</code> アクションのリダイレクトをテストする
<ul>
<li>例によって TDD</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span>       <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@other_user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect index when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">users_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>before フィルターの <code>logged_in_user</code> に <code>index</code> アクションを追加し、このアクションを保護する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:index</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:correct_user</span><span class="p">,</span>   <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">show</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>すべてのユーザーを表示するために、全ユーザーが格納された変数を作成し、順々に表示する index ビューを実装する
<ul>
<li><code>User.all</code> を使って DB 上の全ユーザーを取得し、ビューで使えるインスタンス変数 <code>@user</code> に代入する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:index</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@users</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">all</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ユーザーのindexビューを作成する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#39;All users&#39;) %&gt;
&lt;h1&gt;All users&lt;/h1&gt;

&lt;ul class=&#34;users&#34;&gt;
  &lt;% @users.each do |user| %&gt;
    &lt;li&gt;
      &lt;%= gravatar_for user, size: 50 %&gt;
      &lt;%= link_to user.name, user %&gt;
    &lt;/li&gt;
  &lt;% end %&gt;
&lt;/ul&gt;
</code></pre><ul>
<li>SCSS 側も修正しておく</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scss" data-lang="scss"><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="o">/*</span> <span class="nt">Users</span> <span class="nt">index</span> <span class="o">*/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nc">.users</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="na">list-style</span><span class="o">:</span> <span class="ni">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="na">margin</span><span class="o">:</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">li</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="na">overflow</span><span class="o">:</span> <span class="ni">auto</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="na">padding</span><span class="o">:</span> <span class="mi">10</span><span class="kt">px</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="na">border-bottom</span><span class="o">:</span> <span class="mi">1</span><span class="kt">px</span> <span class="ni">solid</span> <span class="nv">$gray-lighter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>最後にサイト内移動用のヘッダーにユーザー一覧表示用のリンクを追加する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;header class=&#34;navbar navbar-fixed-top navbar-inverse&#34;&gt;
  &lt;div class=&#34;container&#34;&gt;
    &lt;%= link_to &#34;sample app&#34;, root_path, id: &#34;logo&#34; %&gt;
    &lt;nav&gt;
      &lt;ul class=&#34;nav navbar-nav navbar-right&#34;&gt;
        &lt;li&gt;&lt;%= link_to &#34;Home&#34;, root_path %&gt;&lt;/li&gt;
        &lt;li&gt;&lt;%= link_to &#34;Help&#34;, help_path %&gt;&lt;/li&gt;
        &lt;% if logged_in? %&gt;
          &lt;li&gt;&lt;%= link_to &#34;Users&#34;, users_path %&gt;&lt;/li&gt;
          &lt;li class=&#34;dropdown&#34;&gt;
            &lt;a href=&#34;#&#34; class=&#34;dropdown-toggle&#34; data-toggle=&#34;dropdown&#34;&gt;
              Account &lt;b class=&#34;caret&#34;&gt;&lt;/b&gt;
            &lt;/a&gt;
            &lt;ul class=&#34;dropdown-menu&#34;&gt;
              &lt;li&gt;&lt;%= link_to &#34;Profile&#34;, current_user %&gt;&lt;/li&gt;
              &lt;li&gt;&lt;%= link_to &#34;Settings&#34;, edit_user_path(current_user) %&gt;&lt;/li&gt;
              &lt;li class=&#34;divider&#34;&gt;&lt;/li&gt;
              &lt;li&gt;
                &lt;%= link_to &#34;Log out&#34;, logout_path, method: :delete %&gt;
              &lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;% else %&gt;
          &lt;li&gt;&lt;%= link_to &#34;Log in&#34;, login_path %&gt;&lt;/li&gt;
        &lt;% end %&gt;
      &lt;/ul&gt;
    &lt;/nav&gt;
  &lt;/div&gt;
&lt;/header&gt;
</code></pre><h3 id="1032-サンプルのユーザー">10.3.2 サンプルのユーザー</h3>
<ul>
<li>Rubyを使ってユーザーを一気に作成してみる</li>
<li><code>Gemfile</code> に Faker gem を追加して <code>bundle install</code>
<ul>
<li>中二心をくすぐる名前</li>
<li>実際にいそうなユーザー名を作成する gem</li>
</ul>
</li>
<li>サンプルユーザーを追加する Ruby スクリプトを追加する
<ul>
<li>Rails タスクとも言う</li>
<li><code>db/seeds.rb</code> というファイルが標準</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">User</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span>  <span class="s2">&#34;Example User&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;example@railstutorial.org&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;foobar&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;foobar&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="mi">99</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="nb">name</span>  <span class="o">=</span> <span class="no">Faker</span><span class="o">::</span><span class="no">Name</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl"> <span class="n">email</span> <span class="o">=</span> <span class="s2">&#34;example-</span><span class="si">#{</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s2">@railstutorial.org&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="n">password</span> <span class="o">=</span> <span class="s2">&#34;password&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="no">User</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span>  <span class="nb">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="ss">email</span><span class="p">:</span> <span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="ss">password</span><span class="p">:</span>              <span class="n">password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="ss">password_confirmation</span><span class="p">:</span> <span class="n">password</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>DB をリセットし、上記 Rails タスクを実行する
<ul>
<li>100人分なので少し時間がかかる</li>
</ul>
</li>
</ul>
<h3 id="1033-ページネーション">10.3.3 ページネーション</h3>
<ul>
<li>大量のユーザー表示に対応するためにページネーションを実装する
<ul>
<li><code>Gemfile</code> に will_paginate gem と bootstrap-will_paginate gem を追加する
<ul>
<li>gem で対応できるのが Rails っぽい</li>
</ul>
</li>
</ul>
</li>
<li>ページネーションが動作するには、ユーザーのページネーションを行うように Rails に指示するコードを index ビューに追加する必要がある
<ul>
<li>まずはビューに <code>will_paginate</code> メソッドを追加する</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#39;All users&#39;) %&gt;
&lt;h1&gt;All users&lt;/h1&gt;

&lt;%= will_paginate %&gt;

&lt;ul class=&#34;users&#34;&gt;
  &lt;% @users.each do |user| %&gt;
    &lt;li&gt;
      &lt;%= gravatar_for user, size: 50 %&gt;
      &lt;%= link_to user.name, user %&gt;
    &lt;/li&gt;
  &lt;% end %&gt;
&lt;/ul&gt;

&lt;%= will_paginate %&gt;
</code></pre><ul>
<li>この <code>will_paginate</code> メソッドは、<code>users</code> ビューのコードの中から <code>@users</code> オブジェクトを自動的に見つけ出し、それから他のページにアクセスするためのページネーションリンクを作成している
<ul>
<li>どうやってるの。。。</li>
<li><code>@users</code> 変数には <code>User.all</code> の結果が含まれているが、<code>will_paginate</code> では <code>paginate</code> メソッドを使った結果が必要なため上記コードはまだ動かない</li>
</ul>
</li>
<li><code>paginate</code> を使うことで、ユーザーのページネーションを行えるようになる
<ul>
<li><code>index</code> アクション内の <code>all</code> を <code>paginate</code> メソッドに置き換える</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl"> <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:index</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">   <span class="vi">@users</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">page</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1034-ユーザー一覧のテスト">10.3.4 ユーザー一覧のテスト</h3>
<ul>
<li>ページネーションに対する簡単なテストを書く
<ul>
<li>ログイン</li>
<li>index ページにアクセス</li>
<li>最初のページにユーザーがいることを確認</li>
<li>ページネーションのリンクがあることを確認</li>
</ul>
</li>
<li>fixture に大量のユーザーを追加する必要があるが、手動は面倒
<ul>
<li>埋め込み Ruby を利用してさらに30人のユーザーを追加する</li>
</ul>
</li>
<li>index ページに対するテストを書く
<ul>
<li>いつものように TDD で</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersIndexTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;index including pagination&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">users_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/index&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;div.pagination&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="no">User</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">page</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert_select</span> <span class="s1">&#39;a[href=?]&#39;</span><span class="p">,</span> <span class="n">user_path</span><span class="p">(</span><span class="n">user</span><span class="p">),</span> <span class="ss">text</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1035-パーシャルのリファクタリング">10.3.5 パーシャルのリファクタリング</h3>
<ul>
<li>index ビューのリファクタリングとして、ユーザーの <code>li</code> を <code>render</code> 呼び出しに置き換える</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#39;All users&#39;) %&gt;
&lt;h1&gt;All users&lt;/h1&gt;

&lt;%= will_paginate %&gt;

&lt;ul class=&#34;users&#34;&gt;
  &lt;% @users.each do |user| %&gt;
    &lt;%= render user %&gt;
  &lt;% end %&gt;
&lt;/ul&gt;

&lt;%= will_paginate %&gt;
</code></pre><ul>
<li><code>render</code> をパーシャルではなく、<code>User</code> クラスの <code>user</code> 変数に対して実行している
<ul>
<li>この場合、Rails は自動的に <code>_user.html.erb</code> という名前のパーシャルを探しに行くので、このパーシャルを作成する必要がある</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;li&gt;
  &lt;%= gravatar_for user, size: 50 %&gt;
  &lt;%= link_to user.name, user %&gt;
&lt;/li&gt;
</code></pre><ul>
<li>さらに <code>render</code> を <code>@users</code> に対して直接実行する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#39;All users&#39;) %&gt;
&lt;h1&gt;All users&lt;/h1&gt;

&lt;%= will_paginate %&gt;

&lt;ul class=&#34;users&#34;&gt;
  &lt;%= render @users %&gt;
&lt;/ul&gt;

&lt;%= will_paginate %&gt;
</code></pre><ul>
<li>Rails は <code>@users</code> を <code>User</code> オブジェクトのリストであると推測する</li>
<li>ユーザーのコレクションを与えて呼び出すと、Rails は自動的にユーザーのコレクションを列挙し、それぞれのユーザーを <code>_user.html.erb</code> パーシャルで出力する
<ul>
<li>いつもの黒魔術</li>
</ul>
</li>
</ul>
<h2 id="104-ユーザーを削除する">10.4 ユーザーを削除する</h2>
<ul>
<li>ユーザー一覧ページは完成したので、残るは <code>destroy</code> だけ</li>
<li>まずは削除を実行できる admin ユーザ−のクラスを作成する</li>
</ul>
<h3 id="1041-管理ユーザー">10.4.1 管理ユーザー</h3>
<ul>
<li>特権を持つ管理ユーザーを識別するために、論理値をとる <code>admin</code> 属性を User モデルに追加する
<ul>
<li>自動的に <code>admin?</code> メソッドも使えるようになる
<ul>
<li>なぜ？</li>
</ul>
</li>
</ul>
</li>
<li>いつものようにマイグレーションを実行して <code>admin</code> 属性を追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> rails generate migration add_admin_to_users admin:booleanadmin:boolean
</span></span></code></pre></div><ul>
<li>マイグレーションを実行すると、<code>admin</code> カラムが <code>users</code> テーブルに追加される
<ul>
<li><code>default: false</code> という引数を <code>add_column</code> に追加することにより、デフォルトでは管理者になれないということを示す</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">AddAdminToUsers</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="o">[</span><span class="mi">5</span><span class="o">.</span><span class="mi">0</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">change</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:admin</span><span class="p">,</span> <span class="ss">:boolean</span><span class="p">,</span> <span class="ss">default</span><span class="p">:</span> <span class="kp">false</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>いつものようにマイグレーションを実行する
<ul>
<li><code>admin?</code> メソッドも利用できるようになる
<ul>
<li>うーん黒魔術</li>
</ul>
</li>
</ul>
</li>
<li>最初のユーザーだけをデフォルトで管理者にするようにサンプルデータを更新する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">User</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span>  <span class="s2">&#34;Example User&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;example@railstutorial.org&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;foobar&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;foobar&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="ss">admin</span><span class="p">:</span> <span class="kp">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="mi">99</span><span class="o">.</span><span class="n">times</span> <span class="k">do</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="nb">name</span>  <span class="o">=</span> <span class="no">Faker</span><span class="o">::</span><span class="no">Name</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl"> <span class="n">email</span> <span class="o">=</span> <span class="s2">&#34;example-</span><span class="si">#{</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s2">@railstutorial.org&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="n">password</span> <span class="o">=</span> <span class="s2">&#34;password&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="no">User</span><span class="o">.</span><span class="n">create!</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span>  <span class="nb">name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="ss">email</span><span class="p">:</span> <span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="ss">password</span><span class="p">:</span>              <span class="n">password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="ss">password_confirmation</span><span class="p">:</span> <span class="n">password</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>DB をリセットし、サンプルデータを再度生成する</li>
<li>上記コードでは初期化ハッシュに <code>admin: true</code> を設定することでユーザーを管理者にしている
<ul>
<li>攻撃者が PATCH リクエストにより任意のユーザーの権限を書き換える可能性がある</li>
<li>Stroing Parameters で対策</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span></code></pre></div><ul>
<li>許可された属性リストに <code>admin</code> が含まれていないため、任意のユーザーが自分自身にアプリケーションの管理者権限を与えることを防止できる</li>
<li>演習問題で少しハマる
<ul>
<li>失敗すべきテストが通ってしまう</li>
<li>has_secure_password によって守られているため、テストでは <code>@other_user</code> 経由で passowrd と password_confirmation を呼び出せないのが原因（ググると同じ問題にハマっている人がいた）</li>
<li>password と password_confirmation をベタ書きにすることで対応できた
<ul>
<li>しかし微妙になっとくできないのでもう少し調べてみよう。。。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="1042-destroy-アクション">10.4.2 destroy アクション</h3>
<ul>
<li><code>destroy</code> アクションへのリンクを追加する
<ul>
<li>ユーザー index ページの各ユーザーに削除用のリンクを追加し、管理ユーザーへのアクセスを制限する
<ul>
<li>現在のユーザーが管理者のときに限り <code>destroy</code> リンクが表示されるようになる</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">  &lt;% if current_user.admin? &amp;&amp; !current_user?(user) %&gt;
    | &lt;%= link_to &#34;delete&#34;, user, method: :delete,
                                  data: { confirm: &#34;You sure?&#34; } %&gt;
  &lt;% end %&gt;
</code></pre><ul>
<li>やはり erb 内に登場する if 文が気になる。。。</li>
<li>ブラウザではネイティブで DLETE リクエストを送信できないため、Railsでは JavaScript を使って偽造する</li>
<li>削除リンクを動作させるために <code>destroy</code> アクションを追加する
<ul>
<li>該当するユーザーを見つけて Acticve Record の <code>destroy</code> メソッドを使って削除し、最後にユーザー index に移動する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:index</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:correct_user</span><span class="p">,</span>   <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;User deleted&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span> <span class="n">users_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>コマンドラインで DELETE リクエストを直接発行するという方法でサイトの全ユーザーを削除するというセキュリティホールがまだある
<ul>
<li><code>destroy</code> アクションにもアクセス制限を行うことにより、管理者だけがユーザーを削除できるようになる</li>
</ul>
</li>
<li>before フィルターで <code>destroy</code> アクションへのアクセスを制御する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:logged_in_user</span><span class="p">,</span> <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:index</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="p">,</span> <span class="ss">:destroy</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:correct_user</span><span class="p">,</span>   <span class="ss">only</span><span class="p">:</span> <span class="o">[</span><span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_action</span> <span class="ss">:admin_user</span><span class="p">,</span>     <span class="ss">only</span><span class="p">:</span> <span class="ss">:destroy</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="o">.</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 管理者かどうか確認</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">admin_user</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span><span class="p">(</span><span class="n">root_url</span><span class="p">)</span> <span class="k">unless</span> <span class="n">current_user</span><span class="o">.</span><span class="n">admin?</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="1043-ユーザー削除のテスト">10.4.3 ユーザー削除のテスト</h3>
<ul>
<li>ユーザー削除のテストを書く
<ul>
<li>fixture ファイルを修正してサンプルユーザーの一人を管理者にする</li>
</ul>
</li>
<li>Users コントローラをテストするために、アクション単位でアクセス制御をテストする
<ul>
<li>削除をテストするために DELETE リクエストを発行して <code>destroy</code> アクションを直接動作させる
<ul>
<li>ログインしていないユーザーであればログイン画面へリダイレクト</li>
<li>ログイン済みユーザーであっても管理者でなければ、ホーム画面へリダイレクト</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span>       <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@other_user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect destroy when not logged in&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;User.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">login_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should redirect destroy when logged in as a non-admin&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@other_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_no_difference</span> <span class="s1">&#39;User.count&#39;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>管理者ユーザーの振る舞いも一緒に確認できると良い
<ul>
<li>管理者であればユーザー一覧画面に削除リンクが表示される仕様を利用してテストを追加する</li>
<li>DELETE リクエストを適切な URL に向けて発行し、<code>User.count</code> でユーザー数が1減ったかどうかを確認する</li>
<li>管理者や一般ユーザーのテスト、ページネーションや削除リンクのテストをすべてまとめる</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersIndexTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@admin</span>     <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@non_admin</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:archer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;index as admin including pagination and delete links&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@admin</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">users_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/index&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;div.pagination&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">first_page_of_users</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">page</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">first_page_of_users</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">      <span class="n">assert_select</span> <span class="s1">&#39;a[href=?]&#39;</span><span class="p">,</span> <span class="n">user_path</span><span class="p">(</span><span class="n">user</span><span class="p">),</span> <span class="ss">text</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">name</span>
</span></span><span class="line"><span class="cl">      <span class="k">unless</span> <span class="n">user</span> <span class="o">==</span> <span class="vi">@admin</span>
</span></span><span class="line"><span class="cl">        <span class="n">assert_select</span> <span class="s1">&#39;a[href=?]&#39;</span><span class="p">,</span> <span class="n">user_path</span><span class="p">(</span><span class="n">user</span><span class="p">),</span> <span class="ss">text</span><span class="p">:</span> <span class="s1">&#39;delete&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;User.count&#39;</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">delete</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@non_admin</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;index as non-admin&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@non_admin</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">users_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s1">&#39;a&#39;</span><span class="p">,</span> <span class="ss">text</span><span class="p">:</span> <span class="s1">&#39;delete&#39;</span><span class="p">,</span> <span class="ss">count</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="105-最後に">10.5 最後に</h2>
<ul>
<li>Web サイトとしての十分な基盤（ユーザーの認証認可）を整えたサンプルアプリケーションが出来上がった</li>
</ul>
<h2 id="所感">所感</h2>
<p>認可の仕組みやページネーションを実装し、よりWebアプリケーションらしさが増してきた一方、本来このチュートリアルから得たかったことからは徐々に離れつつある気もしてきている。Rails の特定な便利機能の動きを学べるのはプラスにはなるのだが、正直言ってあまり興味は持てない。とはいえ残り4章なので、最後まで頑張って走りきってみる。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第9章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter9/</link>
			<pubDate>Sat, 22 Feb 2020 20:15:18 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter9/</guid>
			<description>第9章 発展的なログイン機構 永続クッキーを使用してブラウザを再起動した後でもすぐにログインできる機能を実装する 9.1 Remember me 機能 ユーザーのログイン状態</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第9章-発展的なログイン機構">第9章 発展的なログイン機構</h1>
<ul>
<li>永続クッキーを使用してブラウザを再起動した後でもすぐにログインできる機能を実装する</li>
</ul>
<h2 id="91-remember-me-機能">9.1 Remember me 機能</h2>
<ul>
<li>ユーザーのログイン状態をブラウザを閉じた後でも有効にする remember me 機能を実装していく
<ul>
<li>ユーザーが明示的にログアウトを実行しない限り、ログイン状態を維持することができる</li>
</ul>
</li>
</ul>
<h3 id="911-記憶トークンと暗号化">9.1.1 記憶トークンと暗号化</h3>
<ul>
<li>セッションの永続化の第一歩として記憶トークンを生成し、<code>cookies</code> メソッドによる永続的 cookies の生成や、安全性の高い記憶ダイジェストによるトークン認証にこの記憶トークンを活用する</li>
<li><code>session</code> メソッドで保存した情報は自動的に安全性が保たれるが、<code>cookies</code> メソッドに保存する情報はそうではない
<ul>
<li>cookies を永続化するとセッションハイジャックを受ける可能性がある</li>
<li>cookies を盗み出す有名な方法とその対処法
<ul>
<li>パケットスニッファで直接 cookies を取り出す
<ul>
<li>SSL でネットワークデータを暗号化</li>
</ul>
</li>
<li>DBから記憶トークンを取り出す
<ul>
<li>記憶トークンのハッシュ値を保存</li>
</ul>
</li>
<li>XSS
<ul>
<li>ビューのテンプレートで入力した内容は Rails によって自動的に対策される</li>
</ul>
</li>
<li>直接端末を操作
<ul>
<li>ログアウト時にトークンを変更し、デジタル署名を用いた情報表示で二次被害を最小限に留める</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>上記を考慮して永続的セッションを作成する
<ul>
<li>記憶トークンはランダム文字列で生成</li>
<li>トークンは有効期限を設けて cookies に保存</li>
<li>トークンはハッシュ値に変換して DB に保存</li>
<li>cookies に保存するユーザー ID は暗号化する</li>
<li>永続ユーザー ID を含む cookies を受け取ったら、その ID で DB を検索し、記憶トークンの cookies が DB 内のハッシュ値を一致するかを確認する</li>
</ul>
</li>
<li>最初に <code>remember_digest</code> 属性を User モデルに追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="go">rails generate migration add_remember_digest_to_users remember_digest:string
</span></span></span></code></pre></div><ul>
<li>相変わらず簡単にマイグレーションが作成される</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">AddRememberDigestToUsers</span> <span class="o">&lt;</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span><span class="o">[</span><span class="mi">5</span><span class="o">.</span><span class="mi">0</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">change</span>
</span></span><span class="line"><span class="cl">    <span class="n">add_column</span> <span class="ss">:users</span><span class="p">,</span> <span class="ss">:remember_digest</span><span class="p">,</span> <span class="ss">:string</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>記憶ダイジェストはユーザーが直接読み出すことはないので、<code>remember_digest</code> カラムにインデックスを追加する必要はない
<ul>
<li>上記マイグレーションをそのまま使用する</li>
</ul>
</li>
<li>記憶トークンとして何を使うか
<ul>
<li>Ruby 標準ライブラリの <code>SecureRandom</code> モジュールにある <code>urlsafe_base64</code> メソッドがこの用途にぴったり</li>
</ul>
</li>
<li>同一のパスワードを持つユーザーが複数いても問題ないように、同一の記憶トークンを持つユーザーが複数しても問題はない
<ul>
<li>とはいえトークンは一意である方が安全</li>
</ul>
</li>
<li>ユーザーを記憶するには記憶トークンを作成し、そのトークンをダイジェストに変換したものを DB に保存する</li>
<li>User モデルのクラスメソッドとして新しいトークン作成用の <code>new_token</code> メソッドを追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_save</span> <span class="p">{</span> <span class="nb">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span><span class="o">.</span><span class="n">downcase</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span>  <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">50</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">VALID_EMAIL_REGEX</span> <span class="o">=</span> <span class="sr">/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">255</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">format</span><span class="p">:</span> <span class="p">{</span> <span class="ss">with</span><span class="p">:</span> <span class="no">VALID_EMAIL_REGEX</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="ss">uniqueness</span><span class="p">:</span> <span class="p">{</span> <span class="ss">case_sensitive</span><span class="p">:</span> <span class="kp">false</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_secure_password</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">minimum</span><span class="p">:</span> <span class="mi">6</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡された文字列のハッシュ値を返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">User</span><span class="o">.</span><span class="nf">digest</span><span class="p">(</span><span class="n">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cost</span> <span class="o">=</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">SecurePassword</span><span class="o">.</span><span class="n">min_cost</span> <span class="p">?</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">::</span><span class="no">MIN_COST</span> <span class="p">:</span>
</span></span><span class="line"><span class="cl">                                                  <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">.</span><span class="n">cost</span>
</span></span><span class="line"><span class="cl">    <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="ss">cost</span><span class="p">:</span> <span class="n">cost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ランダムなトークンを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">User</span><span class="o">.</span><span class="nf">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="no">SecureRandom</span><span class="o">.</span><span class="n">urlsafe_base64</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>記憶トークンをユーザーと関連付け、トークンに対応する記憶ダイジェストを DB に保存するために <code>user.remember</code> メソッドを作成する</li>
<li>マイグレーションは実行済みなので、User モデルには <code>remember_digest</code> 属性が追加されているが、<code>remember_token</code> 属性はまだ追加されていない
<ul>
<li><code>user.remember_token</code> メソッドを使ってトークンにアクセスできるようにし、かつトークンを DB に保存せずに実装する必要がある
<ul>
<li><code>attr_accessor</code> を使って仮想の属性を作成する</li>
</ul>
</li>
</ul>
</li>
<li>最初に <code>User.new_token</code> で記憶トークンを作成し、続いて <code>User.digest</code> を適用した結果で記憶ダイジェストを更新する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:remember_token</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 永続セッションのためにユーザーをデータベースに記憶する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">remember</span>
</span></span><span class="line"><span class="cl">    <span class="nb">self</span><span class="o">.</span><span class="n">remember_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:remember_digest</span><span class="p">,</span> <span class="no">User</span><span class="o">.</span><span class="n">digest</span><span class="p">(</span><span class="n">remember_token</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="912-ログイン状態の保持">9.1.2 ログイン状態の保持</h3>
<ul>
<li>ユーザーの暗号化済み ID と記憶トークンをブラウザの永続 cookies に保存して永続セッションを作成する準備ができた
<ul>
<li>実際に行うには <code>cookies</code> メソッドを使う</li>
<li><code>session</code> のときと同様にハッシュとして扱える
<ul>
<li>個別の cookie は <code>value</code> と <code>expires</code> からできている</li>
</ul>
</li>
<li><code>permanent</code> メソッドで20年で期限切れになる cookie 設定が可能</li>
</ul>
</li>
<li>ユーザー ID を cookies にそのまま保存するとセキュリティ上危険なため、cookie をブラウザに保存する前に安全に暗号化する署名付き cookie を使う</li>
<li>ユーザー ID と記憶トークンはペアで扱う必要があるため、cookie も永続化する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">cookies</span><span class="o">.</span><span class="n">permanent</span><span class="o">.</span><span class="n">signed</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span></code></pre></div><ul>
<li>cookies を設定すると、以後のページのビューで cookies からユーザーを取り出せるようになる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">cookies</span><span class="o">.</span><span class="n">signed</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li><code>cookies.signed[:user_id]</code> では自動的にユーザー ID の cookies の暗号が解除され、元に戻る</li>
<li>続いて bcrypt を使って <code>cookies[:remember_token]</code> が <code>remember_digest</code> と一致することを確認する</li>
<li>仮に攻撃者が暗号化された ID を奪った場合、記憶トークンがなければそのまま攻撃者がログイン可能になってしまう
<ul>
<li>現在の設計の場合、本物のユーザーがログアウトすると攻撃者はログインできない設計になっている</li>
</ul>
</li>
<li>最後に渡されたトークンがユーザーの記憶ダイジェストと一致することを確認する
<ul>
<li>User モデルに <code>authenticated?</code> メソッドを作る</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:remember_token</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_save</span> <span class="p">{</span> <span class="nb">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span><span class="o">.</span><span class="n">downcase</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span>  <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">50</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">VALID_EMAIL_REGEX</span> <span class="o">=</span> <span class="sr">/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">255</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">format</span><span class="p">:</span> <span class="p">{</span> <span class="ss">with</span><span class="p">:</span> <span class="no">VALID_EMAIL_REGEX</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="ss">uniqueness</span><span class="p">:</span> <span class="p">{</span> <span class="ss">case_sensitive</span><span class="p">:</span> <span class="kp">false</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_secure_password</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">minimum</span><span class="p">:</span> <span class="mi">6</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡された文字列のハッシュ値を返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">User</span><span class="o">.</span><span class="nf">digest</span><span class="p">(</span><span class="n">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cost</span> <span class="o">=</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">SecurePassword</span><span class="o">.</span><span class="n">min_cost</span> <span class="p">?</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">::</span><span class="no">MIN_COST</span> <span class="p">:</span>
</span></span><span class="line"><span class="cl">                                                  <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">.</span><span class="n">cost</span>
</span></span><span class="line"><span class="cl">    <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="ss">cost</span><span class="p">:</span> <span class="n">cost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ランダムなトークンを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">User</span><span class="o">.</span><span class="nf">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="no">SecureRandom</span><span class="o">.</span><span class="n">urlsafe_base64</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 永続セッションのためにユーザーをデータベースに記憶する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">remember</span>
</span></span><span class="line"><span class="cl">    <span class="nb">self</span><span class="o">.</span><span class="n">remember_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:remember_digest</span><span class="p">,</span> <span class="no">User</span><span class="o">.</span><span class="n">digest</span><span class="p">(</span><span class="n">remember_token</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡されたトークンがダイジェストと一致したらtrueを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">authenticated?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">remember_digest</span><span class="p">)</span><span class="o">.</span><span class="n">is_password?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ログインしてユーザーを保存するため、<code>remember</code> ヘルパーメソッドを追加して <code>log_in</code> と連携させてみる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:email</span><span class="o">].</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:password</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="n">remember</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">.</span><span class="n">now</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;Invalid email/password combination&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_out</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>log_in</code> のときと同様に、実際の Sessions ヘルパーの動作は <code>remember</code> メソッド定義の <code>user.remember</code> を呼び出すまで遅延され、そこで記憶トークンを生成してトークンのダイジェストを DB に保存する</li>
<li>続いて <code>cookies</code> メソッドでユーザー ID と記憶トークンの永続 <code>cookies</code> を作成する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡されたユーザーでログインする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_in</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーのセッションを永続的にする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">remember</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">remember</span>
</span></span><span class="line"><span class="cl">    <span class="n">cookies</span><span class="o">.</span><span class="n">permanent</span><span class="o">.</span><span class="n">signed</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">    <span class="n">cookies</span><span class="o">.</span><span class="n">permanent</span><span class="o">[</span><span class="ss">:remember_token</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">remember_token</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 現在ログインしているユーザーを返す (いる場合)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">current_user</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@current_user</span> <span class="o">||=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーがログインしていればtrue、その他ならfalseを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="o">!</span><span class="n">current_user</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 現在のユーザーをログアウトする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_out</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="ss">:user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@current_user</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記コードでは一時セッション用に作成した <code>current_user</code> が正常に動作しない
<ul>
<li>永続セッションの場合は <code>session[:user_id]</code> が存在すれば一時セッションからユーザーを取り出し、それ以外の場合は <code>cookies[:user_id]</code> からユーザーを取り出して、対応する永続セッションにログインする必要がある</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># 渡されたユーザーでログインする</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">log_in</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># ユーザーのセッションを永続的にする</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">remember</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="n">user</span><span class="o">.</span><span class="n">remember</span>
</span></span><span class="line"><span class="cl">   <span class="n">cookies</span><span class="o">.</span><span class="n">permanent</span><span class="o">.</span><span class="n">signed</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">   <span class="n">cookies</span><span class="o">.</span><span class="n">permanent</span><span class="o">[</span><span class="ss">:remember_token</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">remember_token</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># 記憶トークンcookieに対応するユーザーを返す</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">current_user</span>
</span></span><span class="line"><span class="cl">   <span class="k">if</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="vi">@current_user</span> <span class="o">||=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="k">elsif</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">cookies</span><span class="o">.</span><span class="n">signed</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">     <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="n">cookies</span><span class="o">[</span><span class="ss">:remember_token</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">       <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">       <span class="vi">@current_user</span> <span class="o">=</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">     <span class="k">end</span>
</span></span><span class="line"><span class="cl">   <span class="k">end</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># ユーザーがログインしていればtrue、その他ならfalseを返す</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">logged_in?</span>
</span></span><span class="line"><span class="cl">   <span class="o">!</span><span class="n">current_user</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># 現在のユーザーをログアウトする</span>
</span></span><span class="line"><span class="cl"> <span class="k">def</span> <span class="nf">log_out</span>
</span></span><span class="line"><span class="cl">   <span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="ss">:user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">   <span class="vi">@current_user</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>残りの問題はブラウザの cookies を削除してユーザーをログアウトさせること</li>
</ul>
<h3 id="913-ユーザーを忘れる">9.1.3 ユーザーを忘れる</h3>
<ul>
<li>ユーザーがログアウトできるようにするために、<code>user.forget</code> メソッドを定義する
<ul>
<li><code>user.remember</code> が取り消される</li>
<li>記憶ダイジェストを <code>nil</code> で更新する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="kp">attr_accessor</span> <span class="ss">:remember_token</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_save</span> <span class="p">{</span> <span class="nb">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span><span class="o">.</span><span class="n">downcase</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span>  <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">50</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">VALID_EMAIL_REGEX</span> <span class="o">=</span> <span class="sr">/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">255</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">format</span><span class="p">:</span> <span class="p">{</span> <span class="ss">with</span><span class="p">:</span> <span class="no">VALID_EMAIL_REGEX</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="ss">uniqueness</span><span class="p">:</span> <span class="p">{</span> <span class="ss">case_sensitive</span><span class="p">:</span> <span class="kp">false</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_secure_password</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">minimum</span><span class="p">:</span> <span class="mi">6</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡された文字列のハッシュ値を返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">User</span><span class="o">.</span><span class="nf">digest</span><span class="p">(</span><span class="n">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cost</span> <span class="o">=</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">SecurePassword</span><span class="o">.</span><span class="n">min_cost</span> <span class="p">?</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">::</span><span class="no">MIN_COST</span> <span class="p">:</span>
</span></span><span class="line"><span class="cl">                                                  <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">.</span><span class="n">cost</span>
</span></span><span class="line"><span class="cl">    <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="ss">cost</span><span class="p">:</span> <span class="n">cost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ランダムなトークンを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">User</span><span class="o">.</span><span class="nf">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="no">SecureRandom</span><span class="o">.</span><span class="n">urlsafe_base64</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 永続セッションのためにユーザーをデータベースに記憶する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">remember</span>
</span></span><span class="line"><span class="cl">    <span class="nb">self</span><span class="o">.</span><span class="n">remember_token</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new_token</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:remember_digest</span><span class="p">,</span> <span class="no">User</span><span class="o">.</span><span class="n">digest</span><span class="p">(</span><span class="n">remember_token</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡されたトークンがダイジェストと一致したらtrueを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">authenticated?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">remember_digest</span><span class="p">)</span><span class="o">.</span><span class="n">is_password?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーのログイン情報を破棄する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">forget</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:remember_digest</span><span class="p">,</span> <span class="kp">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>永続セッションを終了するには、<code>forget</code> ヘルパーメソッドを追加して <code>log_out</code> ヘルパーメソッドから呼び出す</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡されたユーザーでログインする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_in</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 永続的セッションを破棄する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">forget</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span><span class="o">.</span><span class="n">forget</span>
</span></span><span class="line"><span class="cl">    <span class="n">cookies</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="ss">:user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cookies</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="ss">:remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 現在のユーザーをログアウトする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_out</span>
</span></span><span class="line"><span class="cl">    <span class="n">forget</span><span class="p">(</span><span class="n">current_user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="ss">:user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@current_user</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>これでログアウトできるようになり、テストも通るようになった</li>
</ul>
<h3 id="914-2つの目立たないバグ">9.1.4 2つの目立たないバグ</h3>
<ul>
<li>小さなバグが2つ残っている
<ul>
<li>ひとつは複数タブで同じサイトを開いているときに、片方でログアウトした後、もう片方でログアウトしようとするとエラーになること
<ul>
<li>ユーザーがログイン中にのみログアウトさせる必要がある</li>
</ul>
</li>
<li>もうひとつはユーザーが複数のブラウザでログインしている場合に起こる
<ul>
<li>片方でログアウトし、もう片方でログアウトせずにブラウザを終了して再度起動したときに問題が発生する
<ul>
<li>終了した方のブラウザの中に <code>cookies</code> が残り続けているため、DB からユーザーを見つけることができてしまう</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>1つ目の問題への対応
<ul>
<li><code>logged_in?</code> が true の場合に限って <code>log_out</code> を呼び出すように修正する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_out</span> <span class="k">if</span> <span class="n">logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>2つ目の問題への対応
<ul>
<li>統合テストで2種類のブラウザをシュミレートするのは困難</li>
<li>その代わりに User モデルで直接テストする</li>
<li>記憶ダイジェストを持たないユーザーを用意し、<code>authenticated?</code> を呼び出す</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UserTest</span> <span class="o">&lt;</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Example User&#34;</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;user@example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                     <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;foobar&#34;</span><span class="p">,</span> <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;foobar&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;authenticated? should return false for a user with nil digest&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="vi">@user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記テストが成功するように修正する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡されたトークンがダイジェストと一致したらtrueを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">authenticated?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kp">false</span> <span class="k">if</span> <span class="n">remember_digest</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">    <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">remember_digest</span><span class="p">)</span><span class="o">.</span><span class="n">is_password?</span><span class="p">(</span><span class="n">remember_token</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーのログイン情報を破棄する</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">forget</span>
</span></span><span class="line"><span class="cl">    <span class="n">update_attribute</span><span class="p">(</span><span class="ss">:remember_digest</span><span class="p">,</span> <span class="kp">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="92-remember-me-チェックボックス">9.2 [Remember me] チェックボックス</h2>
<ul>
<li>[remember me] チェックボックスをログインフォームに追加してでログインを保持する
<ul>
<li>チェックボックスはヘルパーメソッドで作成可能</li>
</ul>
</li>
<li>チェックボックスがオンのときにユーザーを記憶し、オフのときには記憶しないようにする
<ul>
<li>必要な準備は終わっているので実装は1行で完了する
<ul>
<li>本当に？？？</li>
</ul>
</li>
</ul>
</li>
<li>[remember me] チェックボックスの送信結果を処理する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:email</span><span class="o">].</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:password</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:remember_me</span><span class="o">]</span> <span class="o">==</span> <span class="s1">&#39;1&#39;</span> <span class="p">?</span> <span class="n">remember</span><span class="p">(</span><span class="n">user</span><span class="p">)</span> <span class="p">:</span> <span class="n">forget</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">.</span><span class="n">now</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;Invalid email/password combination&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_out</span> <span class="k">if</span> <span class="n">logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="93-remember-me-のテスト">9.3 [Remember me] のテスト</h2>
<h3 id="931-remember-me-ボックスをテストする">9.3.1 [Remember me] ボックスをテストする</h3>
<ul>
<li>テスト内でユーザーがログインするためのヘルパーメソッド <code>log_in_as</code> を定義
<ul>
<li><code>session</code> を直接操作して <code>:user_id</code> キーに <code>user.id</code> の値を代入する</li>
</ul>
</li>
<li>統合テストでも同様のヘルパーメソッドを実装する
<ul>
<li>統合テストでは <code>session</code> を直接取り扱うことができないので、代わりに Sessions リソースに対して <code>post</code> を送信することで代用する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;RAILS_ENV&#39;</span><span class="o">]</span> <span class="o">||=</span> <span class="s1">&#39;test&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">  <span class="n">fixtures</span> <span class="ss">:all</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># テストユーザーがログイン中の場合にtrueを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">is_logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="o">!</span><span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">].</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># テストユーザーとしてログインする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_in_as</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># テストユーザーとしてログインする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_in_as</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="ss">password</span><span class="p">:</span> <span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="ss">remember_me</span><span class="p">:</span> <span class="s1">&#39;1&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="n">login_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">session</span><span class="p">:</span> <span class="p">{</span> <span class="ss">email</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                          <span class="ss">password</span><span class="p">:</span> <span class="n">password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                          <span class="ss">remember_me</span><span class="p">:</span> <span class="n">remember_me</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>チェックボックスの動作を確認するため、チェックボックスがオンになっている場合とオフになっている場合のテストを作成する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersLoginTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;login with remembering&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">,</span> <span class="ss">remember_me</span><span class="p">:</span> <span class="s1">&#39;1&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not_empty</span> <span class="n">cookies</span><span class="o">[</span><span class="s1">&#39;remember_token&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;login without remembering&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># クッキーを保存してログイン</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">,</span> <span class="ss">remember_me</span><span class="p">:</span> <span class="s1">&#39;1&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">delete</span> <span class="n">logout_path</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># クッキーを削除してログイン</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_in_as</span><span class="p">(</span><span class="vi">@user</span><span class="p">,</span> <span class="ss">remember_me</span><span class="p">:</span> <span class="s1">&#39;0&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_empty</span> <span class="n">cookies</span><span class="o">[</span><span class="s1">&#39;remember_token&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="932-remember-me-をテストする">9.3.2 [Remember me] をテストする</h3>
<ul>
<li><code>current_user</code> 内のある複雑な分岐処理についてはこれまでまったくテストが行われていない
<ul>
<li>テスト漏れが疑われる箇所に例外処理を入れて確認する
<ul>
<li>Ruby にも例外という概念があったのか</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 記憶トークンcookieに対応するユーザーを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">current_user</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@current_user</span> <span class="o">||=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elsif</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">cookies</span><span class="o">.</span><span class="n">signed</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">raise</span>       <span class="c1"># テストがパスすれば、この部分がテストされていないことがわかる</span>
</span></span><span class="line"><span class="cl">      <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticated?</span><span class="p">(</span><span class="n">cookies</span><span class="o">[</span><span class="ss">:remember_token</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">        <span class="vi">@current_user</span> <span class="o">=</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記コードでテストが通ってしまうので、コードは正常ではない</li>
<li><code>log_in_as</code> ヘルパーメソッドでは、<code>session[:user_id]</code> と定義しているため、<code>current_user</code> メソッドにある複雑な分岐処理を統合テストでチェックすることが非常に困難
<ul>
<li><code>current_user</code> を直接テストすれば、この制約を突破することができる</li>
</ul>
</li>
<li>永続的セッションのテストを追加
<ul>
<li>fixture で<code>user</code> 変数を定義</li>
<li>渡されたユーザーを <code>remember</code> メソッドで記憶</li>
<li><code>current_user</code> が渡されたユーザーと同じであることを確認</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsHelperTest</span> <span class="o">&lt;</span> <span class="no">ActionView</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">remember</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;current_user returns right user when session is nil&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_equal</span> <span class="vi">@user</span><span class="p">,</span> <span class="n">current_user</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">is_logged_in?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;current_user returns nil when remember digest is wrong&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">update_attribute</span><span class="p">(</span><span class="ss">:remember_digest</span><span class="p">,</span> <span class="no">User</span><span class="o">.</span><span class="n">digest</span><span class="p">(</span><span class="no">User</span><span class="o">.</span><span class="n">new_token</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_nil</span> <span class="n">current_user</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="94-最後に">9.4 最後に</h2>
<ul>
<li>基本的な認証機構に必要な残りの作業は、ログイン状態やログイン済みユーザー ID に基づいて、ページへのアクセス権限を制限する実装だけ</li>
</ul>
<h2 id="所感">所感</h2>
<p>cookies によるログイン状態の永続化を学ぶことができ、cookie まわりの良い復習になった章だった。脆弱性を考慮した実装も行ったので、セキュリティ周りの簡単な学びにも繋がった。</p>
<p>ここ数章は Rails を学ぶというより、Rails を通じて Web アプリケーションの作り方を学ぶことに楽しみを見出すようになってきたように思う。これまでは体系的に Web アプリケーション作成について学ぶ機会がなかったので、非常にありがたい。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第8章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter8/</link>
			<pubDate>Wed, 12 Feb 2020 21:39:45 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter8/</guid>
			<description>第8章 基本的なログイン機構 ログインの基本的な仕組みを実装する ここでいうログインの基本的な仕組みとは、ブラウザがログインしている状態を保持し、</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第8章-基本的なログイン機構">第8章 基本的なログイン機構</h1>
<ul>
<li>ログインの基本的な仕組みを実装する
<ul>
<li>ここでいうログインの基本的な仕組みとは、ブラウザがログインしている状態を保持し、ユーザーによってブラウザが閉じられた状態を破棄すると言った仕組みのこと</li>
</ul>
</li>
</ul>
<h2 id="81-セッション">8.1 セッション</h2>
<ul>
<li>HTTP はステートレスなプロトコルなため、ユーザーログインの必要な Web アプリケーションでは半永続的な接続であるセッションをコンピュータ間で設定する</li>
<li>Rails でセッションを実装する最も一般的方法は、cookie を使用すること
<ul>
<li><code>session</code> という Rails メソッドで一時セッションが作成可能
<ul>
<li>一時セッションはブラウザを閉じると自動的に終了する</li>
</ul>
</li>
</ul>
</li>
<li>セッションを RESTful なリソースとしてモデリングできると、他の RESTful リソースと統一的に理解できて便利
<ul>
<li>ログインページでは new で新規セッションを出力</li>
<li>そのページでログインすると create でセッションを実際に作成して保存</li>
<li>ログアウトすると destroy でセッションを破棄</li>
</ul>
</li>
</ul>
<h3 id="811-sessions-コントローラ">8.1.1 Sessions コントローラ</h3>
<ul>
<li>ログインとログアウトの要素を、Sessions コントローラの特定の REST アクションにぞれぞれ対応付ける
<ul>
<li>ログインのフォームは <code>new</code> アクションで処理</li>
<li><code>create</code> アクションに POST リクエストを送信すると実際にログインする</li>
<li><code>destroy</code> アクションに DELETE リクエストを送信すると、ログアウトする</li>
</ul>
</li>
<li>まずは Sessions コントローラと <code>new</code>  アクションを生成</li>
<li>リソースを追加して標準的な RESTful アクションを get できるようにする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span>   <span class="s1">&#39;static_pages#home&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/help&#39;</span><span class="p">,</span>    <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#help&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/about&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#about&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/contact&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#contact&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/signup&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;users#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>    <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">post</span>   <span class="s1">&#39;/login&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#create&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">delete</span> <span class="s1">&#39;/logout&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;sessions#destroy&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:users</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>Sessions コントローラのテストで名前付きルートを使うようにする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsControllerTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;should get new&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">login_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_response</span> <span class="ss">:success</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>rails routes</code> コマンドで全ルーティングが出力可能
<ul>
<li>このコマンドが業務で少しだけ触ったことがある
<ul>
<li>今なら Controller#Action の意味がよく分かる</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="812-ログインフォーム">8.1.2 ログインフォーム</h3>
<ul>
<li>ログインフォームを整える
<ul>
<li>ログインフォームとユーザー登録フォームは似ている</li>
<li>ログインに失敗したときに表示するエラーメッセージはフラッシュメッセージで対応する</li>
</ul>
</li>
<li>Rails では下記コードだけで「フォームの <code>action</code> は /users という URL への POST である」を自動的に判定する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">form_for</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>しかしセッションの場合は Sessions モデルが存在しないので、リソースの名前とそれに対応するURLを具体的に指定する必要がある</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">form_for</span><span class="p">(</span><span class="ss">:session</span><span class="p">,</span> <span class="ss">url</span><span class="p">:</span> <span class="n">login_path</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>適切な <code>form_for</code> を使うことで、ログインフォームを簡単に作成できる</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#34;Log in&#34;) %&gt;
&lt;h1&gt;Log in&lt;/h1&gt;

&lt;div class=&#34;row&#34;&gt;
  &lt;div class=&#34;col-md-6 col-md-offset-3&#34;&gt;
    &lt;%= form_for(:session, url: login_path) do |f| %&gt;

      &lt;%= f.label :email %&gt;
      &lt;%= f.email_field :email, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :password %&gt;
      &lt;%= f.password_field :password, class: &#39;form-control&#39; %&gt;

      &lt;%= f.submit &#34;Log in&#34;, class: &#34;btn btn-primary&#34; %&gt;
    &lt;% end %&gt;

    &lt;p&gt;New user? &lt;%= link_to &#34;Sign up now!&#34;, signup_path %&gt;&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre><h3 id="813-ユーザーの検索と認証">8.1.3 ユーザーの検索と認証</h3>
<ul>
<li>ログインでセッションを作成する場合に最初に行うのは、入力が無効な場合の処理</li>
<li>最小限の <code>create</code> アクションを Session コントローラで定義し、空の <code>new</code>  アクションと <code>destroy</code> アクションもついでに作成しておく</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>/sessions/new フォームで送信する <code>params</code> ハッシュでは <code>sessions</code> キーの下にメールアドレスとパスワードがある
<ul>
<li><code>create</code> アクションの中ではユーザーの認証に必要なあらゆる情報を <code>params</code> ハッシュから取り出せる</li>
</ul>
</li>
<li>以上を踏まえてユーザーをデータベースから見つけて検証する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:email</span><span class="o">].</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:password</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># ユーザーログイン後にユーザー情報のページにリダイレクトする</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># エラーメッセージを作成する</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>DBから取り出したメールアドレスを <code>downcase</code> で有効なメールアドレスに確実にマッチすうようにするのは Rails では定番の手法</li>
</ul>
<h3 id="814-フラッシュメッセージを表示する">8.1.4 フラッシュメッセージを表示する</h3>
<ul>
<li>ログインに失敗したときにはフラッシュメッセージを表示する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl"><span class="err">​</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="err">​</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:email</span><span class="o">].</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:password</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># ユーザーログイン後にユーザー情報のページにリダイレクトする</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;Invalid email/password combination&#39;</span> <span class="c1"># 本当は正しくない</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="err">​</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記コードにはフラッシュメッセージが一度表示されると残り続けるという問題がある
<ul>
<li>Home ページに移動しても残っている</li>
</ul>
</li>
</ul>
<h3 id="815-フラッシュのテスト">8.1.5 フラッシュのテスト</h3>
<ul>
<li>フラッシュメッセージが消えないのはバグなのでまずはテストを書く
<ul>
<li>ログイン用のパスを開く</li>
<li>新しいセッションのフォームが正しく表示されたことを確認する</li>
<li>わざと無効な <code>params</code> ハッシュを使ってセッション用パスに POST する</li>
<li>新しいセッションのフォームが再度送信され、フラッシュメッセージが追加されることを確認する</li>
<li>別のページにいったん移動する</li>
<li>移動先のページでフラッシュメッセージが表示されていないことを確認する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersLoginTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;login with invalid information&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">login_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;sessions/new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="n">login_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">session</span><span class="p">:</span> <span class="p">{</span> <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;sessions/new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">root_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">flash</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記テストがこれだけのコードで確認できるのはやはりすごい…</li>
<li><code>flash</code> を <code>flash.now</code> に変更すればレンダリングが終わっているページで特別にフラッシュメッセージを表示できる
<ul>
<li><code>flash.now</code> のメッセージはその後のリクエストが発生したときに消滅する</li>
</ul>
</li>
</ul>
<h2 id="82-ログイン">8.2 ログイン</h2>
<ul>
<li>実際にログイン中の状態での有効な値の送信をフォームで正しく扱えるようにする</li>
<li>cookie を使った一時セッションでユーザーをログインできるようにする
<ul>
<li>ブラウザを閉じると自動的に有効期限が切れるもの</li>
</ul>
</li>
<li>Session コントローラを生成した時点ですでにセッション用ヘルパーモジュールも自動生成されている
<ul>
<li>Rails のセッション用ヘルパーはビューにも自動的に読み込まれる</li>
<li>Rails の全コントローラの親クラスである Application コントローラにこのモジュールを読み込ませれば、どのコントローラでも使えるようになる
<ul>
<li>安易な共有だ</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o">&lt;</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
</span></span><span class="line"><span class="cl">  <span class="n">protect_from_forgery</span> <span class="ss">with</span><span class="p">:</span> <span class="ss">:exception</span>
</span></span><span class="line"><span class="cl">  <span class="kp">include</span> <span class="no">SessionsHelper</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="821-log_in-メソッド">8.2.1 log_in メソッド</h3>
<ul>
<li>Rails で事前定義済みの <code>session</code> メソッドを使って、単純なログインを行えるようにする
<ul>
<li><code>session</code> メソッドはハッシュのように扱える</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span></code></pre></div><ul>
<li>上記コードを実行すると、ユーザーのブラウザ内の一時 cookies に暗号化済みのユーザー ID が自動で作成される
<ul>
<li>どうやっているのか。。。</li>
<li>この一時 cookie はブラウザを閉じた瞬間に有効期限が終了する</li>
</ul>
</li>
<li>同じログイン手法を使い回せるように、Sessions ヘルパーに <code>log_in</code> という名前のメソッドを定義する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡されたユーザーでログインする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_in</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>session</code> メソッドで作成した一時 cookie は自動的に暗号化される
<ul>
<li>一時 cookie なので攻撃者に盗まれてもそれを使って本物のユーザーとしてログインすることは不可能</li>
</ul>
</li>
<li>ユーザーログインを行ってセッションの <code>create</code> アクションを完了し、ユーザーのプロフィールページにリダイレクトする</li>
</ul>
<h3 id="822-現在のユーザー">8.2.2 現在のユーザー</h3>
<ul>
<li>一時セッション内にあるユーザー ID を別のページで取り出す
<ul>
<li><code>current_user</code> メソッドでDBからユーザー名を取り出す</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">current_user</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>セッションにユーザー ID が存在しない場合、上記コードは <code>nil</code> を返す
<ul>
<li>DBへの問い合わせ回数を減らす</li>
</ul>
</li>
<li><code>User.find_by</code> の結果をインスタンス変数に保存
<ul>
<li>1リクエスト内におけるデータベースへの問い合わせは最初の1回だけとなる</li>
</ul>
</li>
<li>ユーザーがログインしているかどうかに応じてアプリケーションの動作を変更するための準備が整った</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡されたユーザーでログインする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_in</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 現在ログイン中のユーザーを返す (いる場合)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">current_user</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@current_user</span> <span class="o">||=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="823-レイアウトリンクを変更する">8.2.3 レイアウトリンクを変更する</h3>
<ul>
<li>ユーザーがログインしているといないときでレイアウトを変更してみる</li>
<li>考えられるのは、ERB コードの中で条件分岐すること
<ul>
<li>お手軽だけど変更が入ると辛そう。。。</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% if logged_in? %&gt;
  # ログインユーザー用のリンク
&lt;% else %&gt;
  # ログインしていないユーザー用のリンク
&lt;% end %&gt;
</code></pre><ul>
<li>上記コードを書くためには論理値を返す <code>logged_in?</code> メソッドが必要</li>
<li>ユーザーがログイン中の状態とは、session にユーザー id が存在している、すなわち <code>current_user</code> が <code>nil</code> でないこと</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡されたユーザーでログインする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_in</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 現在ログイン中のユーザーを返す (いる場合)</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">current_user</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">      <span class="vi">@current_user</span> <span class="o">||=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="nb">id</span><span class="p">:</span> <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># ユーザーがログインしていればtrue、その他ならfalseを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="o">!</span><span class="n">current_user</span><span class="o">.</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ログイン中のユーザー用のレイアウトのリンクを変更する</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;header class=&#34;navbar navbar-fixed-top navbar-inverse&#34;&gt;
  &lt;div class=&#34;container&#34;&gt;
    &lt;%= link_to &#34;sample app&#34;, root_path, id: &#34;logo&#34; %&gt;
    &lt;nav&gt;
      &lt;ul class=&#34;nav navbar-nav navbar-right&#34;&gt;
        &lt;li&gt;&lt;%= link_to &#34;Home&#34;, root_path %&gt;&lt;/li&gt;
        &lt;li&gt;&lt;%= link_to &#34;Help&#34;, help_path %&gt;&lt;/li&gt;
        &lt;% if logged_in? %&gt;
          &lt;li&gt;&lt;%= link_to &#34;Users&#34;, &#39;#&#39; %&gt;&lt;/li&gt;
          &lt;li class=&#34;dropdown&#34;&gt;
            &lt;a href=&#34;#&#34; class=&#34;dropdown-toggle&#34; data-toggle=&#34;dropdown&#34;&gt;
              Account &lt;b class=&#34;caret&#34;&gt;&lt;/b&gt;
            &lt;/a&gt;
            &lt;ul class=&#34;dropdown-menu&#34;&gt;
              &lt;li&gt;&lt;%= link_to &#34;Profile&#34;, current_user %&gt;&lt;/li&gt;
              &lt;li&gt;&lt;%= link_to &#34;Settings&#34;, &#39;#&#39; %&gt;&lt;/li&gt;
              &lt;li class=&#34;divider&#34;&gt;&lt;/li&gt;
              &lt;li&gt;
                &lt;%= link_to &#34;Log out&#34;, logout_path, method: :delete %&gt;
              &lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;% else %&gt;
          &lt;li&gt;&lt;%= link_to &#34;Log in&#34;, login_path %&gt;&lt;/li&gt;
        &lt;% end %&gt;
      &lt;/ul&gt;
    &lt;/nav&gt;
  &lt;/div&gt;
&lt;/header&gt;
</code></pre><ul>
<li>Bootstrap のドロップダウンメニュー機能を適用できる状態になったので、この機能を有効化するために Rails の <code>application.js</code> に Bootstrap の JavaScript ライブラリを追加する</li>
<li>ログインパスにアクセスして有効なユーザーとしてログインできるようになった</li>
</ul>
<h3 id="824-レイアウトの変更をテストする">8.2.4 レイアウトの変更をテストする</h3>
<ul>
<li>以下の流れの統合テストを作成する
<ul>
<li>ログイン用のパスを開く</li>
<li>セッション用パスに有効な情報を post する</li>
<li>ログイン用リンクが表示されなくなったことを確認する</li>
<li>ログアウト用リンクが表示されていることを確認する</li>
<li>プロフィール用リンクが表示されていることを確認する</li>
</ul>
</li>
<li>Rails ではテスト用データを fixture で作成できる
<ul>
<li><code>digest</code> メソッドを独自に定義し、 <code>password_digest</code> 属性をユーザーの fixure に追加する</li>
</ul>
</li>
<li><code>digest</code> メソッドは User モデルにクラスメソッドとして追加する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_save</span> <span class="p">{</span> <span class="nb">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span><span class="o">.</span><span class="n">downcase</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span>  <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">50</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">VALID_EMAIL_REGEX</span> <span class="o">=</span> <span class="sr">/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">255</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">format</span><span class="p">:</span> <span class="p">{</span> <span class="ss">with</span><span class="p">:</span> <span class="no">VALID_EMAIL_REGEX</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="ss">uniqueness</span><span class="p">:</span> <span class="p">{</span> <span class="ss">case_sensitive</span><span class="p">:</span> <span class="kp">false</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">has_secure_password</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">minimum</span><span class="p">:</span> <span class="mi">6</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡された文字列のハッシュ値を返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nc">User</span><span class="o">.</span><span class="nf">digest</span><span class="p">(</span><span class="n">string</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">cost</span> <span class="o">=</span> <span class="no">ActiveModel</span><span class="o">::</span><span class="no">SecurePassword</span><span class="o">.</span><span class="n">min_cost</span> <span class="p">?</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">::</span><span class="no">MIN_COST</span> <span class="p">:</span>
</span></span><span class="line"><span class="cl">                                                  <span class="no">BCrypt</span><span class="o">::</span><span class="no">Engine</span><span class="o">.</span><span class="n">cost</span>
</span></span><span class="line"><span class="cl">    <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">string</span><span class="p">,</span> <span class="ss">cost</span><span class="p">:</span> <span class="n">cost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>有効なユーザーを表す fixture を作成できるようになった</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">michael</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Michael Example</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">email</span><span class="p">:</span><span class="w"> </span><span class="l">michael@example.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">password_digest</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;%= User.digest(&#39;password&#39;) %&gt;</span><span class="w">
</span></span></span></code></pre></div><ul>
<li>fixture では ERB を利用できる</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= User.digest(&#39;password&#39;) %&gt;
</code></pre><ul>
<li>上記コードでテストユーザー用の有効なパスワードを作成できる</li>
<li>fixture ではハッシュ化されていない生のパスワードは参照できない</li>
<li>テストで fixture のデータを参照するように変更する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersLoginTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">setup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="n">users</span><span class="p">(</span><span class="ss">:michael</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;login with valid information&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">login_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">post</span> <span class="n">login_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">session</span><span class="p">:</span> <span class="p">{</span> <span class="ss">email</span><span class="p">:</span>    <span class="vi">@user</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                          <span class="ss">password</span><span class="p">:</span> <span class="s1">&#39;password&#39;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_redirected_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="n">follow_redirect!</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/show&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s2">&#34;a[href=?]&#34;</span><span class="p">,</span> <span class="n">login_path</span><span class="p">,</span> <span class="ss">count</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s2">&#34;a[href=?]&#34;</span><span class="p">,</span> <span class="n">logout_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_select</span> <span class="s2">&#34;a[href=?]&#34;</span><span class="p">,</span> <span class="n">user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="825-ユーザー登録時にログイン">8.2.5 ユーザー登録時にログイン</h3>
<ul>
<li>ユーザー登録中にログインを済ませる
<ul>
<li>Users コントローラの <code>create</code> アクションに <code>log_in</code> を追加するだけ</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">show</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Welcome to the Sample App!&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>テストにおいてユーザーがログイン中かどうかをチェックするため、<code>is_logged_in?</code> メソッドを定義しておく
<ul>
<li>テストのセッションにユーザーがあれば <code>true</code> を返し、それ以外の場合は <code>false</code> を返す</li>
</ul>
</li>
<li>ヘルパーメソッドはテストから呼び出せないので、 <code>current_user</code> は使えない
<ul>
<li><code>session</code> メソッドで代用する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;RAILS_ENV&#39;</span><span class="o">]</span> <span class="o">||=</span> <span class="s1">&#39;test&#39;</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ActiveSupport</span><span class="o">::</span><span class="no">TestCase</span>
</span></span><span class="line"><span class="cl">  <span class="n">fixtures</span> <span class="ss">:all</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># テストユーザーがログイン中の場合にtrueを返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">is_logged_in?</span>
</span></span><span class="line"><span class="cl">    <span class="o">!</span><span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">].</span><span class="n">nil?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>上記コードを使うと、ユーザー登録の終わったユーザーがログイン状態になっているかどうかを確認できる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersSignupTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;valid signup information&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">signup_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;User.count&#39;</span><span class="p">,</span> <span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">users_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="nb">name</span><span class="p">:</span>  <span class="s2">&#34;Example User&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;user@example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;password&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">follow_redirect!</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/show&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert</span> <span class="n">is_logged_in?</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h2 id="83-ログアウト">8.3 ログアウト</h2>
<ul>
<li>このアプリケーションではユーザーが明示的にログアウトするまではログイン状態を保てなくてはならない</li>
<li>ログアウト用リンクは作成済みなのでユーザーセッションを破棄するための有効なアクションをコントローラで作成する</li>
<li>RESTful ルールに従い、<code>destroy</code> アクションを作成する</li>
<li>ログアウト処理ではセッションからユーザー ID を削除する
<ul>
<li><code>delete</code> メソッドを実行するだけ</li>
<li>現在のユーザーを <code>nil</code> に設定できる</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="ss">:user_id</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>Session ヘルパーモジュールに配置する <code>log_out</code> メソッドとして上記コードを定義する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">SessionsHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 渡されたユーザーでログインする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_in</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">[</span><span class="ss">:user_id</span><span class="o">]</span> <span class="o">=</span> <span class="n">user</span><span class="o">.</span><span class="n">id</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># 現在のユーザーをログアウトする</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">log_out</span>
</span></span><span class="line"><span class="cl">    <span class="n">session</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="ss">:user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@current_user</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ここで定義した <code>log_out</code> メソッドは、Session コントローラの <code>destroy</code> アクションでも使用する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">SessionsController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find_by</span><span class="p">(</span><span class="ss">email</span><span class="p">:</span> <span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:email</span><span class="o">].</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">user</span> <span class="o">&amp;&amp;</span> <span class="n">user</span><span class="o">.</span><span class="n">authenticate</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:session</span><span class="o">][</span><span class="ss">:password</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="n">log_in</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="n">user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">.</span><span class="n">now</span><span class="o">[</span><span class="ss">:danger</span><span class="o">]</span> <span class="o">=</span> <span class="s1">&#39;Invalid email/password combination&#39;</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">destroy</span>
</span></span><span class="line"><span class="cl">    <span class="n">log_out</span>
</span></span><span class="line"><span class="cl">    <span class="n">redirect_to</span> <span class="n">root_url</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ログアウト機能をテストするために、ユーザーログインのテストを修正する
<ul>
<li>ログイン後、<code>delete</code> メソッドで DELETE リクエストをログアウト用パスに発行し、ユーザーがログアウトしてルート URL にリダイレクトされたことを確認する</li>
</ul>
</li>
<li>以上でサンプルアプリケーションの基本となるユーザー登録・ログイン・ログアウトの機能が完成</li>
</ul>
<h2 id="84-最後に">8.4 最後に</h2>
<ul>
<li>サンプルアプリケーションのログイン機構を実装した</li>
<li>次はセッションより長くログイン情報を維持する</li>
</ul>
<h2 id="所感">所感</h2>
<p>ついにログインの機構を実装した。session や cookie の話も出てきていよいよ Web アプリケーションの開発をしている感が増してきた。フラッシュメッセージという概念は初耳だったのだが、Rails アプリでは一般的に使われるものなのだろうか？また、ERB の中での条件分岐も一般的なのだろうか？単純な if 文であれば問題ないかもしれないが、少しでも分岐するとカオスなロジックが生まれる土壌になるのではと危惧してしまった。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第7章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter7/</link>
			<pubDate>Fri, 31 Jan 2020 22:28:48 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter7/</guid>
			<description>第7章 ユーザー登録 いよいよユーザー登録機能を追加 HTML フォームを使って Web アプリケーションに登録情報を送信する ユーザーを新規作成して情報を DB に保存</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第7章-ユーザー登録">第7章 ユーザー登録</h1>
<ul>
<li>いよいよユーザー登録機能を追加
<ul>
<li>HTML フォームを使って Web アプリケーションに登録情報を送信する</li>
<li>ユーザーを新規作成して情報を DB に保存する</li>
</ul>
</li>
</ul>
<h2 id="71-ユーザーを表示する">7.1 ユーザーを表示する</h2>
<ul>
<li>ユーザーの名前とプロフィール写真を表示するためのページを作成する</li>
</ul>
<h3 id="711-デバッグと-rails-環境">7.1.1 デバッグと Rails 環境</h3>
<ul>
<li>このアプリにおける初めての真に動的なページを作成する
<ul>
<li>Web サイトのレイアウトにデバッグ情報を追加する</li>
<li>ビルトインの <code>debug</code> メソッドと <code>param</code> 変数を使ってページにデバッグ情報が表示されるようになる</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  .
  .
  .
  &lt;body&gt;
    &lt;%= render &#39;layouts/header&#39; %&gt;
    &lt;div class=&#34;container&#34;&gt;
      &lt;%= yield %&gt;
      &lt;%= render &#39;layouts/footer&#39; %&gt;
      &lt;%= debug(params) if Rails.env.development? %&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><ul>
<li>本番環境でデバッグ情報を表示しないための <code>if Rails</code>
<ul>
<li>Rails には3つの環境がデフォルトで用意されている
<ul>
<li>テスト環境 (test)</li>
<li>開発環境 (development)</li>
<li>本番環境 (production)</li>
</ul>
</li>
</ul>
</li>
<li>デバッグ情報整形のために CSS も更新</li>
</ul>
<h3 id="712-users-リソース">7.1.2 Users リソース</h3>
<ul>
<li>DBに登録されているユーザー情報を Web アプリケーション上に表示する
<ul>
<li>REST アーキテクチャの慣習に従う
<ul>
<li>データの作成、表示、更新、削除をリソースとして扱う</li>
</ul>
</li>
</ul>
</li>
<li>ユーザーをリソースとみなす場合、id = 1 のユーザーを参照するということは、/users/1 という URL に対して GET リクエストを発行することを意味する
<ul>
<li>ここでの <code>show</code> というアクションは暗黙のリクエストになる</li>
<li>Rails のREST 機能が有効になっていると、GET リクエストは自動的に <code>show</code> アクションとして扱われる
<ul>
<li>すなわち Rails はREST 機能を有効無効にできるということか</li>
</ul>
</li>
</ul>
</li>
<li>/users/1 のURL を有効にするために、 <code>config/routes.rb</code> を更新</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span> <span class="s1">&#39;static_pages#home&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>  <span class="s1">&#39;/help&#39;</span><span class="p">,</span>    <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#help&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>  <span class="s1">&#39;/about&#39;</span><span class="p">,</span>   <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#about&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>  <span class="s1">&#39;/contact&#39;</span><span class="p">,</span> <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;static_pages#contact&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">get</span>  <span class="s1">&#39;/signup&#39;</span><span class="p">,</span>  <span class="ss">to</span><span class="p">:</span> <span class="s1">&#39;users#new&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">resources</span> <span class="ss">:users</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>この1行の追加で RESTful な Users リソースで必要となる全てのアクションが利用可能になる</li>
<li>ユーザー情報表示用の仮のビューを作成</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= @user.name %&gt;, &lt;%= @user.email %&gt;
</code></pre><ul>
<li>Users コントローラの <code>show</code> アクションに対応する <code>@user</code> 変数を定義する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">show</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>Users コントローラにリクエストが正常に送信されると、 <code>params[:id]</code> の部分はユーザー id の1に置き換わる
<ul>
<li>つまり、 <code>User.find(1)</code> と同じ</li>
</ul>
</li>
<li>ビューとアクションが定義されたので、/users/1 は動作するようになった</li>
</ul>
<h3 id="711-debugger-メソッド">7.1.1 debugger メソッド</h3>
<ul>
<li><code>byebug</code> gem でより直接的なデバッグが可能</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">show</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">debugger</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ターミナルで Rails コンソールのようにコマンドを呼び出すことが可能になる
<ul>
<li>gdb みたい</li>
</ul>
</li>
</ul>
<h3 id="714-gravatar-画像とサイドバー">7.1.4 Gravatar 画像とサイドバー</h3>
<ul>
<li>ユーザーのプロフィール写真で Gravatar を使用する</li>
<li><code>gravatar_for</code> ヘルパーメソッドで Gravatar の画像を利用できるようになる</li>
<li>Gravatar の URL はユーザーのメールアドレスを MD5 でハッシュ化している
<ul>
<li>Ruby では <code>Digest</code> ライブラリの <code>hexdigest</code> メソッドで MD5 のハッシュ化が実現できる</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">module</span> <span class="nn">UsersHelper</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1"># 引数で与えられたユーザーのGravatar画像を返す</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">gravatar_for</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">gravatar_id</span> <span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">MD5</span><span class="o">::</span><span class="n">hexdigest</span><span class="p">(</span><span class="n">user</span><span class="o">.</span><span class="n">email</span><span class="o">.</span><span class="n">downcase</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">gravatar_url</span> <span class="o">=</span> <span class="s2">&#34;https://secure.gravatar.com/avatar/</span><span class="si">#{</span><span class="n">gravatar_id</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">image_tag</span><span class="p">(</span><span class="n">gravatar_url</span><span class="p">,</span> <span class="ss">alt</span><span class="p">:</span> <span class="n">user</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="ss">class</span><span class="p">:</span> <span class="s2">&#34;gravatar&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>モックアップに近づけるため、ユーザーのサイドバーを作っていく</li>
<li>HTML 要素と CSS クラスを配置したことにより、プロフィールページに SCSS でスタイルを与えることができるようになった
<ul>
<li>これは Asset Pipeline でSass エンジンが使われている場合に限られる</li>
</ul>
</li>
</ul>
<h2 id="72-ユーザー登録フォーム">7.2 ユーザー登録フォーム</h2>
<ul>
<li>ユーザー登録フォームを作る</li>
</ul>
<h3 id="721-form_for-を使用する">7.2.1 form_for を使用する</h3>
<ul>
<li><code>form_for</code> ヘルパーメソッドをユーザー登録ページで使う
<ul>
<li>Active Record のオブジェクトを取り込み、そのオブジェクトの属性を使ってフォームを構築する</li>
</ul>
</li>
<li>ユーザー登録ページ /signup のルーティングはUsers コントローラーの <code>new</code> アクション紐付けられている
<ul>
<li><code>new</code> アクションに <code>@user</code> 変数を追加する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">show</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>フォームは SCSS で見栄えを整える</li>
</ul>
<h3 id="722-フォーム-html">7.2.2 フォーム HTML</h3>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= form_for(@user) do |f| %&gt;
</code></pre><ul>
<li>f というオブジェクトは、HTML フォーム要素に対応するメソッドが呼び出されると、@user の属性を設定するための HTML を返す</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= f.label :name %&gt;
&lt;%= f.text_field :name %&gt;
</code></pre><ul>
<li>上記では、User モデルの <code>name</code> 属性を設定するラベル付きテキストフィールド要素を作成するのに必要な HTML を返す
<ul>
<li>埋め込み Ruby からモデルの属性を設定できる…だと…</li>
</ul>
</li>
<li>テキストフィールドでは内容をそのまま表示</li>
<li>パスワードフィールドではセキュリティの都合上文字が隠蔽される
<ul>
<li>完璧な気遣い</li>
</ul>
</li>
<li>ユーザーの作成で重要なのは <code>input</code> ごとにある特殊な <code>name</code> 属性</li>
</ul>
<pre tabindex="0"><code>&lt;input id=&#34;user_name&#34; name=&#34;user[name]&#34; - - - /&gt;
</code></pre><ul>
<li>Rails は <code>name</code> の値を使い、初期化したハッシュを構成する
<ul>
<li>このハッシュは入力された値に基づいてユーザーを作成するときに使われる</li>
</ul>
</li>
<li>Rails は <code>form</code> タグを作成するときに <code>@user</code> オブジェクトを使う
<ul>
<li>すべての Ruby クラスは自分のクラスを知っているので、Rails は <code>@user</code> のクラスが User であることを認識する</li>
<li>また、<code>@user</code> は新しいユーザーなので、Rails は <code>post</code> メソッドを使ってフォームを構築すべきだと判断する</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;form action=&#34;https://sore8sore104te.com/users&#34; class=&#34;new_user&#34; id=&#34;new_user&#34; method=&#34;post&#34;&gt;
</code></pre><ul>
<li>上記 <code>class</code> と <code>id</code> 属性はアーキテクチャとしては基本的に無関係</li>
<li>/users に対して HTTP の POST リクエストを送信するということが大事</li>
</ul>
<h2 id="73-ユーザー登録失敗">7.3 ユーザー登録失敗</h2>
<ul>
<li>フォームを理解するにはユーザー登録の失敗のときが最も参考になる</li>
</ul>
<h3 id="731-正しいフォーム">7.3.1 正しいフォーム</h3>
<ul>
<li>/users への POST リクエストは <code>create</code> アクションに送られる
<ul>
<li><code>create</code> アクションでフォーム送信を受け取り、 <code>User.new</code> を使って新しいユーザーオブジェクトを作成し、ユーザーを保存し、再度の送信用のユーザー登録ページを表示する
<ul>
<li>やること多いな</li>
</ul>
</li>
</ul>
</li>
<li>ユーザー登録の失敗に対応できる <code>create</code> アクションを定義</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">show</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">new</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:user</span><span class="o">]</span><span class="p">)</span>    <span class="c1"># 実装は終わっていないことに注意!</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">      <span class="c1"># 保存の成功をここで扱う。</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>不正なデータでユーザー登録しようとするとエラーになる</li>
<li>パラメーターハッシュの <code>user</code> は Users コントローラに <code>params</code> として渡される
<ul>
<li>このハッシュのキーが、 <code>input</code> タグにあった <code>name</code> 属性の値になる</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;input id=&#34;user_email&#34; name=&#34;user[email]&#34; type=&#34;email&#34; /&gt;
</code></pre><ul>
<li>例えば <code>user[email]</code> は <code>user</code> ハッシュの <code>:email</code> キーの値と一致する</li>
<li>Rails は文字列ではなく <code>params[:user]</code> のようにシンボルとして Users コントローラにハッシュのキーを渡している</li>
<li>つまり、下記の2つのコードはほぼ同じである</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:user</span><span class="o">]</span><span class="p">)</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Foo Bar&#34;</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;foo@invalid&#34;</span><span class="p">,</span> <span class="ss">password</span><span class="p">:</span> <span class="s2">&#34;foo&#34;</span><span class="p">,</span> <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;bar&#34;</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>昔のバージョンの Rails では1つ目のコードでも動いたが、脆弱性があったため Rails 4.0 移行ではエラーとしている
<ul>
<li>Strong Parameters というテクニックで対策することを標準とした</li>
</ul>
</li>
</ul>
<h3 id="732-strong-parameters">7.3.2 Strong Parameters</h3>
<ul>
<li>値のハッシュを使って Ruby の変数を初期化することをマスアサインメントという</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:user</span><span class="o">]</span><span class="p">)</span>    <span class="c1"># 実装は終わっていないことに注意!</span>
</span></span></code></pre></div><ul>
<li><code>params</code> ハッシュ全体を初期化することは、ユーザーが送信したデータをまるごと <code>User.new</code> に渡していることになり、セキュリティ上極めて危険</li>
<li>以前のバージョンの Rails ではモデル層で <code>attr_accessible</code> を使うことで防止していたが、 Rails 4.0 ではコントローラ層で Strong Parameters というテクニックを使うことが推奨されている。
<ul>
<li>Strong Pararmeters を使うことにより、必須パラメータと許可されたパラメータを指定することが可能</li>
<li>さらに、<code>prams</code> ハッシュを丸ごと渡すとエラーが発生する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>上記の <code>params</code> ハッシュでは <code>:user</code> 属性を必須とし、名前、メールアドレス、パスワード、パスワードの確認をそれぞれ許可し、それ以外を許可しないようにしている</li>
<li>上記コードの戻り値は、許可された属性のみが含まれた <code>params</code> のハッシュ</li>
<li>これらのパラメータを使いやすくするために、<code>user_params</code> という外部メソッドを使うのが慣習
<ul>
<li>このメソッドは適切に初期化したハッシュを返し、<code>params[:user]</code> の代わりとして使われる
<ul>
<li>Rails でよく出てくる慣習の由来とは何なんだろう 🤔</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>この <code>user_params</code> メソッドは Users コントローラの内部でのみ実行され、Web 経由で外部ユーザーにさらされる必要はないため、Ruby の <code>private</code> キーワードを使って外部から使えないようにする
<ul>
<li>クラス内に唐突に現れる private 空間は inner class 的なものだろうか</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><h3 id="733-エラーメッセージ">7.3.3 エラーメッセージ</h3>
<ul>
<li>ユーザー登録に失敗したときのエラーメッセージ
<ul>
<li>Rails はこのようなメッセージを Users モデルの検証時に自動的に生成してくれる</li>
</ul>
</li>
<li>モデルの保存に失敗するうと、<code>@user</code> オブジェクトに関連付けられたエラーメッセージの一覧が生成される
<ul>
<li>ユーザーの <code>new</code> ページでエラーメッセージのパーシャルを出力する</li>
<li>パーシャルとは部分テンプレートのこと</li>
<li><code>form_control</code> という CSS クラスも一緒に追加することで、Bootstrap がうまく取り扱ってくれるようになる</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% provide(:title, &#39;Sign up&#39;) %&gt;
&lt;h1&gt;Sign up&lt;/h1&gt;

&lt;div class=&#34;row&#34;&gt;
  &lt;div class=&#34;col-md-6 col-md-offset-3&#34;&gt;
    &lt;%= form_for(@user) do |f| %&gt;
      &lt;%= render &#39;shared/error_messages&#39; %&gt;

      &lt;%= f.label :name %&gt;
      &lt;%= f.text_field :name, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :email %&gt;
      &lt;%= f.email_field :email, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :password %&gt;
      &lt;%= f.password_field :password, class: &#39;form-control&#39; %&gt;

      &lt;%= f.label :password_confirmation, &#34;Confirmation&#34; %&gt;
      &lt;%= f.password_field :password_confirmation, class: &#39;form-control&#39; %&gt;

      &lt;%= f.submit &#34;Create my account&#34;, class: &#34;btn btn-primary&#34; %&gt;
    &lt;% end %&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre><ul>
<li>Rails 全般の慣習として、複数のビューで使用されるパーシャルは専用の <code>shared</code> ディレクトリに置かれる</li>
<li>フォーム送信時にエラーメッセージを表示するためのパーシャル</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;% if @user.errors.any? %&gt;
  &lt;div id=&#34;error_explanation&#34;&gt;
    &lt;div class=&#34;alert alert-danger&#34;&gt;
      The form contains &lt;%= pluralize(@user.errors.count, &#34;error&#34;) %&gt;.
    &lt;/div&gt;
    &lt;ul&gt;
    &lt;% @user.errors.full_messages.each do |msg| %&gt;
      &lt;li&gt;&lt;%= msg %&gt;&lt;/li&gt;
    &lt;% end %&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;% end %&gt;
</code></pre><ul>
<li><code>pluuralize</code> という英語専用のテキストヘルパー
<ul>
<li>最初の引数に整数が与えられると、それに基づいて2番目の引数の英単語を複数形に変更したものを返す
<ul>
<li>なんて細かいヘルパーなのか</li>
</ul>
</li>
</ul>
</li>
<li>SCSS でエラーメッセージを整形し、無効なユーザー登録情報を送信したときにわかりやすいエラーメッセージを表示する準備は完了</li>
<li>ただし、<code>presence:true</code> によるバリデーションも、<code>has_secure_password</code> によるバリデーションも、空のパスワード(nil)を検知してしまうため、ユーザー登録フォームで空のパスワードを入力すると2つの同じエラーメッセージが表示されてしまう
<ul>
<li><code>allow_nil:true</code> というオプションでこの問題は解決可能</li>
</ul>
</li>
</ul>
<h3 id="734-失敗時のテスト">7.3.4 失敗時のテスト</h3>
<ul>
<li>Rails ではフォーム用のテストを書くことが可能
<ul>
<li>無効な送信をしたときの正しい振る舞いについてテストを書いていく</li>
</ul>
</li>
<li>新規ユーザー登録用の統合テストを生成</li>
<li>ユーザーが作成されないことを確認するテストから
<ul>
<li>現在のユーザー数を覚えた後にデータを投稿し、ユーザー数が変わらないかどうかを検証する
<ul>
<li><code>assert_no_difference</code> を使うのが慣習</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="74-ユーザー登録成功">7.4 ユーザー登録成功</h2>
<ul>
<li>新規ユーザーを実際にデータベースに保存できるようにする</li>
</ul>
<h3 id="741-登録フォームの完成">7.4.1 登録フォームの完成</h3>
<ul>
<li>まだ登録フォームは正常に動かない
<ul>
<li>Rails はデフォルトのアクションに対応するビューを表示しようとするが、<code>create</code>  アクションに対応するビューのテンプレートがないことが原因</li>
<li><code>create</code> アクションに対応するテンプレートを作成することも可能だが、Rails の一般的な慣習に倣いユーザー登録成功時にはページを描画せずに別のページにリダイレクトさせる
<ul>
<li>具体的には新規作成されたユーザーのプロフィールページへリダイレクト</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">redirect_to</span> <span class="vi">@user</span>
</span></span></code></pre></div><ul>
<li>これだけでリダイレクト可能
<ul>
<li>つよい</li>
</ul>
</li>
<li>上記コードは以下のコードと等価</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">redirect_to</span> <span class="n">user_url</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span>
</span></span></code></pre></div><ul>
<li>無事ユーザー登録→プロフィールページリダイレクトが実装できた</li>
</ul>
<h3 id="742-flash">7.4.2 flash</h3>
<ul>
<li>ユーザー情報登録完了後、表示されるページにメッセージを表示し、2度目以降にはそのページにメッセージを表示しないようにする
<ul>
<li>Rails では flash という変数を使用する</li>
</ul>
</li>
<li>ユーザー登録ページにフラッシュメッセージを追加する
<ul>
<li><code>:success</code> というキーに成功時のメッセージを代入</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">create</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">user_params</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="vi">@user</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">      <span class="n">flash</span><span class="o">[</span><span class="ss">:success</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&#34;Welcome to the Sample App!&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="n">redirect_to</span> <span class="vi">@user</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">      <span class="n">render</span> <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kp">private</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">user_params</span>
</span></span><span class="line"><span class="cl">      <span class="n">params</span><span class="o">.</span><span class="n">require</span><span class="p">(</span><span class="ss">:user</span><span class="p">)</span><span class="o">.</span><span class="n">permit</span><span class="p">(</span><span class="ss">:name</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">:password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="ss">:password_confirmation</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>flash</code> 変数に代入したメッセージは、リダイレクトした直後のページに表示できるようになる
<ul>
<li><code>flash</code> 内に存在するキーがあるかを調べ、もしあればその値をすべて表示するようにレイアウトを修正する</li>
</ul>
</li>
</ul>
<h3 id="743-実際のユーザー登録">7.4.3 実際のユーザー登録</h3>
<ul>
<li>実際にサンプルアプリケーションでユーザー登録を試してみる</li>
<li>ユーザー登録後に無事フラッシュメッセージが表示された！
<ul>
<li>僅かな修正だけでメッセージが表示される凄さよ</li>
</ul>
</li>
</ul>
<h3 id="744-成功時のテスト">7.4.4 成功時のテスト</h3>
<ul>
<li>有効な送信に対するテストを追加する</li>
<li><code>assert_difference</code> で有効なユーザー登録をテストする</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;test_helper&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">UsersSignupTest</span> <span class="o">&lt;</span> <span class="no">ActionDispatch</span><span class="o">::</span><span class="no">IntegrationTest</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="nb">test</span> <span class="s2">&#34;valid signup information&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">get</span> <span class="n">signup_path</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_difference</span> <span class="s1">&#39;User.count&#39;</span><span class="p">,</span> <span class="mi">1</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">post</span> <span class="n">users_path</span><span class="p">,</span> <span class="ss">params</span><span class="p">:</span> <span class="p">{</span> <span class="ss">user</span><span class="p">:</span> <span class="p">{</span> <span class="nb">name</span><span class="p">:</span>  <span class="s2">&#34;Example User&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;user@example.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">password</span><span class="p">:</span>              <span class="s2">&#34;password&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                         <span class="ss">password_confirmation</span><span class="p">:</span> <span class="s2">&#34;password&#34;</span> <span class="p">}</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">    <span class="n">follow_redirect!</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_template</span> <span class="s1">&#39;users/show&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>ユーザー登録成功後、どのテンプレートが表示されているかも検証する
<ul>
<li>ビューのテストもここでやることに驚き</li>
</ul>
</li>
</ul>
<h2 id="75-プロのデプロイ">7.5 プロのデプロイ</h2>
<ul>
<li>プロレベルのデプロイ
<ul>
<li>とは</li>
</ul>
</li>
<li>ユーザー登録をセキュアにするために本番用のアプリケーションに機能を追加する</li>
</ul>
<h3 id="751-本番環境での-ssl">7.5.1 本番環境での SSL</h3>
<ul>
<li>サンプルアプリケーションのセキュリティの欠陥
<ul>
<li>SSL を使用していないこと
<ul>
<li>たしかに</li>
</ul>
</li>
</ul>
</li>
<li><code>production.rb</code> という本番用の設定ファイルを修正するだけ
<ul>
<li>まじかよ。。。</li>
</ul>
</li>
<li>Heroku では SSL の使用をブラウザに強制しない
<ul>
<li>SSL を強制するように変更する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">configure</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Force all access to the app over SSL, use Strict-Transport-Security,</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># and use secure cookies.</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">force_ssl</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl">  <span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>サーバーの SSL セットアップ
<ul>
<li>Heroku の SSL 証明書に便乗する
<ul>
<li>Heroku のサブドメインでのみ有効な手段</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="752--本番環境用の-web-サーバー">7.5.2  本番環境用の Web サーバー</h3>
<ul>
<li>Heroku のデフォルト Web サーバーは WEBrick だが、著しいトラフィックを扱うのには適していない
<ul>
<li>Puma に置き換える
<ul>
<li>Puma は聞いたことがある！</li>
</ul>
</li>
</ul>
</li>
<li>Rails 5 では Puma はデフォルトで Gemfile に追加されている
<ul>
<li>つよい</li>
</ul>
</li>
<li><code>config/puma.rb</code> を修正</li>
<li>Heroku 上で Puma のプロセスを走らせるために<code>./Procfile</code> を作成</li>
</ul>
<h3 id="753-本番環境へのデプロイ">7.5.3 本番環境へのデプロイ</h3>
<ul>
<li>heroku へデプロイして完了！と思いきやここでドはまり</li>
<li>エラーでアプリが起動しない</li>
<li><code>heroku logs --tail</code> でログを見る
<ul>
<li><code>Address already in use - bind(2) for 0.0.0.0:xxxxx (Errno::EADDRINUSE)</code></li>
<li>どうやらポートがすでに使われているらしい
<ul>
<li>しかし何もしていないぞ…？（何かしているやつのセリフ）</li>
</ul>
</li>
</ul>
</li>
<li>色々ググって格闘し、<code>config/puma.rb</code> の設定ミスと判明</li>
<li>デフォルトの設定にチュートリアル記載の設定を追記したが、実は置き換えが正解だったようだ
<ul>
<li>中身をきちんと呼んでいればポート番号の重複に気づけたのではと反省</li>
</ul>
</li>
<li>ようやく heroku でも起動してめでたしめでたし</li>
</ul>
<h2 id="所感">所感</h2>
<p>ユーザー登録という機能を実装するために、わずかな量のコードしか追加していない。Rails の真骨頂を見たような章だった。一方、強力すぎるがゆえに変更には弱いのではという印象が強まってきた。短期立ち上げが必要でかつ継続的開発を行わない Web アプリにおいては Rails は有力な候補になるのではと感じている。</p>
<p>また、「Rails の慣習」というワードがよく出てきたのもこの章の特徴であったように思える。慣習とはどこからやってくるのか 🤔</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第6章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter6/</link>
			<pubDate>Fri, 17 Jan 2020 20:37:02 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter6/</guid>
			<description>第6章 ユーザーのモデルを作成する 第6章から第12章を通して Rails のログインと認証システムを一通り開発する 第6章ではユーザー用のデータモデルの作成</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第6章-ユーザーのモデルを作成する">第6章 ユーザーのモデルを作成する</h1>
<ul>
<li>第6章から第12章を通して Rails のログインと認証システムを一通り開発する</li>
<li>第6章ではユーザー用のデータモデルの作成と、データを保存する手段の確保について</li>
</ul>
<h2 id="61-user-モデル">6.1 User モデル</h2>
<ul>
<li>ユーザー登録のために必要なのは、まずは情報を保存するためのデータ構造を作成すること</li>
<li>Rails ではデータモデルとして扱うデフォルトのデータ構造のことをモデルと呼ぶ
<ul>
<li>データ永続化のデフォルトの選択肢はデータベース</li>
<li>データベースとやりとりするデフォルトのライブラリは Active Record
<ul>
<li>SQLを意識しなくてよい
<ul>
<li>意識しなくてよいことのメリットよりも、ブラックボックス化するデメリットの方が大きいと思えてならない</li>
</ul>
</li>
</ul>
</li>
<li>Rails にはマイグレーション機能もある</li>
<li>データ定義を Ruby で記述可能
<ul>
<li>Rails はデータベースの細部をほぼ完全に隠蔽し、切り離してくれる</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="611-データベースの移行">6.1.1 データベースの移行</h3>
<ul>
<li>永続化可能なユーザーのモデルを作成する</li>
<li>モデルを作成するときには <code>generate model</code> コマンドを使用する
<ul>
<li>Rails の慣習として、コントローラ名には複数形を、モデル名には単数形を用いる</li>
<li>マイグレーションファイルやテストクラスも作成される便利コマンド</li>
<li>マイグレーションファイルによりインクリメンタルな変更が可能になる</li>
<li>マイグレーション自体はデータベースに与える変更を定義した <code>change</code> メソッドの集合体</li>
<li><code>t.timestamp</code> により <code>created_at</code> と <code>updated_at</code> のマジックカラムが生成される
<ul>
<li>このカラム名、業務で見たことのあるやつだ…！！</li>
<li>作成・更新時に自動更新される</li>
</ul>
</li>
</ul>
</li>
<li>マイグレーションは <code>rails db:migrate</code> で実行可能
<ul>
<li><code>db/schema.rb</code> が更新される</li>
</ul>
</li>
<li><code>rails db:rollback</code> でもとに戻すことも可能
<ul>
<li>ロールバックすると <code>db/schema.rb</code> の内容も元に戻る</li>
<li>中では <code>drop_table</code> を呼び出している
<ul>
<li>こわやこわや</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="612-model-ファイル">6.1.2 model ファイル</h3>
<ul>
<li>以後モデル用ファイルを理解することに専念する</li>
</ul>
<h3 id="613-ユーザーオブジェクトを作成する">6.1.3 ユーザーオブジェクトを作成する</h3>
<ul>
<li>Rails コンソールをサンドボックスモードで起動
<ul>
<li>すべての変更は終了時にロールバックされる</li>
</ul>
</li>
<li><code>User.new</code> を引数無しで呼んだ場合はすべての属性が <code>nil</code></li>
<li><code>user.valid?</code> でオブジェクトの有効性が確認できる</li>
<li>DBに保存するためには <code>user.save</code> する必要がある
<ul>
<li>これだけでSQLが実行される</li>
<li>当然クエリは指定できない</li>
</ul>
</li>
<li>Active Record では <code>User.create</code> でモデルの生成と保存を同時に行うことが可能
<ul>
<li>相変わらず強力。。。</li>
<li><code>destroy</code> で delete 可能
<ul>
<li>SQLはもちろん自動的に実行される</li>
<li><code>destroy</code> しても削除されたオブジェクトはまだメモリ上に残っている点に注意
<ul>
<li>本当に削除されたのかはどうやって分かるのか？？</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="614-ユーザーオブジェクトを検索する">6.1.4 ユーザーオブジェクトを検索する</h3>
<ul>
<li>Active Record ではオブジェクトを検索する方法が複数ある
<ul>
<li><code>find</code> メソッドで結果が得られなかったときは例外が発生する
<ul>
<li><code>ActiveRecord::RecordNotFound</code></li>
</ul>
</li>
<li>属性でユーザーを検索することも可能</li>
<li><code>find</code> だけでなく <code>first</code> や <code>all</code> もある</li>
</ul>
</li>
</ul>
<h3 id="615-ユーザーオブジェクトを更新する">6.1.5 ユーザーオブジェクトを更新する</h3>
<ul>
<li>基本的な更新方法は2つ
<ul>
<li>ひとつは属性を個別に代入する
<ul>
<li>save を忘れずに</li>
</ul>
</li>
<li>もうひとつは <code>update_attributes</code> を使う方法</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">&gt;</span>&gt; user.update_attributes<span class="o">(</span>name: <span class="s2">&#34;The Dude&#34;</span>, email: <span class="s2">&#34;dude@abides.org&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl"><span class="go">=&gt; true
</span></span></span><span class="line"><span class="cl"><span class="go"></span><span class="gp">&gt;</span>&gt; user.name
</span></span><span class="line"><span class="cl"><span class="go">=&gt; &#34;The Dude&#34;
</span></span></span><span class="line"><span class="cl"><span class="go"></span><span class="gp">&gt;</span>&gt; user.email
</span></span><span class="line"><span class="cl"><span class="go">=&gt; &#34;dude@abides.org&#34;
</span></span></span></code></pre></div><ul>
<li><code>update_attributes</code> メソッドは属性のハッシュを受け取り、成功時には更新と保存を続けて同時に行う
<ul>
<li>どんな SQL が吐かれているのか？単純に update しているだけ？</li>
<li>検証に一つでも失敗すると、この処理は成功しない</li>
</ul>
</li>
</ul>
<h2 id="62-ユーザーを検証する">6.2 ユーザーを検証する</h2>
<ul>
<li>User モデルの属性に制限を設ける
<ul>
<li>Active Record の Validationを使う</li>
</ul>
</li>
</ul>
<h3 id="621-有効性を検証する">6.2.1 有効性を検証する</h3>
<ul>
<li>バリデーション機能実装には TDD がぴったり</li>
<li>User 用のテストを書いていく</li>
<li><code>setup</code> メソッド
<ul>
<li>各テストの直前に実行される
<ul>
<li>よくあるテスティングフレームワークと同じ</li>
</ul>
</li>
</ul>
</li>
<li><code>rails test:models</code> コマンドでモデルのテストだけが実行可能</li>
</ul>
<h3 id="622-存在性を検証する">6.2.2 存在性を検証する</h3>
<ul>
<li>もっとも基本的なバリデーションは存在性</li>
<li>User モデルに存在性に関するテストを追加する</li>
<li><code>validates</code> メソッドに <code>presence:true</code> の引数を与えると、属性の存在を検査できる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>変数が有効かどうかを <code>valid?</code> メソッドでチェック可能になる
<ul>
<li>すなわち、name 属性が空の場合は検証に失敗するということ</li>
<li><code>erros.full_messages</code> を使えばさらに詳細なエラーが確認できる</li>
<li><code>errors.messages</code> だとハッシュでエラー詳細が確認可能</li>
</ul>
</li>
</ul>
<h3 id="623-長さを検証する">6.2.3 長さを検証する</h3>
<ul>
<li>User モデルの属性に名前の長さの制限を加える</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span>  <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">50</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">255</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>valid?</code> メソッドで長さも検証可能になる</li>
<li>検証に失敗した場合は同様に <code>errors.messages</code> で詳細が確認可能</li>
</ul>
<h3 id="624-フォーマットを検証する">6.2.4 フォーマットを検証する</h3>
<ul>
<li>メールアドレスの有効性を検証する
<ul>
<li>パターンが多いので大変</li>
</ul>
</li>
<li>メールアドレスのフォーマット検証用に <code>format</code> というオプションを使用する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="nb">format</span><span class="p">:</span> <span class="p">{</span> <span class="ss">with</span><span class="p">:</span> <span class="sr">/&lt;regular expression&gt;/</span> <span class="p">}</span>
</span></span></code></pre></div><ul>
<li>引数に正規表現を取る
<ul>
<li><a href="https://rubular.com/">Rubular</a> というサイトで学習可能
<ul>
<li>正規表現を今まで避けてきたけどやってみると結構わかりやすい気がしてきた</li>
</ul>
</li>
</ul>
</li>
<li>User モデルに検証用定数を追加
<ul>
<li>大文字で始まる名前は Ruby では定数を意味する</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl">  <span class="no">VALID_EMAIL_REGEX</span> <span class="o">=</span> <span class="sr">/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">255</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">format</span><span class="p">:</span> <span class="p">{</span> <span class="ss">with</span><span class="p">:</span> <span class="no">VALID_EMAIL_REGEX</span> <span class="p">}</span>
</span></span></code></pre></div><ul>
<li>ただし、上記パターンでは <code>foo@bar..com</code> を検出できないので、以下のように変更する</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">50</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">VALID_EMAIL_REGEX</span> <span class="o">=</span> <span class="sr">/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span>   <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">255</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">format</span><span class="p">:</span>     <span class="p">{</span> <span class="ss">with</span><span class="p">:</span> <span class="no">VALID_EMAIL_REGEX</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>()</code> と <code>*</code> がポイント
<ul>
<li><code>()</code> でグループ化して</li>
<li><code>*</code> は直前の文字がないか、直前の文字が1つ以上連続するかをチェック</li>
</ul>
</li>
</ul>
<h3 id="625-一意性を検証する">6.2.5 一意性を検証する</h3>
<ul>
<li>メールアドレスの一意性を検証する
<ul>
<li><code>validates</code> メソッドの <code>:unique</code> オプションを使用</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">test</span> <span class="s2">&#34;email addresses should be unique&#34;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">duplicate_user</span> <span class="o">=</span> <span class="vi">@user</span><span class="o">.</span><span class="n">dup</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@user</span><span class="o">.</span><span class="n">save</span>
</span></span><span class="line"><span class="cl">    <span class="n">assert_not</span> <span class="n">duplicate_user</span><span class="o">.</span><span class="n">valid?</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li><code>@user.dup</code> で <code>@user</code> と同じメールアドレスのユーザーは作成できないことをテストする</li>
<li>上記テストをパスさせるために、 User モデルの <code>email</code> のバリデーションに <code>uniquness:true</code> を追加する</li>
<li>通常メールアドレスでは大文字小文字が区別されないため、検証でもこの点を考慮する必要がある</li>
<li>バリデーションを <code>uniqueness: { case_sensitive: false }</code> に変更すると、 <code>uniquness:true</code> のまま大文字小文字を区別しなくなる</li>
<li>しかしこの時点では Active Record がデータベースレベルでは一意性を保証していないという問題が残る
<ul>
<li>データベースレベルでも一意性を強制することで解決する
<ul>
<li>具体的には email カラムにインデックスを追加し、そのインデックスが一位であるようにする</li>
</ul>
</li>
</ul>
</li>
<li>email インデックスの追加にはデータモデリングの変更が必要
<ul>
<li>Rails ではマイグレーションでインデックスを追加する
<ul>
<li><code>migration</code> ジェネレーターを使用</li>
<li>まさかマイグレーションファイルを用意し、 Rails のメソッドでインデックスを追加できるとは。。。</li>
</ul>
</li>
</ul>
</li>
<li>この時点では DB 用のサンプルデータが含まれている fixture 内で一意性の制限が保たれていないためテストが通らない
<ul>
<li>fixutre とは
<ul>
<li>第8章で再登場するので今回はスルー</li>
</ul>
</li>
</ul>
</li>
<li>データベースのアダプタによっては常に大文字小文字を区別するインデックスを使っているとは限らない
<ul>
<li>DBに保存される直前にすべての文字列を小文字に変換する</li>
<li>Active Record のコールバックメソッドを使用
<ul>
<li>特定の時点で呼び出されるメソッド</li>
<li>今回はオブジェクトが保存される時点で処理を実行したいので <code>before_save</code> を使用する</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
</span></span><span class="line"><span class="cl">  <span class="n">before_save</span> <span class="p">{</span> <span class="nb">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span><span class="o">.</span><span class="n">downcase</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span>  <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">50</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="no">VALID_EMAIL_REGEX</span> <span class="o">=</span> <span class="sr">/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i</span>
</span></span><span class="line"><span class="cl">  <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence</span><span class="p">:</span> <span class="kp">true</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">maximum</span><span class="p">:</span> <span class="mi">255</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="nb">format</span><span class="p">:</span> <span class="p">{</span> <span class="ss">with</span><span class="p">:</span> <span class="no">VALID_EMAIL_REGEX</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">                    <span class="ss">uniqueness</span><span class="p">:</span> <span class="p">{</span> <span class="ss">case_sensitive</span><span class="p">:</span> <span class="kp">false</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>左辺の <code>self</code> は省略できないが、右辺の <code>self</code> は省略可能</li>
<li><code>self.email = email.downcase</code> は <code>email.downcase!</code> と書いても結果は変わらない
<ul>
<li><code>downcase!</code> は属性を直接変更できる</li>
</ul>
</li>
</ul>
<h2 id="63-セキュアなパスワードを追加する">6.3 セキュアなパスワードを追加する</h2>
<ul>
<li>セキュアパスワード
<ul>
<li>ユーザーにパスワードとパスワードの確認を入力させる</li>
<li>パスワードをハッシュ化したものをDBに保存する</li>
</ul>
</li>
<li>ユーザーの認証手順
<ul>
<li>パスワードの送信</li>
<li>ハッシュ化</li>
<li>DB内のハッシュ化された値との比較
<ul>
<li>比較結果が一致すれば送信されたパスワードが正しいと認識される</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="631-ハッシュ化されたパスワード">6.3.1 ハッシュ化されたパスワード</h3>
<ul>
<li>セキュアなパスワード実装は <code>has_secure_password</code> という Rails メソッドを呼び出すだけでほとんど終わってしまう
<ul>
<li>相変わらずなんて強力な。。。</li>
</ul>
</li>
<li>User モデルに <code>has_secure_password</code> メソッドを追加
<ul>
<li>セキュアにハッシュ化したパスワードをDB内の <code>password_digest</code> という属性に保存できるようになる</li>
<li><code>password</code> と <code>password_confirmation</code> という仮想的なペア属性が使えるようになる
<ul>
<li>存在性と値が一致するかどうかのバリデーションも追加される</li>
</ul>
</li>
<li><code>authenticate</code> メソッドが使えるようになる</li>
</ul>
</li>
<li>「この魔術的な <code>has_secure_password</code> 機能を使えるようにするには一つだけ条件があります。」
<ul>
<li>自分で魔術って言っちゃったよ</li>
<li>モデル内に <code>password_digest</code> という属性が含まれていることが条件</li>
</ul>
</li>
<li><code>password_digest</code> カラム用のマイグレーションを実施する
<ul>
<li>末尾に <code>to_users</code> を付けたマイグレーション名にしておくと、 <code>users</code> テーブルにカラムを追加するマーグレーションが Rails によって自動的に追加される
<ul>
<li>どこまでやってくれるんだ Rails は。。。</li>
</ul>
</li>
</ul>
</li>
<li><code>has_secure_password</code> を使ってパスワードをハッシュ化するためには、ハッシュ関数の bcrypt が必要
<ul>
<li>bcrypt gem を Gemfile に追加しておく</li>
</ul>
</li>
</ul>
<h3 id="632-ユーザーがセキュアなパスワードを持っている">6.3.2 ユーザーがセキュアなパスワードを持っている</h3>
<ul>
<li>テストが通るようにしていく
<ul>
<li><code>has_secure_password</code> には仮想的な <code>password</code> 属性と <code>password_confirmation</code> 属性に対してバリデーションする機能が強制的に追加されているため、その対応が必要</li>
</ul>
</li>
</ul>
<h3 id="633-パスワードの最小文字数">6.3.3 パスワードの最小文字数</h3>
<ul>
<li>Rails でパスワードの長さを設定する方法はたくさんある
<ul>
<li>たくさんある。。。</li>
<li>簡単でないことと最小文字数（6文字）を設定する</li>
</ul>
</li>
<li><code>maximum</code> と同様に <code>minimum</code> というオプションで最小文字数のバリデーションを実装できる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">validates</span> <span class="ss">:password</span><span class="p">,</span> <span class="ss">length</span><span class="p">:</span> <span class="p">{</span> <span class="ss">minimum</span><span class="p">:</span> <span class="mi">6</span> <span class="p">}</span>
</span></span></code></pre></div><ul>
<li><code>has_secure_password</code> メソッドは存在性のバリデーションもしてくれるが、新しくレコードが追加されたときだけに適用される
<ul>
<li>更新はバリデーションが効かない</li>
</ul>
</li>
</ul>
<h3 id="634-ユーザーの作成と認証">6.3.4 ユーザーの作成と認証</h3>
<ul>
<li>User モデルの基本部分が完成
<ul>
<li>Web からのユーザー登録はまだできないので、Rails コンソールから手動で追加する</li>
<li>追加後に <code>password_digest</code> 属性が参照可能</li>
<li>さらに <code>authenticate</code> メソッドに正しいパスワードを渡すと、モデルがユーザーオブジェクトを返す</li>
</ul>
</li>
</ul>
<h2 id="64-最後に">6.4 最後に</h2>
<ul>
<li>ゼロから User モデルを作成し、name, email, password の各属性を追加、さらにそれらのバリデーションも追加した</li>
<li>パスワードをセキュアに認証できる機能も実装</li>
<li>これらが12行で実現できた
<ul>
<li>Rails 恐ろしや</li>
</ul>
</li>
</ul>
<h1 id="所感">所感</h1>
<p>Rails チュートリアルもようやく折返し地点まできた。今まで避けてきた正規表現にトライし、見た目ほど難しくないということが実感できたのが大きな収穫だった。Rails は相変わらず強力な機能を発揮している。わずか12行のコードで多機能なモデルを構築できたことには驚きしかない。当初は強力すぎるがゆえにマイナスの印象が強かった Rails だが、徐々に「適切に使えばこれほど便利なものはない」という認識に変わりつつある。それがどれほど難しいかはプロダクトコードで使用してみないと分からないのだろうが。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第5章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter5/</link>
			<pubDate>Sat, 04 Jan 2020 22:23:35 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter5/</guid>
			<description>第5章 レイアウトを作成する サンプリアプリケーションにレイアウトを追加する HTML も CSS も体系的に学習したことはないので勉強になりそう 5.1 構造を追加する</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第5章-レイアウトを作成する">第5章 レイアウトを作成する</h1>
<ul>
<li>サンプリアプリケーションにレイアウトを追加する</li>
<li>HTML も CSS も体系的に学習したことはないので勉強になりそう</li>
</ul>
<h2 id="51-構造を追加する">5.1 構造を追加する</h2>
<ul>
<li>レイアウトに構造と CSS を与えて最小限のスタイルを追加する</li>
<li>Bootstrap も利用する</li>
</ul>
<h3 id="511-ナビゲーション">5.1.1 ナビゲーション</h3>
<ul>
<li><code>application.html.erb</code> に HTML 構造を追加</li>
<li>Rails はデフォルトで HTML5
<ul>
<li>HTML5 への対応できていない IE9 への対応が含まれている…</li>
<li><code>&lt;!--[if lt IE 9]&gt;</code> という見慣れない構文
<ul>
<li>Rails の機能ではない</li>
<li>条件付きコメント
<ul>
<li>他ブラウザに影響を与えずに IE9 未満の場合にのみ適用される</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><code>header</code> タグはページの上部に来るべき要素を表す</li>
<li>すべての HTML 要素にはクラスと id の両方を指定することができる
<ul>
<li>これらは単なるラベル</li>
<li>クラスはページ内で何度でも使える</li>
<li>id は一度しか使えない</li>
</ul>
</li>
<li><code>div</code> タグは一般的な表示領域を示し、要素を別々のパーツに分けるときに使用される</li>
<li>リンク生成用の Rails ヘルパーである <code>link_to</code>
<ul>
<li>引数は順にリンクテキスト、URL、オプションハッシュ</li>
</ul>
</li>
<li><code>nav</code> タグは「その内側がナビゲーションリンクである」と明示的に伝えている</li>
<li><code>image_tag</code> ヘルパーで Rails は該当する画像ファイルをアセットパイプラインを通して <code>app/assets/images/</code> ディレクトリ配下から探す</li>
<li>カスタム CSS を使用していない Home ページが完成</li>
</ul>
<p><img src="./screenshot.png" alt="image"></p>
<ul>
<li><code>image_tag</code> の効果
<ul>
<li>重複しないような画像ファイル名を Rails が付けている</li>
<li><code>src</code> 属性には <code>images</code> というディレクトリ名が含まれていない
<ul>
<li>高速化のため</li>
<li>Rails は <code>assets</code> ディレクトリ直下の画像を <code>app/assets/images</code> ディレクトリにある画像と紐付けている
<ul>
<li>これにより、ブラウザから見るとすべてのファイルが同じディレクトリにあるように見え、ファイルをより高速にブラウザに渡すことができる</li>
</ul>
</li>
</ul>
</li>
<li><code>alt</code> 属性は画像がない場合に表示される文字列</li>
</ul>
</li>
</ul>
<h3 id="512-bootstrap-とカスタム-css">5.1.2 Bootstrap とカスタム CSS</h3>
<ul>
<li>Bootstrap を使用してアプリケーションをレスポンシブルデザインにできる</li>
<li><code>bootstrap-sass</code> gem で Rails アプリケーションに Bootstrap を導入できる
<ul>
<li>また gem か</li>
</ul>
</li>
<li>Bootstrap は LESS CSS 言語を使用</li>
<li>Rails の Asset Pipline はデフォルトで Sass 言語を使っている
<ul>
<li>Sass とは？
<ul>
<li>SaaS ではない</li>
<li><code>Syntactically Awesome Stylesheets</code>
<ul>
<li>スタイルシート言語</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><code>bootstrap-sass</code> は LESS を Sass へ変換する</li>
<li><code>rails generate</code> でコントローラごとに分けられた CSS ファイルが自動的に生成されるが、正しい順番で読み込ませるのは至難の技らしい
<ul>
<li>なぜ自動生成したのか 🤔</li>
</ul>
</li>
<li>カスタム CSS ファイルを作成</li>
<li><code>app/assets/styleseets</code> は Asset Pipeline の一部
<ul>
<li>このディレクトリにあるスタイルシートは <code>application.css</code> の一部として Web サイトのレイアウトに読み込まれる</li>
</ul>
</li>
<li>Bootstrap CSS のフレームワークを導入してアプリケーションに反映
<ul>
<li>ボタンが表示されて色使いが変わった！</li>
</ul>
</li>
<li>さらに CSS を追加して色々確認</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scss" data-lang="scss"><span class="line"><span class="cl"><span class="nc">.center</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="na">text-align</span><span class="o">:</span> <span class="ni">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li><code>.center</code> の <code>.</code> はこのルールがクラス全体に対してスタイルを適用することを示す</li>
<li>冒頭が <code>#</code> で始まる場合は、そのルールが CSS の id に対してスタイルを適用することを示す</li>
<li>タイポグラフィーでテキストの見栄えを良くする</li>
<li>ロゴも追加する
<ul>
<li>CSS はほとんど触ったことがないので新鮮</li>
</ul>
</li>
</ul>
<h3 id="513-パーシャル-partial">5.1.3 パーシャル (partial)</h3>
<ul>
<li>partial という機能
<ul>
<li>HTML shim を隠すことができる</li>
<li>HTML ヘッダーを論理的な単位として分けて一箇所にまとめられる</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= render &#39;layouts/shim&#39; %&gt;
</code></pre><ul>
<li><code>render</code> という Rails ヘルパーで HTML shim のスタイルシート行を置換</li>
<li><code>app/views/layouts/_shim.html.erb</code> というファイルを探してその内容を評価し、結果をビューに挿入
<ul>
<li>先頭のアンダースコアはパーシャルで使う普遍的な命名規則</li>
</ul>
</li>
<li><code>application.html.erb</code> から ヘッダー、フッター、shim を <code>render</code> に切り出していく
<ul>
<li>手動生成が一般的</li>
</ul>
</li>
</ul>
<h2 id="52-sass-とアセットパイプライン">5.2 Sass とアセットパイプライン</h2>
<h3 id="521-アセットパイプライン">5.2.1 アセットパイプライン</h3>
<ul>
<li>アセットディレクトリ
<ul>
<li>Rails のアセットパイプラインには静的ファイルを目的別に分類する3つのディレクトリがある
<ul>
<li><code>app/assets</code>
<ul>
<li>現在のアプリケーション固有のアセット</li>
</ul>
</li>
<li><code>lib/assets</code>
<ul>
<li>開発者によって作成されたライブラリ用のアセット</li>
</ul>
</li>
<li><code>vendor/assets</code>
<ul>
<li>サーボパーティのアセット</li>
</ul>
</li>
</ul>
</li>
<li>それぞれにアセットクラス用のサブディレクトリがある</li>
</ul>
</li>
<li>マニフェストファイル
<ul>
<li>静的ファイル（アセット）を上記の各ディレクトリに配置すれば、マニフェストファイルを使ってそれらをどのように1つのファイルにまとめるのかを Rails に指示することができる
<ul>
<li>実際に処理を行うのは Sprockets という gem</li>
</ul>
</li>
<li>CSS と JavaScript には適用されるが、画像ファイルには適用されない</li>
<li>Rails デフォルトのマニフェストファイルは実用的</li>
</ul>
</li>
<li>プリプロセッサエンジン
<ul>
<li>必要なアセットをディレクトリに配置してまとめた後、Rails は様々なプリプロセッサエンジンを介してそれらを実行し、ブラウザに配信できるようにそれらをマニフェストファイルを用いて結合し、サイトテンプレート用に準備する
<ul>
<li>どのプリプロセッサを使用するかはファイル名の拡張子で判断する
<ul>
<li><code>.scss</code></li>
<li><code>.coffee</code></li>
<li><code>.erb</code>
<ul>
<li>などが一般的</li>
<li>CoffeeScript！貴様死んだはずでは！</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>繋げて実行も可能
<ul>
<li><code>foobar.js.erb.coffee</code>
<ul>
<li>右から左の順に実行</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>本番環境での効率性
<ul>
<li>Asset Pipeline は本番アプリで効率的になるように最適化されたアセットを自動的に生成可能
<ul>
<li>人に読みやすいアセットは本番環境にとっては非効率
<ul>
<li>最小化されていない CSS や JavaScript ファイルを多数に分割すると、ページの読み込み時間が遅くなるため</li>
</ul>
</li>
<li>Asset Pipeline により、開発環境では人に読みやすいまま、本番環境ではファイルを最小化させることが可能になる
<ul>
<li>すべてのスタイルシートを <code>application.css</code></li>
<li>すべての JavaScript ファイルを <code>javascript.js</code>
<ul>
<li>にまとめてくれる
<ul>
<li>さらにそれらすべての不要な空白やインデントも除去し、ファイルサイズを最小化してくれる
<ul>
<li>やるならとことん</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="522-素晴らしい構文を備えたスタイルシート">5.2.2 素晴らしい構文を備えたスタイルシート</h3>
<ul>
<li>Sass の2つの重要な機能
<ul>
<li>ネストと変数</li>
</ul>
</li>
<li>Sass は SCSS というフォーマットに対応
<ul>
<li>SCSS は CSS に新しい機能を追加しただけで、新しい構文を定義しているわけではない</li>
</ul>
</li>
<li>Rails のアセットパイプラインは <code>.scss</code> という拡張子を持つファイルを Sass を使って自動的に処理している</li>
<li>ネストの例1</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">center</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">center</span> <span class="nt">h1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">center</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="err">h1</span> <span class="err">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="err">}</span>
</span></span></code></pre></div><ul>
<li>ネストの例2</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">#</span><span class="nn">logo</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">float</span><span class="p">:</span> <span class="kc">left</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">margin-right</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">font-size</span><span class="p">:</span> <span class="mf">1.7</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">text-transform</span><span class="p">:</span> <span class="kc">uppercase</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">letter-spacing</span><span class="p">:</span> <span class="mi">-1</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">padding-top</span><span class="p">:</span> <span class="mi">9</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">#</span><span class="nn">logo</span><span class="p">:</span><span class="nd">hover</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">#</span><span class="nn">logo</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">float</span><span class="p">:</span> <span class="kc">left</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">margin-right</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">font-size</span><span class="p">:</span> <span class="mf">1.7</span><span class="kt">em</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">text-transform</span><span class="p">:</span> <span class="kc">uppercase</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">letter-spacing</span><span class="p">:</span> <span class="mi">-1</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">padding-top</span><span class="p">:</span> <span class="mi">9</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="err">&amp;:hover</span> <span class="err">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-decoration</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="err">}</span>
</span></span></code></pre></div><ul>
<li>変数</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scss" data-lang="scss"><span class="line"><span class="cl"><span class="nt">h2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nt">color</span><span class="nd">:</span> <span class="nn">#777</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nt">footer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nt">color</span><span class="nd">:</span> <span class="nn">#777</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scss" data-lang="scss"><span class="line"><span class="cl"><span class="nv">$light-gray</span><span class="o">:</span> <span class="mh">#777</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nt">h2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nt">color</span><span class="nd">:</span> <span class="err">$</span><span class="nt">light-gray</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nt">footer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nc">.</span>
</span></span><span class="line"><span class="cl">  <span class="nt">color</span><span class="nd">:</span> <span class="err">$</span><span class="nt">light-gray</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ul>
<li>Bootstrap では多くの色に対して変数を定義している
<ul>
<li>boostrap-sass という gem を使えば、 さらに以下のようにできる</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-scss" data-lang="scss"><span class="line"><span class="cl"><span class="nt">h2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nc">.</span>
</span></span><span class="line"><span class="cl"> <span class="nc">.</span>
</span></span><span class="line"><span class="cl"> <span class="nc">.</span>
</span></span><span class="line"><span class="cl"> <span class="nt">color</span><span class="nd">:</span> <span class="err">$</span><span class="nt">gray-light</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nc">.</span>
</span></span><span class="line"><span class="cl"><span class="nt">footer</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nc">.</span>
</span></span><span class="line"><span class="cl"> <span class="nc">.</span>
</span></span><span class="line"><span class="cl"> <span class="nc">.</span>
</span></span><span class="line"><span class="cl"> <span class="nt">color</span><span class="nd">:</span> <span class="err">$</span><span class="nt">gray-light</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="53-レイアウトのリンク">5.3 レイアウトのリンク</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hoge.html" data-lang="hoge.html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/static_pages/about&#34;</span><span class="p">&gt;</span>About<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span></code></pre></div><ul>
<li>上記のように HTML に直接リンクを記述することは可能だが</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= link_to &#34;About&#34;, about_path %&gt;
</code></pre><ul>
<li>Railsであれば上記のように名前付きルートを使うのが慣例</li>
</ul>
<h3 id="531-contactページ">5.3.1 Contactページ</h3>
<ul>
<li>新しいページをTDDで追加
<ul>
<li>第3章の演習で実装済みだった</li>
</ul>
</li>
</ul>
<h3 id="532-railsのルートurl">5.3.2 RailsのルートURL</h3>
<ul>
<li>名前付きルートを静的ページで使うためにルーティングを変更する</li>
<li>ルートURL のルーティング定義の効果はブラウザからアクセスしやすくすることだけではなく、生の URL ではなく名前付きルートを使って URL を参照できることも含まれる
<ul>
<li>例えば <code>root_path</code> や <code>root_url</code> といったメソッドを通して URL を参照できる</li>
</ul>
</li>
<li>既存の静的ページのルーティングを名前付きルートに変更していく</li>
</ul>
<pre tabindex="0"><code class="language-before.ruby" data-lang="before.ruby">get &#39;static_pages/help&#39;
</code></pre><pre tabindex="0"><code class="language-after.ruby" data-lang="after.ruby">get  &#39;/help&#39;, to: &#39;static_pages#help&#39;
</code></pre><h3 id="533-名前付きルート">5.3.3 名前付きルート</h3>
<ul>
<li><code>link_to</code> メソッドの2番目の引数を名前付きルートに変更していく</li>
</ul>
<h3 id="534-リンクのテスト">5.3.4 リンクのテスト</h3>
<ul>
<li>リンクをクリックして動作しているかの確認は可能だが、変更のたびにこの作業を繰り返すのは大変なので、統合テストで自動化する
<ul>
<li>Rails チュートリアルは E2E テストまでやるのか…</li>
</ul>
</li>
<li><code>site_layout</code> というテストテンプレート作成から
<ul>
<li><code>rails generate integration_test site_layout</code></li>
<li><code>test/integration/site_layout_test.rb</code> が作成される
<ul>
<li>ここまでサポートしている Rails の底が見えない</li>
</ul>
</li>
</ul>
</li>
<li><code>assert_template</code> メソッドで Home ページが正しいビューを描画しているか確認する</li>
<li><code>assert_select &quot;a[href=?]&quot;, about_path</code> と記述すると、 <code>&lt;a href=&quot;/about&quot;&gt;...&lt;/a&gt;</code> のような HTML が存在するかをチェックできる</li>
<li><code>assert_select &quot;a[href=?]&quot;, root_path, count: 2</code> と記述すると、ルート URL へのリンクが2つあることをチェックできる</li>
<li><code>assert_select</code> は柔軟でパワフルなため、多様なオプションがある
<ul>
<li>しかしこのメソッドで複雑なテストはしない方が懸命</li>
</ul>
</li>
<li>統合テストが通るかどうかは、Rake タスクの実行で確認可能
<ul>
<li><code>rails test</code> とは別物</li>
</ul>
</li>
</ul>
<h2 id="54-ユーザー登録-最初のステップ">5.4 ユーザー登録: 最初のステップ</h2>
<ul>
<li>ユーザー登録ページへのルーティング作成
<ul>
<li>2番目のコントローラを作る</li>
</ul>
</li>
</ul>
<h3 id="541-usersコントローラ">5.4.1 Usersコントローラ</h3>
<ul>
<li><code>rails generate controller Users new</code>
<ul>
<li>テストも自動作成されており、この時点では通る</li>
</ul>
</li>
</ul>
<h3 id="542-ユーザー登録用url">5.4.2 ユーザー登録用URL</h3>
<ul>
<li><code>/users/new</code> から <code>/singup</code> に変更するためルーティングを修正
<ul>
<li><code>rails generate</code> したときに自動的に先頭に新しいルーティングが追加されている模様</li>
<li><code>get '/signup'</code> と追記したので <code>signup_path</code> という名前付きルートが使えるようになった</li>
</ul>
</li>
</ul>
<h2 id="55-最後に">5.5 最後に</h2>
<ul>
<li>以後サンプルアプリケーションを肉付けすることに専念する</li>
<li>heroku にデプロイしてこの章は終了</li>
</ul>
<h3 id="551-本章のまとめ">5.5.1 本章のまとめ</h3>
<ul>
<li>HTML5を使ってheaderやfooter、logoやbodyといったコンテンツのレイアウトを定義しました</li>
<li>Railsのパーシャルは効率化のために使われ、別ファイルにマークアップを切り出すことができます</li>
<li>CSSは、CSSクラスとidを使ってレイアウトやデザインを調整します</li>
<li>Bootstrapフレームワークを使うと、いい感じのデザインを素早く実装できる</li>
<li>SassとAsset Pipelineは、(開発効率のために切り分けられた) CSSの冗長な部分を圧縮し、本番環境に最適化した結果を出力する</li>
<li>Railsのルーティングでは自由にルールを定義することができ、また、その際に名前付きルートも使えるようになる</li>
<li>統合テストは、ブラウザによるページ間の遷移を効率的にシミュレートする</li>
</ul>
<h1 id="所感">所感</h1>
<p>Rails チュートリアルの本筋からは逸れるかもしれないが、HTML と CSS についてはほとんど学習したことがなかったのでよい導入になった。Sass も単語は知っていたが中身は全く知らなかったので、触れることができて有意義な章だった。本章での一番の驚きは Rails が E2E テストまでサポートしていたことだ。実践での実用度は低いように感じたが、徹底的にテストをサポートしようとする Rails の姿勢が垣間見え、やるやら徹底的にやるというフレームワークの思想をひしひしと感じた。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第4章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter4/</link>
			<pubDate>Sat, 14 Dec 2019 20:34:03 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter4/</guid>
			<description>第4章 Rails風味のRuby 「Ruby は巨大な仕様を持つ言語ですが、幸い、Rails開発者にとって必要な知識は比較的少なくて済みます。」 幸</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第4章-rails風味のruby">第4章 Rails風味のRuby</h1>
<ul>
<li>「Ruby は巨大な仕様を持つ言語ですが、幸い、Rails開発者にとって必要な知識は比較的少なくて済みます。」
<ul>
<li>幸い…？</li>
</ul>
</li>
</ul>
<h2 id="41-動機">4.1 動機</h2>
<ul>
<li>この章で Ruby に関する知識と経験の限界に真正面から挑むらしい</li>
</ul>
<h3 id="411-組み込みヘルパー">4.1.1 組み込みヘルパー</h3>
<ul>
<li>混乱を生じさせる可能性のあるRubyの概念
<ul>
<li>Rails の組み込み関数</li>
<li>カッコを使わないメソッド呼び出し</li>
<li>シンボル</li>
<li>ハッシュ</li>
</ul>
</li>
</ul>
<h3 id="412-カスタムヘルパー">4.1.2 カスタムヘルパー</h3>
<ul>
<li>Railsのビューでは組み込み関数を使えるだけでなく、新しく作成することもできる
<ul>
<li><code>ApplicationHelper</code> にカスタムヘルパーを定義</li>
<li>テストを Red にしてから Green へ</li>
</ul>
</li>
<li>定義したカスタムヘルパーに含まれている概念
<ul>
<li>モジュール</li>
<li>メソッド定義</li>
<li>任意のメソッド引数</li>
<li>コメント</li>
<li>ローカル変数の割り当て</li>
<li>論理値（boolean）</li>
<li>制御フロー</li>
<li>文字列の結合</li>
<li>戻り値
<ul>
<li>全く触れたことにない概念はモジュールくらいか</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="42-文字列とメソッド">4.2 文字列とメソッド</h2>
<ul>
<li>Railsコンソール
<ul>
<li>Railsアプリケーションを対話的に操作するためのコマンドラインツール</li>
<li>正常終了は Ctrl + D</li>
<li>強制終了は Ctrl + C</li>
</ul>
</li>
</ul>
<h3 id="421-コメント">4.2.1 コメント</h3>
<ul>
<li>Rubyでは <code>#</code>
<ul>
<li><code>//</code> ではない</li>
</ul>
</li>
</ul>
<h3 id="422-文字列">4.2.2 文字列</h3>
<ul>
<li>ダブルクォートで囲う</li>
<li><code>+</code> で文字列同士の結合も可能
<ul>
<li>式展開( <code>#{}</code> )という方法もある
<ul>
<li>C#では <code>@</code> できる便利なやつ</li>
</ul>
</li>
</ul>
</li>
<li>文字列を出力したいときに使われる <code>puts</code>
<ul>
<li>戻り値は <code>nil</code>
<ul>
<li><code>null</code> ではなく <code>nil</code>かあ…</li>
</ul>
</li>
</ul>
</li>
<li>Rubyはシングルクォートでも文字列をサポートしている
<ul>
<li>ただしシングルクォート文字列の中で式展開ができない</li>
<li>シングルクォートは入力文字列をエスケープせずにそのまま保持するときに便利</li>
</ul>
</li>
</ul>
<h3 id="423-オブジェクトとメッセージ受け渡し">4.2.3 オブジェクトとメッセージ受け渡し</h3>
<ul>
<li>オブジェクトとはいついかなる場合にもメッセージに応答するもの
<ul>
<li>オブジェクト指向！</li>
</ul>
</li>
<li>Ruby では <code>boolean</code> を返すメソッドの末尾に疑問符をつける慣習がある
<ul>
<li>わかりやすい</li>
</ul>
</li>
<li><code>nil</code> かどうかを調べる <code>nil?</code> メソッドもある</li>
<li>後続 if という書き方
<ul>
<li><code>unless</code> も同様</li>
</ul>
</li>
</ul>
<h3 id="424-メソッドの定義">4.2.4 メソッドの定義</h3>
<ul>
<li>Ruby はデフォルト引数が使える</li>
<li>暗黙の戻り値
<ul>
<li>メソッド内で最後に評価された式の値が自動的に返される</li>
<li>例えば以下の二番目の <code>return</code> はなくても暗黙的に文字列が返される（ただ書いてあったほうが可読性は高い）</li>
</ul>
</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="o">&gt;&gt;</span> <span class="k">def</span> <span class="nf">string_message</span><span class="p">(</span><span class="n">str</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;</span>   <span class="k">return</span> <span class="s2">&#34;It&#39;s an empty string!&#34;</span> <span class="k">if</span> <span class="n">str</span><span class="o">.</span><span class="n">empty?</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;</span>   <span class="k">return</span> <span class="s2">&#34;The string is nonempty.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">&gt;&gt;</span> <span class="k">end</span>
</span></span></code></pre></div><ul>
<li>メソッドの引数の変数名はどんなものでも呼び出し元に影響は与えない</li>
</ul>
<h3 id="425-title-ヘルパー再び">4.2.5 title ヘルパー、再び</h3>
<ul>
<li>やはり <code>return</code> がなくても暗黙的に戻り値が決まるのは気持ち悪い…</li>
<li><code>module ApplicationHelper</code> という要素
<ul>
<li>モジュールは関連したメソッドをまとめる方法の一つ</li>
<li><code>include</code> メソッドでモジュールを読み込むことができる</li>
<li>単なる Ruby コードであればモジュールは明示できな <code>include</code> が必要だが、Rails の場合は自動的にヘルパーモジュールを読み込んでくれるため、 <code>include</code> が不要
<ul>
<li>つまり <code>application_helper.rb</code>  定義のモジュールは自動的にすべてのビューで参照できるようになっているということ
<ul>
<li>便利すぎる</li>
<li>ただの static メソッドでは？？（静的型付け脳）</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="43-他のデータ構造">4.3 他のデータ構造</h2>
<h3 id="431-配列と範囲演算子">4.3.1 配列と範囲演算子</h3>
<ul>
<li>Ruby の配列もゼロオリジン
<ul>
<li>添字にはマイナスも使える</li>
</ul>
</li>
<li>比較演算子としての <code>==</code>
<ul>
<li>値と参照の比較みたいな話はないのだろうか…</li>
</ul>
</li>
<li>配列に対して <code>sort</code> <code>reverse</code> <code>shuffle</code> しても配列自体に影響はない
<ul>
<li>配列自体を変更したい場合は <code>sort!</code> のように末尾に <code>!</code> を追加して破壊的メソッドにするのが Ruby の慣習</li>
</ul>
</li>
<li>配列への要素の追加は <code>push</code> だったり <code>&lt;&lt;</code> だったり</li>
<li>Ruby の配列は異なる型が配列の中で共存できる
<ul>
<li>これは耐えられないかも</li>
</ul>
</li>
</ul>
<h3 id="432-ブロック">4.3.2 ブロック</h3>
<ul>
<li>短い1行のブロックには波カッコ、長い1行や複数行のブロックには <code>do..end</code></li>
<li>「ブロックを十分に理解するためには相当なプログラミング経験が必要です。」
<ul>
<li>たしかにプログラミング初心者には理解しづらいかもしれない</li>
</ul>
</li>
<li>今までやってきた単体テストにもブロックが使われている</li>
</ul>
<h3 id="433-ハッシュとシンボル">4.3.3 ハッシュとシンボル</h3>
<ul>
<li>ハッシュは並び順が保証されていない</li>
<li>ハッシュロケットという簡単な記法</li>
<li>Ruby ではハッシュのキーに文字列ではなくシンボルを使うほうが普通
<ul>
<li><code>:name</code> のような表現</li>
</ul>
</li>
<li>シンボルとハッシュロケットの組み合わせは以下のようにも書ける（わかりやすい！）</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="p">{</span> <span class="nb">name</span><span class="p">:</span> <span class="s2">&#34;Michael Hartl&#34;</span><span class="p">,</span> <span class="ss">email</span><span class="p">:</span> <span class="s2">&#34;michael@example.com&#34;</span> <span class="p">}</span>
</span></span></code></pre></div><ul>
<li>オブジェクトを表示するための <code>inspect</code> メソッド
<ul>
<li><code>p</code> メソッドでショートカット可能</li>
</ul>
</li>
</ul>
<h3 id="434-css再び">4.3.4 CSS、再び</h3>
<ul>
<li>Ruby では丸カッコは使用してもしなくてもよい
<ul>
<li>紛らわしいのでどちらかに決めてほしい。。。</li>
</ul>
</li>
<li>ハッシュがメソッド呼び出しの最後の引数である場合は波カッコを省略可能</li>
<li>Ruby は改行と空白を区別していないので、以下のコードでは <code>stylesheet_link_tag</code> メソッドを2つの引数で呼んでいることになる</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">stylesheet_link_tag</span> <span class="s1">&#39;application&#39;</span><span class="p">,</span> <span class="ss">media</span><span class="p">:</span> <span class="s1">&#39;all&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                   <span class="s1">&#39;data-turbolinks-track&#39;</span><span class="p">:</span> <span class="s1">&#39;reload&#39;</span>
</span></span></code></pre></div><h2 id="44-rubyにおけるクラス">4.4 Rubyにおけるクラス</h2>
<ul>
<li>Ruby にもクラスはある</li>
</ul>
<h3 id="441-コンストラクタ">4.4.1 コンストラクタ</h3>
<ul>
<li>クラス自身に対して呼び出すメソッド
<ul>
<li>クラスメソッド</li>
</ul>
</li>
<li>インスタンスに対して呼び出すメソッド
<ul>
<li>インスタンスメソッド</li>
</ul>
</li>
</ul>
<h3 id="442-クラス継承">4.4.2 クラス継承</h3>
<ul>
<li>Ruby におけるすべてのクラスは <code>BasicObject</code> を継承している</li>
<li>継承の書き方は <code>class Word &lt; String</code> のような感じ</li>
<li>継承先クラスでの <code>self.</code> は省略可能</li>
</ul>
<h3 id="443-組み込みクラスの変更">4.4.3 組み込みクラスの変更</h3>
<ul>
<li>Ruby では組み込みの基本クラスの拡張が可能
<ul>
<li>クラス設計者でなくともクラスにメソッドを自由に追加することができる
<ul>
<li>怖</li>
</ul>
</li>
<li>正当な理由がない限りは組み込みクラスにメソッドを追加することは無作法
<ul>
<li>そりゃそうだ</li>
</ul>
</li>
</ul>
</li>
<li>Rails は <code>blank?</code> メソッドを Ruby に追加している
<ul>
<li>😇</li>
</ul>
</li>
</ul>
<h3 id="444-コントローラクラス">4.4.4 コントローラクラス</h3>
<ul>
<li><code>ApplicationController</code> の継承階層がかなり深い
<ul>
<li><code>ApplicationController::Base</code> の <code>::Base</code> とは
<ul>
<li><code>ActionController</code> モジュールの <code>Base</code> クラス</li>
</ul>
</li>
</ul>
</li>
<li>「実は、Railsは確かにRubyで書かれていますが、既にRubyとは別物なのです。Railsのクラスは、普通のRubyオブジェクトと同様に振る舞うものもありますが、多くのクラスにはRailsの魔法の粉が振りかけられています。Railsは独特であり、Rubyとは切り離して学習する必要があります。」
<ul>
<li>🙄</li>
</ul>
</li>
</ul>
<h3 id="445-ユーザークラス">4.4.5 ユーザークラス</h3>
<ul>
<li>属性に対するアクセサーである `attr_accessor'
<ul>
<li>@ 記号で始まるインスタンス変数にアクセスするメソッドが用意される</li>
<li>Rails ではインスタンス変数をコントローラ内で宣言するだけでビューで使えるようになる</li>
</ul>
</li>
<li>オブジェクト初期化時にハッシュ引数を渡す技法
<ul>
<li>マスアサインメント</li>
</ul>
</li>
</ul>
<h1 id="所感">所感</h1>
<p>本章は Rails ではなく Ruby にフォーカスした章だった。Rails と違い、素の Ruby の書き味は意外にも悪くなかった。というか書きやすかったと言ったほうが正しいかもしれない。基本クラスを拡張できる言語仕様は衝撃的だったが、かなり柔軟性の高い言語という印象を受けた。Rails はこの柔軟性を生かしてあの黒魔術を生み出しているのだろうか。。。</p>
]]></content>
		</item>
		
		<item>
			<title>分割キーボードのすゝめ</title>
			<link>https://sore8sore104te.com/separate-keyboard/</link>
			<pubDate>Thu, 05 Dec 2019 08:30:00 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/separate-keyboard/</guid>
			<description>Photo by Harlie Raethel on Unsplash はじめに この記事はスタディスト Advent Calendar 2019の5日目の記事です。 スタディストのメンバーが素敵な記事を続々と投稿してくれている中、箸</description>
			<content type="html"><![CDATA[<p><img src="./shoulder.jpg" alt="shoulder">
Photo by <a href="https://unsplash.com/@harlsta">Harlie Raethel</a> on <a href="https://unsplash.com/">Unsplash</a></p>
<h1 id="はじめに">はじめに</h1>
<p>この記事は<a href="https://adventar.org/calendars/4203">スタディスト Advent Calendar 2019</a>の5日目の記事です。
スタディストのメンバーが素敵な記事を続々と投稿してくれている中、箸休め的に（来年以降の社内ハードルを下げるためにも）お手軽なテーマで記事を書いてみます。
今回のテーマは分割キーボードについてです。</p>
<h1 id="分割キーボードとは">分割キーボードとは？</h1>
<p>分割キーボードとはその名の通り、左右に分割されたキーボードのことです。
本記事は、私を含めスタディストの一部メンバーが愛してやまない分割キーボードについて紹介し、一人でも多くの分割キーボーダーを増やすことを目的としています。</p>
<h1 id="社内状況">社内状況</h1>
<p>現在スタディストでは、開発部で3名、情シスで1名、計4名が分割キーボードを使用しています。
内訳は、圧倒的存在感を放つErgoDox EZが2名、そしてBaroccoが2名です。
私はBarrcco（JIS配列）を愛用しています。赤軸派です。</p>
<p>余談ですが、分割キーボード利用者というだけで割と変態扱いされます。私はさらにMacBook ProでBoot Campしているので、変態属性が増しています。</p>
<p><img src="./barocco-bootcamp.jpg" alt="barocco-bootcamp">
※さらに画面焼けしている（要修理）</p>
<h1 id="分割キーボードのメリット">分割キーボードのメリット</h1>
<p>分割キーボーダーを増やすためには、その圧倒的メリットを紹介しないといけないでしょう。
私の考えるメリットは3つあります。</p>
<h3 id="1-肩凝りが軽減する">1. 肩凝りが軽減する</h3>
<p>普通のキーボードを使うと肩が閉じ、知らず知らずのうちに肩に力が入ってしまいます。
その結果、肩こりが酷くなります。私もそうでした。
これは普段キーボードをカタカタする仕事をしている人間にとっては死活問題です。</p>
<p>しかし分割キーボードの場合、キーボードに手を置いた際に自然と肩が開き、肩に無駄な力が入らなくなります。
私は重度の肩こりでしたが、分割キーボードに変えてから肩こりが軽減した気がします。（個人の感想です）</p>
<h3 id="2-目立つ">2. 目立つ</h3>
<p>人とは違うことをやりたいあなたにお勧めです。
圧倒的ギーク感が出せます。（個人の感想です）</p>
<h3 id="3-いざとなれば連結してhhkb風にできる">3. いざとなれば連結してHHKB風にできる</h3>
<p>私が使用しているBaroccoの場合、いざとなれば結合して使用できます。
結合すると、某HappyでHackingなキーボードに見えなくもないです。
気分を変えてオーソドックスなスタイルでカタカタしたいときにもお勧めできるキーボードなのです。（肩が犠牲になります）</p>
<h1 id="デメリット">デメリット</h1>
<p>モノにはメリットがあれば必ずデメリットもあります。
本来分割キーボーダーを増やすために書いている記事ではありますが、スタディストのValueである<a href="https://studist.jp/value/">Fair Play</a>に則ってデメリットも紹介しなくてはなりません。
なお、ここでのデメリットはあくまで私がBaroccoを使用したときに感じたものです。</p>
<h3 id="1-アローキー操作が大変">1. アローキー操作が大変</h3>
<p>アローキー、すなわち方向キーの操作が難しいです。
普通のキーボードであればアローキーはそれ単独で実装されていることが多いですが、Baroccoの場合、通常はFNキーと組み合わせて使わなくてはなりません。
慣れるまでは操作が難しいです。</p>
<p>しかし、いったん慣れてしまえばアローキーのあるキーボードで操作した際にそのありがたみを享受できるので、自らに制限を課す人にはお勧めできます。
また、<a href="http://www.archisite.co.jp/products/mistel/md650l-barocco/">最新のBarocco</a>ではアローキーも実装されていますよ！</p>
<p><img src="./barocco-arrowkey.jpg" alt="barocco-arrowkey"></p>
<h3 id="2-左右の接続コードが見苦しい">2. 左右の接続コードが見苦しい</h3>
<p>PCの操作環境（見た目的に）に拘る人には致命傷かもしれませんが、左右のキーボードを接続するコードが非常に見苦しいです。
絶妙に長さが足りないため、ディスプレイの背部に回すこともできず、キーボードの上を醜くコードが這っています。
ディスプレイの背部に回すためにもう少し長いコードに交換したいところですが、Baroccoの場合はMicroB-MicroBのコードが必要なため、部品調達が難しいです。
社内のもう一名のBaroccoユーザーは、コードを延長し背面に回して凌いでいます。</p>
<p><img src="./barocco-cable.jpg" alt="barocco-cable"></p>
<h1 id="まとめ">まとめ</h1>
<p>さて、分割キーボーダーをふやすべく、その魅力について紹介してきました。
心なしかデメリットの分量が多い気がしますが気のせいです。
デメリットを書いている時の方が軽やかにBaroccoでタイプしていた気がしますが、それもきっと気のせいです。</p>
<p>分割キーボードはとても良いものです。2年ほど使用していますが、手に非常になじんでいます。
これなしには生きられません。きっと次のキーボードも分割キーボードにするでしょう。</p>
<p>ここまで読んでくださった方は、是非分割キーボードに手を出してみてはいかがでしょうか。
最近は自作キーボード界隈でも分割キーボードがアツいので、分割と自作の二つの沼に沈むのも悪くはないと思います。</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第3章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter3/</link>
			<pubDate>Wed, 27 Nov 2019 22:32:09 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter3/</guid>
			<description>第3章 ほぼ静的なページの作成 Rails は動的なWebサイトを開発するように設計されているが、静的なページを作ってみる 自動化テストにも触れるよ！ 3.1 セッ</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第3章-ほぼ静的なページの作成">第3章 ほぼ静的なページの作成</h1>
<ul>
<li>Rails は動的なWebサイトを開発するように設計されているが、静的なページを作ってみる</li>
<li>自動化テストにも触れるよ！</li>
</ul>
<h2 id="31-セットアップ">3.1 セットアップ</h2>
<ul>
<li>今までと同様に Gemfile 更新
<ul>
<li>test グループだけちょっと異なる</li>
<li>これらの gem が何をサポートしてくれるのだろうか</li>
</ul>
</li>
<li>安定の <code>bundle update</code> からの <code>bundle install --without production</code></li>
<li>GitHub にリポジトリを作成して Heroku へ push</li>
<li>Rails のデフォルトページは Heroku 上でうまく表示されない仕様らしいので ApplicationController を微修正して push
<ul>
<li>なんでうまく表示されないのか？</li>
</ul>
</li>
<li>heroku に展開するときにエラーが表示された場合は <code>heroku logs</code> コマンドで本番環境のログが取得できる</li>
</ul>
<h2 id="32-静的ページ">3.2 静的ページ</h2>
<ul>
<li>まずは Rails のアクションやビューを使って静的ページを作成し、その後動的ページに作り変えていく</li>
<li>コントローラとは基本的に動的な Web ページの集合を束ねるコンテナのこと</li>
<li><code>rails generate</code> でコントローラを生成
<ul>
<li>アクション名をまとめて指定できる
<ul>
<li><code>rails g</code> は <code>rails generate</code> の短縮形</li>
<li><code>rails s</code> とかも可能</li>
<li><code>bundle install</code> の短縮形は <code>bundle</code>
<ul>
<li>なんでや！</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><code>rails generate</code> するときにコントローラ名をキャメルケースで渡すと、コントローラ名をスネークケースにしたファイルが自動的に生成される
<ul>
<li>スネークケースがクラス名になっているのが慣れない。。。</li>
<li>Rails の generate スクリプトでは underscore メソッドでキャメルケースをスネークケースに変換している</li>
</ul>
</li>
<li><code>rails generate</code> で作成したコントローラ名を変更したい場合は <code>rails destory</code> でコントローラだけでなく、既存ファイルに挿入されたコードもまとめて削除できる</li>
<li>DB のマイグレーションももとに戻すことができる
<ul>
<li>強すぎでは</li>
<li><code>db rollback</code>
<ul>
<li>簡単すぎる。。。</li>
</ul>
</li>
</ul>
</li>
<li>Ruby にも継承はある
<ul>
<li>ApplicationController を継承しているだけでRails 特有の振る舞いをすることになる
<ul>
<li>アクションに対応するビューが出力される</li>
</ul>
</li>
</ul>
</li>
<li>Home と Help の html を修正
<ul>
<li>拡張子は <code>.html.erb</code>
<ul>
<li><code>.erb</code> は eRuby のこと</li>
<li>html へ Ruby スクリプトを埋め込める
<ul>
<li>本当に Rails 側で全部やるんだなあ</li>
</ul>
</li>
</ul>
</li>
<li>html もよくわからん
<ul>
<li>最後に触ったのは何年前か</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="33-テストから始める">3.3 テストから始める</h2>
<ul>
<li>まずはコントローラのテストから</li>
<li>コードよりも先にテストを書く
<ul>
<li>面倒な部分は既に Railsが 面倒を見てくれているらしい
<ul>
<li>とことん隠蔽してくるな。。。</li>
</ul>
</li>
</ul>
</li>
<li><code>rails test</code> でテストが実行される</li>
<li>テスト実行に時間がかかる
<ul>
<li>その一因は Spring サーバーを起動しているから</li>
<li>ここでいう Spring とは？
<ul>
<li>アプリケーションプリローダーのこと</li>
<li>Java は関係ない</li>
<li>Rails 内では様々なライブラリのロードなどの前処理が行われるので、コマンドを実行するための待ち時間がかかってしまう</li>
<li>事前にバックグラウンドでライブラリをロードしておくことで、その待ち時間を短くする</li>
<li>なので時間がかかるのは最初の一回だけ</li>
</ul>
</li>
</ul>
</li>
<li>先に失敗するテストを書く
<ul>
<li>TDD!TDD!</li>
</ul>
</li>
<li><code>routes.rb</code> に <code>get 'static_pages/about'</code> を追加することによりヘルパーが使えるようになる</li>
<li>再度テスト実行してもアクションがないのでやはり Red</li>
<li>コントローラにアクションを追加してもまだ Red
<ul>
<li>テンプレート、すなわちビューがない</li>
</ul>
</li>
<li><code>touch</code> コマンドで erb ファイルを作成
<ul>
<li><code>touch</code> コマンドは本来タイムスタンプを更新するだけのものだが、ファイルが存在しない場合は作成される
<ul>
<li>知らなかった</li>
</ul>
</li>
</ul>
</li>
<li>erb を追加して再度テスト実行すれば Green
<ul>
<li>やったね</li>
</ul>
</li>
<li>コードの腐敗臭はどんな小さな隙間からもやってくる</li>
<li>だからこまめにリファクタリングしてコードを常にきれいに保つ必要がある
<ul>
<li>そのためのテスト</li>
</ul>
</li>
</ul>
<h2 id="34-少しだけ動的なページ">3.4 少しだけ動的なページ</h2>
<ul>
<li>作成した静的ページを動的変更する</li>
<li><code>assert_select</code> メソッドで特定の html タグが存在するかどうかのテストが可能
<ul>
<li>あまりやったことのないテストだ</li>
</ul>
</li>
<li><code>html.erb</code> ファイルを更新してテストを Green に</li>
<li>テストに <code>setup</code> メソッドを追加して共通化</li>
<li>埋め込み Ruby で重複を取り除いていく</li>
<li><code>provive</code> メソッドでタイトルをページごとに変更する
<ul>
<li>Ruby を html に埋め込んでいく</li>
<li>なるほどだから erb なのか！
<ul>
<li>erb は Web ページに動的な要素を加えるときに使うテンプレートシステム</li>
</ul>
</li>
</ul>
</li>
<li><code>&lt;% ... %&gt;</code> は中に書かれたコードを実行するだけ</li>
<li><code>&lt;%= ... %&gt;</code> は中のコードの実行結果がテンプレートの該当部分に挿入される</li>
<li>すべてのページの構造を同じものに寄せていく
<ul>
<li>そのための <code>application.html.erb</code></li>
<li><code>&lt;%= yield %&gt;</code> という特殊なコード
<ul>
<li>各ページの内容をレイアウトに挿入する</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code class="language-erb" data-lang="erb">&lt;%= csrf_meta_tags %&gt;
&lt;%= stylesheet_link_tag ... %&gt;
&lt;%= javascript_include_tag &#34;application&#34;, ... %&gt;
</code></pre><ul>
<li>上記 erb はそれぞれスタイルシート, JavaScript, <code>csrf_meta_tags</code> メソッドをページ内で展開するためのもの
<ul>
<li>スタイルシートと JavaScript は Asset Pipeline の一部
<ul>
<li>Asset Pipeline とは
<ul>
<li>JavaScript や CSS のアセットを minify または圧縮して連結するためのフレームワーク</li>
<li>詳しくは5.2.1章で</li>
</ul>
</li>
</ul>
</li>
<li><code>csrf_meta_tags</code> はクロスサイトリクエストフォージェリーの防衛用Railsメソッド
<ul>
<li><a href="https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B5%E3%82%A4%E3%83%88%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%AA">Cross-Site Request Forgery</a> とは</li>
</ul>
</li>
</ul>
</li>
<li>html 構造を削除したページに更新していく
<ul>
<li>テストがある安心感！</li>
</ul>
</li>
<li>ルーティングの設定を変更
<ul>
<li><code>routes.rb</code> に1行追加するだけでヘルパーが使えるようになるのはまだ慣れない。。。</li>
</ul>
</li>
</ul>
<h2 id="35-最後に">3.5 最後に</h2>
<ul>
<li>ブランチをマージして heroku にデプロイして終わり
<ul>
<li>heroku でも動いている！（当たり前）</li>
</ul>
</li>
</ul>
<h2 id="36-高度なセットアップ">3.6 高度なセットアップ</h2>
<ul>
<li>Cloud9 テスト結果表示が見やすくなった</li>
<li>おそらく Rails チュートリアルが AmaxonLinux 前提で説明されているため、Linux コマンドでちょっとハマる</li>
<li>CentOS で <code>yum</code> だったのは Ubuntu では <code>apt-get</code>
<ul>
<li>CentOS は少しだけ触っていたが Ubuntu は初めてなのでささいなところでつまづく
<ul>
<li>おとなしく AmazonLinux を選択しておくべきだったか。。。</li>
</ul>
</li>
</ul>
</li>
<li><code>.gitignore</code> に Spring を追加
<ul>
<li>Spring は挙動が若干不安定らしい</li>
<li>テスト実行が異常に遅いときはプロセスを見て適宜 Spring を kill すべし</li>
</ul>
</li>
<li>gurad の設定を行いテストが自動で走ることを確認
<ul>
<li>便利だけどテスト数増えたらいちいち自動実行するとパフォーマンス悪くなるよなあ。。。</li>
<li>そのへんってうまくできているのだろうか？</li>
<li>それとも普通に OFF にするだけ？</li>
</ul>
</li>
</ul>
<h1 id="所感">所感</h1>
<p>Ruby が組み込みで動くこと、チュートリアルがテストファーストを徹底してくること、html のテストを書くことなど、意外続きの3章だった。一箇所修正するだけで各所に影響を及ぼす Rails の感じにも少しずつ慣れてきた感。むしろ心地よくなってきているような…？</p>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第2章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter2/</link>
			<pubDate>Wed, 13 Nov 2019 22:09:38 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter2/</guid>
			<description>第2章 Toyアプリケーション scaffold という即席のアプローチを使うらしい 2.1 アプリケーションの計画 toy_appという新しいアプリを rails new bundle install は大体うま</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="第2章-toyアプリケーション">第2章 Toyアプリケーション</h1>
<ul>
<li><code>scaffold</code> という即席のアプローチを使うらしい</li>
</ul>
<h2 id="21-アプリケーションの計画">2.1 アプリケーションの計画</h2>
<ul>
<li>toy_appという新しいアプリを <code>rails new</code></li>
<li><code>bundle install</code> は大体うまくいかないので <code>bundle update</code> してから実行
<ul>
<li>そういえば <code>bundle install/update</code> とはなんぞや</li>
</ul>
</li>
<li>Hello, worldと同じようにherokuへデプロイ
<ul>
<li>今回はハマらず完了</li>
</ul>
</li>
<li>いきなりDBのテーブルのようなユーザーのデータモデルが登場</li>
<li>マイクロポスト？
<ul>
<li>マイクロソフトなら知ってる</li>
<li>ユーザーに関連付けられたテキスト
<ul>
<li>最小限の投稿ということか</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="22-usersリソース">2.2 Usersリソース</h2>
<ul>
<li>噂の <code>scaffold</code>
<ul>
<li><code>scaffold</code> で生成された膨大なコードを今詳細に読む必要はありません。今の段階ではおそらく混乱するだけでしょう。
<ul>
<li>そう言われると詳細に読んでみたくなる</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre tabindex="0"><code>    invoke  active_record
    create    db/migrate/20191106133342_create_users.rb
    create    app/models/user.rb
    invoke    test_unit
    create      test/models/user_test.rb
    create      test/fixtures/users.yml
    invoke  resource_route
    route    resources :users
    invoke  scaffold_controller
    create    app/controllers/users_controller.rb
    invoke    erb
    create      app/views/users
    create      app/views/users/index.html.erb
    create      app/views/users/edit.html.erb
    create      app/views/users/show.html.erb
    create      app/views/users/new.html.erb
    create      app/views/users/_form.html.erb
    invoke    test_unit
    create      test/controllers/users_controller_test.rb
    invoke    helper
    create      app/helpers/users_helper.rb
    invoke      test_unit
    invoke    jbuilder
    create      app/views/users/index.json.jbuilder
    create      app/views/users/show.json.jbuilder
    create      app/views/users/_user.json.jbuilder
    invoke  test_unit
    create    test/system/users_test.rb
    invoke  assets
    invoke    coffee
    create      app/assets/javascripts/users.coffee
    invoke    scss
    create      app/assets/stylesheets/users.scss
    invoke  scss
    create    app/assets/stylesheets/scaffolds.scss
</code></pre><ul>
<li>Railsアプリに必要なものをゴリゴリ用意している模様
<ul>
<li>なんという黒魔術
<ul>
<li>オプション追加でDBのテーブル定義も可能
<ul>
<li>idパラメータは主キーとして自動付与とか強すぎるのでは（悪い意味で）</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>データベースのmigration
<ul>
<li><code>scaffold</code> で用意された <code>.rb</code> ファイルをもとにmigrateするという理解で合っている？</li>
<li>昔は <code>rails</code> コマンドではなく <code>rake</code> コマンドだった
<ul>
<li><code>rake</code> はRuby版の <code>make</code>
<ul>
<li>C言語でやったやつだ！</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>URLを変更してうろうろしてみる
<ul>
<li>色々ページができている</li>
<li><code>scaffold</code> すごい（語彙）</li>
</ul>
</li>
<li>ユーザーの追加や削除ができる
<ul>
<li>すごい（語彙）</li>
<li>バリデーションはない模様</li>
</ul>
</li>
<li>MVCの観点からUsersリソースを見てみる</li>
<li>ブラウザからのリクエストがどう処理されるか一連の流れが図解されている</li>
<li><code>ERB</code> は <code>Embedded RuBy</code> のこと
<ul>
<li>ビューのHTMLに埋め込まれているRubyコード</li>
</ul>
</li>
<li><code>routes.rb</code> を書き換えてリロードするだけでページの表示が切り替わるので便利</li>
<li>RESTアーキテクチャ
<ul>
<li>RESTをきちんと調べたことはない</li>
<li><code>REpresentational State Transfer</code></li>
<li>RailsアプリケーションにおけるRESTとは、アプリケーションを構成するコンポーネント (ユーザーやマイクロポストなど) を「リソース」としてモデル化すること</li>
<li>これらのリソースは、リレーショナルデータベースのCRUD操作と、4つの基本的なHTTP requestメソッド (POST/GET/PATCH/DELETE) の両方に対応</li>
<li>RESTfulとは？</li>
</ul>
</li>
<li>Active RecordというRubyライブラリのおかげで、リスト 2.9のUserモデルは <code>User.all</code> というリクエストに対して、DB上のすべてのユーザーを返すことが可能
<ul>
<li>これが悪名高きActiveRecordか&hellip;</li>
</ul>
</li>
<li><code>@</code> で始まる変数はRubyではインスタンス変数という
<ul>
<li>コントローラー内で宣言したインスタンス変数はビューでも使える
<ul>
<li>なんやて！</li>
</ul>
</li>
</ul>
</li>
<li>Update Userボタンのラベル名がどこにもない
<ul>
<li>ボタン名すら動的に生成しているということか&hellip;？</li>
</ul>
</li>
<li>バリデーション、認証、テストなし、レイアウトが整っていない</li>
</ul>
<h2 id="23-micropostsリソース">2.3 Micropostsリソース</h2>
<ul>
<li>Usersと同様にMicropostsも <code>scaffold</code> でコード生成してみる</li>
<li>マイクロポストに文字数制限を加える
<ul>
<li><code>validation</code></li>
<li>modelのクラスに文字数制限を追加するだけ
<ul>
<li>SpringのEntityにこんなのがあった気がする</li>
</ul>
</li>
<li><code>Content is too long</code> のエラーメッセージが表示された
<ul>
<li>簡単すぎる</li>
</ul>
</li>
</ul>
</li>
<li>「異なるデータモデル同士の関連付けは、Railsの強力な機能」
<ul>
<li>もしかして：強力すぎ</li>
</ul>
</li>
<li>Railsのconsole
<ul>
<li>Railsアプリを対話的に操作することができるツール</li>
<li>コード実行によりSQLが実行されてデータが取得される</li>
</ul>
</li>
<li>UsersモデルとMicropostモデルは <code>ApplicationRecord</code> を継承
<ul>
<li><code>ApplicationRecord</code> は <code>ActiveRecord</code> を継承</li>
<li>これを継承するだけでモデルはDBにアクセスできるようになる
<ul>
<li>やめて</li>
</ul>
</li>
<li>DBのカラムをあたかもRubyの属性のように扱えるようになる
<ul>
<li>扱った結果いったいどんなSQLが発行されるんですかねえ&hellip;</li>
</ul>
</li>
</ul>
</li>
<li>コントローラの多彩な機能は <code>ActionController</code> を継承しているから</li>
<li>heroku上で動かして確認</li>
</ul>
<h2 id="24-最後に">2.4 最後に</h2>
<ul>
<li>この章で作成したアプリの弱点
<ul>
<li>理解が困難
<ul>
<li>草</li>
</ul>
</li>
</ul>
</li>
<li>「Scaffoldは何よりも手っ取り早いのがとりえだが、これを元にRailsを理解するには向いていない」
<ul>
<li>チュートリアルがこれを言うのが面白すぎる</li>
<li>黒魔術すぎてプログラミング初心者が触るのは危険すぎるのを体感</li>
</ul>
</li>
</ul>
<h1 id="所感">所感</h1>
<ul>
<li>ここまでRailsを触ってきて、Rubyでコードを書く以外のことを極力意識させないでおこうという強い意思を感じた
<ul>
<li>ActiveRecordとか</li>
</ul>
</li>
<li>それがわかった上でRailsに乗っかるのと、何も考えずにレールの上を走るのは全然意味が違うよなという月並みな感想</li>
<li>まともなWebアプリ開発はJava/Springでしかやったことがないが、RailsからはSpringを初めて触ったときに感じたものとは比にならないくらいの黒魔術感を感じた</li>
</ul>
]]></content>
		</item>
		
		<item>
			<title>静的型付け言語原理主義者によるRailsチュートリアル（第1章）</title>
			<link>https://sore8sore104te.com/rails-tutorial-chapter1/</link>
			<pubDate>Sun, 03 Nov 2019 00:02:17 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/rails-tutorial-chapter1/</guid>
			<description>動機 なぜRailsチュートリアルをやろうと思ったか。それは自社プロダクトでRailsを採用しているにも関わらず、自分の主担当がネイティブアプ</description>
			<content type="html"><![CDATA[<p><img src="./rails_logo.png" alt="image"></p>
<h1 id="動機">動機</h1>
<p>なぜRailsチュートリアルをやろうと思ったか。それは自社プロダクトでRailsを採用しているにも関わらず、自分の主担当がネイティブアプリ開発であるため、Railsを何一つ理解していなくても日々を過ごせてしまうことへの不安や停滞感を解消し、開発者としてもう一歩先へ進みたいと思ったことが大きな要因である。
このブログ記事は、静的型付け言語原理主義者である自分が、Railsチュートリアルに取り組む中でのメモ、発見、所感を垂れ流していくだけのものである。</p>
<h1 id="第1章-ゼロからデプロイまで">第1章 ゼロからデプロイまで</h1>
<h2 id="11-はじめに">1.1 はじめに</h2>
<ul>
<li>Railsを採用しているプロダクト
<ul>
<li>Kickstarterは自分も使っている</li>
</ul>
</li>
<li>DHHという謎の三文字の人はRailsの人だった（今更）</li>
<li>サンプルコードの時点で静的型付け言語原理主義脳が拒絶し始める</li>
</ul>
<h2 id="12-さっそく動かす">1.2 さっそく動かす</h2>
<ul>
<li>Cloud9
<ul>
<li>重そう</li>
<li>Channel9なら知っている（Microsoft脳）</li>
</ul>
</li>
<li>ローカルで動かしたい気もするがお勧めにならってCloud9を選択
<ul>
<li>AWSアカウントを作る
<ul>
<li>ググりつつCloud9用のIAMを作成</li>
</ul>
</li>
<li>OSはAmazon LinuxではなくUbuntuを選択</li>
<li>インデントがスペース2つなのが受け入れられない</li>
</ul>
</li>
<li>Railsインストール
<ul>
<li>ドキュメントのインストールで時間を使わないためのオプション
<ul>
<li>昔はVisualStudioのインストール時にMSDNドキュメントのインストールも追加で行うと死ぬほど時間がかかったなあという謎の思い出し</li>
</ul>
</li>
<li>最新のRailsは6.0.0だがおとなしくチュートリアルに従い5.1.6を選択</li>
</ul>
</li>
</ul>
<h2 id="13-最初のアプリケーション">1.3 最初のアプリケーション</h2>
<ul>
<li><code>rails new</code> するだけでアプリのスケルトンができる
<ul>
<li>つよい</li>
</ul>
</li>
<li>Railsはディレクトリ構成が標準化されているので、別プロジェクトでもどこに何のファイルがあるかわかりやすい
<ul>
<li>パッケージを自由に切れる開発環境と比較すると、標準化されているのは確かに良い</li>
</ul>
</li>
<li>Gemfile
<ul>
<li>Gradleみたいなものだろうか？</li>
<li>バージョン指定は結構細かく制御できる</li>
<li>Gemはちょっとしたマイナーアップグレードですら問題を引き起こすことがある
<ul>
<li>怖すぎでは</li>
</ul>
</li>
</ul>
</li>
<li><code>rails new</code> して <code>bundle install</code> 一発でアプリができてしまった
<ul>
<li>簡単すぎて怖い</li>
</ul>
</li>
<li>そして<code>rails server</code> するだけでアプリが起動
<ul>
<li>簡単すぎて怖い</li>
<li>Yay! You&rsquo;re on Rails!
<ul>
<li>これからレールの上を走らされるんですねわかります</li>
</ul>
</li>
</ul>
</li>
<li>コマンドでRubyとRailsのバージョンを確認</li>
<li>RailsはMVC</li>
<li>Controllerにアクションを定義</li>
<li>routes.rbでリクエストを振り分ける</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">draw</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">root</span> <span class="s1">&#39;application#hello&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div><ul>
<li>これだけで振り分けられるのがすごい（語彙）</li>
<li>helloアクションで表示される文字列を変更してみる</li>
<li>別のアクションを定義してみる</li>
<li>コンパイルし直さなくてもリロードするだけで結果の変更が確認できるのは確かに便利だ
<ul>
<li>静的型付け言語原理主義者による手のひら返し</li>
</ul>
</li>
</ul>
<h2 id="14-gitによるバージョン管理">1.4 Gitによるバージョン管理</h2>
<ul>
<li>普段から使用しているので詳細は割愛
<ul>
<li>sshまわりの設定は毎回ググらないとよくわからなくなる</li>
</ul>
</li>
</ul>
<h2 id="15-デプロイする">1.5 デプロイする</h2>
<ul>
<li>名前は知っていたが触ったことのなかったHeroku
<ul>
<li>Webアプリ用のホスティングフレームワーク</li>
<li>Gitを使用していればRailsアプリを簡単にデプロイできる</li>
</ul>
</li>
<li>Herokuのセットアップ
<ul>
<li>HerokuではPostgreSQLを使用している</li>
<li>発音は “post-gres-cue-ell&quot;
<ul>
<li>ぽすと ぐれす きゅーえる？知らなかった</li>
</ul>
</li>
<li>HerokuではSQLiteがサポートされていない</li>
<li>Gemfile.lockとは？</li>
</ul>
</li>
<li>Herokuにデプロイする
<ul>
<li>コマンド一発簡単デプロイ</li>
<li>1分くらい待ったかも</li>
</ul>
</li>
<li>色々やっていたら下記エラーに遭遇</li>
</ul>
<pre tabindex="0"><code>$ git push heroku master
remote: !   No such app as xxx-yyy-zzzz.
fatal: repository &#39;https://git.heroku.com/xxx-yyy-zzzz.git/&#39; not found
</code></pre><ul>
<li>gitのリモートURLを <code>heroku create</code> した時のURLに変更し、再度pushで対応</li>
</ul>
<pre tabindex="0"><code>git remote set-url heroku https://git.heroku.com/XXX-YYY-ZZZZ.git
git push heroku master
</code></pre><ul>
<li>pushは成功したが <code>hello, world!</code> が表示されない
<ul>
<li>どうやらプリコンパイルというものが必要？？</li>
<li><code>rake assets:precompile</code> とは</li>
<li>アセットパイプラインとは</li>
<li>ググってプリコンパイルで対応
<ul>
<li>これで良かったのだろうか</li>
</ul>
</li>
</ul>
</li>
<li>チュートリアル通りに進まず少しハマったものの、ようやく第1章完了
<ul>
<li>アセットパイプライン周りは別途学習が必要</li>
<li>今後のRailsチュートリアルの中でも出てくるのだろうか？</li>
</ul>
</li>
</ul>
<h1 id="所感">所感</h1>
<p>Railsはたしかに便利だが、前評判通り強力すぎるなという印象。冒頭に出てきた <code>scaffold</code> は完全に黒魔術なのではないか。Rubyの記法には徐々に慣れていくと思うが、肝心のRailsとは仲良く慣れないかもしれない。この章では本質的でないところでハマったものの、チュートリアル通りに進まないことで理解が深まるはずなので、ラッキーだったと捉えて先へ進みたい。</p>
]]></content>
		</item>
		
		<item>
			<title>ブログをWordPressからHugoへ移行した</title>
			<link>https://sore8sore104te.com/from-wordpress-to-hugo/</link>
			<pubDate>Sun, 06 Oct 2019 00:17:03 +0900</pubDate>
			
			<guid>https://sore8sore104te.com/from-wordpress-to-hugo/</guid>
			<description>Photo by Drew Beamer on Unsplash さようならWordPress、こんにちはHugo まともに記事を書かないのでレンタルサーバー代がもったいなくなってきたのと、技術ブ</description>
			<content type="html"><![CDATA[<p><img src="./drew-beamer-3SIXZisims4-unsplash.jpg" alt="image">
Photo by <a href="https://unsplash.com/@drew_beamer">Drew Beamer</a> on <a href="https://unsplash.com/">Unsplash</a></p>
<h1 id="さようならwordpressこんにちはhugo">さようならWordPress、こんにちはHugo</h1>
<p>まともに記事を書かないのでレンタルサーバー代がもったいなくなってきたのと、技術ブログ感の出せる気に入ったテーマがなかったのでWordPressからの離脱を決意。上記理由を満たせる代替手段としてHugo+Netlifyを選択した。
実は今年の1月にすでにHugo+Netlify環境に移行していたが、WordPress上の記事を移行する前にレンタルサーバーを解約するという愚行を犯し、記事がない状態の新ブログが爆誕していた。</p>
<p>最近同僚がブログを始めたことに触発されたので、いい加減このブログも再起動させようと思う。あと今のテーマも暫定なのでおいおい変更したい。（これは変更しないパターン）</p>
]]></content>
		</item>
		
	</channel>
</rss>
