Ruby on Rails
基礎1

  • 前回までで、基本的なWEBアプリ作りは出来るようになったと思います。
  • ここからは、より本格的なアプリケーション開発に進んでいきたいと思います。

前回の確認

  • 本格的な機能として、まずログイン機能を作っていきましょう。
    • ユーザー名とパスワードを入力するアレです。​
  • 尚、ここで作っていくサイトは Myブック サイト の続きとして進めていきます。
  • ログイン機能というからには、まずはユーザーがいないといけません。
  • 現在はbookモデルのみなので、ここにuser(ユーザー)モデルを追加します。
  • コンソールに以下のコマンドを入力しましょう。

userモデル作成

  • コマンドの解説です。
  • userのモデルを追加し、カラムとして name(ネーム)password_digest(パスワード_ダイジェスト)の2つを設定しています。
    • ※password_digest はパスワードとして使います。名前は必ず password_digest にしてください。
  • ​カラムは 性別、年齢 などもっと増やしても良いのですが、ココではわかりやすさのために【名前】と【パスワード】だけで進めます。次へ。
  • userモデルの作成が成功したら「 app/models/user.rb 」が増えていますので、確認しておきましょう。→の画像

userモデル作成

  • また、マイグレーションファイルも作成されていますので、確認しておきましょう。
  • ↓の画像
  • マイグレーションファイルは、データベースに反映しないと意味がないので、下のコマンドを実行します。
    • ※モデルの作成や、カラムの追加、変更などがあれば、必ず以下のコマンドを実行します。

userモデル作成

  • 次に、user用のコントローラー や ビュー を作ります。
  • ビューは、コントローラを作る時に同時に作れますので、以下のようにします。
    • ※モデルの時とは違って、userじゃなく、users なので注意。

userモデル作成

  • これで、user用のコントローラーと、indexファイル、showファイル、newファイル、editファイルが自動で作られます。
  • また「 users_controller.py 」という user用のコントローラーが作られ、上記のアクショルート設定も自動で作られ、さらにルート設定も作ってくれるので、これだけで「 ~/users/index 」や「 ~/users/show 」でページが開くようになっています。

①ビューファイルが作られる

②userコントローラーと、その中にアクションメソッドが作られる

③ルート設定も作られる

①②③があれば、ページにアクセスできる

userモデル作成

  • まずはルート設定を resources で作り直します。
    • 以下のようにしてください。※先ほど、勝手に作られた「 get 'users/index' 」などはすべて削除しています。
  • 次は、ユーザーの コントローラー と ビュー の中身を作って行きます。
  • とりあえず book とほとんど同じで良いので、コピペしながら以下のようにしてください。※「bookをuser」に、「BookはUser」に変更

usersのコントローラー

  • 次は、createメソッド と updateメソッド を作ります。
    • とりあえず、bookコントローラーからコピペして、bookをuserに、BookをUserに変えた状態が以下です。 
  • 追加する場所は、bookコントローラーと同じように、 create は new の下、update は edit の下、にしておくと分かりやすいです。
  • ただ、これだけだとまだ問題があります。
    • なにが問題か分かりますか?

usersのコントローラー

  • 正解は カラム です。
  • Bookには「 date 」「 star 」「 impression 」というカラムがありましたが、Userには「 name 」「 password_digest 」というカラムだけでしたね。
    • なのでこれを変更してあげます。
    • 出来そうであればチャレンジしてみてください。答えは次です。

usersのコントローラー

  • 以下のようにすればOKですね。

usersのコントローラー

  • 最後は destroy です。
  • これも以下のようにします。

usersのコントローラー

  • userのコントローラーに関しては、今はこれで完了です。次はビューに行きます。
  • とりあえず book の index から、user の index ファイルへ、中身をまるまるコピペしてきてください。
  • 後は「book」の部分を「user」に変えて、user に存在しないカラムを消して、テキストも少しいじります。
  • 細かい部分は自由でOKですが、ある程度は同じ感じにしておいてください。

usersのビュー

  • ページを開いてみると以下のようになるはずです。

usersのビュー

  • ページを開いてみると以下のようになるはずです。
  • ページを開いてみると以下のようになるはずです。
    • ※ユーザーの index へのパスは「 ~/users 」ですね。
    • まだユーザーが登録されていないので、だれも表示されていません。

