0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Railsでメモアプリを作る(入門)

Last updated at Posted at 2021-02-23

ここで使用している環境

OS: Windows10
IDE: RubyMine 2020.2.3
Rails: 6.1.0

Ruby
> C:\\Ruby30\\bin\\ruby.exe -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [i386-mingw32]

注意

  • 一部、分かりやすくするために冗長な説明をしている箇所があります。
  • アクション関数と呼んでいます。
  • IDEにRubyMineを使用することを前提としています。

※ただし、やっていることは他エディタ/IDEでも同じなので参考にはなるかもしれません。

以上の点について、ご理解ください。

プロジェクト作成

  1. New Project画面を開く
  2. Rails->Applicationを選択
  3. Locationに好きなディレクトリ(プロジェクト名になる)を指定
  4. Ruby SDKで使用するRubyを選択
  5. Rails VersionでRailsバージョンを選択
  6. Createをクリック

Createをクリックした後、少し処理に時間が掛かるのでしばらく待ちます。

※この解説ではディレクトリ(プロジェクト名)にRailsTestAppを使用します。

screenshot_20210223_160126.png

手順4で使いたいRubyが無い場合

  1. Ruby SDKドロップダウンからAdd Ruby SDKを選択
  2. 追加したいruby.exeを指定する(C:\Ruby30\bin\ruby.exeなど)
    screenshot_20210223_155625.png

手順5で使いたいRailsバージョンが無い場合

  1. Rails VersionドロップダウンからInstall Rails Gem...を選択
  2. 少し待って出てきた画面のVersion to installで好きなバージョンを選択してInstall(今回は6.1.0を使用)

screenshot_20210223_155739.png

LoadError: cannot load such file -- rexml/document

  1. Gemfileの一番下に、gem 'rexml'を追加する
  2. Tools->Bundler->Installをクリック
  3. Runをクリック

screenshot_20210223_233422.png
screenshot_20210223_233604.png

ポートを変更する(任意)

  1. 右上のドロップダウンからEdit Configurations...を選択
  2. Portに好きなポートを入力する

screenshot_20210224_013522.png
screenshot_20210224_013624.png

早速起動する

  1. 右上の再生ボタンのようなものをクリック

Listening on http://127.0.0.1:3000と表示されたら起動完了です。

screenshot_20210223_160933.png
screenshot_20210223_161042.png

エラーで起動できない場合の対処

  • A server is already running. Check path/to/app/tmp/pids/server.pid.

/tmp/pids/server.pidを削除して再度起動します。

サイトにアクセスしてみる

  1. ブラウザでlocalhost:3000にアクセスする

以下のようなページが表示されたら成功です。
screenshot_20210223_161316.png

Routing・Model・View・Controllerの役割

Routing: リクエストから使用するコントローラを決定する
Model: データの塊(オブジェクト)
View: 見た目を制御する処理(主にHTMLなど)
Controller: 必要なModelをViewに渡す、受け取ったパラメータからModelを作成する、などの内部的な処理

例えば、全てのメモを表示する場合の全体的な順序としては

  1. Routingによって適切なControllerの関数を呼び出し
  2. ControllerでメモのModelを全て取得してViewに渡す
  3. Viewでそれらのデータを視覚的に表示する
    といった感じです。MVCなどで調べてみると面白いかもしれません。

恐らく、書いているうちに理解できると思います。

とりあえず適当なページを作ってみる

※ここではあえてジェネレータを使用していません。

ここでは、localhost:3000/helloにアクセスしたらHomeコントローラのindex関数が呼ばれ、app/views/home/index.html.erbが表示されるようにしてみます。

コントローラ作成

Homeコントローラを作成したいため、コントローラのクラス名はHomeControllerとなります。

  1. app/controllersディレクトリを右クリック
  2. New->Ruby File/Classをクリック
  3. HomeControllerと入力
  4. Classを選択
  5. Enter

これでapp/controllers/home_controller.rbが作成されました。

screenshot_20210223_164214.png
screenshot_20210223_164313.png

コントローラ編集

  1. app/controllers/home_controller.rbclass HomeControllerの行に< ApplicationControllerを追加してApplicationControllerを継承する
  2. 空のindex関数を作成
app/controllers/home_controller.rb
class HomeController < ApplicationController
  def index
  end
end

View作成

Homeコントローラのindex関数に対応するViewを作成したいので、app/views/home/index.html.erbを作成します。

  1. app/viewsディレクトリを右クリック
  2. New->ERB Fileをクリック
  3. home/index.htmlと入力
  4. Enter

