Railsを学ぶ ~環境構築とCRUD実装~

はじめに

Rubyを学習していて、Railsを学びながら掴んでいこうと思ったので、Railsの学習をこちらで記録していきます。

※Rubyに関しては以下で記録

Rubyを学ぶ

✅参考

Progate:Rails学習コース

書籍:現場Rails

環境構築

Progate:Ruby on Railsの環境構築をしてみよう!(Windows)

Windows環境よりLinuxの方が安定して動作するらしい(現場Rails参考)ですが、以前Ubuntu環境で作って、その後何かしらエラーで躓いた記憶があるのでWindowsのローカル環境下で今回は試してみます。

現場で使える Ruby on Rails 5速習実践ガイドの環境構築(Windows編)

①rubyの確認

> ruby -v
ruby 2.6.6p146 (2020-03-31 revision 67876) [x64-mingw32]

②SQLite3のダウンロード

こちらからダウンロードして、Rubyのbinファイル下に格納。

③railsのインストール

> gem install rails -v "5.2.3"
> rails -v
Rails 5.2.3

④アプリ作成

rails new rails_app

⑤起動

rails s

⑥確認

localhost:3000へアクセス

🙄WindowsでもUbuntu使わずできた◎(ローカルあれだけど)初めてVSCodeでRails弄るから新鮮です。

✅WindowsユーザーでVSCode使っている方はデフォルトのターミナルをgit bashにしましょう!!

※VSCode上ではwhichコマンドやbin/railsコマンドが使えないので、git bashから実行。

WindowsのVSCodeでGit Bashをターミナルに設定するWSL と VSCode と Windows Terminal でコマンドプロンプトにさようなら

"terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe",

よく使うrailsコマンドまとめ

プロジェクト作成

rails new rails_app
rails _5.1.6_ new rails_app // バージョン指定
rails new rails_app -B // bundle installを実行しない

bundleインストール

bundle i --path vendor/bundle

※オプションでpathを指定すると、グローバルにインストールせずにプロジェクトのvendor/bundleにインストールされる。

.bundle/config(設定ファイル)が作成される。

---
BUNDLE_PATH: "vendor/bundle"

パスの確認

$ which bin/rails
/c/xampp/htdocs/rails_app3/bin/rails
$ which rails
/c/Ruby26-x64/bin/rails

🙄bin/railsだとプロジェクトの環境を参照していますね!!

gemfileを編集してgemを追加してみる

gem 'bootstrap'

再度インストール

$ bundle
Bundled gems are installed into `./vendor/bundle`

🙄2回目以降はデフォルトでプロジェクト直下にインストールされますね!!

サーバー起動

rails s

モデル作成

rails g model [モデル名] [属性名:データ型] [オプション]

rails g model Task name:string

コントローラ作成(※トップページ自動生成含む)

rails g controller コントローラ名 [アクション名 アクション名] [オプション]

rails g controller tasks index show new edit

コントローラ名のディレクトリ、アクションに紐づいたビューファイル(css込)作成。
ルーティングに自動追記。

Railsをとりあえず使ってみる

※ProgateⅠの内容です

トップページ自動生成

rails generate controller home top

rails generate controller コントローラ名 アクション名

※上記コマンドは同じ名前のコントローラが既にある場合使えない

①ビューの確認

app/views/home/top.html.erbが作成される

<h1>Home#top</h1>
<p>Find me in app/views/home/top.html.erb</p>

http://localhost:3000/home/topへアクセス

②コントローラの確認

上記コマンドでhome_controller.rbも作成

class HomeController < ApplicationController
  def top
  end
end

ファイルの中にtopメソッドが追加されている。
コントローラ内のメソッドをアクションと呼ぶ。

コントローラ内のアクションは、ブラウザに返すビューをviewsフォルダの中から見つけ出す役割を担う(※コントローラと同じ名前のフォルダ、アクションと同じ名前のHTMLファイル名が紐づく。)

③ルーティングの確認

config/routes.rbにも自動追記されているので確認

Rails.application.routes.draw do
  get 'home/top'
  get 'top' => "home#top"
end

get “URL” => “コントローラー名#アクション名”で記載する。

コントローラとアクション名がルーティングと同じ場合、省略も可能。

④ルーティングとアクションの追加

routes.rb

get 'about' => 'home#about'

home_controller

def about
end

views/home/about.html.erbの作成

<h1>Home#about</h1>
<p>hello about page</p>

確認

⑤CSSを使う

