Ruby on Rails

Ruby on Rails入門

小さな記事アプリを作りながら、Rails 8の考え方とWeb開発の流れを学びましょう。

この講座はRuby 3.3以降、Rails 8を対象にしています。

01

Ruby on Railsとは

Ruby on Rails(Rails)はRubyでWebアプリケーションを作るためのフレームワークです。URLを受け取り、必要なデータを処理し、HTMLやJSONを返すまでの共通部分を用意してくれます。

Railsの2つの考え方
  • 設定より規約:よくある名前やファイル配置を守ると、設定を減らせます。
  • DRY:同じ知識を複数の場所へ重複して書かず、変更箇所を一つにします。

Railsの学習では、魔法のように見える自動処理を丸暗記するより、ブラウザーからのリクエストがルート、コントローラー、モデル、ビューを通る順番を確認することが大切です。

02

学習前の準備

RubyとRailsのバージョンを確認します。プロジェクトの中では、インストール済みのRailsではなく、プロジェクトが指定したbin/railsを使います。

バージョン確認COMMAND
ruby --version
rails --version
表示例OUTPUT
ruby 3.3.6
Rails 8.0.2

表示される細かなバージョンは環境によって異なります。Rails 8を使えることを確認し、エディターとターミナルを用意してください。データベースは最初は標準構成のSQLiteで進められます。

コマンドの場所新しいアプリを作った後は、そのアプリのディレクトリーへ移動してからbin/railsを実行します。
03

アプリケーションを作る

rails newでアプリケーションの土台を作ります。ここでは記事を管理するrails_notesを作成します。

作成と起動COMMAND
rails new rails_notes
cd rails_notes
bin/rails db:prepare
bin/rails server
ブラウザーURL
http://localhost:3000

ブラウザーでRailsの初期画面が表示されれば、Ruby、Rails、依存ライブラリ、サーバーが連携しています。サーバーを止めるときはターミナルでCtrl+Cを押します。

主なディレクトリー

見る場所TEXT
app/controllers/  リクエストを処理するコントローラー
app/models/       データとルールを扱うモデル
app/views/        HTMLなどの表示テンプレート
config/routes.rb  URLと処理の対応
db/migrate/       テーブル変更の履歴
config/database.yml データベース設定

最初から全ファイルを覚える必要はありません。自分で追加したファイルがどの役割に属するかを、ディレクトリー名から判断できるようにしましょう。

04

MVCで役割を分ける

Railsは主にModel、View、Controllerの3つに処理を分けます。Controllerは依頼を受け、Modelからデータを取得し、Viewへ渡します。Viewは表示を担当し、Modelはデータとそのルールを担当します。

リクエストの流れFLOW
ブラウザー
  ↓ GET /articles
routes.rb
  ↓ ArticlesController#index
モデル Article からデータを取得
  ↓
app/views/articles/index.html.erb でHTMLを作成
  ↓
ブラウザーへレスポンス

ControllerにHTMLを書いたり、Viewにデータベース検索を書いたりすると、後で修正しにくくなります。小さな例でも役割を分ける習慣を身につけましょう。詳しいルーティングと画面の作成はMVCとルーティングで確認できます。

05

ルーティングと画面

config/routes.rbには、HTTPメソッド、URL、コントローラーのアクションを登録します。まず固定の文字列を表示して、URLから処理へ到達することを確かめます。

config/routes.rbRUBY
Rails.application.routes.draw do
  root "pages#home"
  get "/about", to: "pages#about"
end
app/controllers/pages_controller.rbRUBY
class PagesController < ApplicationController
  def home
    @message = "Railsへようこそ"
  end

  def about
  end
end
app/views/pages/home.html.erbERB
<h1><%= @message %></h1>
<p>最初のRails画面です。</p>
表示結果BROWSER
Railsへようこそ
最初のRails画面です。

同じ画面を作るときも、まずルートを追加し、次にアクション、最後にビューを作ると、どこが不足しているかを確認しやすくなります。

06

モデルとデータベース

モデルを生成すると、Rubyのクラスとテーブル変更の設計図であるマイグレーションが作られます。マイグレーションを実行して初めてデータベースへ反映されます。

Articleを作成COMMAND
bin/rails generate model Article title:string body:text published:boolean
bin/rails db:migrate
consoleで保存COMMAND / RUBY
bin/rails console
article = Article.create!(title: "最初の記事", body: "Railsを学んでいます", published: true)
article.title
# => "最初の記事"
Article.count
# => 1

db:migrateは、マイグレーションに書かれた変更を適用します。既に適用したマイグレーションを書き換えず、新しい変更は新しいマイグレーションとして追加します。詳しい検索・関連付け・バリデーションはActive Recordとデータベースで学べます。

07

CRUDを一周する

データを扱う基本操作は、Create(作成)、Read(読み取り)、Update(更新)、Delete(削除)の4つです。RailsではActive Recordのメソッドでこの操作を表現できます。

CRUDの例RUBY
# Create
article = Article.create!(title: "入門", body: "本文")

# Read
Article.all
Article.find(article.id)
Article.where(published: true)

# Update
article.update!(title: "更新した入門")

# Delete
article.destroy!
検索結果の例OUTPUT
Article.where(published: true).pluck(:title)
# => ["最初の記事", "入門"]

実際の画面では、一覧にArticle.all、詳細にArticle.find(params[:id])のような処理を置きます。IDが存在しない場合のエラーや、削除前の確認も考える必要があります。

