💎

【第2章】PHP歴8年・Ruby歴0秒の僕のRailsチュートリアル日記

2020/03/20に公開

こんにちは、たつきちです。

現在PHP歴8年ちょいで、4年ほど前には副著で 技術書を執筆 したりもした、PHPチョットデキル人です←

今更ながらRuby on Railsを学んでみようと思い立ったので、Rails入門者の登竜門 Railsチュートリアル に取り組んでみた様子をブログに連載していきます💪

今回は、全14章中の第2章です。

この連載の目次

  • 第1章 ゼロからデプロイまで
  • 第2章 Toyアプリケーション
  • 第3章 ほぼ静的なページの作成
  • 第4章 Rails風味のRuby(準備中)
  • 第5章 レイアウトを作成する(準備中)
  • 第6章 ユーザーのモデルを作成する(準備中)
  • 第7章 ユーザー登録(準備中)
  • 第8章 基本的なログイン機構(準備中)
  • 第9章 発展的なログイン機構(準備中)
  • 第10章 ユーザーの更新・表示・削除(準備中)
  • 第11章 アカウントの有効化(準備中)
  • 第12章 パスワードの再設定(準備中)
  • 第13章 ユーザーのマイクロポスト(準備中)
  • 第14章 ユーザーをフォローする(準備中)

第2章 Toyアプリケーション

scaffoldを使って簡単なアプリを作ってみるそうです。自動生成されたコードについてこの段階ではまだ深い説明はなく、一旦作ってみることが目的っぽいです。

やっていきます💪

2.1 アプリケーションの計画

前回、1つのGitHubリポジトリに各章のプロジェクトをフォルダ分けしてpushしていこうとしたら、Herokuにデプロイする段階になって、 rails server コマンドでは php -S とかと違ってディレクトリを指定して起動することができないということが分かり、泣く泣くフォルダ分けを諦めました。

第2章に入って、やっぱり新たに rails new して別アプリを作っていく流れのようなので、このままだと章ごとにGitHubリポジトリを量産することになりそうです。

それはなんとなく美学として嫌なので、

  • GitHubリポジトリは https://github.com/ttskch/railstutorial/ のみ
  • 章ごとに 01 02 といった名前でブランチを切っていく
  • Herokuアプリは章ごとに作成
  • Herokuに1つのパイプラインを作り、章ごとのアプリをパイプライン配下に設置し、 01 02 といったGitHubブランチから自動デプロイするようにしておく

という構成でやってみようかと思います👍

準備が整ったところでさっそく進めていきます。

と、その前に。現状、Bundlerのバージョンが 2.1.21.17.3 の両方がインストールされていて、 2.1.2 がデフォルトになっています。

$ gem list bundler

*** LOCAL GEMS ***

bundler (default: 2.1.2, 1.17.3)

が、前回の記事に書いたように、今はまだ2系は新しすぎてハマりどころが多そうなので、デフォルトで 1.7.3 を使うようにしたいです。

2.1.2 を削除しようとしましたが、

$ gem uninstall bundler -v 2.1.2
Gem bundler-2.1.2 cannot be uninstalled because it is a default gem

default gemは削除不可とのこと。

よく分かりませんが、gemには default gembundled gem があって、ただ単に「どれがデフォルトで使われるか」というだけの話ではないようです。

あとよく考えたら 2.1.2 のほうのBundlerはそもそも明示的にインストールした覚えはなく、どうやらHomebrewでrubyを入れたときに一緒に入ったものっぽいです。

$ /usr/local/opt/ruby/bin/bundle -v
Bundler version 2.1.2

紛らわしいし、もう環境でハマるのは嫌なので、みんなやってるっぽいrbenvを使った環境構築をこのタイミングでやってしまおうと思います。

ちょっと古いですが この記事 あたりを参考にします。

まずは、Homebrewで入れてあったrubyを削除します。

$ brew uninstall ruby

続いて、rbenvをインストール。

$ brew install rbenv ruby-build

シェルのプロファイルにPATHと rbenv init を追加して、

export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

rbenvが使えるようになりました👍

$ rbenv -v
rbenv 1.1.2

チュートリアルの第1章で動作実績のある2.7系を入れることにします。

$ rbenv install 2.7.0
$ rbenv global 2.7.0
$ ruby -v

rbenvの操作自体は、phpenvで慣れ親しんでいる ので戸惑いはないですね😁

しかし、rbenvにしても結局 ~/.rbenv/shims/bundle にBundler 2.1.2 がインストールされてしまい、グローバルでこれではなく gem install したBundlerを優先して使う方法がさっぱり分かりません😓

rbenvで入れるrubyのバージョンを調整すればBundlerの 1.17.x が入るようにもできるんでしょうが、それだと完全に負けた気がするので・・・結局一旦無視して、毎回 bundle _1.17.3_ install とかやることにします😓

・・・

というわけで、気を取り直してチュートリアルを進めていきましょう!

$ rm -rf * .bundle .gitignore
$ rails _5.1.6_ new .

