まさ@ブログ書き込み中

まさ@ブログ書き込み中

まさの旅、英語、プログラミング、プライベートについて、色々記録しています。

ユーザーのマイクロポスト【Railsチュートリアル13章】

 

こんにちは、Railsチュートリアルも終わりに近づいていて興奮しているまさです。

 

今回は、第13章の内容について書いていきたいと思います。

 

ここ最近の僕の記事はRailsチュートリアルの内容をある程度要約しながら全体の流れを一つひとつ示していきましたが、僕が気になることをざっくりまとめるだけの、前回のスタイルに戻していこうと思います。その方が理解が深まると思ったからです。

 

では早速やっていきましょう!

 

 

Micropostモデル

このサンプルアプリケーションで「モデル」といえばユーザーモデルしか用意していませんでしたが、ここでやっと第二のモデル「マイクロポストモデル」をつくります。

 

基本的なモデル

マイクロポストモデルを設計する際に気をつけるべきポイントは関連付けを行うことです。has_manyとbelong_toメソッドを使って関連付けをしていきます。

 

一つのマイクロポストに一人のユーザーが対応するので、マイクロポストとユーザーは一対一の関係です。そのような関係の場合、belong_toメソッドを使います。特に、モデルを生成する際にはreferencesをデータ型として宣言すれば勝手にbelong_toメソッドが作られるみたいです。

$ rails generate model Micropost content:text user:references


その結果作られたコードは以下の通りです。

app/models/micropost.rb

class Micropost < ApplicationRecord
   belongs_to :user
end

 

これで、マイクロポストはid, content, created_at, updated_atだけでなくuser_id属性も持つようになります。

 

バリデーション

さて、マイクロポストのバリデーション(正しいデータが追加されるための仕組み)を追加します。

 

Twitterなどのマイクロポストのバリデーションとしてすぐに思いつきそうなのは、contentが空ではない、文字数制限以内である、ユーザーIDが特定されているなどですね。

 

よって、micropost.rbに以下のようなバリデーションを追加します。

  validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }

 

関連付け 

先ほどモデルを生成する際にbelong_toメソッドをuserに対して指定しましたが、もう一つ関連付けをすべきことが残っています。それはあるユーザーは複数のマイクロポストを持つという一対多の関連付けをするhas_manyメソッドを使うことです。

 

具体的には、Userモデルに以下のように指定します。

app/models/user.rb

class User < ApplicationRecord
  has_many :microposts
  .
  .
  .
end

 

 

さて、関連付けをしたことで何かいいことがあるのかというと、例えば以下のようなメソッドが使えるようになります。

f:id:masaincebu:20170905135245p:plain

user.microposts.buildはMicropost.new(user_id: 投稿したユーザーのID)と同義です。

 

マイクロポストのデフォルトの呼び出し順を変える

現状ではuser.micropostsとした時のマイクロポストの呼び出し順はランダムになってしまい、フィードを表示する際に困ってしまうので、デフォルトの呼び出し順を変えるようにします。

 

具体的にはdefault_scopeメソッドを使ってデータベースから要素を取得したときの、デフォルトの順序を指定します。

app/models/micropost.rb

  default_scope -> { order(created_at: :desc) }

 

「->」という表記はラムダ式と言われていて、ブロックを引数にとってProcまたはlambda(もしくは無名関数)と呼ばれるオブジェクトを返すそうです。本章によると

このオブジェクトは、callメソッドが呼ばれたとき、ブロック内の処理を評価します。

 

だそうで、例えば以下のように動作するみたいです。

>> -> { puts "foo" } => #<Proc:0x007fab938d0108@(irb):1 (lambda)> >> -> { puts "foo" }.call foo => nil

 

ユーザーが削除されたらそのユーザーのポストも削除されるようにする

前回までの実装で、管理者はユーザーを削除できるようになりました。そのユーザーが削除された場合、ユーザーのマイクロポストも削除されるべきです。

 

その仕組みは以下のコードによって実装できます。

app/models/user.rb

  has_many :microposts, dependent: :destroy

 

 

マイクロポストを表示する

本章ではマイクロポストをユーザーのプロフィール画面とホーム画面に表示させるため、Micropostコントローラは作成するものの、ビューに関してはパーシャルを利用しています。

 

よって、show.html.erb(プロフィール画面)には以下のようなコードを追加しなければなりません。

<ol class="microposts">
  <%= render @microposts %>
</ol>

 

これは、@microposts変数内のそれぞれのマイクロポストを出力し_micropost.html.erbパーシャルを使ってマイクロポストのコレクションを表示するためのコードです。

 

ちなみに_micropost.html.erbパーシャルは以下のようになっています。