usersのビュー

  • 次は newファイル にいきましょう。
  • こちらも、bookからのコピペして、必要なところ変更すると早いです。

usersのビュー

  • これで、ユーザーの新規登録ができるようになったはずなので、試しに適当なユーザーを追加してみましょう。

usersのビュー

  • ユーザーの indexページ に、ちゃんとユーザーが追加されていればOKです。

usersのビュー

  • 後は、showページ と editページ です。
  • これもとりあえずは、bookと同じようにします。
    • 以下のように作ってみてください。出来たら次のページへ。

usersのビュー

  • 以下のようにすると良いですね。
    • まずは showページ です。(editは次のページ)

usersのビュー

  • editファイルはこんな感じですね。※削除もちゃんとできるか、確かめておいてください。
  • ここまででユーザーのCRUDは完成しました。
    • ※CRUDとは、【 Create 】【 Read 】【 Update 】【 Delete 】の事でしたね。
  • では、次は実際にユーザーがログインできるように作っていきましょう。
  • まず、ログイン画面を作っていきたいのですが、その前に、今後必要になる「 session(セッション)」というものを追加します。
  • セッションとは、状態(じょうたい)を管理するための仕組み です。
  • WEBアプリ(サイト)とブラウザは、 HTTPという技術でやり取りしていますが、このHTTPというのは 状態を保存する仕組み がありません。
    • つまり ログインしている という状態が保存できないのです。
  • ログインしている状態が保存できない、ということは、ログイン画面でログイン完了したとしても、別のページに移動すると、ログインされていないことになってしまいます。

ログインページを作る

ログイン出来ました!

ユーザー名:abc

パスワード:********

画面を変えると…

ログインしてください

  • そのため、何かしらの方法で、ログイン状態を保存できるようにする仕組みが必要になります。
    • それをセッションと言います。
  • このセッションは、色んな方法があり セッションとはこの方法 と1つに決まっているわけではありません。
  • つまり、なんらかの方法で状態を管理する方法のことをセッションと言います。
    • (「セッションをはる」「セッションを繋ぐ」みたいに使います)
  • ​​​​​​​Railsでは、このセッションを簡単に導入できるメソッドが用意されていますので、やっていきましょう。
  • まずは、セッション管理用の コントローラーと、ログイン画面用に ビュー(newのみ)を用意します。
  • 以下のコマンドをコンソールに入力してください。 

ログインページを作る

  • これで、sessions_controller と、newファイルや、ルート設定が作られます。

sessionコントローラー作成

①ビューファイルが作られる

②userコントローラーと、その中にアクションメソッドが作られる

③ルート設定も作られる

  • これで「 ~/sessions/new 」で、セッションのnewページが開きますが、これを「 ~/login 」で開けるようにしたいと思います。
    • 以下のようにしてください。

ルート設定

  • 「 ~/login 」で、以下のページにアクセスできるか確認しておきましょう。
  • ルーティングが出来たところで、次はビューを変更していきましょう。
    • 先に完成画面を見ておくと以下の感じです。

ログインページを作る

  • ほとんど user の newページと同じですね!

ログインページを作る

  • まずはnewページのコードを、まるまるコピペしてください。
    • ※コピペするのは「 app/views/sessions/new.html.erb 」ファイルです。
  • そして一部変更します。
    • 次へ。

ログインページを作る

  • 重要な点として、session は モデルを作っていません。
  • そのため、form_with の model 部分は削除します。
    • さらに、url は login_path に変更しておきます。
  • 後、細かい部分としては「 form.submit "登録" 」を「 form.submit "ログイン" 」に、
  • 「 button_to "登録せずに戻る" 」を「 button_to "ログインせずに戻る" 」に変更しておきます。

ログインページを作る

  • これで現在は以下のようなページになります。
  • ここからが重要なのですが、前に書いた通り、ログイン画面でログインできたとして、「現在ログインしている」という「状態」は、どこかに保存して管理する必要がありました。
    • 保存しておかないと、ページを変えるたびに「ログインしている」という状態が消えてしまいます。
  • データベースに保存ということも可能ですが、Rails では、もっと軽く、簡単に管理できる仕組みがありますので、それを使います。次へ。