これでapp/views/home/index.html.erbが作成されました。

screenshot_20210223_164934.png
screenshot_20210223_170919.png

View編集

  1. app/views/home/index.html.erbに適当な内容を書く

拡張子が.erbですが普通のHTMLです。

app/views/home/index.html.erb
<h1>index.html.erb</h1>
<p>Hello, world!</p>

ルーティング編集

localhost:3000/helloへのGETリクエストを受けたらHomeコントローラのindex関数を呼ぶように設定します。
そのため、必要なルーティングはget 'hello' => 'home#index'となります。

※GETというのはHTTPメソッドの一種で、ブラウザで普通にアクセスした場合はGETになります。

  1. config/routes.rbget 'hello' => 'home#index'を追加
config/routes.rb
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  get 'hello' => 'home#index'
end

ブラウザで確認

  1. ブラウザでlocalhost:3000/helloにアクセスする

app/views/home/index.html.erbの内容が表示されます。

※Railsの再起動は不要です。

screenshot_20210223_171344.png

作ったページをrootに割り当てる

localhost:3000/へのGETリクエストを受けたらHomeコントローラのindex関数を呼ぶように設定します。
そのため、必要なルーティングはroot 'home#index'となります。
やっていることはget '/' => 'home#index'とほぼ同じですが、rootを使用することで、それがルートであることを明示することができます。

  1. config/routes.rbroot 'home#index'を追加
config/routes.rb
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  get 'hello' => 'home#index'

  root 'home#index'
end

ブラウザで確認

  1. ブラウザでlocalhost:3000/にアクセスする

localhost:3000/でもlocalhost:3000/helloでも同じ内容が表示されていることが分かります。

※お好みでlocalhost:3000/helloへのルーティング(get 'hello' => 'home#index')を削除しても良いです。

メモを投稿できる機能を作る

モデルはMemo、コントローラはMemosController、テーブル名はmemos、メモの内容を入れるカラム名はcontentとし、以下の通りに使用出来るようにします。

コントローラ関数名 パス 内容
index GET /memos メモの一覧を表示する
show GET /memos/:id IDが:idのメモを表示する
new GET /memos/new メモを作成するためのフォームを表示する
create POST /memos メモを作成するためのフォームの送信先(ここでメモが追加される)

さらにメモの内容は空にできなく、最大1000文字までとします。

モデル作成

「メモ」というデータの塊(オブジェクト)を扱うために、「モデル」と呼ばれるものを作成する必要があります。
ここでは、Memoというモデルを作成します。

モデル名がMemoText型のcontentカラムを作成したいので、Generator argumentsMemo content:textとなります。
ここでmemosというテーブル名を入力していない理由は、モデル名のMemoから自動的に推測されるためです。

  1. Ctrl+Alt+GもしくはTools->Run Rails Generator...をクリック
  2. rails g modelを選択してEnter
  3. Generator argumentsMemo content:textを入力
  4. OKをクリック

Ctrl2回->Run Anything起動->rails g modelでもOKです

screenshot_20210223_161941.png
screenshot_20210223_175426.png
screenshot_20210223_175252.png

マイグレーションファイル編集

テーブルを作成したり、カラムを追加するための「マイグレーション」と呼ばれる仕組みがあります。
これは、どんな名前のテーブルを作るのか、どんなカラムを作るのか、を定義するものです。

このファイルはモデルを作成するときのrails g modelコマンドによって自動的に生成されています。
ここでは、これを編集してcontentカラムをnullにできないようにします。

  1. db/migrate/xxxxxxxx_create_memos.rbを開く
  2. t.text :contentの行にnull: falseを追加する
db/migrate/xxxxxxxx_create_memos.rb
class CreateMemos < ActiveRecord::Migration[6.1]
  def change
    # memosテーブルを作成する
    create_table :memos do |t|
      # Text型のcontentカラムを作成する
      t.text :content, null: false

      # created_at(作成日時)、updated_at(更新日時)のカラムを作成する(デフォルト)
      t.timestamps
    end
  end
end

マイグレーション実行

  1. Ctrl2回->Run Anything起動->db:migrateを選択する
  2. Enter

※選択肢にdb:migrateが無い場合は、入力欄にdb:migrateと入力する。

screenshot_20210223_194956.png

モデル編集

contentを空に出来ないように、そして文字数を1000文字までであることを検証するようにします。

  1. app/models/memo.rbを開く
