Naomi's notebook

Naomi's notebook

Ruby on Rails tutorial +alpha(3)生活記録を作成する

注意:この記事はメモなので、この記事に書いてあることをやっただけでは同じものができません。(大体の雰囲気は書いてあると思います。)

注意:全くのweb開発初心者なので、間違っているところなどがあるかと思いますが、教えていただけると嬉しいです。

naomi-notebook.hatenablog.com

続きです。eventを作成するフォームを作ります。

homeにもカレンダーを追加

前回はユーザページにしかカレンダーと生活記録が表示されませんでしたが、ホームから投稿した時にわかりやすいように自分の生活記録がホームに表示されるようにします。 ちょっと困ったのは、homeからuser/1/eventsにアクセスするのと同じなのはusersのshowからは1/eventsにアクセスしなければならないので、うまくカレンダー部分をパーシャルに分けられなかったことでした。とりあえずパーシャルに分けないで二回書いています。(教えてください…)

event追加フォームをつくる

ほとんどmicropostを作った時と同じで、roots.rbを編集し、createアクションをeventコントローラに追加した後、viewでフォームを作ります。

eventのidをすぐ知ることができるようにする

eventを削除するためには、簡単にeventのidを知ることができる必要があります。今回は、カレンダーをクリックした時にそのeventの編集・削除ページへ飛ばされるようにします。

Fullcalender.ioのeventclickで、イベントがクリックされた時にjsonで渡した情報を確認できます。 この機能で、イベントがクリックされた時には/events/(event_id)/edit に飛ばし、そこで記録の編集・削除を行うことにします。

eventClick: function(info) {
        window.location.replace("/events/"+info.event._def.publicId);
      } 

eventの編集フォームを作る

/events/:id/editを作ります。 コントローラー

def edit
        @event = Event.find(params[:id])
    end

    def update
        @event = Event.find(params[:id])
        if @event.update(event_params)
          flash[:success] = "生活記録が更新されました!"
          redirect_to root_url
        else
          render 'edit'
        end 
    end

ビュー

<h1>生活記録を変更</h1>

記録id:<%=@event.id%><br>
タイトル:<%=@event.title%><br>
タイプ:<%=@event.eventtype%><br>
開始時刻:<%=@event.starttime%><br>
終了時刻:<%=@event.endtime%><br>
<div class="row">
  <div class="col-md-6 col-md-offset-3">

<%= form_with(model: @event, local: true) do |f| %>
    <%= render 'shared/error_messages', object: f.object %>
    <%= f.label :title, "記録のタイトル" %>
    <%= f.text_field :title, class: 'form-control'%>

    <%= f.label :eventtype, "記録のタイプ(カレンダーでの色)"%>
    <%= f.number_field :eventtype %>

    <%= f.label :starttime, "開始時刻" %>
    <%= f.datetime_field :starttime, placeholder: Time.now.ago(30.minutes) %>

    <%= f.label :endtime, "終了時刻" %>
    <%= f.datetime_field :endtime, placeholder: Time.now %>

    <%= f.submit "変更", class: "btn btn-primary" %>
<% end %>

   
</div>
</div>

eventが削除できるようにする

deleteを送信してdestroyアクションを実行するボタンを作ります。 ビュー

<%= link_to "削除", @event, method: :delete,data: { confirm: "この生活記録を消しますか?" }, class: "btn btn-danger" %>

コントローラ

def destroy
    Event.find(params[:id]).destroy
    flash[:success] = "行動記録が削除されました"
    redirect_to root_url
  end

これで削除ができるようになります。

バグの修正

いじっているうちに、micropostやeventでinvalidな投稿をするとundefined method `errors' for nil:NilClassエラーが出るようになってしまいました。 とりあえずは以下で治りました。

<% if object && object.errors.any? %>

が、別のエラーが発生… micropostかeventの投稿に失敗した後、もう一方で投稿しようとするとエラーが出ます。 form_withのmodelによるurl予測がうまくいっていないようだったので、form_with(url:events_path,method: :post)にしました。そうするとparams[:events]がなくなるので、require(:events)を消します。そうするとupdateの時にeventsの下にデータがあるので失敗してしまいます。updateの方は別の関数を作ることで解決しました… これでエラーは出なくなりました。 が、制限に反する入力をした時にエラーメッセージが出なくなってしまいました…なぜだろう…

とりあえずflashにメッセージを入れて、それをエラーメッセージとすることにしました。

セキュリティーの追加・テストを書く

最後にテストを書きながら、権限の制約を追加していきます。 確認することは以下です。

・users/(user_id)/と、users/1/events/へは、ログインしてなければアクセスできず、ログイン画面にリダイレクトされる

test "usersページには、ログインしていないときはリダイレクトする" do
    get user_path(@user)
    assert_redirected_to login_url
  end

  test "eventsページには、ログインしていないときはリダイレクトする" do
    get user_path(@user)+"/events"
    assert_redirected_to login_url
  end
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers,:events,:show]

・(そのuserを)フォローしているアカウントじゃないとevents/:idが見られない

require 'test_helper'

class EventsControllerTest < ActionDispatch::IntegrationTest
  def setup
    @user = users(:michael)
    @following_user = users(:lana)
    @not_following_user = users(:archer)
    log_in_as(@user)
  end
  test "users/1/eventsを自分が閲覧できる" do
    get user_path(@user)+"/events"
    events = JSON.parse(@response.body)
    assert_equal "食事", events[0]['title']
  end
  test "users/1/eventsをフォローしているユーザは閲覧できる" do
    get user_path(@following_user)+"/events"
    events = JSON.parse(@response.body)
    assert_equal "睡眠", events[0]['title']
  end
  test "users/1/eventsをフォローしていないユーザは閲覧できない" do
    get user_path(@not_following_user)+"/events"
    assert_redirected_to root_url
  end
end
before_action :follower_user,   only: [:events]
...
private
def follower_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless @user.followers.include?(current_user) || current_user?(@user)
    end

・同じアカウントじゃないとeventが作成できない

・eventを作ったのと同じアカウントじゃないとevent編集画面に行けない

・eventを作ったのと同じアカウントじゃないとeventが編集できない

・同じアカウントじゃないとeventが削除できない

ここはmicropostを作った時のテストを参考に書きます

できました。 f:id:Naomi_Lilienthal:20200815165945p:plainf:id:Naomi_Lilienthal:20200815165949p:plainf:id:Naomi_Lilienthal:20200815165954p:plain