# [読書感想文] Ruby on Rails 5アプリケーションプログラミング
## 今回読んだ書籍
[Ruby on Rails 5アプリケーションプログラミング](https://www.amazon.co.jp/Ruby-Rails-5%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E5%B1%B1%E7%94%B0-%E7%A5%A5%E5%AF%9B/dp/4774188832)
([サンプルソース](https://wings.msn.to/index.php/-/A-07/978-4-7741-8883-6/))
- 発刊:2017/4/14
- 著者:山田 祥寛
- Rails5対応
## Railsというフレームワーク
### フレームワーク導入のメリット
- 生産性の向上
- 基盤がすでにある
- コードの一貫性を維持しやすく、結果として品質を均質化できる
- 大規模開発に向いている
- メンテナンス性に優れる
- 可読性が向上する
- アプリ統合も容易になる
- 開発ノウハウを後の開発/保守に援用できる
- 先端の技術トレンドにも対応しやすい
- 技術トレンドを意識して日々アップデートされている
- セキュリティ対策も行われる
- 一定以上の品質が期待できる
- フルスクラッチよりも多くの人間が関わっているため相対的に信頼性がある
### Railsの特徴
- Model-View-Controllerパターンを採用
- プログラマーとデザイナーとで並行作業を行いやすい
- デザインとロジックのそれぞれの修正が相互に影響しない
- 機能単位のテストを独立して実施できる(テストを自動化しやすい)
- Railsはアプリ開発のレールを提供する
- DRY(Don'tRepeatYourself)=同じ記述を繰り返さない
- CoC(ConventionoverConfiguration)=設定よりも規約
- Railsがあらかじめ用意している名前付けのルール
- 例えば「usersテーブルを読み込むためにはUserという名前のクラスになる」というようなルール
- フルスタックなフレームワーク
- 最新の技術トレンドにいち早く対応(Rails5のはなし)
- Javascript
- WebSocket
- API特化
## Ruby on Railsの基本
### コントローラーの基本
- 主にビジネスロジック(Model)を呼び出しや、その結果を出力(View)に引き渡す役割
- コントローラー名はできるだけリソース(操作対象のデータ)の名前に沿って命名するのが望ましい
- たとえば、membersテーブルを操作するコントローラーであれば、membersコントローラー(members_controller.rb)とするのが望ましい
### ビューの基本
- 基本的にERB(Embedded Ruby)と呼ばれるHTMLにRubyのコードを仕込むテンプレートを利用する
- ビューヘルパーで、直接データベースから値を取得しリンクやフォーム要素などをシンプルなコードで生成できる
- コントローラーのインスタンス変数がそのままビューにアサインされる
- 動的な処理は<%...%>や<%=...%>で記述する
- <%=...%>は結果を出力する
### モデルの基本
- データベースや外部サービスへのアクセスをはじめ、ビジネスロジック全般を担当する
- データベースはマイグレーションファイルで管理している
- テーブル保守の作業を半自動化できる
- ロールバックも容易
- (個人的にはスキーマのバージョン管理ができて、ファイルに日付がついているところからいつどのような変更をしたのか明確なのがいいと思っている)
## Scaffolding機能によるアプリ開発
定型的なCRUD(Create-Read-Update-Delete)機能を持ったアプリを構築するための機能。
以下からかんたんなbooksテーブルを管理するアプリ制作を通してScaffolding機能を紹介する。
### 作成する
以下のコマンドを実行する。
```
$ rails generate scaffold book isbn:string title:string
$ railsdb:migrate
```
これだけで必要なコントローラーやモデル、ビューがまとめて作成されCRUD機能をもったアプリがつくれる。

以下にアクセスして確認する。
```
http://localhost:3000/books
```
### ルーティング
以下のコマンドでURLとコントローラーを紐付けが確認できる。
```
$ rails routes
```

- URI
- :id
- 引数
- :format
- レスポンスの形式を指定できる
### 一覧画面の作成
レコード全件を取得しテンプレートに渡すコードが生成されている。
```ruby:books_controller.rb
def index
@books = Book.all
end
```
ビュー側には **ビューヘルパー** が使用されている。
例えば以下のようなコードはHTMLとスクリプトブロックが混在し読みづらい。
```html+ruby
<a href="<%= url %>"><%= text %></a>
```
ilnk_toを使用するとスッキリした書き方ができる。
```html+ruby
<%= link_to(text, url) %>
```
URLは設定されているルーティングに応じて自動的に変数が割り当てられている。
以下のように記述すれば `book` オブジェクトを編集するURLが生成されれる。
```html+ruby
<%= link_to(text, edit_book_path(book)) %>
```

methodを指定することもできる。
以下のように confirm をつければ確認ダイアログを呼び出せる。
```html+ruby
<%= link_to 'Destroy', book, method::delete, data:{ confirm:'Are you sure?' } %>
```
### 詳細画面の作成
詳細を表示するIDを受け取っている。
```ruby:books_controller.rb
before_action :set_book, only: [:show, :update, :edit, :destroy]
def show
end
private
def set_book
@book = Book.find(params[:id])
end
```
- before_actionメソッドは、アクションメソッドの前に実行すべきメソッドを指定する
- 複数のアクションで共通するようなフィルター処理は before_action としてメソッドで切り出すことでアクションメソッドの記述をシンプルに保つことができる
- params[]にGET/POSTパラメーターが格納されている
ビュー側は以下のように記述することで対応するフィールド値にアクセスできる。
```html+ruby
<%= @book.title %>
```
### 新規画面の作成
フォームから送信した情報をDBに登録する。
入力フォームは非常にシンプルな作りになっている。
```ruby:books/new.html.erb
<h1>NewBook</h1>
<%= render 'form', book: @book %>
<%= link_to 'Back', books_path %>
```
実際の入力フォームは `_form.html.erb` で共通化されており、new.html.erb ではビューヘルパー`render`を使用して呼び出している。
```html+ruby:_form.html.erb
<%= form_for(book) do |f| %>
~略~
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
~略~
<%= end %>
```
ビューで生成されたものをコントローラー側で受け取る処理は以下のようになっている。
```ruby:books_controller.rb
~略~
# /books/newに遷移した際に呼ばれるメソッド
def new
@book = Book.new # フォームの器となるオブジェクトを作成
end
~略~
# フォームからデータを送信された際に呼ばれるメソッド
def create
@book = Book.new(book_params)
respond_to do |format|
if @book.save # DBに登録する
# 成功時は指定されたフォーマットに応じてレスポンスを返す
# リクエストURLが /books/create.json ならJSON形式
format.html { redirect_to @book, notice: 'Book was successfully created.' } # HTML形式なら /books/:id
format.json { render :show, status: :created, location: @book } # JSON形式なら /books/show
else
# 失敗時も指定されたフォーマットで返す
format.html { render :new } # HTML形式なら再度 /books/new
format.json { render json: @book.errors, status: :unprocessable_entity } # JSON形式ならJSONデータでエラーを渡す
end
end
end
~略~
private
~略~
def book_params
# フォームから値を安全に受け取る
params.require(:book).permit(:isbn, :title, :price, :publish, :published, :dl)
end
end
```
### 編集画面の作成
指定されたIDのデータを編集する画面。
まずコントローラーから。
```ruby
# before_action の set_book でデータを取得しているのでここでは何も記述しない
def edit
end
# データ更新の処理
def update
respond_to do |format|
# @book を book_params に更新する
if @book.update(book_params)
# create と同様に指定されたフォーマットに応じて処理する
format.html { redirect_to @book, notice: 'Book was successfully updated.' }
format.json { render :show, status: :ok, location: @book }
else
format.html { render :edit }
format.json { render json: @book.errors, status: :unprocessable_entity }
end
end
end
```
ビューは新規作成画面と同様に `_form.html.erb`を使用している。
### 削除機能
指定されたIDのデータを削除する機能。
削除画面などはないのでコントローラーのみ。
```ruby
def destroy
@book.destroy
# destroyをクラスメソッドとして呼び出し
#Book.destroy(params[:id])
# deleteメソッド
#Book.delete(params[:id])
respond_to do |format|
format.html { redirect_to books_url, notice: 'Book was successfully destroyed.' }
format.json { head :no_content }
end
end
```
## ビュー開発
### フォーム関連のビューヘルパー
- 一覧

フォームを生成する方法としては2種類あり、それぞれ用途と使えるヘルパーが異なる。
- form_forは、特定のモデルオブジェクトを編集するのに特化したメソッド
- form_tagは、モデルに関係しない汎用的なフォームを生成するためのメソッド
- 検索フォームなど
- モデル編集もできないわけではないが冗長になる

各ビューヘルパーの詳細な使い方は[Railsガイド](https://railsguides.jp/form_helpers.html)などで必要なときに調べればよさそう。
### 文字列のヘルパー
- simple_format
- 文字列全体を`<p>`要素で囲む
- 単一の改行文字には`<br/>`を付与
- 連続した改行文字には`</p><p>`を付与
- truncate
- 文字列を指定桁で切り捨てる
- 指定桁を超えると以降は「...」の表示で切り捨てられる。
- excerpt
- 文字列から特定の部分のみを抜粋する
- `<%=excerpt(msg,'Catalyst',radius:10)%>`
- `...フレームワークには、Catalystやsymfonyなど...`
- cycle
- テーブルやリストの背景色をn行おきに変更する
- highlight
- 特定のキーワードをハイライト表示する
- concat
- スクリプトブロックの中に出力コードを埋め込む
- <%==...%>
- 文字列をHTMLエスケープする
- sanitize
- 文字列から要素を除去する
- sprintf
- 文字列を整形するどの言語にもあるやつ
### 数値のヘルパー
- number_xx
- 数値をさまざまな形式で加工する
- 
- strftime
- 日付データを整形する
- `<%= Time.now.strftime('%Y年%m月%d日 %H:%M:%S') %>`
### リンク関連のビューヘルパー
- link_to
- ハイパーリンクを生成する
- url_for
- ルート定義から動的にURLを生成する
- `<%= url_for(action: :login, controller: :members, only_path: false, protocol: 'https') %>`
- https://localhost:3000/members/login
- link_to_if / link_to_unless
- 条件に応じてリンクを生成する
- `<%= link_to_if @user.nil?, 'ログイン', controller: :login, action: :index %>`
- link_to_unless_current
- 現在のページの場合はリンクを無効にする
- mail_to
- メールアドレスへのリンクを生成する
### 外部リソース指定のためのビューヘルパー
- image_tag
- 画像を表示する
- audio_tab / video_tag
- 音声/動画をブラウザーで再生する
- auto_discovery_link_tag
- ブラウザーのフィード検出機能を有効にする
- favicon_link_tag
- サイトのFaviconを定義する
### その他のビューヘルパー
- debug
- 構造化データをダンプ出力する
- capture
- 出力結果を変数に格納する
```
<% @current = capture do %>
ただいまの時刻は<%= Time.now %>です。
<% end %>
<%= @current %>
```
- tag
- 本体を持たない任意の要素を生成する
- `<%= tag(:input, { type: 'radio', disabled: true }, true) %>`
- `<input disabled="disabled" type="radio">`
- content
- 本体を持つ任意の要素を生成する
- `<%= content_tag: p, '今日もいい天気', id: :exam, class: :body %>`
- `<p id="exam" class="body">今日もいい天気</p>`
## ビューヘルパーを自作する
`/app/helpers`フォルダー配下の`xxxxx_helper.rb`に記述する
```ruby:view_helper.rb
module ViewHelper
#datetime:整形対象の日付時刻値(Timeオブジェクト)
#type:出力形式(日付時刻:datetime、日付のみ:date、時刻のみ:time)
def format_datetime(datetime, type = :datetime)
~処理~
end
end
```
以下のようにして使える。
```html+ruby
<%= format_datetime(current, :date) %>
```
### アプリ共通のデザインを定義するーレイアウト
ヘッダーやフッター、メニューなどアプリ共通の部分はレイアウトとして用意しておき、コンテンツ部分だけをページ単位で作成する。
サイト全体のレイアウトを変更する場合は
`layouts/application.html.erb`
を編集する。
#### レイアウトを適用するさまざまな方法
- コントローラー単位でレイアウトを設定する
- /app/views/layoutsフォルダー配下にコントローラー名.html.erbという名前で保存
- コントローラー単位でレイアウトを設定する(layoutメソッド)
- コントローラーのフィールドに`layout 'product'`のように記載する
- アクション単位でレイアウトを設定する
- メソッド内に`render layout: 'sub'`のように記載する
## モデル開発
### findメソッド
- find(value)
- 主キー(ID)による検索
- find_by(key: value)
- 任意のkeyによる検索
### クエリメソッド

- findやfind_byなどのメソッドと違って、その場ではデータベースにアクセスしない
- 遅延ロードという
- これにより各メソッドを組み合わせることができる
- `@books = Book.where(publish: '技術評論社').order(published: :desc)`
それぞれの詳しい説明については[Railsガイドにも詳しい使い方があった](https://railsguides.jp/active_record_querying.html)。
### レコードの更新
レコードの作成・更新・削除を行う。
こちらも[Railsガイドに詳しい説明があった](https://railsguides.jp/active_record_basics.html)。
- update_all
- 複数のレコードをまとめて更新する
- destroy / delete
- レコードの削除
- destroyははSELECT→DELETE
- deleteはDELETEのみ
- destroy_all
- 複数のレコードをまとめて削除する
- transaction
- トランザクション処理を実装する
- 分離レベルの設定
- 
- オプティミスティック同時実行制御(楽観的同時実行制御)
- 更新の競合を管理する機能
- テーブルに `lock_version` カラムを追加
- ビューにhiddenとして `lock_version` を渡す
- レコード更新時に `lock_version` の数値に変化がなければ更新、増えていた(他人が更新していた)場合は例外が発生する
- `rescue ActiveRecord::StaleObjectError`で例外をキャッチできる
- Active Record enums
- 列挙型のカラムを定義する
- statusなど
- モデルに以下のように定義するだけでできる
- `enum status: { draft: 0, published: 1, deleted: 2 }`
- Active Record enumsでは、published!、draft!、deleted!のように「enumで定義したキーワード+!」の形式で、statusフィールドを設定できる
- 特定のステータスのレコードを取得できる
- `@reviews = Review.published`だと`status = 1`のレコードを取得する
- クエリメソッドをつなげることもできる
- `@reviews = Review.published.where('updated_at < ?', 6.months.ago)`
- フィールド値をシンボルで記述することができる
- `@review.status = :published`
### 検証機能の実装
ActiveModelでは、検証処理の内容に応じて、それぞれ専用の検証クラス(ActiveModel::Validations::xxxxxValidatorクラス)が存在する。

以下のようにモデル側に記述すると `book.save` 時などに自動的にバリデーションしてくれるようになる。
任意のタイミングで行う場合は `@book.valid?`のようにする。
```ruby:book.rb
class Book < ApplicationRecord
validates :isbn,
presence: true,
uniqueness: true,
length: { is: 17 },
format: { with: /\A[0-9]{3}-[0-9]{1}-[0-9]{3,5}-[0-9]{4}-[0-9X]{1}\z/ }
validates :title,
presence: true,
length: { minimum: 1, maximum: 100 }
validates :price,
numericality: { only_integer: true, less_than: 10000 }
validates :publish,
inclusion:{ in: ['技術評論社', '翔泳社', '秀和システム', '日経BP社', 'ソシム'] }
```
バリデーションを行うメソッド
- create
- create!
- save
- save!
- update
- update!
バリデーションが**行われない**メソッド
- decrement!
- decrement_counter
- increment!
- increment_counter
- toggle!
-touch
- update_all
- update_attribute
- update_counters
- update_column
- update_columns
- save(validate:false)
### ActiveModel::Modelモジュール
データベースに関連づかないモデルを定義する。
「データベースの項目ではないが、フォームからの入力を受け取って検証を行う」必要があるような処理を、(アクションメソッドを検証処理などで汚すことなく)モデルクラスとしてまとめるような用途で利用する。
```ruby:search_keyword.rb
class SearchKeyword
include ActiveModel::Model # 必須
attr_accessor :keyword
validates :keyword, presence: true
end
```
### アソシエーションによる複数テーブルの処理
テーブル間のリレーションシップをモデル上の関係として操作できるようにするしくみ。
- 外部キー列は「参照先のモデル名_id」の形式であること(例:book_id、user_id)
- 中間テーブルは参照先のテーブル名を「\_」で連結したものであること。ただし、連結順は辞書順(例:authors_books)
#### belongs_toアソシエーション
参照元テーブルから参照先テーブルの情報にアクセスする。
例えば、reviewからbookを参照する場合は以下のように記述する。
```ruby:review.rb
class Review < ApplicationRecord
belongs_to :book
end
```
このようにすれば`@review = Review.find(3)`で呼び出した際に、`@review.book.title`のようにしてbookの情報も同時に取得できる。
#### has_manyアソシエーション
1:nで双方向に関係性をもたせることができる。
```ruby:book.rb
class Book < ApplicationRecord
has_many :reviews
end
```
例えば`@book = Book.find(1)`のように取得した場合、付随するreviwも取得できる。
```ruby:hasmany.html.erb
<h2>「<%= @book.title %>」のレビュー</h2>
<hr />
<ul>
<% @book.reviews.each do |review| %>
<li><%= review.body %>(<%= review.updated_at %>)</li>
<% end %>
```
#### has_oneアソシエーション
1:1の関係を表現する。
以下のような場合は、1つのUserオブジェクトに対して最大1つAuthorオブジェクトが存在するという意味になる。
```ruby:user.rb
class User < ApplicationRecord
has_one :author
end
```
```ruby:author.rb
class Author < ApplicationRecord
belongs_to :user
end
```
#### has_and_belongs_to_manyアソシエーション
m:nの関係を表現する。
中間テーブルを使って表現するのが一般的。

```ruby:book.rb
class Book < ApplicationRecord
has_and_belongs_to_many :authors
end
```
```ruby:author.rb
class Author < ApplicationRecord
has_and_belongs_to_many :books
end
```
#### has_manythroughアソシエーション
同様にm:nの関係を表現するが中間テーブルを利用できない以下のようなパターンで使用する。

互いの1:nの関係をまず定義しておく。
その状態でhas_manythroughアソシエーションにより、books/usersテーブル間を、reviewsテーブルを中間テーブルとして関連付ける。
```ruby:book.rb
class Book < ApplicationRecord
has_many :reviews
has_many :users, through: :reviews
end
```
```ruby:review.rb
class Review < ApplicationRecord
belongs_to :book
belongs_to :user
end
```
```ruby:user.rb
class User < ApplicationRecord
has_many :reviews
has_many :books, through: :reviews
end
```
#### 関連の命名を変更する
以下のようなケースにも対応できる。(詳細略。そういうケースに遭遇したらググる。)
- AuthorモデルからはFanCommentモデルを(fan_commentsメソッドではなく)commentsメソッドで参照
- fan_commentsテーブルの外部キーはauthor_noフィールド
- AuthorモデルからFanCommentモデルを取得する際、deleted列がfalse(未削除)であるもののみを抽出したい
#### counter_cacheオプション
たとえばあるユーザー(usersテーブル)が投稿したレビュー(reviewsテーブル)の件数をusersテーブルで保存しておければ、件数を取得するためだけに両者を結合する必要がなくなり便利。
ラクマだと商品のいいね数がそういうケースにあたるのかな...?(未確認)
- 「子テーブル名_count」という名前でinteger型の列(カウンター列)を作成
- 子テーブルに `belongs_to:user, counter_cache: true` を追加する
- コントローラー側は`@user.reviews.size`で件数を取得できるようになる
内部的には子テーブルにinsertが発生した際に親テーブルのxx_countがインクリメントされている。
#### ポリモーフィック関連
1つのモデルを複数の親モデルに関連付ける。
- xxxxx_type(紐づけるモデル)
- xxxxx_id(外部キー)
のような列をテーブルに準備しておく。
モデル側では以下のような宣言が必要となる。
- 親モデル側でasオプション付きのhas_manyメソッドを宣言
- 子モデル側でpolymorphicオプション付きのbelongs_toメソッドを宣言
#### 関連するモデルと結合する
- joins
- left_outer_joins
- includes
### コールバック
ActiveRecordによる検索/登録/更新/削除、および、検証処理のタイミングで実行されるメソッド。
Ex:
- ユーザー情報を登録する際にパスワードが指定されていなかったら、ランダムのパスワードを生成
- 書籍情報を削除する際に、削除される書籍情報を履歴情報として記録する
- 著者情報を削除する際に、ファイルシステムで管理していたサムネイル画像も削除
- 著者情報が登録/更新されたタイミングで、管理者にメールを送信


### マイグレーション
テーブルレイアウトを作成/変更するための機能。
データ型の対応は以下の通り。

マイグレーションファイルの生成
```bash
$ rails generate migration name [field: type ...] [option]
```
名前はイカのルールに基づいて生成するとマイグレーションファイルも対応したものができる。
- AddXxxxxToテーブル名
- RemoveXxxxxFromテーブル名
マイグレーションファイルで利用できる主なメソッド

それぞれのメソッドの詳細は[こちらにも記載されていた](https://railsguides.jp/active_record_migrations.html)ので省略する。
マイグレーションの実行

#### データの初期化
- seed
- マスターテーブルなどの初期データを投入
- fixture
- テストデータの投入
## コントローラー開発
### リクエスト操作
- params[:key]
- リクエスト時に送信されたGET/POSTパラメーターを受け取ることができる
- StrongParameter
- `params.require(model).permit(attr, ...)`
- マスアサインメント脆弱性に対する、いわゆるホワイトリスト対策
- request.headers
- リクエスト時のヘッダー情報を格納している
- 
- ファイル操作
- 使う機会があまりなさそうなので省略
### レスポンス操作
処理結果を返すメソッド

#### HTML以外のレスポンス操作
- `render xml: @books`
- to_xmlメソッドでモデルをXML形式に変換
- ContentTypeヘッダーとして"application/xml"を設定
- `render json: @books`
- 同様にJSON形式でレスポンスを返す
- respond_to
- マルチフォーマット出力に対応する
- デフォルトでは`html、xml、json、rss、atom、yaml、text、js、css、csv、ics`などが定義されている
### 状態管理
HTTPとはステートレス(状態を維持できない)なプロトコルであるため同一セッションを保持する仕組みが必要。
- cookies
- クライアントのcookie情報を取得/変更するメソッド
- cookiesメソッドに指定できるオプション
- 
- session
- 設定を変更することで、保存先(データストア)をcookie/cache/DBなどに変更できる
### フィルター
認証やリクエスト時のバリデーションなどに使われる。
- before_action: method [, ...] / after_action: method [, ...]
- アクションの事前/事後に処理を実行する
- around_action: method [, ...]
- アクションの前後で処理を実行する
- フィルターの適用範囲をカスタマイズ
- only
- 指定されたアクションでのみ実行する
- except
- 指定されたアクション以外で実行する
### ApplicationController
すべてのコントローラーの基底クラス。
アプリ共通の処理を記載する。
- 個別のコントローラーから呼び出せるヘルパーメソッド
- すべて(あるいはほとんど)のコントローラーで利用するフィルター
- クロスサイトリクエストフォージェリ対策を行う
- protect_from_forgery
- 共通ロジックをモジュールにまとめる
- concernsフォルダー
- アプリ共通とまではいかないまでも複数のコントローラーで利用する処理などをまとめる
- アプリ共通の設定
- ログイン
- 例外処理(400/500系エラーなど)
- rescue_from
- デバイス単位でビューを振り分ける
- ActionPackVariants
- 独自のフラッシュメッセージを追加する
- add_flash_types
## ルーティング
RESTの考え方

### resources
複数の要素をもつ場合に使う。
```ruby:routes.rb
Rails.application.routes.draw do
resources: users
end
```
上記を設定するだけでURLとコントローラー・アクションの一通りのマッピングが行われる。
また同時に以下のURLヘルパーも作成される。

### resource
単一の要素の場合に使う。(ユーザー1人に対してレコードが1つしかないなど)
基本的には`resources`と同じだが単一のため`:id`がない。
### RESTfulインターフェイスのカスタマイズ
- ルートパラメーターの制約条件
- constraints
- `:id`の範囲を指定したりできる
- `resources: books, constraints: { id: /[09]{1, 2}/ }`
- より複雑な制約条件の設定
- 制約を定義するクラスを作成し`matches?`メソッドを定義する
- 引数としてリクエスト情報(requestオブジェクト)を受け取り
- 戻り値としてルートを有効にすべきかどうか(true/false)を返す
- このクラスをルーティングの`contraints`に設定する
- `resources: books, constraints: TimeConstraint.new`
- formatオプションを無効にする
- レスポンス形式をしていするフォーマット指定(json,xmlなど)がデフォルトでONになっているので無効にする場合は指定する
- `resources: books, format: false`
- コントローラークラス/Urlヘルパーの名前を修正する
- controllers
- コントローラーのマッピングを変更する
- `resources: users, controller: :members`
- UsersControllerコントローラーではなく、MembersControllerコントローラーにマッピングされる
- as
- URLヘルパーの名前を変更する
- `resources: reviews, as: :comments`
- reviews_pathやreview_pathが、comments_path comment_pathになる
- モジュール配下のコントローラーをマッピングする
- サブフォルダーにコントローラーをまとめる際に使う
- namespaceブロック
- controller/admin/books_controller.rb の場合
- `namespace: admin { resources:books }`
- URLは`/admin/books`になる
- scopeブロック
- controller/admin/books_controller.rb の場合
- `scope module: :admin { resources:books }`
- URLは`/books`になる
- RESTfulインターフェイスに自前のアクションを追加する
- collection
- 複数オブジェクトを扱うアクションメソッドを定義
- member
- 単一オブジェクトを扱うアクションメソッドを定義
- RESTfulインターフェイスのアクションを無効化する
- only/exceptオプション
- 指定されたアクションメソッドを限定的にする
- 階層構造を持ったリソースを表現する
- resourcesメソッドのネスト
- 書籍1のレビュー情報は「~/books/1/reviews」というURLにしたい場合
- `resources: books { resources: reviews }`
- 浅いネスト
- `/books/10/reviews/8`ではなく`reviews/8`としたい場合`shallow`オプションを付ける
- `resources: books { resources: reviews, shallow: true }`
- ルート定義を再利用可能にする
- concernメソッド&concernsオプション
### 非RESTfulなルート定義
```ruby:routes.rb
match '/details(/:id)' => 'hello#index', via: [:get, :post]
```
とした場合、
```bash
$ rails routes
GET|POST /details(/:id)(.:format) hello#index
```
というようなルーティングになる。
その他にも非常に柔軟に対応できるので[詳しくはRailsガイド](https://railsguides.jp/routing.html#%E3%82%BB%E3%82%B0%E3%83%A1%E3%83%B3%E3%83%88%E3%82%92%E5%88%B6%E9%99%90%E3%81%99%E3%82%8B)など。
### root設定
トップページへのマッピングを定義する場合
```ruby
Rails.application.routes.draw do
root to: 'books#index'
end
```
## テスト
こちらは[RSpecで書いた]()ので省略。
## クライアントサイド開発
こちらも現時点では(個人的に)必要としていないので記事としては省略。
## Railsの高度な機能
詳細や使い方などは省略。
### メールを送信する機能
- AcrtionMailer
### 時間のかかる処理を非同期実行する
- ActiveJob
- 大量データの集計処理、外部サービスとの連携、メール送信など
- 主なJob管理ライブラリ
- Backburner
- DelayedJob
- Qu
- Que
- queue_classic
- Resque1.x
- Sidekiq
- Sneakers
- SuckerPunch
- ActiveJobAsyncJob
- ActiveJobInline
- ラクマではsidekiqを使っている
### キャッシュ機能の実装
- フラグメントキャッシュという機能がある
- ページの中に、キャッシュしたい静的な領域(たとえば、記事コンテンツ)と、キャッシュできない動的に生成すべき領域(たとえば、現在時刻の表示など)が混在している場合でも、適切なキャッシュポリシーを設定できる
- 書籍情報の配下にレビュー情報が属するような階層的なページ構造でも、効率よくキャッシュを管理できる
- キャッシュの保存先
- 
### アプリの国際化対応
- i18n
- 文字列を別ファイルとして切り出し、言語に応じた文字列を出力する仕組み。
### Railsの拡張機能
- [The Ruby Toolbox](https://www.ruby-toolbox.com/)ではライブラリがカテゴリ別にランク付けされているので、目的ごとによく使われているライブラリを検索するには便利
### 本番環境への移行
- Railsでは、デフォルトでPumaというWebサーバーを提供しており、Railsをインストールしただけで、最低限、Railsアプリを手もとで動作させることができる
- 静的コンテンツはNginx/ApacheHttpServerなどに委ね、RailsアプリだけをPumaで処理する、という役割分担が一般的
{"metaMigratedAt":"2023-06-15T01:21:53.142Z","metaMigratedFrom":"Content","title":"[読書感想文] Ruby on Rails 5アプリケーションプログラミング","breaks":true,"contributors":"[{\"id\":\"e09d90c1-5024-46dc-b910-434c5913133c\",\"add\":20840,\"del\":202}]"}