app/assets/stylesheets/home.scssが自動生成されているので編集

h1 {
  color: blue;
}

確認

🙄デフォでSassが使えるのはいいですね!!

※ここで以前作成したSassファイルを適用してみました。

⑥画像を使う

publicディレクトリ下に置くことで「/パス」で読み込める

※一旦読込はステイ

⑦リンク作成

about.html.erbに追記

<a href="/">Top</a>

DBからデータ取得

※ProgateⅡの内容

①自動生成

rails g controller post index

http://localhost:3000/post/indexへアクセス

🙄フォルダ事に適用されるCSSは別かと思いきや適用されていました💦スコープ。。。

②erb活用

views/post/index.html.erbの編集

<div class="ly" style="margin-top:20px;">
  <h1>Post#index</h1>
  <p>Find me in app/views/post/index.html.erb</p>
</div>
<div class="ly">
  <% post1 = "投稿機能作ります" %>
  <p><%= post1%></p>
</div>

確認

<% %>の内容は表示されず、<%= %>の内容は表示されますね!!

③each文活用

<div class="ly">
  <% posts = [
    "投稿機能作ります",
    "erbを使います",
    "これからDBを使ってみます"
  ]
  %>
  <div>
    <% posts.each do |post| %>
    <p><%= post %></p>
    <% end %>
  </div>
  <button>作成</button>
</div>

確認

④アクションで変数を定義

※変数はアクションで定義するのが一般的とのこと。

post_controller.rbを編集

class PostController < ApplicationController
  def index
    @posts = [
      "投稿機能作ります",
      "erbを使います",
      "これからDBを使ってみます"
    ]
  end
end

アクションで定義する場合は「@」から始める必要がある。

post/index.thml.erbも編集

<% @posts.each do |post| %>

⑤マイグレーションファイルの作成

rails g model Post content:text

🙄Laravel同様、テーブル名が複数形の場合は単数形でモデル作成してますね!

Postがモデル名、contentはカラム名、textはデータ型になります。

db/migrate下にファイルが作成されているため確認

class CreatePosts < ActiveRecord::Migration[5.2]
  def change
    create_table :posts do |t|
      t.text :content
      t.timestamps
    end
  end
end

⑥テーブル作成

rails db:migrate

※contentカラムに加えてid, create_at, updated_atという3つのカラムが自動生成される。 また、マイグレートされていないファイルがある状態でアクセスするとエラーになるので注意!!

VSCodeの場合はSQLiteをインストールすると簡単に確認できますね!!

確認

⑦モデルの確認

app/models/post.rbを確認

class Post < ApplicationRecord
end

※ApplicationRecordクラスを継承するPostクラスが定義

⑧データ操作

rails consoleを利用

> rails console
Loading development environment (Rails 5.2.4.3)
irb(main):001:0>

🙄チェリー本でも出てくるirbですね!!

newメソッドでPostインスタンス生成

irb(main):001:0> post = Post.new(content: "Hello World")
=> #<Post id: nil, content: "Hello World", created_at: nil, updated_at: nil>

保存

saveメソッド

irb(main):002:0> post.save
(0.1ms) begin transaction
Post Create (2.8ms) INSERT INTO "posts" ("content", "created_at", "updated_at") VALUES (?, ?, ?) [["content", "Hello World"], ["created_at", "2020-07-01 08:43:35.967598"], ["updated_at", "2020-07-01 08:43:35.967598"]] 
(8.7ms) commit transaction
=> true

※saveはPostモデル(クラス)が継承しているApplocationRecordに定義されたメソッド

保存されてますね!!

取得

Post.first

postsテーブルの最初のデータをオブジェクトで取得

irb > post = Post.first
irb > post.content
=> "Hello World"

Post.all

postsテーブルからすべてのデータを配列で取得

irb > posts = Post.all
irb > posts[0].content
=> "Hello World"

⑨データの表示

post_controller.rbの編集

class PostController < ApplicationController
  def index
    # @posts = Post.all
    @posts = Post.all.order(created_at: :desc) # 降順にする場合 昇順はasc
  end
end

index.html.erbの編集

<div>
  <% @posts.each do |post| %>
  <p><%= post.content %></p>
  <% end %>
</div>

⑩レイアウトをまとめる

「views/layouts/application.html.erb」に共通のHTMLを書いておくことが可能。

views/layouts/application.html.erbの確認

<!DOCTYPE html>
<html>
  <head>
    <title>RailsApp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

<%= yield %>の部分に各ビューファイルが代入されている。

