# 通知設計書 8-BaseMailer#explicit_multipart

## 概要

本ドキュメントは、Rails ActionMailerフレームワークにおけるBaseMailer#explicit_multipartメソッドによる明示的マルチパートメール通知の設計を記述する。

### 本通知の処理概要

BaseMailer#explicit_multipartは、ブロック構文を使用して明示的にマルチパートメール（テキストとHTMLの両形式を含む）を生成するテスト用メーラーメソッドである。mailメソッドにブロックを渡し、format.textとformat.htmlを指定することで、各パートの内容を明示的に定義できる。

**業務上の目的・背景**：テンプレートファイルを使用せず、コード内で直接パート内容を生成したい場合や、異なるテンプレートを各パートに使用したい場合に使用される。暗黙的マルチパートより柔軟な制御が可能。本実装はテスト用であり、ブロック構文によるマルチパート生成機能の動作確認を主目的とする。

**通知の送信タイミング**：メーラーメソッドが呼び出された時点で送信される。

**通知の受信者**：デフォルトでは`system@test.lindsaar.net`宛に送信される（BaseMailerのクラスデフォルト設定）。

**通知内容の概要**：ブロック内でrender plainを使用し、テキストパート「TEXT Explicit Multipart」とHTMLパート「HTML Explicit Multipart」を生成する。Content-Typeはmultipart/alternativeとなる。

**期待されるアクション**：受信者のメールクライアントは、サポートする形式に応じてHTMLまたはテキストパートを表示する。テスト用途では、ブロック構文によるマルチパート生成が正しく動作することを確認する。

## 通知種別

メール

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（deliver_now）/ 非同期（deliver_later） |
| 優先度 | 中（デフォルト） |
| リトライ | 配信メソッドに依存 |

### 送信先決定ロジック

BaseMailerのクラスデフォルト設定として`system@test.lindsaar.net`が指定されている。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | jose@test.plataformatec.com |
| 送信元名称 | （未設定） |
| 件名 | Explicit multipart（I18nデフォルト） |
| 形式 | multipart/alternative |

### 本文テンプレート

#### テキストパート（ブロック内で生成）

```
TEXT Explicit Multipart
```

#### HTMLパート（ブロック内で生成）

```
HTML Explicit Multipart
```

