# 画面設計書 280-スニペット新規作成

## 概要

本ドキュメントは、GitLabの「スニペット新規作成」画面の設計書です。個人スニペットを新規に作成するための画面仕様を記載しています。

### 本画面の処理概要

**業務上の目的・背景**：開発者がコードの断片、設定ファイル、メモなどを保存し、必要に応じて共有できるようにするための機能です。プロジェクトに属さない個人スニペットを作成することで、再利用可能なコード片やドキュメントを管理できます。

**画面へのアクセス方法**：ダッシュボードのスニペット一覧画面から「新規スニペット」ボタンをクリック、またはグローバルナビゲーションの「+」メニューから「New snippet」を選択してアクセスします。

**主要な操作・処理内容**：
1. スニペットタイトルの入力
2. スニペット説明の入力（Markdown対応）
3. ファイル名の入力
4. ファイル内容の入力（コードエディタ）
5. 公開設定の選択（Private/Internal/Public）
6. 複数ファイルの追加（最大10ファイル）
7. スニペットの作成・保存

**画面遷移**：ダッシュボードスニペット一覧から遷移。作成完了後はスニペット詳細画面（279）へリダイレクトされます。

**権限による表示制御**：ログインユーザーのみがアクセス・作成可能です。未ログインの場合はログイン画面にリダイレクトされます。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 72 | 個人スニペット | 主機能 | 個人スニペット作成 |

## 画面種別

登録

## URL/ルーティング

```
GET /-/snippets/new
POST /-/snippets
```

## 入出力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|-----|------|------|
| title | 入力 | String | 必須 | スニペットタイトル（最大255文字） |
| description | 入力 | Text | - | スニペット説明（Markdown対応、最大1MB） |
| file_name | 入力 | String | - | ファイル名（最大255文字） |
| content | 入力 | Text | 必須 | ファイル内容 |
| visibility_level | 入力 | Integer | 必須 | 公開設定（0:private/10:internal/20:public） |

## 表示項目

| 項目名 | 説明 |
|--------|------|
| ページタイトル | New snippet |
| タイトル入力フィールド | スニペットタイトル |
| 説明入力フィールド | スニペット説明（Markdownプレビュー対応） |
| ファイル名入力フィールド | ファイル名 |
| コンテンツエディタ | ファイル内容入力（Monaco Editor） |
| 公開設定選択 | Private/Internal/Public |
| ファイル追加ボタン | 複数ファイル追加用 |
| 作成ボタン | スニペット作成実行 |

## イベント仕様

### 1-スニペット作成

**トリガー**: 「Create snippet」ボタン押下

**処理フロー**:
1. フォームデータのバリデーション実行
2. スパムチェック実行（公開スニペットの場合）
3. スニペットレコード作成
4. Gitリポジトリ作成・ファイル追加
5. 成功時: スニペット詳細画面にリダイレクト
6. 失敗時: エラーメッセージを表示し、フォームを再表示

### 2-ファイル追加

**トリガー**: 「Add another file」ボタン押下

**処理フロー**:
1. 新しいファイル入力フィールドを追加
2. 最大10ファイルまで追加可能
3. 制限到達時はボタンを非表示

### 3-Markdownプレビュー

**トリガー**: 説明フィールドでプレビュータブ選択

**処理フロー**:
1. `/preview_markdown`エンドポイントにMarkdownを送信
2. レンダリング結果を表示

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| スニペット作成 | snippets | INSERT | スニペットレコード作成 |
| スニペット作成 | snippet_repositories | INSERT | スニペットリポジトリ追跡 |
| スニペット作成 | snippet_statistics | INSERT | 統計レコード作成 |
| スパム検出時 | spam_logs | INSERT | スパムログ記録 |

### テーブル別更新項目詳細

#### snippets

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | title | フォーム入力値 | 必須 |
| INSERT | title_html | タイトルのMarkdownレンダリング結果 | キャッシュ |
| INSERT | description | フォーム入力値 | |
| INSERT | description_html | Markdownレンダリング結果 | キャッシュ |
| INSERT | file_name | フォーム入力値 | |
| INSERT | content | フォーム入力値 | 必須 |
| INSERT | content_html | Markdownレンダリング結果 | キャッシュ（Markdown形式の場合） |
| INSERT | author_id | 現在ユーザーID | |
| INSERT | visibility_level | フォーム選択値 | |
| INSERT | created_at | 現在日時 | |
| INSERT | updated_at | 現在日時 | |