app/models/memo.rb
class Memo < ApplicationRecord
  # contentカラムのバリデーションを設定する
  # presence: true - 空に出来ない
  # lenght: {maximum: 1000} - 長さは最大1000文字
  validates :content, presence: true, length: {maximum: 1000}
end

コントローラ作成

Homeコントローラを作成するときは分かりやすくするため手動でファイルを作成しましたが、今回はモデルの時のようにrails gコマンドを使用して生成します。
Controller nameは、基本的にモデル名の複数形にします。
Actionsにはコントローラ関数名の一覧をスペース区切りで入れます。

  1. Ctrl+Alt+GもしくはTools->Run Rails Generator...をクリック
  2. rails g controllerを選択してEnter
  3. Controller nameMemosを入力
  4. Actionsindex show new createを入力
  5. OKをクリック

screenshot_20210223_184038.png

ルーティング編集

先ほどのrails g controllerによってルーティングが生成されています。
自動で生成されたルーティングを削除し、以下のように編集します。
ルーティングの意味が分からない場合は前述のルーティング編集を参照してください。

  1. config/routes.rbを開く
  2. 以下のように編集する
config/routes.rb
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  get 'hello' => 'home#index'

  resources 'memos', only: %i[index show new create]

  root 'home#index'
end

resourcesは、RESTfulなルーティングをいい感じに作ってくれるやつです。
今回はindexshownewcreateしか使用しないので、onlyでシンボルの配列を指定しています。

上記のルーティングは、以下とほぼ同じです。

/memos/:idというルーティングが出てきますが、これは:idの部分に任意の値が入るという意味です。
例えば/memos/1にアクセスした場合、変数params[:id]に1が入ります。この変数はコントローラやViewで使用することができます。

ルーティングの優先順位は上からです。そのため、get 'memos/:id' => 'memos#show'より下にget 'memos/new' => 'memos#new'を配置すると、memos#showid=newとしてルーティングされてしまいます。

config/routes.rb
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  get 'hello' => 'home#index'

  # GET /memos
  get 'memos' => 'memos#index'
  # GET /memos/new
  get 'memos/new' => 'memos#new'
  # GET /memos/:id
  get 'memos/:id' => 'memos#show'
  # POST /memos
  post 'memos' => 'memos#create'

  root 'home#index'
end

コントローラ編集

コントローラでは、主に必要なモデルを取得する処理をします。
@を付けた変数をコントローラで定義することによって後述するViewで使用することができるようになります。

  1. app/controllers/memos_controller.rbを以下のように編集します。
app/controllers/memos_controller.rb
class MemosController < ApplicationController
  def index
    # すべてのメモを取得する
    @memos = Memo.all
  end

  def show
    # :idのメモを取得する
    memo_id = params[:id]
    @memo = Memo.find(memo_id)
  end

  def new
    # 空のメモを作成する
    @memo = Memo.new
  end

  def create
    # 受け入れるパラメータを定義
    # requireにはモデル名、permitにはカラム名
    memo_param = params.require(:memo).permit(:content)
    # メモの中身を埋めて作成
    @memo = Memo.new(memo_param)
    # セーブする(データベースに書き込む)
    if @memo.save
      # 成功した場合はメモ一覧にリダイレクト
      redirect_to(@memo)
    else
      # エラーが発生した場合はnew(フォーム)を再表示する
      render :new
    end
  end
end

View編集

以下のように編集します。

これはerbファイルなので、中にRubyの文を埋め込むことができます。
Rubyのifやeachなどは<% %>で囲います。
変数の中身などの結果を表示したい場合は<%= %>で囲います。
コメントを書きたい場合は<%# %>で囲います。
HTMLの<!-- -->も使用可能ですが、こちらはブラウザの「ページのソースを表示」などから見ることができます。

<%= link_to('戻る', :back) %>は、リンクを生成する関数です。
第二引数に入れている:backは前のページのパスを指しています。

link_tomemoを入れていますが、これはメモのパス(/memos/:id)として機能します。

index

app/views/memos/index.html.erb
<h1>メモ一覧</h1>

<ul>
  <% @memos.each do |memo| %>
    <%= link_to memo do %>
      <li><%= memo.content %></li>
    <% end %>
  <% end %>
</ul>

<%= link_to('新規作成', new_memo_path) %>
<%= link_to('戻る', :back) %>

show

app/views/memos/show.html.erb
<h1>メモ閲覧</h1>