以下追記※bodyタグ内のみ

<body>
  <header>
    <h1>RailsApp</h1>
    <%= link_to("Top", "/") %>
    <%= link_to("About", "/about") %>
    <%= link_to("Post", "/post") %>
  </header>
  <%= yield %>
</body>

link_toメソッドは第一引数に表示する文字、第二引数にURLを書く

DBから特定のデータ取得とDB追加

ProgateⅢの内容です

①find_byメソッドでデータ取得

「モデル名.find_by(カラム名: 値)」で、特定のデータをデータベースから取得

irb > post = Post.find_by(id: 1)
irb > post.content
=> "Hello World"

②詳細ページ作成

routes.rbの編集

get 'post' => 'post#index'
get 'post/:id' => 'post#show'

※post/indexより上に書かないこと!!

post_controller.rbの編集

def show
  @id = params[:id]
end

URLはparams変数にハッシュとして入るので、params[:id]とすることで値を取得可能。

show.html.erbを作成

<div class="ly">
  <%= @id %>
</div>

http://localhost:3000/post/1へアクセス

③idが合致したデータの描画

post_controller.rbの編集

def show
  @posts = Post.find_by(id: params[:id])
end

show.html.erbの編集

<div class="ly">
  <p><%= @posts.content %></p>
  <p><%= @posts.created_at %></p>
</div>

④詳細ページへのリンク作成

<div class="ly">
  <div>
    <% @posts.each do |post| %>
    <p><%= link_to(post.content, "/post/#{post.id}") %></p>
    <% end %>
  </div>
  <button>作成</button>
</div>

※postsコントローラをpostで作ってしまってややこい。。。苦笑

http://localhost:3000/post/へアクセス

⑤DB追加のための投稿ページ作成

※ここからテンプレートエンジンにはSlimを活用していきます

routes.rb

get 'post/new' => 'post#new'
post 'post/create' => 'post#create'

※フォームを受け取るのでpost送信

post/new.html.slimを作成

= form_tag("/post/create") do
  textarea cols="30" rows="10" name="content"
  input type="submit" value="投稿"

form_tagメソッドを用いてフォームの内容をデータ送信。

textareaにname属性を指定すると、値をキーとしたハッシュがRails側に送られる。

{content: “入力内容です”}

これをコントローラのアクション内で受け取る。

post_controller.rb

class PostController < ApplicationController
  def index
    @posts = Post.all.order(created_at: :desc)
  end
  def show
    @posts = Post.find_by(id: params[:id])
  end
  def new
  end
  def create
    @post = Post.new(content: params[:content])
    @post.save
    redirect_to("/post")
  end
end

※newアクションで現状定義していないが、”@post = Post.new”がないとバリデーションエラーでrender実装するとエラーになるので要注意。

indexアクションではorderメソッドを活用してデータを降順に並び替え。

createアクションでORMでDBにデータを保存。

確認

※1つ適当すぎる文字を入れたので隠しました笑(削除は後程。。。)

DB編集と削除

※ProgateⅣの内容です

①DB更新(irb)

post = Post.find_by(id: 4)
post.content = "Rails"
post.save

②DB削除(irb)

post = Post.find_by(id: 3)
post.destroy

③編集ページの作成

routes.rb

get 'post/:id/edit' => 'post#edit'
post 'post/:id/update' => 'post#update'

post_controller.rb

def edit
  @post = Post.find_by(id: params[:id])
end
def update
  @post = Post.find_by(id: params[:id])
  @post.content = params[:content]
  @post.save
  redirect_to("/post")
end

post/show.html.slim

.ly
  p = @post.content
  p = @post.created_at
  = link_to("編集", "/post/#{@post.id}/edit")

post/edit.html.slim

.ly
  = form_tag("/post/#{@post.id}/update") do
    textarea name="" cols="30" rows="10" = @post.content
    input type="submit" value="保存"

④削除機能作成

routes.rb

post 'post/:id/destroy' => 'post#destroy'

show.html.slimへ追記

= link_to("削除", "/post/#{@post.id}/destroy", {method: "post"})

※post送信時はlink_toメソッドの第3引数に{method: “post”}を追加する。

post_controller.rbの編集

def destroy
  @post = Post.find_by(id: params[:id])
  @post.destroy
  redirect_to("/post")
end

バリデーション実装

※ProgateⅤの内容です

①空の投稿へのバリデーション

以下の書き方でモデルに実装する。

validates : 検証カラム名, {検証内容}

models/post.rbの編集