#### snippet_repositories

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | snippet_id | スニペットID | |
| INSERT | shard_name | 割り当てられたストレージシャード | |
| INSERT | disk_path | リポジトリディスクパス | |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|---------|
| MSG001 | エラー | Title can't be blank | タイトル未入力時 |
| MSG002 | エラー | Content can't be blank | 内容未入力時 |
| MSG003 | エラー | is too long (maximum is 255 characters) | タイトルまたはファイル名が255文字超過時 |
| MSG004 | エラー | Bytesize is too long | 説明が1MB超過時 |
| MSG005 | エラー | Bytesize is too long | 内容がサイズ制限超過時（snippet_size_limit） |
| MSG006 | エラー | スパム検出メッセージ | スパム判定時 |

## 例外処理

| 例外 | 処理内容 |
|------|---------|
| 未ログイン | ログイン画面にリダイレクト |
| バリデーションエラー | エラーメッセージを表示し、フォームを再表示 |
| スパム検出 | reCAPTCHA表示またはエラーメッセージ |
| Gitリポジトリ作成失敗 | エラーメッセージを表示 |

## 備考

- 複数ファイルスニペットは最大10ファイル（`MAX_FILE_COUNT = 10`）
- スニペットサイズ制限は`Gitlab::CurrentSettings.snippet_size_limit`で設定
- デフォルトの公開設定は`Gitlab::CurrentSettings.default_snippet_visibility`
- スパムチェックは公開レベル変更時または公開スニペットでのspammable属性変更時に実行
- reCAPTCHAサポート（`supports_recaptcha?`がtrue）
- Vue.jsコンポーネントでフォームを実装（`#js-snippet-edit`）

---

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

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

### 推奨読解順序

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

スニペットモデルとバリデーションを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | snippet.rb | `app/models/snippet.rb` | スニペットモデル、バリデーション |

**読解のコツ**:
- 25行目で`MAX_FILE_COUNT = 10`を確認
- 27行目で`DESCRIPTION_LENGTH_MAX = 1.megabyte`を確認
- 59-66行目でバリデーションを確認（title必須、content必須、長さ制限）
- 289-295行目で`check_for_spam?`メソッドを確認

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

コントローラの処理フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | snippets_controller.rb | `app/controllers/snippets_controller.rb` | newアクション |

**主要処理フロー**:
1. **11行目**: before_actionで`authorize_create_snippet!`
2. **23-25行目**: `new`アクションで新規PersonalSnippetオブジェクト作成

#### Step 3: ビューを理解する

フォーム構成を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | new.html.haml | `app/views/snippets/new.html.haml` | 新規作成画面テンプレート |
| 3-2 | _form.html.haml | `app/views/shared/snippets/_form.html.haml` | フォームパーシャル |

**主要処理フロー**:
- **1行目（new）**: パンくずリスト設定
- **2行目**: ページタイトル設定
- **4行目**: ページヘッダーコンポーネント
- **5行目**: フォームパーシャルレンダリング

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

```
SnippetsController#new
    │
    ├─ authorize_create_snippet! (before_action)
    │      └─ can?(:create_snippet)
    │
    └─ @snippet = PersonalSnippet.new
           │
           └─ Snippet#initialize
                  └─ default_snippet_visibility設定

render new.html.haml
    │
    └─ shared/snippets/_form.html.haml
           │
           └─ #js-snippet-edit（Vue.jsコンポーネント）
                  ├─ project_path
                  ├─ snippet-gid
                  ├─ markdown-preview-path
                  ├─ visibility_levels
                  └─ selected_level
```

### データフロー図

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

フォーム入力データ ───▶ SnippetsController#create
(title, description,      │
 file_name, content,      ├─▶ Snippets::CreateService
 visibility_level)        │       │
                          │       ├─▶ バリデーション
                          │       │
                          │       ├─▶ check_for_spam?
                          │       │
                          │       ├─▶ create_repository
                          │       │
                          │       └─▶ snippet.save!
                          │
                          └─▶ redirect / render ───▶ 詳細画面 / エラー表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| snippet.rb | `app/models/snippet.rb` | モデル | スニペットデータモデル |
| personal_snippet.rb | `app/models/personal_snippet.rb` | モデル | 個人スニペットモデル |
| snippets_controller.rb | `app/controllers/snippets_controller.rb` | コントローラ | スニペット作成処理 |
| new.html.haml | `app/views/snippets/new.html.haml` | ビュー | 新規作成画面テンプレート |
| _form.html.haml | `app/views/shared/snippets/_form.html.haml` | ビュー | フォームパーシャル |
| create_service.rb | `app/services/snippets/create_service.rb` | サービス | スニペット作成サービス |
