jp_address_complement 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.agent/rules/specify-rules.md +29 -0
- data/.agent/workflows/speckit.analyze.md +184 -0
- data/.agent/workflows/speckit.checklist.md +294 -0
- data/.agent/workflows/speckit.clarify.md +181 -0
- data/.agent/workflows/speckit.constitution.md +84 -0
- data/.agent/workflows/speckit.implement.md +135 -0
- data/.agent/workflows/speckit.plan.md +90 -0
- data/.agent/workflows/speckit.specify.md +258 -0
- data/.agent/workflows/speckit.tasks.md +137 -0
- data/.agent/workflows/speckit.taskstoissues.md +30 -0
- data/.claude/commands/speckit.analyze.md +184 -0
- data/.claude/commands/speckit.checklist.md +294 -0
- data/.claude/commands/speckit.clarify.md +181 -0
- data/.claude/commands/speckit.constitution.md +84 -0
- data/.claude/commands/speckit.implement.md +135 -0
- data/.claude/commands/speckit.plan.md +90 -0
- data/.claude/commands/speckit.specify.md +258 -0
- data/.claude/commands/speckit.tasks.md +137 -0
- data/.claude/commands/speckit.taskstoissues.md +30 -0
- data/.cursor/commands/speckit.analyze.md +184 -0
- data/.cursor/commands/speckit.checklist.md +294 -0
- data/.cursor/commands/speckit.clarify.md +181 -0
- data/.cursor/commands/speckit.constitution.md +84 -0
- data/.cursor/commands/speckit.implement.md +135 -0
- data/.cursor/commands/speckit.plan.md +90 -0
- data/.cursor/commands/speckit.specify.md +258 -0
- data/.cursor/commands/speckit.tasks.md +137 -0
- data/.cursor/commands/speckit.taskstoissues.md +30 -0
- data/.cursor/rules/specify-rules.mdc +32 -0
- data/.rubocop.yml +118 -0
- data/.specify/memory/constitution.md +130 -0
- data/.specify/scripts/bash/check-prerequisites.sh +166 -0
- data/.specify/scripts/bash/common.sh +156 -0
- data/.specify/scripts/bash/create-new-feature.sh +297 -0
- data/.specify/scripts/bash/setup-plan.sh +61 -0
- data/.specify/scripts/bash/update-agent-context.sh +810 -0
- data/.specify/templates/agent-file-template.md +28 -0
- data/.specify/templates/checklist-template.md +40 -0
- data/.specify/templates/constitution-template.md +50 -0
- data/.specify/templates/plan-template.md +104 -0
- data/.specify/templates/spec-template.md +115 -0
- data/.specify/templates/tasks-template.md +251 -0
- data/CHANGELOG.md +26 -0
- data/LICENSE +9 -0
- data/README.md +274 -0
- data/Rakefile +19 -0
- data/Steepfile +9 -0
- data/examples/rails/jp_address_complement_demo/.gitignore +15 -0
- data/examples/rails/jp_address_complement_demo/.ruby-version +1 -0
- data/examples/rails/jp_address_complement_demo/Gemfile +22 -0
- data/examples/rails/jp_address_complement_demo/Gemfile.lock +252 -0
- data/examples/rails/jp_address_complement_demo/README.md +57 -0
- data/examples/rails/jp_address_complement_demo/Rakefile +6 -0
- data/examples/rails/jp_address_complement_demo/app/assets/images/.keep +0 -0
- data/examples/rails/jp_address_complement_demo/app/assets/stylesheets/application.css +1 -0
- data/examples/rails/jp_address_complement_demo/app/controllers/addresses_controller.rb +59 -0
- data/examples/rails/jp_address_complement_demo/app/controllers/application_controller.rb +4 -0
- data/examples/rails/jp_address_complement_demo/app/controllers/concerns/.keep +0 -0
- data/examples/rails/jp_address_complement_demo/app/helpers/application_helper.rb +2 -0
- data/examples/rails/jp_address_complement_demo/app/models/application_record.rb +3 -0
- data/examples/rails/jp_address_complement_demo/app/models/concerns/.keep +0 -0
- data/examples/rails/jp_address_complement_demo/app/views/addresses/index.html.erb +22 -0
- data/examples/rails/jp_address_complement_demo/app/views/addresses/prefecture.html.erb +20 -0
- data/examples/rails/jp_address_complement_demo/app/views/addresses/prefix.html.erb +25 -0
- data/examples/rails/jp_address_complement_demo/app/views/addresses/reverse.html.erb +30 -0
- data/examples/rails/jp_address_complement_demo/app/views/addresses/validate.html.erb +23 -0
- data/examples/rails/jp_address_complement_demo/app/views/layouts/application.html.erb +40 -0
- data/examples/rails/jp_address_complement_demo/app/views/pwa/manifest.json.erb +22 -0
- data/examples/rails/jp_address_complement_demo/app/views/pwa/service-worker.js +26 -0
- data/examples/rails/jp_address_complement_demo/bin/ci +6 -0
- data/examples/rails/jp_address_complement_demo/bin/dev +2 -0
- data/examples/rails/jp_address_complement_demo/bin/rails +4 -0
- data/examples/rails/jp_address_complement_demo/bin/rake +4 -0
- data/examples/rails/jp_address_complement_demo/bin/setup +35 -0
- data/examples/rails/jp_address_complement_demo/config/application.rb +42 -0
- data/examples/rails/jp_address_complement_demo/config/boot.rb +3 -0
- data/examples/rails/jp_address_complement_demo/config/ci.rb +15 -0
- data/examples/rails/jp_address_complement_demo/config/credentials.yml.enc +1 -0
- data/examples/rails/jp_address_complement_demo/config/database.yml +31 -0
- data/examples/rails/jp_address_complement_demo/config/environment.rb +5 -0
- data/examples/rails/jp_address_complement_demo/config/environments/development.rb +54 -0
- data/examples/rails/jp_address_complement_demo/config/environments/production.rb +67 -0
- data/examples/rails/jp_address_complement_demo/config/environments/test.rb +42 -0
- data/examples/rails/jp_address_complement_demo/config/initializers/content_security_policy.rb +29 -0
- data/examples/rails/jp_address_complement_demo/config/initializers/filter_parameter_logging.rb +8 -0
- data/examples/rails/jp_address_complement_demo/config/initializers/inflections.rb +16 -0
- data/examples/rails/jp_address_complement_demo/config/locales/en.yml +31 -0
- data/examples/rails/jp_address_complement_demo/config/puma.rb +39 -0
- data/examples/rails/jp_address_complement_demo/config/routes.rb +19 -0
- data/examples/rails/jp_address_complement_demo/config.ru +6 -0
- data/examples/rails/jp_address_complement_demo/db/migrate/20260228083709_create_jp_address_complement_postal_codes.rb +44 -0
- data/examples/rails/jp_address_complement_demo/db/schema.rb +33 -0
- data/examples/rails/jp_address_complement_demo/db/seeds.rb +24 -0
- data/examples/rails/jp_address_complement_demo/lib/tasks/.keep +0 -0
- data/examples/rails/jp_address_complement_demo/log/.keep +0 -0
- data/examples/rails/jp_address_complement_demo/public/400.html +135 -0
- data/examples/rails/jp_address_complement_demo/public/404.html +135 -0
- data/examples/rails/jp_address_complement_demo/public/406-unsupported-browser.html +135 -0
- data/examples/rails/jp_address_complement_demo/public/422.html +135 -0
- data/examples/rails/jp_address_complement_demo/public/500.html +135 -0
- data/examples/rails/jp_address_complement_demo/public/icon.png +0 -0
- data/examples/rails/jp_address_complement_demo/public/icon.svg +3 -0
- data/examples/rails/jp_address_complement_demo/public/robots.txt +1 -0
- data/examples/rails/jp_address_complement_demo/script/.keep +0 -0
- data/examples/rails/jp_address_complement_demo/storage/.keep +0 -0
- data/examples/rails/jp_address_complement_demo/vendor/.keep +0 -0
- data/lib/generators/jp_address_complement/install_generator.rb +34 -0
- data/lib/generators/jp_address_complement/templates/create_jp_address_complement_postal_codes.rb.erb +45 -0
- data/lib/jp_address_complement/address_record.rb +36 -0
- data/lib/jp_address_complement/configuration.rb +21 -0
- data/lib/jp_address_complement/importers/csv_importer.rb +148 -0
- data/lib/jp_address_complement/ken_all_downloader.rb +122 -0
- data/lib/jp_address_complement/models/postal_code.rb +21 -0
- data/lib/jp_address_complement/normalizer.rb +77 -0
- data/lib/jp_address_complement/prefecture.rb +105 -0
- data/lib/jp_address_complement/railtie.rb +27 -0
- data/lib/jp_address_complement/repositories/active_record_postal_code_repository.rb +78 -0
- data/lib/jp_address_complement/repositories/csv_postal_code_repository.rb +200 -0
- data/lib/jp_address_complement/repositories/postal_code_repository.rb +36 -0
- data/lib/jp_address_complement/searcher.rb +85 -0
- data/lib/jp_address_complement/validators/address_validator.rb +41 -0
- data/lib/jp_address_complement/version.rb +6 -0
- data/lib/jp_address_complement.rb +129 -0
- data/lib/tasks/jp_address_complement.rake +32 -0
- data/rbs_collection.lock.yaml +380 -0
- data/rbs_collection.yaml +19 -0
- data/sig/generated/generators/jp_address_complement/install_generator.rbs +18 -0
- data/sig/generated/jp_address_complement/configuration.rbs +18 -0
- data/sig/generated/jp_address_complement/importers/csv_importer.rbs +78 -0
- data/sig/generated/jp_address_complement/ken_all_downloader.rbs +49 -0
- data/sig/generated/jp_address_complement/normalizer.rbs +37 -0
- data/sig/generated/jp_address_complement/prefecture.rbs +27 -0
- data/sig/generated/jp_address_complement/railtie.rbs +8 -0
- data/sig/generated/jp_address_complement/repositories/active_record_postal_code_repository.rbs +38 -0
- data/sig/generated/jp_address_complement/repositories/csv_postal_code_repository.rbs +100 -0
- data/sig/generated/jp_address_complement/repositories/postal_code_repository.rbs +29 -0
- data/sig/generated/jp_address_complement/searcher.rbs +43 -0
- data/sig/generated/jp_address_complement/validators/address_validator.rbs +24 -0
- data/sig/generated/jp_address_complement/version.rbs +5 -0
- data/sig/generated/jp_address_complement.rbs +84 -0
- data/sig/manual/address_record.rbs +40 -0
- data/sig/manual/gem_rubyzip.rbs +17 -0
- data/sig/manual/postal_code.rbs +9 -0
- data/sig/manual/stdlib_csv_invalid_encoding_error.rbs +5 -0
- data/sig/manual/stdlib_net_http.rbs +33 -0
- data/sig/manual/stdlib_openuri.rbs +9 -0
- data/sig/manual/stdlib_tmpdir.rbs +4 -0
- data/specs/001-jp-address-complement-gem/checklists/requirements.md +36 -0
- data/specs/001-jp-address-complement-gem/contracts/public-api.md +209 -0
- data/specs/001-jp-address-complement-gem/data-model.md +207 -0
- data/specs/001-jp-address-complement-gem/plan.md +124 -0
- data/specs/001-jp-address-complement-gem/quickstart.md +151 -0
- data/specs/001-jp-address-complement-gem/research.md +139 -0
- data/specs/001-jp-address-complement-gem/spec.md +153 -0
- data/specs/001-jp-address-complement-gem/tasks.md +279 -0
- data/specs/002-rbs-type-annotations/checklists/requirements.md +37 -0
- data/specs/002-rbs-type-annotations/contracts/rbs-public-api.md +116 -0
- data/specs/002-rbs-type-annotations/data-model.md +119 -0
- data/specs/002-rbs-type-annotations/plan.md +116 -0
- data/specs/002-rbs-type-annotations/quickstart.md +105 -0
- data/specs/002-rbs-type-annotations/research.md +173 -0
- data/specs/002-rbs-type-annotations/spec.md +125 -0
- data/specs/002-rbs-type-annotations/tasks.md +189 -0
- data/specs/003-csv-remove-obsolete/checklists/requirements.md +34 -0
- data/specs/003-csv-remove-obsolete/contracts/csv-import.md +41 -0
- data/specs/003-csv-remove-obsolete/data-model.md +47 -0
- data/specs/003-csv-remove-obsolete/plan.md +73 -0
- data/specs/003-csv-remove-obsolete/quickstart.md +40 -0
- data/specs/003-csv-remove-obsolete/research.md +71 -0
- data/specs/003-csv-remove-obsolete/spec.md +85 -0
- data/specs/003-csv-remove-obsolete/tasks.md +167 -0
- data/specs/004-prefecture-code-reverse-lookup/checklists/requirements.md +34 -0
- data/specs/004-prefecture-code-reverse-lookup/contracts/public-api-prefecture-and-reverse.md +122 -0
- data/specs/004-prefecture-code-reverse-lookup/data-model.md +81 -0
- data/specs/004-prefecture-code-reverse-lookup/plan.md +92 -0
- data/specs/004-prefecture-code-reverse-lookup/quickstart.md +91 -0
- data/specs/004-prefecture-code-reverse-lookup/research.md +62 -0
- data/specs/004-prefecture-code-reverse-lookup/spec.md +120 -0
- data/specs/004-prefecture-code-reverse-lookup/tasks.md +190 -0
- metadata +451 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Implementation Plan: RBS 型注釈の導入
|
|
2
|
+
|
|
3
|
+
**Branch**: `002-rbs-type-annotations` | **Date**: 2026-02-22 | **Spec**: [spec.md](./spec.md)
|
|
4
|
+
**Input**: Feature specification from `/specs/002-rbs-type-annotations/spec.md`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
gem の実装全体に RBS 型注釈を導入し、利用者(および IDE/LSP)が型情報を参照して安全に API を呼び出せるようにする。
|
|
11
|
+
**技術方針**: `rbs-inline` でソース内に `# @rbs` アノテーションを記述し、`rbs-inline --output sig/ lib/` で `.rbs` を自動生成。Steep で `default` レベル型チェックを実行。`Data.define` の `AddressRecord` は `sig/manual/address_record.rbs` に手書き定義。Rake タスクで `rake rbs:generate`・`rake steep` を提供し、CI で型チェックをゲートとする。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Technical Context
|
|
16
|
+
|
|
17
|
+
**Language/Version**: Ruby 3.2 以上(既存 gemspec 準拠)
|
|
18
|
+
**Primary Dependencies**: 既存(Rails 7.x, RSpec, SimpleCov, RuboCop)+ 開発依存: `rbs-inline`(~> 1.0), `steep`, `rbs-rails`, `gem_rbs_collection`
|
|
19
|
+
**Storage**: 変更なし(利用アプリケーションの DB)
|
|
20
|
+
**Testing**: RSpec(既存)、型チェックは Steep。カバレッジ 90% 以上維持。
|
|
21
|
+
**Target Platform**: Ruby gem(開発・CI で Steep 実行)
|
|
22
|
+
**Project Type**: Ruby gem(Rails 組み込み型ライブラリ)— 型注釈追加のみ
|
|
23
|
+
**Performance Goals**: 既存の郵便番号検索・インポート性能は変更しない
|
|
24
|
+
**Constraints**: Rubocop 100% PASS(disable 禁止維持)、TDD 維持、型エラーゼロ(`# steep:ignore` は理由明記で最小限)
|
|
25
|
+
**Scale/Scope**: `lib/` 配下全 `.rb` のメソッド(public/private/protected)に rbs-inline アノテーション、`sig/` と `sig/manual/` で型定義を管理
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Constitution Check
|
|
30
|
+
|
|
31
|
+
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
32
|
+
|
|
33
|
+
- [x] **I. Gem-First**: 型情報は `JpAddressComplement` 名前空間内の API を記述するのみ。外部名前空間を汚染しない。RBS/Steep は開発時ツールで gem 責務の範囲内(型付き API 提供)。
|
|
34
|
+
- [x] **II. TDD**: 既存 RSpec はすべてパス維持。型アノテーション追加は振る舞いを変えない。Rake タスク・Steepfile の追加はテスト可能な単位で TDD する。
|
|
35
|
+
- [x] **III. Rubocop**: `rubocop:disable` は使用しない。rbs-inline コメントと競合する場合は `.rubocop.yml` を最小限で調整し理由を明記。
|
|
36
|
+
- [x] **IV. データ整合性**: 本機能はデータ取り込み・永続化に変更を加えない。
|
|
37
|
+
- [x] **V. 機能要件**: 既存 3 機能(`search_by_postal_code` / `search_by_postal_code_prefix` / `valid_combination?`)および `configure`・`configuration` のシグネチャを正確に記述。新機能追加は行わない。
|
|
38
|
+
- [x] **VI. シンプルさ**: rbs-inline + Steep は Ruby 型周りの事実上の標準。手動 `sig/manual/` は `Data.define` の制約による最小限の例外。YAGNI で過剰な型抽象は導入しない。
|
|
39
|
+
|
|
40
|
+
**Constitution Check Result**: ✅ 全ゲート PASS
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Project Structure
|
|
45
|
+
|
|
46
|
+
### Documentation (this feature)
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
specs/002-rbs-type-annotations/
|
|
50
|
+
├── plan.md # このファイル (/speckit.plan コマンド出力)
|
|
51
|
+
├── spec.md # 機能仕様
|
|
52
|
+
├── research.md # Phase 0 出力 (/speckit.plan で生成)
|
|
53
|
+
├── data-model.md # Phase 1 出力 (/speckit.plan で生成)
|
|
54
|
+
├── quickstart.md # Phase 1 出力 (/speckit.plan で生成)
|
|
55
|
+
├── contracts/ # Phase 1 出力(必要に応じて型関連コントラクト)
|
|
56
|
+
└── tasks.md # Phase 2 出力 (/speckit.tasks で生成 — plan では作成しない)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Source Code (repository root)
|
|
60
|
+
|
|
61
|
+
既存の 001 で確立した構成を維持し、型用のディレクトリ・設定を追加する。
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
# 既存(001 と同様)
|
|
65
|
+
lib/
|
|
66
|
+
├── jp_address_complement.rb
|
|
67
|
+
├── jp_address_complement/
|
|
68
|
+
│ ├── version.rb
|
|
69
|
+
│ ├── configuration.rb
|
|
70
|
+
│ ├── railtie.rb
|
|
71
|
+
│ ├── address_record.rb # Data.define — 手動 RBS 対象
|
|
72
|
+
│ ├── normalizer.rb
|
|
73
|
+
│ ├── searcher.rb
|
|
74
|
+
│ ├── repositories/
|
|
75
|
+
│ │ ├── postal_code_repository.rb
|
|
76
|
+
│ │ └── active_record_postal_code_repository.rb
|
|
77
|
+
│ ├── models/
|
|
78
|
+
│ │ └── postal_code.rb
|
|
79
|
+
│ ├── validators/
|
|
80
|
+
│ │ └── address_validator.rb
|
|
81
|
+
│ ├── importers/
|
|
82
|
+
│ │ └── csv_importer.rb
|
|
83
|
+
│ └── generators/
|
|
84
|
+
│ └── jp_address_complement/ ...
|
|
85
|
+
lib/tasks/
|
|
86
|
+
└── jp_address_complement.rake # ここに steep / rbs:generate タスクを追加するか、別 rake で管理
|
|
87
|
+
|
|
88
|
+
# 本機能で追加
|
|
89
|
+
sig/ # rbs-inline の出力先(自動生成)
|
|
90
|
+
├── jp_address_complement.rbs # 等(rbs-inline が生成)
|
|
91
|
+
├── jp_address_complement/ # 名前空間別 .rbs
|
|
92
|
+
│ ├── address_record.rbs
|
|
93
|
+
│ ├── configuration.rbs
|
|
94
|
+
│ ├── normalizer.rbs
|
|
95
|
+
│ ├── searcher.rbs
|
|
96
|
+
│ ├── repositories/
|
|
97
|
+
│ │ ├── postal_code_repository.rbs
|
|
98
|
+
│ │ └── active_record_postal_code_repository.rbs
|
|
99
|
+
│ └── ...
|
|
100
|
+
└── manual/ # 手動定義(rbs-inline で上書きされない)
|
|
101
|
+
└── address_record.rbs # Data.define 全フィールド型
|
|
102
|
+
|
|
103
|
+
Steepfile # プロジェクトルート。target: lib + sig、level: default
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Structure Decision**: 既存 `lib/` 構成はそのまま。`sig/` を新設し、自動生成は `sig/` 直下および `sig/jp_address_complement/` 以下、手動補完は `sig/manual/` に分離。Rake タスクは既存 `lib/tasks/jp_address_complement.rake` に `steep` と `rbs:generate` を追加するか、プロジェクト方針に合わせて別ファイルでも可。
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Complexity Tracking
|
|
111
|
+
|
|
112
|
+
| 複雑化要素 | 正当化理由 | より単純な代替案が却下される理由 |
|
|
113
|
+
|-----------|-----------|-------------------------------|
|
|
114
|
+
| RBS + Steep + rbs-inline の導入 | 利用者への型情報提供(P1)と開発時の型維持(P1)を満たすため | 型なしのままでは SC-001〜SC-005 および CC を満たせない |
|
|
115
|
+
| sig/manual の手動 RBS | `Data.define` の AddressRecord は rbs-inline の自動生成が完全に機能しないため | 手書き以外に正確な型を提供する手段がない |
|
|
116
|
+
| 開発依存 gem の追加(steep, rbs-inline, rbs-rails, gem_rbs_collection) | FR-002/FR-003: 生成・型チェックと Rails/AR 型の参照に必須 | これらなしでは Steep のエラーゼロと IDE 連携を達成できない |
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# クイックスタート: RBS 型注釈(開発者向け)
|
|
2
|
+
|
|
3
|
+
**対象**: 本 gem のコントリビューター
|
|
4
|
+
**前提**: Ruby 3.2 以上、`bundle install` 済み
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## セットアップ(初回)
|
|
9
|
+
|
|
10
|
+
開発依存に `rbs-inline`・`steep`・`rbs-rails`・`gem_rbs_collection` が含まれていることを確認し、以下で型定義を生成・型チェックを実行できるようにする。
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
bundle install
|
|
14
|
+
bundle exec rake rbs:generate # sig/ を生成
|
|
15
|
+
bundle exec rake steep # 型チェック(エラーゼロが目標)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 日常の開発フロー
|
|
21
|
+
|
|
22
|
+
### 1. 実装を書いたら rbs-inline アノテーションを付ける
|
|
23
|
+
|
|
24
|
+
メソッドの**直上**に `# @rbs` でシグネチャを書く。
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
# @rbs def search_by_postal_code(code: String) -> Array[AddressRecord]
|
|
28
|
+
def search_by_postal_code(code)
|
|
29
|
+
# ...
|
|
30
|
+
end
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- 引数: `名前: 型`。複数ある場合は `(a: String, b: Integer)` のようにカンマ区切り。
|
|
34
|
+
- 返り値: `-> 型`。配列は `Array[AddressRecord]`、nil 許容は `String?`。
|
|
35
|
+
|
|
36
|
+
### 2. シグネチャを再生成する
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bundle exec rake rbs:generate
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`sig/` 直下および `sig/jp_address_complement/` 以下が更新される。`sig/manual/` は変更されない。
|
|
43
|
+
|
|
44
|
+
### 3. 型チェックを実行する
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bundle exec rake steep
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- エラーゼロで終了すれば OK。
|
|
51
|
+
- エラーが出た場合は、該当する `.rb` のアノテーションか、`sig/manual/` の手動定義を修正する。
|
|
52
|
+
|
|
53
|
+
### 4. 誤検知(false positive)のみ抑制したい場合
|
|
54
|
+
|
|
55
|
+
どうしても Steep の誤検知だけを抑えたい箇所では、**理由をコメントで明記**した上で `# steep:ignore` を使う。
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
# Steep が Rails 内部の型不足で誤検知するため抑制(理由)
|
|
59
|
+
# steep:ignore
|
|
60
|
+
some_rails_internal_call
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
乱用は避け、レビューで確認する。
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 手動 RBS(AddressRecord など)
|
|
68
|
+
|
|
69
|
+
`Data.define` で定義した `AddressRecord` は、rbs-inline が十分な型を出さないため `sig/manual/address_record.rbs` で手書きする。
|
|
70
|
+
|
|
71
|
+
- 編集するのは `sig/manual/` 内のみ。
|
|
72
|
+
- `rake rbs:generate` では `sig/manual/` は上書きされない。
|
|
73
|
+
- フィールド追加・変更時はここを忘れずに更新する。
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## CI での型チェック
|
|
78
|
+
|
|
79
|
+
PR では `bundle exec rake steep` が通ることがマージ条件となる(Phase 2 で CI に組み込み)。ローカルでマージ前に必ず実行すること。
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
bundle exec rake steep
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
終了コードが 0 でない場合は型エラーが残っているので修正する。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## トラブルシューティング
|
|
90
|
+
|
|
91
|
+
| 現象 | 対処 |
|
|
92
|
+
|------|------|
|
|
93
|
+
| `rbs-inline: command not found` | `bundle exec rbs-inline` を使う。Rake タスクは `bundle exec` 経由で実行すること。 |
|
|
94
|
+
| `sig/` が空のまま | `bundle exec rake rbs:generate` を実行。`lib/` に `# @rbs` が付いているか確認。 |
|
|
95
|
+
| Steep が Rails で大量にエラー | `rbs-rails` と `gem_rbs_collection` が入っているか確認。Steepfile の `library` や `ignore` で対象を絞る。 |
|
|
96
|
+
| AddressRecord の型が untyped になる | `sig/manual/address_record.rbs` を正しく定義し、Steep が `sig/` を参照しているか確認。 |
|
|
97
|
+
| RuboCop が rbs コメントで怒る | `.rubocop.yml` で該当ルールを調整し、理由をコメントで明記(`rubocop:disable` は使わない)。 |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 参照
|
|
102
|
+
|
|
103
|
+
- 型の全体像: [data-model.md](./data-model.md)
|
|
104
|
+
- 公開 API の RBS 契約: [contracts/rbs-public-api.md](./contracts/rbs-public-api.md)
|
|
105
|
+
- 調査結果: [research.md](./research.md)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Research: RBS 型注釈の導入 — Phase 0 調査結果
|
|
2
|
+
|
|
3
|
+
**Branch**: `002-rbs-type-annotations`
|
|
4
|
+
**Date**: 2026-02-22
|
|
5
|
+
**Status**: Complete
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. rbs-inline による型アノテーション
|
|
10
|
+
|
|
11
|
+
### Decision
|
|
12
|
+
`lib/` 配下の全 `.rb` ファイルに `rbs-inline` 記法(`# @rbs` コメント)でメソッドの引数・返り値型を記述し、`rbs-inline --output sig/ lib/` で `sig/` に `.rbs` を自動生成する。public / private / protected を問わずすべてのメソッドにアノテーションを付与する。
|
|
13
|
+
|
|
14
|
+
### Rationale
|
|
15
|
+
- 型定義をソースコードと同一ファイルで管理することで、実装変更時の型の更新漏れを防ぎ、FR-002 の「持続的な型維持」を満たす。
|
|
16
|
+
- rbs-inline は Ruby 3.x で広く使われる型注釈方式であり、Steep と組み合わせる標準的な選択。
|
|
17
|
+
- 全メソッド対象にすることで、内部実装の型バグも早期検出でき、gem 規模ではコストが許容範囲内(Clarifications 2026-02-22 で合意済み)。
|
|
18
|
+
|
|
19
|
+
### 記法例(rbs-inline)
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# メソッドの直前に # @rbs でシグネチャを記述
|
|
23
|
+
# @rbs def search_by_postal_code(code: String) -> Array[AddressRecord]
|
|
24
|
+
def search_by_postal_code(code)
|
|
25
|
+
# ...
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- 引数: `name: Type`。返り値: `-> Type` または `-> Array[AddressRecord]`。
|
|
30
|
+
- クラス・モジュールのインスタンス変数や定数の型も必要に応じて `# @rbs` で宣言可能。
|
|
31
|
+
|
|
32
|
+
### Alternatives Considered
|
|
33
|
+
- **手書き .rbs のみ**: 実装と型の二重管理になり、更新漏れが発生しやすい。却下。
|
|
34
|
+
- **型注釈なしで Steep の型推論のみ**: 現状の Steep では Ruby の型推論だけでは不十分で、公開 API の明示的シグネチャが必要。却下。
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 2. Steep の設定(Steepfile・チェックレベル)
|
|
39
|
+
|
|
40
|
+
### Decision
|
|
41
|
+
プロジェクトルートに `Steepfile` を配置し、対象を `lib/` と `sig/` に限定。チェックレベルは `default` とする。Rails/ActiveRecord の型定義は `rbs-rails` と `gem_rbs_collection` から取得する。
|
|
42
|
+
|
|
43
|
+
### Rationale
|
|
44
|
+
- **default**: 標準的な整合性検査を行い、外部ライブラリの型定義が不完全でも過剰な false positive を避けられる(Clarifications 2026-02-22)。
|
|
45
|
+
- `strict` は外部 gem の型不足で多数のエラーが出やすく、本 gem の型品質を保ちつつ CI を安定させるには `default` が適切。
|
|
46
|
+
|
|
47
|
+
### Steepfile 構成案
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
# Steepfile
|
|
51
|
+
target :lib do
|
|
52
|
+
signature "sig"
|
|
53
|
+
check "lib"
|
|
54
|
+
# Rails / ActiveRecord は rbs-rails / gem_rbs_collection で解決
|
|
55
|
+
# 不足分のみ library や ignore で補完
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- `signature "sig"`: `sig/` 直下および `sig/manual/` を参照。
|
|
60
|
+
- 開発依存に `rbs-rails` と `gem_rbs_collection` を追加し、`rbs_collection.yaml` で Rails 等の型を解決。
|
|
61
|
+
|
|
62
|
+
### Alternatives Considered
|
|
63
|
+
- **strict**: 厳格だが Rails 内部の型不足で誤検知が増える。`default` を採用。
|
|
64
|
+
- **型チェックなし**: SC-002(型エラーゼロ)を満たせない。却下。
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 3. Data.define と AddressRecord の RBS
|
|
69
|
+
|
|
70
|
+
### Decision
|
|
71
|
+
`AddressRecord` は Ruby 3.2 で `Data.define` により定義されている。rbs-inline による自動生成が Data クラスで完全に機能しない場合に備え、`sig/manual/address_record.rbs` に全フィールドの型を手書きで定義する。`sig/manual/` は `rbs-inline` の出力対象外とする。
|
|
72
|
+
|
|
73
|
+
### Rationale
|
|
74
|
+
- RBS の `Data` 型表現は Steep/rbs-inline のバージョンによって差があり、自動生成だけでは不十分な場合がある(Edge Cases 記載)。
|
|
75
|
+
- 手動定義を `sig/manual/` に分離することで、`rbs-inline` の再実行で上書きされず、メンテナンスが明確になる。
|
|
76
|
+
|
|
77
|
+
### 手動 RBS の内容(AddressRecord)
|
|
78
|
+
|
|
79
|
+
全フィールド: `postal_code`, `pref_code`, `pref`, `city`, `town`, `kana_pref`, `kana_city`, `kana_town`, `has_alias`, `is_partial`, `is_large_office` を、それぞれ `String` または `String?`、`bool` で定義する。RBS の `Data` クラス定義または `interface` で表現する。
|
|
80
|
+
|
|
81
|
+
### Alternatives Considered
|
|
82
|
+
- **AddressRecord を untyped のまま**: 公開 API の返り値型が不明になり SC-003 に違反。却下。
|
|
83
|
+
- **# steep:ignore で一時回避**: 恒久対応として不適切。手書き RBS で正しく定義する。
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 4. 開発依存 gem とバージョン
|
|
88
|
+
|
|
89
|
+
### Decision
|
|
90
|
+
gemspec の `development_dependency` に以下を追加する。
|
|
91
|
+
|
|
92
|
+
| gem | 用途 | バージョン方針 |
|
|
93
|
+
|-----|------|----------------|
|
|
94
|
+
| `rbs-inline` | ソースから .rbs 生成 | `~> 1.0`(安定版に合わせて調整) |
|
|
95
|
+
| `steep` | 型チェック | 特定マイナー範囲で固定 |
|
|
96
|
+
| `rbs-rails` | Rails/AR 型定義 | 利用可能な安定版 |
|
|
97
|
+
| `gem_rbs_collection` | 他 gem の型定義取得 | 利用可能な安定版 |
|
|
98
|
+
|
|
99
|
+
- 実際のバージョンは導入時に `bundle add --group development` 等で確認し、gemspec に記載する。
|
|
100
|
+
- **rbs_collection の設定**: `rbs_collection.yaml`(または gem_rbs_collection の設定)は、Steep 実行時に Rails/ActiveRecord の型が解決できずエラーが出る場合にのみ追加する。rbs-rails のみで足りる場合は不要。
|
|
101
|
+
|
|
102
|
+
### Rationale
|
|
103
|
+
- FR-002 / FR-003 で必須。rbs-rails と gem_rbs_collection により、ActiveRecord や Rails 内部の型を参照し、Steep の誤検知を減らす(Clarifications 2026-02-22)。
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 5. Rake タスク
|
|
108
|
+
|
|
109
|
+
### Decision
|
|
110
|
+
- `rake rbs:generate`: `rbs-inline --output sig/ lib/` を実行し、`sig/` を更新する。
|
|
111
|
+
- `rake steep`: `steep check` を実行し、型チェック結果の終了コードをそのまま返す(エラー時は非ゼロ)。
|
|
112
|
+
|
|
113
|
+
既存の `lib/tasks/jp_address_complement.rake` に追加するか、プロジェクト方針に合わせて別 Rake ファイルでも可。
|
|
114
|
+
|
|
115
|
+
### Rationale
|
|
116
|
+
- FR-005 / FR-006 を満たす。開発者が `bundle exec rake rbs:generate` と `bundle exec rake steep` で統一して実行できるようにする。
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 6. RuboCop との共存
|
|
121
|
+
|
|
122
|
+
### Decision
|
|
123
|
+
rbs-inline の `# @rbs` コメントが既存 RuboCop ルール(行長・コメントスタイル等)と競合する場合は、`.rubocop.yml` を最小限の変更で調整し、理由をコメントで明記する。`# rubocop:disable` は使用しない(Constitution III)。
|
|
124
|
+
|
|
125
|
+
### Rationale
|
|
126
|
+
- FR-010 および Constitution III 準拠。rbs-inline は行が長くなりがちなため、該当ルールの例外を設定で許可する場合がある。
|
|
127
|
+
|
|
128
|
+
### Alternatives Considered
|
|
129
|
+
- **rubocop:disable**: 禁止のため使用しない。
|
|
130
|
+
- **アノテーションを短く分割**: rbs-inline の記法上、分割が難しい場合がある。設定調整を優先する。
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 7. 型エラー抑制(# steep:ignore)
|
|
135
|
+
|
|
136
|
+
### Decision
|
|
137
|
+
Steep が false positive を報告する箇所に**限定**して `# steep:ignore` を使用する。使用時は理由を直後のコメントで明記し、コードレビューで乱用を防ぐ。
|
|
138
|
+
|
|
139
|
+
### Rationale
|
|
140
|
+
- FR-004: 型エラーゼロを達成しつつ、Rails 内部等の型不足による誤検知のみを抑制する。
|
|
141
|
+
- 理由の明記により、将来の Steep や rbs の改善で削除できるか判断しやすくする。
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 8. sig/ ディレクトリ構成
|
|
146
|
+
|
|
147
|
+
### Decision
|
|
148
|
+
- `sig/` 直下および `sig/jp_address_complement/` 以下: rbs-inline が生成する `.rbs` を配置。
|
|
149
|
+
- `sig/manual/`: 手動定義(`address_record.rbs` 等)を配置。rbs-inline はこのディレクトリを上書きしない。
|
|
150
|
+
- 既存の `sig/jp_address_complement.rbs` はコメントのみの最小ファイルのため、本機能で完全に置き換える(Assumptions 記載)。
|
|
151
|
+
|
|
152
|
+
### Rationale
|
|
153
|
+
- 自動生成と手動補完を分離することで、再生成時の意図しない上書きを防ぎ、FR-007 の AddressRecord 手書き定義を安全に維持する。
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 9. Repository インターフェースの RBS
|
|
158
|
+
|
|
159
|
+
### Decision
|
|
160
|
+
`Repositories::PostalCodeRepository` は抽象インターフェースとして RBS で定義する。実装では `find_by_code(code: String) -> Array[AddressRecord]` および `find_by_prefix(prefix: String) -> Array[AddressRecord]` のシグネチャを持つ(現行実装のメソッド名に合わせる)。
|
|
161
|
+
|
|
162
|
+
### Rationale
|
|
163
|
+
- FR-009 の「インターフェースとしての型定義」を満たす。実装クラス(`ActiveRecordPostalCodeRepository`)はこのインターフェースに従う形で rbs-inline によりシグネチャを生成する。
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 10. 未解決・フォロー事項
|
|
168
|
+
|
|
169
|
+
| 項目 | 対応 |
|
|
170
|
+
|------|------|
|
|
171
|
+
| Ruby 3.0/3.1 の Struct フォールバック | 本機能は Ruby 3.2+ を前提とする型定義を記載。3.0/3.1 用の別 RBS はスコープ外とする。 |
|
|
172
|
+
| CI での steep check | Phase 2(tasks)で CI 設定に `bundle exec rake steep` をゲートとして追加する。 |
|
|
173
|
+
| rbs-inline のバージョン | 導入時に 1.x 系の最新安定版を確認し、gemspec に固定する。 |
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Feature Specification: RBS 型注釈の導入
|
|
2
|
+
|
|
3
|
+
**Feature Branch**: `002-rbs-type-annotations`
|
|
4
|
+
**Created**: 2026-02-22
|
|
5
|
+
**Status**: Draft
|
|
6
|
+
**Input**: User description: "RBSを導入し、gemの実装全体に型を記述します。rbs-inlineを活用してコメントから自動生成するようにし、Steepで型チェックをするようにします。"
|
|
7
|
+
|
|
8
|
+
## User Scenarios & Testing *(mandatory)*
|
|
9
|
+
|
|
10
|
+
### User Story 1 - Gem 利用者が型情報を参照して安全に呼び出せる (Priority: P1)
|
|
11
|
+
|
|
12
|
+
Gem を利用する Rails アプリ開発者(またはエディタ・LSP)が、`JpAddressComplement` モジュールおよびその公開メソッドの引数・返り値の型をシグネチャファイルから確認できる。
|
|
13
|
+
これにより IDE の補完・型エラー検出が機能し、誤った型の引数を渡すミスを事前に検知できる。
|
|
14
|
+
|
|
15
|
+
**Why this priority**: 型情報はまず「使う側」に価値をもたらす。公開 API のシグネチャが正確に記述されていることがすべての型安全性の起点となる。
|
|
16
|
+
|
|
17
|
+
**Independent Test**: Gem の `sig/` ディレクトリに生成された `.rbs` ファイルを Steep でチェックし、エラーゼロが確認できることで独立してテスト可能。
|
|
18
|
+
|
|
19
|
+
**Acceptance Scenarios**:
|
|
20
|
+
|
|
21
|
+
1. **Given** gem をインストールした開発者が Steep を設定した時、**When** `steep check` を実行すると、**Then** gem 公開 API(`search_by_postal_code`・`search_by_postal_code_prefix`・`valid_combination?`・`configure`・`configuration`)に関するシグネチャエラーがゼロで終了する。
|
|
22
|
+
2. **Given** gem の `sig/` ディレクトリが存在する時、**When** 利用者が参照すると、**Then** `AddressRecord` の全フィールド(`postal_code`・`pref_code`・`pref`・`city`・`town` 等)の型が明示されている。
|
|
23
|
+
3. **Given** 誤った型の引数(例:`Integer` の郵便番号)を渡すコードを書いた時、**When** Steep でチェックすると、**Then** 型エラーが報告される。
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
### User Story 2 - Gem 開発者が rbs-inline コメントで型を維持できる (Priority: P1)
|
|
28
|
+
|
|
29
|
+
Gem の開発者(コントリビューター)が、ソースコード(`.rb` ファイル)内に `rbs-inline` 記法のアノテーションコメントを追記することで型を宣言し、`rbs-inline` コマンドで `.rbs` ファイルを自動生成できる。
|
|
30
|
+
実装コードと型シグネチャを1つのファイルで同期管理できるため、実装変更時に型の更新漏れが起きにくい。
|
|
31
|
+
|
|
32
|
+
**Why this priority**: 型ファイルの生成・維持コストが最小化されないと開発サイクルで型管理が形骸化する。P1 の型シグネチャ提供を持続的に維持するための基盤として同等の優先度とする。
|
|
33
|
+
|
|
34
|
+
**Independent Test**: `lib/` 配下すべての `.rb` ファイルに `rbs-inline` アノテーションを追加した後、`rbs-inline --output sig/ lib/` を実行し、`sig/` に正しい `.rbs` ファイルが生成されることを確認することで独立してテスト可能。
|
|
35
|
+
|
|
36
|
+
**Acceptance Scenarios**:
|
|
37
|
+
|
|
38
|
+
1. **Given** `lib/` 配下のすべての `.rb` ファイルに `rbs-inline` 記法のアノテーションが付与されている時、**When** `rbs-inline --output sig/ lib/` を実行すると、**Then** `sig/` 配下に対応する `.rbs` ファイルが生成(または更新)される。
|
|
39
|
+
2. **Given** 既存の実装メソッドにアノテーションが付与されている時、**When** メソッドのシグネチャを変更してもアノテーションを更新しないまま `steep check` を実行すると、**Then** 型の不整合がエラーとして検出される。
|
|
40
|
+
3. **Given** アノテーションなしのメソッドが残存する時、**When** `steep check` を実行すると、**Then** 未注釈メソッドに関する警告が出力され、開発者が気づける。
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### User Story 3 - CI で型チェックが自動実行される (Priority: P2)
|
|
45
|
+
|
|
46
|
+
Gem のコントリビューターがコードを変更してプルリクエストを作成した時、型チェック(`steep check`)が自動的に実行され、型エラーを含む変更がマージされることを防止できる。
|
|
47
|
+
|
|
48
|
+
**Why this priority**: 型チェックが CI に組み込まれていないと、型安全性の維持が属人化しやすい。P1・P2 の成果を継続的に保護するために重要だが、ローカル実行が先に確立されることが前提となるため P2 とする。
|
|
49
|
+
|
|
50
|
+
**Independent Test**: Rakefile または CI 設定ファイルに `steep check` タスクが追加されており、実行して正常に型チェックが完了することを確認することで独立してテスト可能。
|
|
51
|
+
|
|
52
|
+
**Acceptance Scenarios**:
|
|
53
|
+
|
|
54
|
+
1. **Given** `steep check` が Rake タスクとして登録されている時、**When** `bundle exec rake steep` を実行すると、**Then** Steep による型チェックが実行され、エラーゼロで完了する。
|
|
55
|
+
2. **Given** 型エラーを含む変更がある時、**When** `bundle exec rake steep` を実行すると、**Then** 型エラーが出力され、終了コードが非ゼロとなる。
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### Edge Cases
|
|
60
|
+
|
|
61
|
+
- `AddressRecord` は `Data.define` で定義されており `rbs-inline` による自動生成が完全に機能しない場合、`sig/manual/address_record.rbs` に全フィールドの型(`String`・`bool` 等)を手書きする。
|
|
62
|
+
- Steep の型チェックが ActiveRecord や Rails 内部の型まで追跡しようとして誤検知(false positive)が発生する場合は、`Steep::Interface::Builder` のスタブや `# steep:ignore` による局所的な抑制で対処する。
|
|
63
|
+
- `rbs-inline` が生成するシグネチャと、Gem のパブリック API として公開したいシグネチャが乖離する場合(例:内部型の露出)は、`sig/` 内でシグネチャを手動補正する運用を明確にする。
|
|
64
|
+
- Ruby バージョンが `3.2` 以上でのみ動作する `Data.define` の型表現について、Steep 未対応の場合はモンキーパッチ的なアプローチ(型定義のオーバーライド)が必要になることがある。その場合は**発生時のみ**対応し、通常は `sig/manual/address_record.rbs` の手書き定義で足りる想定とする。
|
|
65
|
+
|
|
66
|
+
## Requirements *(mandatory)*
|
|
67
|
+
|
|
68
|
+
### Functional Requirements
|
|
69
|
+
|
|
70
|
+
- **FR-001**: 本機能は、gem の `lib/` 配下のすべての `.rb` ファイルに `rbs-inline` 記法の型アノテーションコメントを付与し、型情報を明示しなければならない。対象ファイルは、公開 API・内部実装・バリデーター・ジェネレーター・リポジトリ・インポーターを含む。アノテーション付与の対象は `public`・`private`・`protected` を問わずすべてのメソッドとする。
|
|
71
|
+
- **FR-002**: `rbs-inline` gem を開発依存として gemspec に追加し、`rbs-inline --output sig/ lib/` コマンドで `sig/` 配下に `.rbs` ファイルを自動生成できなければならない。
|
|
72
|
+
- **FR-003**: `steep`・`rbs-rails`・`gem_rbs_collection` を開発依存として gemspec に追加し、`Steepfile` の設定により `lib/` と `sig/` を対象として `default` レベルで型チェックを実行できなければならない。`default` は標準的な整合性検査を行い、外部ライブラリの型定義が不完全でも過剰な false positive を発生させない。Rails/ActiveRecord の型定義は `rbs-rails` および `gem_rbs_collection` から取得し、不足分がある場合のみ Steep の対象外設定で補完する。
|
|
73
|
+
- **FR-004**: `steep check` が型エラーゼロで完了しなければならない。許容できない型の誤検知がある場合のみ `# steep:ignore` コメントで局所的に抑制し、その理由をコメントで明記しなければならない。
|
|
74
|
+
- **FR-005**: `rbs-inline` による `.rbs` 生成コマンドを Rake タスク(例: `rake rbs:generate`)として登録し、開発者が容易に再生成できなければならない。
|
|
75
|
+
- **FR-006**: `steep check` を Rake タスク(例: `rake steep`)として登録し、`bundle exec rake steep` で型チェックを実行できなければならない。
|
|
76
|
+
- **FR-007**: `AddressRecord`(`Data.define` 使用)・`Configuration`・`Normalizer`・`Searcher`・`Repositories::PostalCodeRepository`(インターフェース)・`Repositories::ActiveRecordPostalCodeRepository`・バリデーター・インポーター・ジェネレーターのすべてに型シグネチャが定義されなければならない。`AddressRecord` については `sig/manual/address_record.rbs` に全フィールド(`postal_code`・`pref_code`・`pref`・`city`・`town`・`kana_pref`・`kana_city`・`kana_town`・`has_alias`・`is_partial`・`is_large_office`)の型を手書きで定義する。
|
|
77
|
+
- **FR-008**: 公開メソッド(`JpAddressComplement.search_by_postal_code`・`search_by_postal_code_prefix`・`valid_combination?`・`configure`・`configuration`)の引数型と返り値型が `.rbs` ファイルに正確に記述されなければならない。
|
|
78
|
+
- **FR-009**: `Repositories::PostalCodeRepository` はインターフェース(抽象型)として定義し、`find_by_code(code: String): Array[AddressRecord]` および `find_by_prefix(prefix: String): Array[AddressRecord]` のシグネチャを持たなければならない(現行実装のメソッド名に合わせる)。
|
|
79
|
+
- **FR-010**: 既存の RuboCop ルールに準拠した状態(`bundle exec rubocop` エラーゼロ)を維持しなければならない。`rbs-inline` アノテーションコメントが既存ルールと競合する場合は、`.rubocop.yml` を最小限の変更で調整する。
|
|
80
|
+
- **FR-011**: 既存のすべての RSpec テスト(`bundle exec rspec`)が引き続き全件パスし、SimpleCov カバレッジが 90% 以上を維持しなければならない。型アノテーションの追加はテストの振る舞いを変更してはならない。
|
|
81
|
+
|
|
82
|
+
### Key Entities
|
|
83
|
+
|
|
84
|
+
- **型アノテーションコメント (rbs-inline annotation)**: `lib/` 配下の `.rb` ファイルに記述する `# @rbs` 記法のコメント。メソッドの引数型・返り値型・インスタンス変数の型などを宣言するために使用する。ソースコードと同一ファイルで管理することで実装との同期を容易にする。
|
|
85
|
+
- **RBS シグネチャファイル (`.rbs`)**: `sig/` ディレクトリ直下に配置される型定義ファイル。`rbs-inline` コマンドにより `.rb` ファイルのアノテーションから自動生成される。Steep がこのファイルを参照して型チェックを実施する。手動で追補が必要な型定義(`Data.define` フォールバック等)は `sig/manual/` サブディレクトリに分離して管理する。
|
|
86
|
+
- **Steepfile**: `steep check` の対象ディレクトリ・型定義の参照先・チェックレベル等を設定するファイル。プロジェクトルートに配置し、gem の型チェック範囲を制御する。チェックレベルは `default`(標準 strictness)を使用する。
|
|
87
|
+
- **型エラー抑制コメント (`# steep:ignore`)**: Steep が false positive を報告する箇所に限定して使用するインラインコメント。理由の明記を必須とし、乱用を避けるためコードレビューで管理する。
|
|
88
|
+
|
|
89
|
+
## Success Criteria *(mandatory)*
|
|
90
|
+
|
|
91
|
+
### Measurable Outcomes
|
|
92
|
+
|
|
93
|
+
- **SC-001**: `bundle exec rake rbs:generate` を実行すると、`sig/` 配下のすべての `.rbs` ファイルが最新の実装に基づいて生成・更新される。実行時にエラーが発生しない。
|
|
94
|
+
- **SC-002**: `bundle exec rake steep` を実行すると、型チェックがエラーゼロで完了する。
|
|
95
|
+
- **SC-003**: `sig/` 配下に生成される `.rbs` ファイルにおいて、gem が公開するすべての public メソッドに引数型と返り値型のシグネチャが定義されている。シグネチャが定義されていない public メソッドがゼロ件であること。
|
|
96
|
+
- **SC-004**: 型アノテーション追加後も `bundle exec rspec` が全件パスし、SimpleCov カバレッジが 90% 以上を維持する。
|
|
97
|
+
- **SC-005**: 型アノテーション追加後も `bundle exec rubocop` がエラーゼロ・`rubocop:disable` コメントなしで完了する。
|
|
98
|
+
|
|
99
|
+
### Constitution Compliance Criteria
|
|
100
|
+
|
|
101
|
+
> これらの基準は Constitution に基づく必須成功条件です。
|
|
102
|
+
|
|
103
|
+
- **CC-001**: `bundle exec rspec` が全テスト PASS し、SimpleCov カバレッジが **90% 以上**である。
|
|
104
|
+
- **CC-002**: `bundle exec rubocop` が PASS(警告・エラーゼロ、`rubocop:disable` コメントなし)。
|
|
105
|
+
- **CC-003**: すべての実装コードは先にテストを書き、Red-Green-Refactor サイクルを経ている(TDD)。ただし本機能はアノテーションの追加が主体であり、型チェックの設定・Rake タスクの実装については TDD サイクルを適用する。Rake タスク(rbs:generate・steep)の動作検証は、`bundle exec rake rbs:generate` および `bundle exec rake steep` の手動実行で代替可能とする。
|
|
106
|
+
- **CC-004**: gem は `JpAddressComplement` 名前空間に閉じており、コア検索ロジックは Repository インターフェースを介してデータアクセスし、ActiveRecord への直接依存を持たない。型導入によりこの設計が損なわれてはならない。
|
|
107
|
+
|
|
108
|
+
## Assumptions
|
|
109
|
+
|
|
110
|
+
- 対象 Ruby バージョンは `>= 3.2.0`(gemspec で既に指定済み)。`Data.define` を使用している `AddressRecord` については、`rbs-inline` による自動生成が完全機能しない場合に備えて `sig/manual/address_record.rbs` に全フィールドの型を手書きで定義する。
|
|
111
|
+
- `rbs-inline` の記法は gem リリース時点の安定バージョン(1.x 系、`~> 1.0`)を使用する。バージョンは gemspec の development_dependency で束縛する(実際の最新安定版に合わせて調整)。
|
|
112
|
+
- `steep` gem についても同様に development_dependency として追加し、特定のマイナーバージョン範囲に固定する。
|
|
113
|
+
- Rails 内部・ActiveRecord の型定義は `rbs-rails` および `gem_rbs_collection` を開発依存として追加することで取得する。これらで解決できない型定義が残る場合のみ Steep の対象外設定(`library` 除外や `ignore` ディレクティブ)を最小限に適用する。
|
|
114
|
+
- `rbs-inline` で自動生成したシグネチャは `sig/` 直下に出力する。手動で追加・修正が必要な型定義(`Data.define` のフォールバック型定義等)は `sig/manual/` サブディレクトリに分離して管理する。`sig/manual/` 配下のファイルは `rbs-inline` コマンドで上書きされない。
|
|
115
|
+
- 既存の `sig/jp_address_complement.rbs` はコメントのみを残した最小ファイルになっているため、本機能で完全に置き換える。
|
|
116
|
+
|
|
117
|
+
## Clarifications
|
|
118
|
+
|
|
119
|
+
### Session 2026-02-22
|
|
120
|
+
|
|
121
|
+
- Q: Steep の型チェック strictness レベルはどれを使用するか? → A: `default`(標準 strictness)。外部 gem の型定義が不完全な環境でも false positive を抑えつつ gem 自体のコードを型チェックできるため。
|
|
122
|
+
- Q: Rails/ActiveRecord の型定義はどのように取得するか? → A: `rbs-rails` + `gem_rbs_collection` を開発依存として追加し公式・準公式の型定義を最大限活用する。不足分のみ Steep の対象外設定で補完する。
|
|
123
|
+
- Q: `sig/` 配下のファイル管理構成(自動生成 vs 手動)はどうするか? → A: `sig/` 直下に自動生成ファイルを配置し、手動補完(`Data.define` フォールバック等)は `sig/manual/` に分離する。`sig/manual/` は `rbs-inline` 生成で上書きされない。
|
|
124
|
+
- Q: `rbs-inline` アノテーションを付与するメソッドのアクセス修飾子範囲は? → A: `public`・`private`・`protected` を問わずすべてのメソッドに付与する。gem の規模が小さくメンテナンスコストが許容範囲内であり、内部実装の型バグも早期検出できるため。
|
|
125
|
+
- Q: `Data.define` で定義された `AddressRecord` の型表現方法は? → A: `sig/manual/address_record.rbs` に全フィールドの型を手書きで定義する。`untyped` 扱いや `# steep:ignore` による一時振りはしない。
|