※contentカラムが空の場合、保存できなくする。

class Post < ApplicationRecord
  validates :content, {presence: true}
end

確認

rails console
irb > post = Post.new(content: "")
irb > post.save
⇒ false

②文字数のバリデーション

models/post.rbを編集

lengthを用いて、{maximum: 数値}で最大文字数を設定

class Post < ApplicationRecord
  validates :content, {presence: true, length: {maximum: 140}}
end

※カンマで区切ることで複数指定可能!

③処理の条件分岐

バリデーションの返り値は真偽値(true/false)なので、これに合わせて処理を変えます。

post_controller.rbの編集

def update
  @post = Post.find_by(id: params[:id])
  @post.content = params[:content]
  if @post.save
    redirect_to("/post")
  else
    redirect_to("/post/#{@post.id}/edit")
  end
end

④直前の投稿を表示

redirect_toメソッドではなくrenderメソッドを使うことで、別のアクションを経由せずに直接ビューを表示することが可能。

post_controller.rbの編集

if @post.save
  redirect_to("/post")
else
  render("post/edit")
end

renderは(“フォルダ名/ファイル名”)とするのがポイント!! redirect_toは(”URLパス”)なので(“/post”)のように/が入る。

👇空の投稿で編集した場合

🙄投稿id=4がURLに表示されていて面白い挙動ですね!!

⑤エラーメッセージの表示

Railsはsaveメソッドで失敗すると自動的にエラーメッセージが生成される。

内容は@post.errors.full_messagesの中に配列で入る。

irb(main):004:0> post.errors.full_messages
=> ["Content can't be blank"]

post/edit.html.erbを編集

  - @post.errors.full_messages.each do | message |
    = message

空の保存をしたところエラーメッセージを確認

⑥サクセスメッセージの表示

アクションで変数flash[:notice]に文字列を代入すると、flash[:notice]をビューで使うことが可能。flashは投稿直後のみ表示可能。

post_controller.rbの編集

if @post.save
  flash[:notice] = "保存成功"
  redirect_to("/post")
else

application.html.erbの編集

body
  header
    h1 RailsApp
    = link_to("Top", "/")
    = link_to("About", "/about")
    = link_to("Post", "/post")
    = link_to("New", "/post/new")
  - if flash[:notice]
    .flash
      = flash[:notice]
  = yield

サクセスメッセージの表示が確認できました◎

※上記は編集時のバリデーションです

⑦新規投稿にバリデーション

🙄モデルでpostsテーブルのcontentカラムへの空or140時以上のバリデートは記載済みなので、コントローラとビューで変更するだけですね!

new.html.erbの編集

= form_tag("/post/create") do
  - @post.errors.full_messages.each do | message |
    = message
  textarea name="content" cols="30" rows="10"
  input type="submit" value="投稿"

post_controller.rbのnewアクション編集

def new
  @post = Post.new
end
def create
  @post = Post.new(content: params[:content])
  if @post.save
    flash[:notice] = "投稿を作成しました"
    redirect_to("/post")
  else
    render("post/new")
  end
end

⑧削除機能にサクセスメッセージの表示

post_controller.rbの編集

def destroy
  @post = Post.find_by(id: params[:id])
  @post.destroy
  flash[:notice] = "投稿を削除しました"
  redirect_to("/post")
end

深掘りメモ

✅erbで記載する際の注意
○  <img src="<%= "/user_images/#{@user.image_name}" %>">
×  <img src="/user_images/#{@user.image_name}">

○  <input name="email" value="<%= @email %>"
×  <input name="email" value="<%= #{@email} %>"

ぱっと見こんがらがります。

出力したい文字列に連結して変数を使う場合は””で囲んで#{}で展開。

変数のみを出力する場合は””が不要で@変数を記入。

以下がいい例。

<%= link_to( post.user.name, "users/#{post.user.id}" ) %>

🙄第一引数はeachで回された変数のみで定義、第二引数は変数を展開して文字列連結

✅レンダーとリダイレクト

renderはアクションに続けてビューを表示させること。
※(“フォルダ名/ファイル名”)とするのがポイント!!

アクションを処理した直後に別のURLに遷移することをリダイレクトと呼ぶ。
※redirect_toは(”URLパス”)なので(“/post”)のように/が入る。

レンダーは1つのリクエストによって画面が表示、リダイレクトは2つ目のリクエストが発生し、それによって画面が表示される。

✅Flashメッセージ