一旦プロジェクトを空にして、カレントディレクトリをターゲットに rails new しました。(コミット

続いて、 Gemfile指定された内容 に変更し、 bundle install し直します。

$ bundle _1.17.3_ install
You must use Bundler 2 or greater with this lockfile.

ああ、なるほど・・・ rails new 中に bundle install がデフォルトのBundlerで実行されて、 Gemfile.lock

BUNDLED WITH
   2.1.2

になっていますね😓

一旦 Gemfile.lock を削除して bundle _1.17.3_ install をし直すか・・・と思ったのですが、もうなんか 1.7 にこだわるのがアホらしくなってきました・・・

2系だとCIの構築時に困るかも、という話でしたが、もはやそのとき考えればいいやという気持ちになってきたので、一旦このまま 2.1.2 を使い続けていこうと思います✋

$ bundle update
$ bundle install --without production

問題なく Gemfile.lock` が更新されました。(コミット

続いて、トップページを hello, world! にする変更です。第1章の復習ですね。(コミット

Herokuへのデプロイは自動化してあるので、GitHubにpushすれば勝手に本番に適用されています👍

次はいよいよモデルを作るようです。ワクワクしてきました!

2.2 Usersリソース

rails generate scaffold コマンドを使ってモデルのscaffoldを生成するようです。

$ rails generate scaffold User name:string email:string

色々ファイルが作られました。(コミット

モデルを作ったので次はデータベースのマイグレーションです。

データベースの設定を明示的にやった覚えはないですが、config/database.yml に設定が書かれていました。 rails new で作ったプロジェクトでは、とりあえず何も考えなくてもsqliteを使う前提でデータベース設定がされている感じなのですね。

$ rails db:migrate

db/schema.rb というファイルが生成されました。(コミット

2.2.1 ユーザーページを探検する で説明されているとおり、ユーザーのCRUDができるようになっていました。便利〜。CakePHPのbakeを思い出します。

UsersリソースUserモデル という表記が出てきていますが、 リソースモデル という言葉の使い分けが今の時点で分かっていません🤔

説明を読みながら何となくコントローラやビューのコードを眺めてみますが、とりあえずの感想としては、「色々なことが暗黙的に行われすぎて読みづらい、難しい」という感じです🙄

慣れれば速く書けるようになるんだろうけど、慣れるまでに覚えること多そうだなーという印象。

Ruby自体が初めてなので単純にsyntaxも覚えていかないとですね。

📝メモと感想

  • @ で始まる変数はRubyの「インスタンス変数」
  • Railsでは、コントローラ内で宣言したインスタンス変数は暗黙的にビューで使えるようになる
  • Userモデルが name email というプロパティを持っていることがモデルクラスに書かれていなくて謎🤔
  • 演習 の、「 /users/1/edit というURLにアクセスしたときにデータベースからユーザー情報を取得しているコードを探してみてください。(意訳)」という問題の答えがパッと分からなかったけど、よく読んだら分かった
    • edit.html.erb にいきなり @user が登場しているけど、コントローラの edit アクションは空っぽ
    • よく見るとコントローラに before_action :set_user, only: [:show, :edit, :update, :destroy] というのがあり、 set_user メソッドの中で @user = User.find(params[:id]) してる

2.3 Micropostsリソース

Usersリソースでやったことを参考に、MicropostsリソースのCRUDをscaffoldで作ってみましょうとのこと。

$ rails generate scaffold Micropost content:text user_id:integer
$ rails db:migrate

とするだけで、当然ながら /microposts 配下のURLでCRUDできるようになった。(scaffoldのコミットmigrateのコミット

次に、Micropostモデルのcontentに140文字の字数制限を設けてみましょうとのことです。

モデルクラスに

validates :content, length: { maximum: 140 }

と追記するだけでバリデーションが実装できました。(コミット

この記法もどこまでがRubyの言語機能でどこからがRailsの機能なのか今の時点ではさっぱり理解できていません😇

続いて、UserとMicropostの間にリレーションシップを加えます。モデル同士のリレーションシップは has_many :microposts belongs_to :user というのをモデルに書くことで設定できるようです。(コミット

モデルのコードを変えただけで、例えばマイクロポスト新規作成画面で存在しないユーザーIDを入力するとエラーになるようになりました。

また、 rails console コマンドで対話的にモデルの情報を調べると、ちゃんとモデル同士が紐づいていることが確認できました。次はデータベースにも外部キーを追加する流れになるのかと思ったのですがその作業はないようです🤔

最後にデプロイします。僕の場合はコードのデプロイは自動化されていますが、DBのマイグレーションは手動でやる必要があります。

僕の構成だと、ローカルのコードベースは第1章から最後までずっと同じなので、 heroku コマンドに紐付けるHerokuアプリを都度変更していかないといけません。

$ heroku git:remote -a {第2章用のHerkouアプリ名}

アプリを切り替えた上で、

$ heroku run rails db:migrate

とするとマイグレーションが走りました。

動いてます🙌

📝メモと感想

  • 演習の「マイクロポストのコンテンツを入力必須にする」(コミット)と「ユーザーのname,emailも入力必須にする」(コミット)をやった
  • モデルクラスにプロパティの情報がないのが気持ち悪い
    • コードを眺めてみたけど、DBのスキーマにしか書かれてない気がするので、アプリ側ではスキーマ情報持たず、DBのカラム名に透過的にアクセスできる感じなのかな(ActiveRecordほぼ初めてなので、そういうものなのかもしれないけど気持ち悪い)
  • チュートリアルの解説によると本番ではPostgreSQLが使われているとのことだけど、何をもって自動的に本番と認識されているのか分からない・・・と思ったら、 heroku config してみたら RACK_ENV: production RAILS_ENV: production を始め、DBの接続情報などなど環境変数が自動でセットされていた
    • HerokuにRailsアプリをデプロイするとこの辺を自動でやってくれるということ?

今回のまとめ

  • scaffold便利だけど実践でどこまで役立つかは未知数だなーという感想
  • 色々なことが暗黙的に行われる感じが個人的にはまだ気持ち悪い
  • これが設定より規約かー
  • モデルがプロパティを持っていなくてDBのカラムに透過的にアクセスしてるっぽいと思っているけどこの理解が正しいのか分からないので引き続き学ぶのが楽しみ
  • 次回も頑張ります!
GitHubで編集を提案

Discussion