<li id="micropost-<%= micropost.id %>">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content"><%= micropost.content %></span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(micropost.created_at) %> ago.
  </span>
</li>

 

また、ユーザー一覧を表示する際にも使ったwill_paginateメソッドは、今回は@microposts変数を与えなければなりません。

<%= will_paginate @microposts %>

 

@user変数に関しては何も引数を与えなかった理由を、本章では以下のように説明しています。

これはwill_paginateが、Usersコントローラのコンテキストにおいて、@usersインスタンス変数が存在していることを前提としているためです。

 

 

マイクロポストを操作する

先ほど述べた通り、Micropostsコントローラは何か真新しいビューを描画するわけでもなく、マイクロポストを作成したり削除したりするためにあります。

 

アクセス制御

よって、ルーティングは以下のように設定しておきます。
config/routes.rb

  resources :microposts,          only: [:create, :destroy]

 

マイクロポストの作成

本章では、マイクロポストをホーム画面で投稿できるようにします。ホーム画面に以下のようなコードを書くのですが、

        <%= render 'shared/micropost_form' %>

 

_micropost_form.html.erbの方は以下のようになっています。

<%= form_for(@micropost) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose new micropost..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
<% end %>

 

上のコードの中にある以下のコードですが、

<%= render 'shared/error_messages', object: f.object %>

 

これはユーザー登録時のエラーメッセージを表示させる以下のコードとは少し違います。

    <%= form_for(@user) do |f| %>
      <%= render 'shared/error_messages' %>

 

このコードでは_error_messages.html.erbでform_forの第一引数となっている変数(@user)を直接参照してエラーメッセージを表示しなければなりませんでした。しかし、ここではキー:f.objectとすることでキーさえ渡しさえすれば、値に応じてエラーメッセージを表示してくれます。

 

マイクロポストの削除

マイクロポストの削除機能を実装するために、まずはフィードに「delete」リンクを用意します。
app/views/microposts/_micropost.html.erb

      <%= link_to "delete", micropost, method: :delete,
                                       data: { confirm: "You sure?" } %>

 

マイクロポストが削除されるようにするために、この削除リンクを押せば

  • 削除すべきマイクロポストを特定する
  • 本当にそのマイクロポストの所有者であるかどうかをチェックする

仕組みを考えなければなりません。

 

そこで、beforeフィルターでcorrect_userメソッドを呼び出すようにしています。

  before_action :correct_user,   only: :destroy
  def destroy
    @micropost.destroy
    flash[:success] = "Micropost deleted"
    redirect_to request.referrer || root_url
  end
    def correct_user
      @micropost = current_user.microposts.find_by(id: params[:id])
      redirect_to root_url if @micropost.nil?
    end

 

 

マイクロポストの画像投稿

さて、マイクロポストでテキストを投稿したり削除したりする仕組みは整いました。

 

ここでマイクロポストで画像を投稿できるようにします。本章では投稿した画像を扱ったり、その画像をMicropostモデルと関連付けするために、今回はCarrierWaveという画像アップローダーを使います。

 

Gemfile

gem 'carrierwave',             '1.1.0'
gem 'mini_magick',             '4.7.0'

 

mini_magickは<Mあとで必要になります。

 

画像のアップロード

データベースには画像を特定する画像名が保存されるため、データ型はstringとしてマイグレーションファイルを作成します。

$ rails generate migration add_picture_to_microposts picture:string

 

ここで注意すべき点としては(ローカルの場合)、画像が保存される場所はデータベースではないということです。ここでは、あくまで、データベースに画像名を保存しておくことで、実際にアップロードされた画像とActiveRecordモデルの属性とを関連付けさせているだけです。

 

CarrierWaveに画像と関連付けたモデルを伝えるためには、mount_uploaderというメソッドを使います。このメソッドは、引数に属性名のシンボルと生成されたアップローダーのクラス名を取ります。

ということで、次は

  mount_uploader :picture, PictureUploader

というコードをマイクロポストモデルに付け加えます。

 

画像のリサイズ

さきほどインストールしたgemの一つにmini_magickというものがありましたが、MiniMagickは「ImageMagickRubyを繋ぐgem」です。これを使って、画像をリサイズすることができます。

 

画像リサイズの処理を実装するためには、以下のように画像アップローダーを修正します。

app/uploaders/picture_uploader.rb

  include CarrierWave::MiniMagick
  process resize_to_limit: [400, 400]

 

 

以上で13章のまとめは終わりです。

本番環境で画像アップローダーを機能させる方法やその他の情報については

Ruby on Rails チュートリアル:実例を使って Rails を学ぼう

を参照してください^^

 

ではまた。