redirect_toとセットで使うことが多く、次のリクエストに対して1回限りのインスタンスメッセージを表示する仕組み。

ハッシュの形でデータを格納する。

flash[:notice] = "保存成功"

👆コントローラ側に定義すれば、ビューで”flash[:notice]”記載すれば呼び出せる。※基本的にif文で囲って書く

✅form_tagメソッド

参考:【Rails】form_tagの使い方を徹底解説!

erbファイル

<%= form_tag('/main', method: :post) do %>
~~~~
<% end %></pre>

htmlコンパイル後

<form accept-charset="UTF-8" action="/main" method="post">~~~~ </form>

🙄コンパイル後、formタグになるので<%= %>で囲むんですね!

※link_toは第3引数に{method: "post"}。書き方異なるので注意。

✅paramsメソッド

URLから送られてきた値やフォームで入力した値を受け取るためのメソッド。

params[:パラメータ名]の形。

パラメータの送り方はリンク、フォーム、配列やハッシュなど。

  def show
    @posts = Post.find_by(id: params[:id])
  end
  def new
  end
  def create
    @post = Post.new(content: params[:content])
    @post.save
    redirect_to("/post")
  end

👆params[:id]はURLのパラメータ、params[:content]はformのname属性。

※ビュー側ではtextareaにname属性を指定すると、値をキーとしたハッシュがRails側に送られる。

✅resourcesについて

参考:Railsのresourcesとresourceついて

config/routes.rb
resources :hoges

確認

$rails routes

    hoges GET /hoges(.:format)          hoges#index 
          POST /hoges(.:format)         hoges#create 
 new_hoge GET /hoges/new(.:format)      hoges#new 
edit_hoge GET /hoges/:id/edit(.:format) hoges#edit 
     hoge GET /hoges/:id(.:format)      hoges#show 
          PATCH /hoges/:id(.:format)    hoges#update 
          PUT /hoges/:id(.:format)      hoges#update 
          DELETE /hoges/:id(.:format)   hoges#destroy

🙄これを知っておくだけで記述がだいぶ楽になりますね!!

CRUD実装時の注意点まとめ

①get通信でposts/:idをposts/indexより上に書かないこと!!
※post通信は別途整理。

②newアクションでは、"@post = Post.new"がないとバリデーションエラーでrender実装するとエラーになるので要注意。

③post送信時はlink_toメソッドの第3引数に{method: "post"}を追加する。

おわりに

ProgateのⅠ~Ⅴまで終えました。

トータル11まであるのでとてもボリューミーですね!!

そして、ざっくりしかやっていないのでCRUD部分復習したい感。

今まではLaravelやってたけどこれはRailsの方が教材充実していると言わざるを得ないですね!!

※どうやらテストは扱っていないみたいなのでRailsチュートリアルなりで学習したほうが良さげ

備忘録

✅今後確認したいこと

・CSSのスコープ

参考:Rails の CSS にスコープを持たせてファイル分割する

✅その他試したこと

<%= %>書くのが手間なのとPug使えないかなと思って調べた結果EmmetとSlimを試してみました。

・Emmet活用※済

Qiita:VScode x Rails:erbファイルでEmmet(エメット)を使う方法

①設定からEmmetのチェック

②setting.jsonに追記

"emmet.includeLanguages": {
  "erb": "html"
},

③拡張機能でerbをインストール

<%= %>をpeで記述できるのは楽すぎる!!

・Slim活用※済

参考:railsアプリケーションにPug(旧jade)を導入したい

pugだとrubyの記法が使えないらしいので、slimがおすすめらしい!試したい!!

Qiita:RailsのHTMLテンプレートエンジン、Slimの基本的な記法

参考:5分で書ける!テンプレートエンジンSlimのススメ

①Gemfileに追記

gem 'slim-rails', '3.1.3'

②インストール

bundle i

③デフォルトでslimを用いるように以下追記

class Application < Rails::Application
  config.generators.template_engine = :slim  
end

③erbファイルを全てslimに変更

※以下一例

index.html.erb

<div class="ly">
  <div>
    <% @posts.each do |post| %>
    <p><%= link_to(post.content, "/post/#{post.id}") %></p>
    <% end %>
  </div>
  <button>作成</button>
</div>

index.html.slim

.ly
  div
    - @posts.each do |post|
      p = link_to(post.content, "/post/#{post.id}")
  button 作成

🙄だいぶすっきりしました◎

変数は<% %>は-で表現、<%= %>は=で記述します!

  - a = "test"
  p #{a}

 

コメントを残す