sessionコントローラー

  • sessionのコントローラーに、ログイン後の処理を書いていきます。
  • まず createメソッド を追加して、以下のようにしてみましょう。
  • 最初の「 @user = User.find_by(name: params[:name]) 」は、user テーブルから params[:name] と同じ名前のユーザーを探して、それを@user に入れています。
    • params[:name] は、ログインフォームの「名前」フィールドの入力内容が入っています。
  • 次の「 session[:user_id] 」が今回のポイントで、sessionというハッシュに、「 user 」というキーを付けて、そこに先ほど探してきたユーザーを代入しています。
    • この session は params と同じような感じで、中にデータを保存できるように用意されている仕組みです。ログイン情報(セッション情報)などはここに入れておきます。
  • 最後の redirect_to は、今まで使ってきたのと同じで、処理後にどこのページに行くか、を指定しています。今回はログインページに戻るようになっています
  • コントローラーが出来たところで、まだこの createメソッド へのルートがないので作りましょう。
    • ぜひチャレンジしてみてください。
    • ヒント①:newページのフォームに設定した、URLと、HTTPメソッドはなんだったか?を確認しましょう。
    • ヒント②:上記がわかれば、後は routes.rb に追加するだけです。

ルート設定

  • こんな感じにすればOKですね。

ルート設定

  • これでとりあえずログインできた感じにはなりましたが、実際はまだ途中です。
  • 先ほどの「 session[:user] = @user 」 で、「ログインしたユーザー」を session[:user] に保存することが出来ました。
  • 次は「ログインした状態」と「ログインしていない状態」で、ビューの表示を変更してみましょう。次のページへ

ログインページの変更

  • ログインページを以下の赤線部分を追加してください。
  • これで「 session[:user_id] に値が入っていたら(つまりログインしていたら)」、"すでにログイン済です!"が表示されます。
    • ログインしていなければ、前と同じように、ログインフォームが表示されます。
  • 試しにログインしてみてください。
    • ※存在しないユーザーでログインしようとするとエラーになります。また、今はパスワードは間違っていてもログイン出来ます。
  • これで session[:user_id] に、ログインした状態が保存されているのが分かったでしょうか?
  • この session のデータは、どんなにページが変わっても、保存されたままのため、別のページでもログイン状態を保存しておけます。
  • ただし、session には、自分で保存した「 user 」以外にも、色々な情報が保存されており、これをビューで直接使うのは宜しくありません。(セキュリティ的に危険ということ)
    • 本来は、必要な情報だけ変数に保存して使います。
  • 次は、その対策をしていきます。次へ。

ログインページの変更

  • では、まずは sessionコントローラー の newメソッドに、以下を追加してみてください。
  •  これで @current_usr という変数に、必要な情報(ログインしているユーザーのid)が代入されることになります。
  • 後は、先ほどのログインページの、if文のところだけを変更します。
  •  これでも、セキュリティを守りつつ、先ほどと同じように出来ました。次へ。

ログインページの変更

  • これでログイン情報は保存されたのですが、今は ログインページでしか、このログイン情報は使えない状態です。
    • 例えば、book や user のどのページでも、ログイン情報は使えません。
    • ログイン情報は基本的に色々な場所で使う事になるので、どこでもログイン情報を使えるようにしたいです。
  • もし使うためには、それぞれのコントローラーの、さらにそれぞれのメソッドの一つ一つに「 @current_user_id = session[:user_id] 」を書いていかなくてはいけません。
    • めんどくさいですよね?
  • ということで、これをまとめて設定する方法をやります。
  • ポイントは「すべてのモデルの、すべてのメソッドで使いたい」ということなので、こういう時は「 application_controller.rb 」で行います。
    • だいぶ前のスライドで説明しましたが、基本的に bookコントローラーや、userコントローラーなど、それぞれのコントローラーは、このapplication(アプリケーション)コントローラーを引き継いでから、実行されています。(クラスが継承されている)
  • つまりは、applicationコントローラーに書いておけば、すべてのコントローラーで読み込まれるコードになります。次へ。

ログインページの変更

  • では、「 app/controllers/application_controller.rb 」を開いて、以下のようにしてください。