テンプレートファイル: なし（コード内でrender plainを使用）

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| invoice.pdf | application/pdf | attachments: trueオプション指定時 | オプショナルな添付ファイル |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| （なし） | ブロック内で直接文字列を指定 | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | `BaseMailer.explicit_multipart` メソッド実行 | なし（常に送信） | メーラーメソッドの直接呼び出し |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| perform_deliveries = false | ActionMailerの設定で配信を無効化している場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[BaseMailer.explicit_multipart 呼び出し] --> B[MessageDelivery オブジェクト生成]
    B --> C[deliver_now/deliver_later 呼び出し]
    C --> D[メーラーインスタンス生成]
    D --> E[explicit_multipart メソッド実行]
    E --> F{attachments オプション?}
    F -->|Yes| G[添付ファイル設定]
    F -->|No| H[mail ブロック付き呼び出し]
    G --> H
    H --> I[ActionMailer::Collector 生成]
    I --> J[format.text ブロック実行]
    J --> K[format.html ブロック実行]
    K --> L[各パートのレスポンス収集]
    L --> M[multipart/alternative 構造生成]
    M --> N{添付ファイルあり?}
    N -->|Yes| O[multipart/mixed でラップ]
    N -->|No| P[Mail::Message 生成]
    O --> P
    P --> Q[配信処理]
    Q --> R[終了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

本メソッドはデータベースを参照しない（テスト用メーラー）。

### 更新テーブル一覧

本メソッドはデータベースを更新しない。

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| format指定なし | format.anyを引数なしで呼び出した場合 | ArgumentError例外（"You have to supply at least one format"） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | ActiveJob設定に依存（deliver_later使用時） |
| リトライ間隔 | ActiveJob設定に依存 |
| リトライ対象エラー | StandardError |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 設定なし |
| 1日あたり上限 | 設定なし |

### 配信時間帯

制限なし

## セキュリティ考慮事項

- render plainを使用しているため、HTMLエスケープは行われない
- HTMLパートに動的コンテンツを含める場合はXSS対策が必要
- テスト用メーラーのため、本番環境での使用は非推奨

## 備考

- 明示的マルチパートはブロック構文で各パートを定義
- format.text/format.htmlのブロック内でrender plain/render htmlを使用
- 暗黙的マルチパートとの違い: テンプレートファイルを使用せず、コード内で直接内容を定義
- format(Hash)でcontent_transfer_encodingなどのオプションを指定可能
- format.anyで複数フォーマットに同一内容を設定可能

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

明示的マルチパートで使用されるCollectorの仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | collector.rb | `actionmailer/lib/action_mailer/collector.rb` | 全体: Collectorクラスの実装 |

**読解のコツ**: Collectorはmethod_missingを使用してformat.text/format.htmlなどの呼び出しを動的に処理する。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | base_mailer.rb | `actionmailer/test/mailers/base_mailer.rb` | L89-95: explicit_multipartメソッドの実装 |

**主要処理フロー**:
1. **L90**: `attachments["invoice.pdf"] = "..." if hash.delete(:attachments)` - 条件付き添付ファイル設定
2. **L91-94**: `mail(hash) do |format| ... end` - ブロック付きmailメソッド呼び出し
3. **L92**: `format.text { render plain: "TEXT Explicit Multipart" }` - テキストパート定義
4. **L93**: `format.html { render plain: "HTML Explicit Multipart" }` - HTMLパート定義

#### Step 3: ブロック処理ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | base.rb | `actionmailer/lib/action_mailer/base.rb` | L968-976: collect_responsesメソッド |
| 3-2 | base.rb | `actionmailer/lib/action_mailer/base.rb` | L978-983: collect_responses_from_blockメソッド |

**主要処理フロー**:
- **L969-970**: `if block_given?` then `collect_responses_from_block`
- **L980**: `ActionMailer::Collector.new(lookup_context) { render(templates_name) }` - Collector生成
- **L981**: `yield(collector)` - ブロック（format.text/format.html）を実行
- **L982**: `collector.responses` - 収集されたレスポンスを返す

#### Step 4: テストケースで動作を確認する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | base_test.rb | `actionmailer/test/base_test.rb` | L465-473: 明示的マルチパートのテスト |
| 4-2 | base_test.rb | `actionmailer/test/base_test.rb` | L475-478: バウンダリ存在確認テスト |
| 4-3 | base_test.rb | `actionmailer/test/base_test.rb` | L480-488: 添付ファイル付きテスト |

**主要処理フロー**:
- **L466-472**: パート数、Content-Type、各パートの内容検証
- **L477**: `assert_not_nil(mail.content_type_parameters[:boundary])` - バウンダリパラメータの検証
- **L482-487**: 添付ファイルがある場合のネスト構造検証

### プログラム呼び出し階層図

```
BaseMailer.explicit_multipart(hash)
    │
    ├─ ActionMailer::Base.method_missing
    │      └─ MessageDelivery.new(self, :explicit_multipart, hash)
    │
    └─ MessageDelivery#deliver_now
           │
           ├─ processed_mailer
           │      └─ BaseMailer.new.process(:explicit_multipart, hash)
           │             │
           │             └─ BaseMailer#explicit_multipart(hash)
           │                    │
           │                    ├─ (条件付き) attachments["invoice.pdf"] = "..."
           │                    │
           │                    └─ mail(hash) do |format|
           │                           │
           │                           ├─ collect_responses_from_block
           │                           │      │
           │                           │      ├─ ActionMailer::Collector.new
           │                           │      │
           │                           │      └─ yield(collector)
           │                           │             │
           │                           │             ├─ format.text { render plain: "..." }
           │                           │             │      └─ Collector#method_missing(:text)
           │                           │             │
           │                           │             └─ format.html { render plain: "..." }
           │                           │                    └─ Collector#method_missing(:html)
           │                           │
           │                           └─ create_parts_from_responses
           │                                  └─ multipart/alternative
           │
           └─ message.deliver
```

### データフロー図

```
[入力]                           [処理]                              [出力]

ブロック引数                 ───▶ mail(hash) do |format| ... end
  format オブジェクト              │
                                   ▼
                            ───▶ collect_responses_from_block
                                   │
                                   ├─ Collector.new
                                   │
                                   └─ yield(collector)
                                          │
format.text ブロック         ───▶        ├─ format.text { render plain: "TEXT..." }
                                          │      └─ {body: "TEXT...", content_type: "text/plain"}
                                          │
format.html ブロック         ───▶        └─ format.html { render plain: "HTML..." }
                                                 └─ {body: "HTML...", content_type: "text/html"}
                                                        │
                                                        ▼
                            ───▶ collector.responses    ───▶ レスポンス配列
                                   │
                                   ▼
                            ───▶ create_parts_from_responses ───▶ Mail::Message
                                                                   multipart/alternative
                                                                   ├─ text/plain
                                                                   └─ text/html
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| base_mailer.rb | `actionmailer/test/mailers/base_mailer.rb` | ソース | テスト用メーラークラス定義 |
| base.rb | `actionmailer/lib/action_mailer/base.rb` | ソース | 明示的マルチパート処理ロジック |
| collector.rb | `actionmailer/lib/action_mailer/collector.rb` | ソース | フォーマットコレクター |
| base_test.rb | `actionmailer/test/base_test.rb` | テスト | 明示的マルチパート機能テスト |