<pre><%= @memo.content %></pre>

<%= link_to('戻る', :back) %>

new

app/views/memos/new.html.erb
<h1>メモ作成</h1>

<%# エラーがあったら表示する %>
<% if @memo.errors.any? %>
  <ul>
    <% @memo.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

<%# modelにはコントローラで作った空のメモを指定する %>
<%= form_with(model: @memo, local: true) do |form| %>
  <div class="field">
    <%# メモ内容を入力するテキストエリア(labelはその説明) %>
    <%= form.label :content %><br>
    <%= form.text_area :content %>
  </div>
  <div class="actions">
    <%# 送信ボタン %>
    <%= form.submit %>
  </div>
<% end %>

<%= link_to('戻る', :back) %>

create

app/views/memos/create.html.erbは使用しないため削除する。

  1. create.html.erbファイルを右クリック
  2. Deleteをクリック
  3. OKをクリック

※これはお好みの方法でOK

screenshot_20210223_193748.png
screenshot_20210223_193759.png

動作を確認する

ブラウザでlocalhost:3000/memosにアクセスして、正常に動作するか確認します。

日本語に対応させる

デフォルトでは、このようにメッセージやボタンなどが英語になっています。
screenshot_20210223_232128.png

rails-i18ngemを入れる

  1. Gemfileの一番下に、gem 'rails-i18n'を追加する
  2. Tools->Bundler->Installをクリック
  3. Runをクリック

screenshot_20210223_233422.png
screenshot_20210223_233604.png

日本語に設定する

  1. config/application.rbを以下のように書き換える。
config/application.rb
require_relative "boot"

require "rails/all"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module RailsTestApp
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.1

    # Configuration for the application, engines, and railties goes here.
    #
    # These settings can be overridden in specific environments using the files
    # in config/environments, which are processed later.
    #
    # config.time_zone = "Central Time (US & Canada)"
    # config.eager_load_paths << Rails.root.join("extras")

    # タイムゾーンを日本に設定
    config.time_zone = 'Tokyo'
    config.active_record.default_timezone = :local
    # デフォルトのロケールを日本語に設定
    config.i18n.default_locale = :ja
    # 使用可能なロケールに日本語を設定
    config.i18n.available_locales = %i[ja]
  end
end

モデルの日本語表記を作成

  1. config/locale/ja.ymlを作成して以下のように編集
config/locale/ja.yml
ja:
  activerecord:
    models:
      # モデル名: モデルの日本語表記
      memo: メモ
    attributes:
      # モデル名
      memo:
        # カラム名: カラムの日本語表記
        content: メモ内容

このように書くことによって、Viewに書いてある<%= form.label :content %>だけでいい感じに置き換わってくれます。

日本語化出来ているか確認

  1. Railsを再起動する(停止ボタン->再生ボタン)
  2. localhost:3000/memos/newでエラーを出して日本語化出来ているか確認する

screenshot_20210224_015843.png

メモにタイトルを設定できるようにする

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

メモを投稿できる機能を作る/マイグレーションファイル編集で編集したファイルを書き換えたくなりますが、一旦マイグレーションを行ったマイグレーションファイルを編集することはしてはいけません。そうすると、マイグレーションの意味が無くなってしまいます。

そのため、タイトルカラムを追加するマイグレーションを作成する必要があります。

今回はモデルの作成は不要なので、マイグレーションファイルのみを作成するコマンドを使用します。

memosテーブルにString型のTitleカラムを追加したいので、Generator argumentsはAddTitleToMemos title:stringとなります。

ここで、Text型ではなくString型を使う理由は、タイトルは255文字を超えないからです。

  1. Ctrl+Alt+GもしくはTools->Run Rails Generator...をクリック
  2. rails g migrationを選択してEnter
  3. Genetaror argumentsAddTitleToMemos title:stringを入力
  4. OKをクリック

screenshot_20210224_004411.png

マイグレーションファイル確認

db/migrate/xxxxxxxx_add_title_to_memos.rbというマイグレーションファイルが作成されているので、見てみます。

db/migrate/xxxxxxxx_add_title_to_memos.rb
class AddTitleToMemos < ActiveRecord::Migration[6.1]
  def change
    add_column :memos, :title, :string
  end
end