アプリケーションコントローラー

  • まず、4行目の「 set_current_user 」メソッドで「 @current_user_id = session[:user_id] 」を作っています。
  • さらに2行目の「 before_action :set_current_user 」で、「何かしらのメソッドが実行される前に、set_current_user を実行する」という意味になります。
    • ※before_action は「何かのメソッドが実行される"直前"に、別のメソッドを実行する」方法でしたね。
  • これで「どのコントローラーの、どのアクションメソッド(index や show など)が実行されたとしても、その"直前"に、ログイン情報を、@current_user_id に入れる」という意味になります。
    • つまり「どこでも @current_user_id を使えるようになった」という事です。次へ。
  • では、ここでちょっとした課題をやってみましょう。
  • 今は「すでにログイン済です!」としか表示されませんが、これを「すでに●●さんでログインしています!」に変更してみます。
    • もちろん●●の部分は、ログインしているユーザーの名前が表示されるようにします。
  • 名前を表示する、ということは、ユーザーの name カラムのデータが必要ですが、現在はログイン情報としてユーザーidだけを session に保存しています。(ユーザーデータをすべて保存することも出来ますが、無駄に session が大きくなるので、idだけにしています。)
    • つまり、id 以外のユーザーのデータを持ってくる必要があります。
  • 色々な方法がありますが、今回は applicationコントローラー内で、ログインしているユーザーのデータを丸ごと取得し、ログインページで使うことにします。
    • applicationコントローラーを以下のようにしてみましょう。
    • 変数名も @current_user に変更しています。

ログイン情報を利用して表示する

  • ちょっと解説です。
  • ここでやっていることは、userコントローラーの editメソッド や showメソッド でやっていることと同じです。
    • Userテーブルから、指定したidのユーザーデータと取得しているだけです。 
  • userコントローラーとの違いは、paramsのデータを使っているか、sessionのデータを使っているか、だけです。
    • params は、フォームやリンクなど、URLと一緒に付与されたパラメータが入っているものでした。
      • 別のページ(アクション)にパラメータを渡すことは出来ますが、ずっと残っているわけではなく、次のアクションで消えてしまいます。
    • session は、基本的にずっと保存してほしいデータを入れておくもので、ページ(アクション)が何度変わっても残っています。
      • ちなみにですが、この session には「セッションID」というIDが保存されており、同時にそのセッションIDを、クライアント(ブラウザ)に送り、クッキーという保存領域にも保存させています。
      • Railsサーバと、クライアントのセッションIDが共通の間は「同じ人からのアクセス」として、セッション情報は保存され続けます。
      • Railsサーバが再起動したり、ブラウザのクッキーを削除するなどで、サーバとブラウザのセッションIDに相違が出ると、session は破棄されます。(ログイン状態が消えるので、再ログインが必要になる)
      • もし、このセッションIDが誰かにバレると、別のPCからでも「ログイン状態」などを引き継いだままアクセスできるようになるため、結構ヤバめです。

ログイン情報を利用して表示する

  • では、話を戻して、画面に名前を表示させてみましょう。
    • ログインページを以下のようにすれば良いですね。

ログイン情報を利用して表示する

  • 上記のように、データベースから取得してきた値は「 変数名.カラム名 」の書き方で使うことができます。
    • 今後、非常によく使うことになるので、覚えておいてください。
  • 尚、newメソッドのコードは必要なくなるので、削除しておきましょう。
  • 次は、ログアウトできるようにしましょう。
  • これは簡単で、session に保存したデータを消すだけです。
  • sessionコントローラーに destroyメソッド を用意して、ログインページにはログアウトボタンを設置しましょう。

ログアウト

sessionコントローラー

ログインページ

  • 最後に、ログアウトボタンを押した場合のルートも用意しましょう。
  • routes.rb ファイルに logout_path を作り、sessionコントローラーの、destroyメソッドが実行されるようにすれば良いですね。
    • 出来そうであれば試してみましょう。
    • 出来たらちゃんとログアウトできるかも、試してください。

ログアウト

  • 以下のようにすれば良いですね。

ログアウト

  • これでログアウトは完成です。
  • ここで、ちょっとややこしい話をしますが、実は、先ほど作ったログアウトボタンの button_to を、link_to に変更すると、ログアウトが出来なくなります。
  • 実は、link_to は基本的に get を送る用で、get 以外を送る時は、何かしら別の処理が必要になります。(いくつか方法はあります)
    • 「 method: :delete 」と付けていても、無視されて get で送られてしまいます。
  • 逆に、button_to の場合は、前に言った通り、何も指定しないと post が送られますが、「 method: :delete 」などを指定すれば get や delete なども送れます。
  • そのため、先ほどのログアウトボタンも、delete を送れる button_to で作っています。
    • link_to で get 以外を送る方法は、また別の機会にやります。