08

バリデーションでデータを守る

フォームから受け取る値は、空欄や長すぎる文字列を含む可能性があります。モデルにルールを書き、保存前に確認します。

app/models/article.rbRUBY
class Article < ApplicationRecord
  validates :title, presence: true, length: { maximum: 100 }
  validates :body, presence: true
end
失敗を確認RUBY
article = Article.new(title: "", body: "本文")
article.valid?
# => false
article.errors.full_messages
# => ["Title can't be blank"]

saveは失敗するとfalseを返し、save!は例外を発生させます。画面からの入力では失敗を画面へ戻し、バッチ処理では例外を記録するなど、場面に応じて使い分けます。

09

フォームから保存する

フォームは表示だけでなく、送信先のルート、コントローラーの処理、入力値の許可が必要です。Railsのフォームヘルパーを使うと、CSRF対策用のトークンも組み込まれます。

routes.rbRUBY
resources :articles
controllerRUBY
def create
  @article = Article.new(article_params)
  if @article.save
    redirect_to @article, notice: "記事を作成しました"
  else
    render :new, status: :unprocessable_entity
  end
end

def article_params
  params.require(:article).permit(:title, :body, :published)
end
form.html.erbERB
<%= form_with model: @article do |form| %>
  <%= form.label :title, "タイトル" %>
  <%= form.text_field :title %>
  <%= form.label :body, "本文" %>
  <%= form.text_area :body %>
  <%= form.submit "保存" %>
<% end %>

permitを忘れると、Railsは受け取った属性をそのままモデルへ渡しません。許可する項目を明示するStrong Parametersは、フォームを作るときの必須手順です。

10

安全なアプリにする

入力値をHTMLへ表示するときは、通常のERB出力<%= ... %>を使います。文字列をHTMLとしてそのまま出力する<%== ... %>html_safeは、内容を十分に検証できる場合だけに限定します。

確認する習慣
  • フォームの属性をStrong Parametersで限定する
  • モデルのバリデーションを画面側のチェックだけに任せない
  • 認証と認可を分け、他人のIDを指定した操作を許さない
  • 秘密情報をソースコードへ直接書かず、環境変数やCredentialsを使う

開発中に表示されるエラー画面には内部情報が含まれることがあります。公開環境では設定を確認し、ログへパスワードやトークンを書き出さないようにします。

11

Gemfileと依存関係

Railsアプリで使うgemはGemfileへ記録し、Bundlerでプロジェクト単位にインストールします。誰かの環境で動いたgemを手作業で追加するのではなく、ファイルとロックファイルを共有することで同じ環境を再現できます。

GemfileRUBY
gem "rails", "~> 8.0"
gem "sqlite3"
gem "bcrypt", "~> 3.1"

group :development, :test do
  gem "debug"
end
インストールCOMMAND
bundle install
bin/rails about
bundle exec ruby -v
確認結果の例OUTPUT
Could not find gem ...  # 依存関係の問題がある場合
Rails version: 8.0.2
Ruby version: 3.3.6

バージョンを変えたらGemfile.lockも確認します。アプリ内ではbin/railsを使い、必要に応じてbundle execでプロジェクトのgemを指定します。

練習Gemfileへgem "httparty"を追加してbundle installを実行し、Gemfile.lockに追加された行を探してください。終わったら不要なgemを削除して再度インストールします。
12

環境変数とCredentials

APIキーや本番用のパスワードはソースコードへ直接書きません。ローカルの一時的な設定には環境変数、Railsが暗号化して管理する設定にはCredentialsを使います。

環境変数を読むRUBY
api_key = ENV.fetch("WEATHER_API_KEY")
legacy_key = ENV["WEATHER_API_KEY"]
puts api_key.length
一時的に設定COMMAND
WEATHER_API_KEY="local-test-key" bin/rails runner 'puts ENV.fetch("WEATHER_API_KEY").length'
Credentialsを編集COMMAND
bin/rails credentials:edit
読み出す例RUBY
token = Rails.application.credentials.dig(:weather, :api_key)
raise "設定がありません" if token.blank?

Credentialsの暗号化ファイルと復号に必要な鍵は、役割が違います。暗号化ファイルをGitで管理する場合も、master.keyや本番の鍵は秘密として扱います。

PRACTICE

練習問題

問題1:トップページを作る

PagesController#homeを作り、/で「今日の学習」を表示してください。

解答例RUBY
# config/routes.rb
root "pages#home"

# app/controllers/pages_controller.rb
def home
  @title = "今日の学習"
end

# app/views/pages/home.html.erb
<h1><%= @title %></h1>
表示結果OUTPUT
今日の学習
問題2:公開記事だけ検索する

公開済みの記事だけを新しい順に取得するActive Recordのコードを書いてください。

解答例RUBY
articles = Article.where(published: true).order(created_at: :desc)
puts articles.map(&:title)
問題3:タイトル必須を確認する

タイトルが空のArticleを作り、保存できないこととエラーメッセージを確認してください。

解答例RUBY
article = Article.new(title: "", body: "本文")
puts article.valid?
puts article.errors.full_messages
実行結果の例OUTPUT
false
Title can't be blank
NEXT

次に学ぶこと

公式のGetting Started with Railsも参照し、手元のアプリでコマンドを実行しながら学習を進めてください。