今回は特に変更したい箇所は無いので変更しません。
ここでnull: falseによってnullを禁止しない理由は、後からnullを許可しないカラムを追加することは少しだけ難しいからです。
というのも、すでにレコードがある場合に必然的にtitleカラムがnullになってしまいます。
そのため、nullを許可しない場合はデフォルト値を設定するなどをする必要があります。
今回はi18nの問題もあり、表示側でnullであったら設定されていない旨の表示をすることにします。

マイグレーション実行

メモを投稿できる機能を作る/マイグレーション実行参照

モデル編集

タイトルの文字数上限を50文字にします。

  1. app/models/memo.rbvalidates :title, length: {maximum: 50}を追記する

以下のようになっていればOKです。

app/models/memo.rb
class Memo < ApplicationRecord
  # contentカラムのバリデーションを設定する
  # presence: true - 空に出来ない
  # lenght: {maximum: 1000} - 長さは最大1000文字
  validates :content, presence: true, length: {maximum: 1000}

  # titleカラムのバリデーションを設定する
  # lenght: {maximum: 50} - 長さは最大50文字
  validates :title, length: {maximum: 50}
end

コントローラ編集

カラムtitleの編集を許可する。

  1. app/controllers/memos_controller.rbを開く
  2. 21行目、~permit(:content)~permit(:content, :title)に変更する

以下のようになっていればOKです。

app/controllers/memos_controller.rb
class MemosController < ApplicationController
  def index
    # すべてのメモを取得する
    @memos = Memo.all
  end

  def show
    # :idのメモを取得する
    memo_id = params[:id]
    @memo = Memo.find(memo_id)
  end

  def new
    # 空のメモを作成する
    @memo = Memo.new
  end

  def create
    # 受け入れるパラメータを定義
    # requireにはモデル名、permitにはカラム名
    memo_param = params.require(:memo).permit(:content, :title)
    # メモの中身を埋めて作成
    @memo = Memo.new(memo_param)
    # セーブする(データベースに書き込む)
    if @memo.save
      # 成功した場合はメモ一覧にリダイレクト
      redirect_to(memos_path)
    else
      # エラーが発生した場合はnew(フォーム)を再表示する
      render :new
    end
  end
end

View書き換え

以下のように編集します。

index

タイトルを表示するようにする。

app/views/memos/index.html.erb
<h1>メモ一覧</h1>

<ul>
  <% @memos.each do |memo| %>
    <%= link_to memo do %>
      <li><%= memo.title.blank? ? 'タイトルなし' : memo.title %>: <%= memo.content %></li>
    <% end %>
  <% end %>
</ul>

<%= link_to('新規作成', new_memo_path) %>
<%= link_to('戻る', :back) %>

show

タイトルを表示するようにする。

app/views/memos/show.html.erb
<h1>メモ閲覧</h1>

<h2><%= @memo.title.blank? ? 'タイトルなし' : memo.title %></h2>

<pre><%= @memo.content %></pre>

<%= link_to('戻る', :back) %>

new

タイトルの入力フィールドを追加する。

app/views/memos/new.html.erb
<h1>メモ作成</h1>

<%# エラーがあったら表示する %>
<% if @memo.errors.any? %>
  <ul>
    <% @memo.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

<%# modelにはコントローラで作った空のメモを指定する %>
<%= form_with(model: @memo, local: true) do |form| %>
  <div class="field">
    <%# タイトルを入力するテキストフィールド(labelはその説明) %>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
  </div>
  <div class="field">
    <%# メモ内容を入力するテキストエリア(labelはその説明) %>
    <%= form.label :content %><br>
    <%= form.text_area :content %>
  </div>
  <div class="actions">
    <%# 送信ボタン %>
    <%= form.submit %>
  </div>
<% end %>

<%= link_to('戻る', :back) %>

モデルの日本語表記を作成

  1. config/locale/ja.ymlを以下のように編集

title: タイトルを追加しています。

config/locale/ja.yml
ja:
  activerecord:
    models:
      # モデル名: モデルの日本語表記
      memo: メモ
    attributes:
      # モデル名
      memo:
        # カラム名: カラムの日本語表記
        content: メモ内容
        title: タイトル

ブラウザで確認

  1. ブラウザでlocalhost:3000/memosにアクセスする

メモのタイトルが表示されていて、タイトル付きのメモを新規作成できるようになっています。

screenshot_20210224_012612.png

完成!

お疲れさまでした!
もし間違っている所や指摘がありましたら伝えて頂けるとありがたいです。
あとよろしければLGTMもよろしくお願いします!
時間があれば、編集や削除機能の付け方やテストの書き方の記事なども作成するかもしれません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?