link_to と button_to

  • 次は、「ちゃんとパスワード認証する」ということをやっていきます。
    • 現在はユーザー名さえあっていればログインできてしまう状態でしたので、ちゃんとパスワードもあっているか確認するということです。
  • まずは、ログインページから、ユーザー名を取得した時のことを、おさらいしてみます。

パスワード認証

①コードのこの部分が、

②この入力欄なって、

④ params に入れられる

③ここに入力した内容が、

※ params[:name]の :name は、

コードのこの部分の名前が使われる

  • パスワードでも同じようにすれば、入力した内容をコントローラーで取得できます。
  • 後は「入力したユーザー名のパスワードが、パスワード欄に入力したパスワードと一致するか」を判定します。
  • つまり、sessionコントローラーを以下のように書きます。

パスワード認証

  • if @user.password_digest == params[:password_digest]」がポイントです。
    • 「名前欄に入力したユーザー名のパスワード(@user.password_digest)」と、
    • 「パスワード欄に入力したパスワード(params[:password_digest])」を比較して、等しい時だけ session に保存し、ページを変化させています。
    • 等しくない(パスワード)が間違っている場合は、何も処理をさせていません。ちゃんと動くか試してください。
  • ただ、まだ色々問題があって、まず第一に「存在しないユーザー名を入力したらエラーになる」のでこれを直します。
  • パスワードが違ってもエラーにはならないのに、なぜ存在しないユーザー名を入力したらエラーになるかというと、
    • 単純に「 @user = User.find_by(name: params[:name]) 」の部分で、データベースに存在しないユーザーは取得できないため、@user の中身は nil になります。
    • そのため、その下の「 @user.password_digest 」の部分で、「@user の中身がないのに、メソッドを使おうとしてる」というエラーになっています。
  • 「ユーザー名を間違っているんだから、別にエラーになってもええやん?」って思うかも知れませんが、セキュリティ的にアウトです。
  • 「エラーが出たってことは、このユーザー名は存在しない」って事がバレてしまいます。
  • 逆に「エラーが出ないってことは、そのユーザー名は存在する」という意味になります。後はパスワードを調べるだけになります。
    • 俗にいうハッカーなどの、悪意あるユーザーは、このような情報を元にして抜け穴を見つけていきます。
  • なので理想は「ユーザー名と、パスワードのどっちが違うかわからない」という状況が好ましいというわけです。
    • 次へ続く。

パスワード認証

  • 以下のようにしましょう。※条件の最初に「@user &&」を追加しただけです。

パスワード認証

  • @user の中身が nil だった場合は false 判定になります。
  • && は「かつ」の意味なので、false が1つでもあった時点で、その条件分の全体でも false になります。
    • Rubyの場合、false があった時点で、それ以降の条件分は無視されますので「 @user.password_digest 」の部分が実行されることはなくなり、エラーにならないということです。
  • 次は、ログイン出来た場合、と出来なかった場合に、それぞれフラッシュメッセージを表示させてあげましょう。
  • これは、ぜひ自分でチャレンジしてみてください。以下のような表示になればOKですね。
    • ※「ログインページ」という文字部分は<p>タグから<h2>タグに変更してます。
    • ヒント①:先ほど作った if文 に else文 も追加して、それぞれ別のフラッシュメッセージを設定すれば良いですね。
    • ヒント②:ログイン画面には、フラッシュメッセージを表示できるように <%= flash[:notice] %> を追加しましょう。

パスワード認証

ログイン成功

ログイン失敗

  • 以下のようにすれば良いですね。

パスワード認証

sessionコントローラー

ログインページ

試してみよう

  • ここまでの内容を、もう一つのオリジナルサイトにも行ってください。
  • お疲れ様でした。
  • 今回はUserモデルを追加し、ログインシステムも作って、かなり本格的なサイトに近づいてきました。
    • ※今はセキュリティがガバガバなので、また後でちゃんとします。
  • 次回はbook と user モデルを紐づけて「Aユーザーの登録した本」「Bユーザーが登録した本」みたいな仕組みを作っていきます。
  • Ruby on Rails基礎2」に進んでください。

お疲れ様です。

Ruby on Rails基礎1

By kinocode

Ruby on Rails基礎1

  • 1,145