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,279 @@
|
|
|
1
|
+
# Tasks: 日本住所補完 Gem (`jp_address_complement`)
|
|
2
|
+
|
|
3
|
+
**Input**: Design documents from `/specs/001-jp-address-complement-gem/`
|
|
4
|
+
**Prerequisites**: plan.md ✅ spec.md ✅ research.md ✅ data-model.md ✅ contracts/ ✅
|
|
5
|
+
|
|
6
|
+
**TDD 方針**: 各タスクの実装は必ず「テストを先に書き → 失敗確認(RED) → 実装(GREEN) → リファクタ」の順で行う。
|
|
7
|
+
**Rubocop**: 各コミット前に `bundle exec rubocop` が 100% PASS すること(`disable` コメント禁止)。
|
|
8
|
+
**Coverage**: SimpleCov で 90% 以上を常時維持すること。
|
|
9
|
+
|
|
10
|
+
## Format: `[ID] [P?] [Story] Description`
|
|
11
|
+
|
|
12
|
+
- **[P]**: 並列実行可能(異なるファイル、依存なし)
|
|
13
|
+
- **[Story]**: 対応するユーザーストーリー(US1〜US4)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Phase 1: Setup(プロジェクト初期化)
|
|
18
|
+
|
|
19
|
+
**Purpose**: Gem の骨格・テスト基盤・Rubocop 設定を整備する
|
|
20
|
+
|
|
21
|
+
- [X] T001 Gem の骨格を作成する(`bundle gem jp_address_complement` または手動): `jp_address_complement.gemspec`, `lib/jp_address_complement.rb`, `lib/jp_address_complement/version.rb`
|
|
22
|
+
- [X] T002 [P] `jp_address_complement.gemspec` に依存 gem を設定する(`activerecord`・`railties` を `add_dependency`、`rspec`・`simplecov`・`rubocop` を `add_development_dependency`)
|
|
23
|
+
- [X] T003 [P] RSpec を初期化する: `spec/spec_helper.rb`(SimpleCov 計測・データベースクリーナー設定を含む)
|
|
24
|
+
- [X] T004 [P] RuboCop の設定ファイルを作成する: `.rubocop.yml`(`rubocop-rails`・`rubocop-rspec` を有効化、プロジェクト固有の除外設定を明記)
|
|
25
|
+
- [X] T005 [P] SQLite ベースのテスト用 DB 接続ヘルパーを作成する: `spec/support/database_helper.rb`(ActiveRecord を SQLite `:memory:` 接続、テーブル作成マイグレーションをここで実行)
|
|
26
|
+
- [X] T006 [P] FakeRepository を作成する(TDD でコアロジックを DB なしでテストするためのメモリ実装): `spec/support/fake_postal_code_repository.rb`
|
|
27
|
+
|
|
28
|
+
**Checkpoint**: `bundle exec rspec` が空で通過し、`bundle exec rubocop` が PASS すること
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Phase 2: Foundational(全ストーリーが依存する基盤)
|
|
33
|
+
|
|
34
|
+
**Purpose**: すべてのユーザーストーリー実装前に完了必須の共通基盤
|
|
35
|
+
|
|
36
|
+
⚠️ **CRITICAL**: このフェーズが完了するまでいかなるユーザーストーリー実装も開始しない
|
|
37
|
+
|
|
38
|
+
- [X] T007 [P] `AddressRecord` の RSpec テストを実装する(TDD: 先に書いて RED を確認): `spec/address_record_spec.rb`
|
|
39
|
+
- [X] T008 `AddressRecord` 値オブジェクトを実装する(Ruby 3.2+ は `Data.define`、下位互換は `Struct`): `lib/jp_address_complement/address_record.rb`
|
|
40
|
+
- [X] T009 [P] `Normalizer` の RSpec テストを実装する(TDD: 先に書いて RED を確認): `spec/normalizer_spec.rb`
|
|
41
|
+
- [X] T010 `Normalizer` クラスを実装する(全角→半角変換・ハイフン除去・`〒` 除去・nil/不正入力で nil 返却): `lib/jp_address_complement/normalizer.rb`
|
|
42
|
+
- [X] T011 `PostalCodeRepository` 抽象基底クラスを実装する(`find_by_code` / `find_by_prefix` を `NotImplementedError` で定義): `lib/jp_address_complement/repositories/postal_code_repository.rb`
|
|
43
|
+
- [X] T012 [P] `Configuration` クラスを実装する(`repository` 属性の DI 設定): `lib/jp_address_complement/configuration.rb`
|
|
44
|
+
- [X] T013 [P] `JpAddressComplement` モジュールのトップレベル設定メソッドを実装する(`configure`, `configuration`, `reset_configuration!`): `lib/jp_address_complement.rb`
|
|
45
|
+
- [X] T014 [P] `PostalCode` ActiveRecord モデルを実装する(`table_name` 設定・基本バリデーション): `lib/jp_address_complement/models/postal_code.rb`
|
|
46
|
+
- [X] T015 [P] マイグレーションテンプレートを作成する(`postal_code`, `pref_code`, `pref`, `city`, `town`, `kana_*`, フラグ3つ、インデックス2つ): `lib/generators/jp_address_complement/templates/create_jp_address_complement_postal_codes.rb.erb`
|
|
47
|
+
|
|
48
|
+
**Checkpoint**: `bundle exec rubocop` PASS / `bundle exec rspec` PASS / SimpleCov >= 90%
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Phase 3: User Story 1 — 郵便番号から住所を取得する (Priority: P1) 🎯 MVP
|
|
53
|
+
|
|
54
|
+
**Goal**: `JpAddressComplement.search_by_postal_code("1000001")` で住所レコードが返る
|
|
55
|
+
|
|
56
|
+
**Independent Test**:
|
|
57
|
+
```bash
|
|
58
|
+
bundle exec rspec spec/searcher_spec.rb spec/repositories/ spec/jp_address_complement_spec.rb --tag us1
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Tests for User Story 1(TDD: 必ず先に書いて RED を確認)
|
|
62
|
+
|
|
63
|
+
- [X] T016 [P] [US1] `PostalCodeRepository` インターフェースの RSpec を実装する(メソッド未実装で `NotImplementedError` が発生することを確認): `spec/repositories/postal_code_repository_spec.rb`
|
|
64
|
+
- [X] T017 [P] [US1] `ActiveRecordPostalCodeRepository#find_by_code` の RSpec を実装する(`FakeRepository` ではなく SQLite メモリ DB を使用): `spec/repositories/active_record_postal_code_repository_spec.rb`
|
|
65
|
+
- [X] T018 [P] [US1] `Searcher#search_by_postal_code` の RSpec を実装する(`FakeRepository` 使用・正常系・空返却・不正入力): `spec/searcher_spec.rb`
|
|
66
|
+
- [X] T019 [P] [US1] トップレベル `JpAddressComplement.search_by_postal_code` の統合テストを実装する: `spec/jp_address_complement_spec.rb`
|
|
67
|
+
|
|
68
|
+
### Implementation for User Story 1
|
|
69
|
+
|
|
70
|
+
- [X] T020 [P] [US1] `ActiveRecordPostalCodeRepository#find_by_code` を実装する(`postal_code` 完全一致 WHERE クエリ → `AddressRecord` 配列変換): `lib/jp_address_complement/repositories/active_record_postal_code_repository.rb`
|
|
71
|
+
- [X] T021 [US1] `Searcher` クラスを実装する(コンストラクタで `repository` を注入。`search_by_postal_code` メソッド: Normalizer で正規化 → Repository 委譲): `lib/jp_address_complement/searcher.rb`
|
|
72
|
+
- [X] T022 [US1] `JpAddressComplement.search_by_postal_code` モジュールメソッドを実装する(`configuration.repository` を使用した `Searcher` 呼び出し): `lib/jp_address_complement.rb`
|
|
73
|
+
- [X] T023 [US1] T016〜T022 がすべて GREEN になることを確認し、`bundle exec rubocop` をパスさせる
|
|
74
|
+
|
|
75
|
+
**Checkpoint**: `search_by_postal_code` が正常動作 / SimpleCov >= 90% / Rubocop PASS
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Phase 4: User Story 2 — 郵便番号の先頭数字から候補住所を取得する (Priority: P2)
|
|
80
|
+
|
|
81
|
+
**Goal**: `JpAddressComplement.search_by_postal_code_prefix("1000")` で候補一覧が返る
|
|
82
|
+
|
|
83
|
+
**Independent Test**:
|
|
84
|
+
```bash
|
|
85
|
+
bundle exec rspec spec/searcher_spec.rb spec/repositories/ --tag us2
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Tests for User Story 2(TDD: 必ず先に書いて RED を確認)
|
|
89
|
+
|
|
90
|
+
- [X] T024 [P] [US2] `ActiveRecordPostalCodeRepository#find_by_prefix` の RSpec を実装する(SQLite メモリ DB。先頭4桁一致・4桁未満ガード・7桁完全一致): `spec/repositories/active_record_postal_code_repository_spec.rb`
|
|
91
|
+
- [X] T025 [P] [US2] `Searcher#search_by_postal_code_prefix` の RSpec を実装する(`FakeRepository` 使用・4桁未満は空返却): `spec/searcher_spec.rb`
|
|
92
|
+
- [X] T026 [P] [US2] トップレベル `JpAddressComplement.search_by_postal_code_prefix` の統合テストを実装する: `spec/jp_address_complement_spec.rb`
|
|
93
|
+
|
|
94
|
+
### Implementation for User Story 2
|
|
95
|
+
|
|
96
|
+
- [X] T027 [P] [US2] `ActiveRecordPostalCodeRepository#find_by_prefix` を実装する(`WHERE postal_code LIKE ?` クエリ・`"#{prefix}%"` バインド): `lib/jp_address_complement/repositories/active_record_postal_code_repository.rb`
|
|
97
|
+
- [X] T028 [US2] `Searcher#search_by_postal_code_prefix` を実装する(Normalizer 正規化 → 4桁未満ガード → Repository 委譲): `lib/jp_address_complement/searcher.rb`
|
|
98
|
+
- [X] T029 [US2] `JpAddressComplement.search_by_postal_code_prefix` モジュールメソッドを実装する: `lib/jp_address_complement.rb`
|
|
99
|
+
- [X] T030 [US2] `FakeRepository` に `find_by_prefix` を追加する: `spec/support/fake_postal_code_repository.rb`
|
|
100
|
+
- [X] T031 [US2] T024〜T030 がすべて GREEN になることを確認し、`bundle exec rubocop` をパスさせる
|
|
101
|
+
|
|
102
|
+
**Checkpoint**: `search_by_postal_code_prefix` が正常動作 / SimpleCov >= 90% / Rubocop PASS
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Phase 5: User Story 3 — 郵便番号と住所の整合性を検証する (Priority: P3)
|
|
107
|
+
|
|
108
|
+
**Goal**: `JpAddressComplement.valid_combination?("1000001", "東京都千代田区千代田1-1")` が `true` を返す
|
|
109
|
+
|
|
110
|
+
**Independent Test**:
|
|
111
|
+
```bash
|
|
112
|
+
bundle exec rspec spec/searcher_spec.rb --tag us3
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Tests for User Story 3(TDD: 必ず先に書いて RED を確認)
|
|
116
|
+
|
|
117
|
+
- [X] T032 [P] [US3] `Searcher#valid_combination?` の RSpec を実装する(`FakeRepository` 使用・部分一致 true / 不一致 false / 存在しない郵便番号 false / nil false): `spec/searcher_spec.rb`
|
|
118
|
+
- [X] T033 [P] [US3] トップレベル `JpAddressComplement.valid_combination?` の統合テストを実装する: `spec/jp_address_complement_spec.rb`
|
|
119
|
+
|
|
120
|
+
### Implementation for User Story 3
|
|
121
|
+
|
|
122
|
+
- [X] T034 [US3] `Searcher#valid_combination?` を実装する(`find_by_code` → `any?` で `(record.pref + record.city + record.town.to_s).include?(address)` 判定。`town` が nil の場合も安全に処理する): `lib/jp_address_complement/searcher.rb`
|
|
123
|
+
- [X] T035 [US3] `JpAddressComplement.valid_combination?` モジュールメソッドを実装する: `lib/jp_address_complement.rb`
|
|
124
|
+
- [X] T036 [US3] T032〜T035 がすべて GREEN になることを確認し、`bundle exec rubocop` をパスさせる
|
|
125
|
+
|
|
126
|
+
**Checkpoint**: `valid_combination?` が正常動作 / SimpleCov >= 90% / Rubocop PASS
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Phase 6: User Story 4 — Rails モデルでバリデーションを使用する (Priority: P4)
|
|
131
|
+
|
|
132
|
+
**Goal**: `validates :postal_code, jp_address_complement: { address_field: :address }` が動作する
|
|
133
|
+
|
|
134
|
+
**Independent Test**:
|
|
135
|
+
```bash
|
|
136
|
+
bundle exec rspec spec/validators/ spec/generators/ --tag us4
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Tests for User Story 4(TDD: 必ず先に書いて RED を確認)
|
|
140
|
+
|
|
141
|
+
- [X] T037 [P] [US4] `JpAddressComplementValidator` の RSpec を実装する(整合 true / 不整合 false / 郵便番号空でスキップ / エラーメッセージ確認): `spec/validators/jp_address_complement_validator_spec.rb`
|
|
142
|
+
- [X] T038 [P] [US4] `InstallGenerator` の RSpec を実装する(`rails g jp_address_complement:install` でマイグレーションファイルが `db/migrate/` に生成されることを確認): `spec/generators/install_generator_spec.rb`
|
|
143
|
+
|
|
144
|
+
### Implementation for User Story 4
|
|
145
|
+
|
|
146
|
+
- [X] T039 [P] [US4] `JpAddressComplementValidator` を実装する(`ActiveModel::EachValidator` を継承・`address_field` オプションで対象フィールドを取得・`valid_combination?` 委譲・郵便番号空はスキップ): `lib/jp_address_complement/validators/jp_address_complement_validator.rb`
|
|
147
|
+
- [X] T040 [P] [US4] `InstallGenerator` を実装する(`Rails::Generators::Base` を継承・`copy_migration` でテンプレートを `db/migrate/` へコピー・タイムスタンプ付きファイル名生成): `lib/generators/jp_address_complement/install_generator.rb`
|
|
148
|
+
- [X] T041 [US4] `Railtie` を実装する(Rails 統合: デフォルトリポジトリの初期化・Rake タスクのロード・Generator の登録): `lib/jp_address_complement/railtie.rb`
|
|
149
|
+
- [X] T042 [US4] `lib/jp_address_complement.rb` の Railtie 自動ロードを設定する(`defined?(Rails)` での条件ロード): `lib/jp_address_complement.rb`
|
|
150
|
+
- [X] T043 [US4] T037〜T042 がすべて GREEN になることを確認し、`bundle exec rubocop` をパスさせる
|
|
151
|
+
|
|
152
|
+
**Checkpoint**: Rails バリデーター・Generator が正常動作 / SimpleCov >= 90% / Rubocop PASS
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Phase 7: Rake タスク — CSV インポート機能
|
|
157
|
+
|
|
158
|
+
**Purpose**: US1〜US4 すべての前提となる住所データ投入手段を提供する
|
|
159
|
+
(論理的には基盤に近いが、コアロジックのテストは FakeRepository で代替できるため後回しにできる)
|
|
160
|
+
|
|
161
|
+
### Tests for Rake Task(TDD: 必ず先に書いて RED を確認)
|
|
162
|
+
|
|
163
|
+
- [X] T044 [P] `CsvImporter` の RSpec を実装する(Shift_JIS テスト CSV を用意・UTF-8 変換確認・upsert 冪等性確認・不正行スキップ確認・CSV パスが存在しない場合は `Errno::ENOENT` または `JpAddressComplement::ImportError` を明確に発生させることを確認): `spec/importers/csv_importer_spec.rb`
|
|
164
|
+
- [X] T045 [P] Rake タスク実行テストを実装する(タスクが `jp_address_complement:import` というタスク名で存在すること、`CSV` 環境変数なしでエラーになること): `spec/tasks/import_task_spec.rb`
|
|
165
|
+
|
|
166
|
+
### Implementation
|
|
167
|
+
|
|
168
|
+
- [X] T046 [P] `CsvImporter` クラスを実装する(`CSV.foreach(path, encoding: 'Shift_JIS:UTF-8')` でストリーミング読み込み・1,000件ごとに `upsert_all` ・不正行は警告ログでスキップ): `lib/jp_address_complement/importers/csv_importer.rb`
|
|
169
|
+
- [X] T047 `jp_address_complement:import` Rake タスクを実装する(`ENV['CSV']` で CSV パスを受け取り・パス未指定でエラーメッセージ出力・`CsvImporter` 呼び出し・進捗ログ対応): `lib/tasks/jp_address_complement.rake`
|
|
170
|
+
- [X] T048 テスト用 Shift_JIS サンプル CSV ファイルを作成する(10件程度): `spec/fixtures/ken_all_sample.csv`
|
|
171
|
+
- [X] T049 T044〜T048 がすべて GREEN になることを確認し、`bundle exec rubocop` をパスさせる
|
|
172
|
+
|
|
173
|
+
**Checkpoint**: フルインポートが冪等に動作 / SimpleCov >= 90% / Rubocop PASS
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Phase 8: Polish & Cross-Cutting Concerns
|
|
178
|
+
|
|
179
|
+
**Purpose**: 複数ストーリーに横断的な品質向上
|
|
180
|
+
|
|
181
|
+
- [X] T050 [P] `README.md` を作成する(インストール・セットアップ・基本 API 使用例・Rake タスク・Rails バリデーター例): `README.md`
|
|
182
|
+
- [X] T051 [P] `CHANGELOG.md` を作成する(0.1.0 の変更内容): `CHANGELOG.md`
|
|
183
|
+
- [X] T052 [P] エッジケースの追加テストを実装する(全角数字・「〒」付きハイフンあり・同一郵便番号複数レコード): `spec/searcher_spec.rb`, `spec/normalizer_spec.rb`
|
|
184
|
+
- [X] T053 `bundle exec rspec` でカバレッジレポートを確認し、SimpleCov 90% 未達のファイルを特定して対処する
|
|
185
|
+
- [X] T054 `bundle exec rubocop -a` を実行して最終 100% PASS を確認する(auto-correct で修正できない違反は `.rubocop.yml` で justify する)
|
|
186
|
+
- [ ] T055 [P] `jp_address_complement.gemspec` の `summary`, `description`, `homepage`, `authors`, `license` を最終化する: `jp_address_complement.gemspec`
|
|
187
|
+
- [ ] T056 quickstart.md の手順に沿って新しい Rails アプリで実際にセットアップし、動作確認する(smoke test)
|
|
188
|
+
- [ ] T057 [P] パフォーマンスベンチマークを実装・計測する(郵便番号完全一致 < 10ms p99・先頭4桁前方一致 < 50ms を RSpec のタイミング計測で検証。SC-001・SC-003 の合否を明示): `spec/performance_spec.rb`
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Dependencies & Execution Order
|
|
193
|
+
|
|
194
|
+
### Phase Dependencies
|
|
195
|
+
|
|
196
|
+
- **Phase 1(Setup)**: 依存なし → 即座に開始可能
|
|
197
|
+
- **Phase 2(Foundational)**: Phase 1 完了後 → **全ユーザーストーリーをブロック**
|
|
198
|
+
- **Phase 3(US1)**: Phase 2 完了後 → 開始可能 🎯 MVP
|
|
199
|
+
- **Phase 4(US2)**: Phase 2 完了後 → Phase 3 と並行可能
|
|
200
|
+
- **Phase 5(US3)**: Phase 3 完了後(`find_by_code` 依存)→ Phase 4 と並行可能
|
|
201
|
+
- **Phase 6(US4)**: Phase 3 完了後(`valid_combination?` 依存)
|
|
202
|
+
- **Phase 7(Rake)**: Phase 2 完了後 → 独立して並行実行可能
|
|
203
|
+
- **Phase 8(Polish)**: Phase 3〜7 完了後
|
|
204
|
+
|
|
205
|
+
### User Story Dependencies
|
|
206
|
+
|
|
207
|
+
| ストーリー | 依存 | 理由 |
|
|
208
|
+
|-----------|------|------|
|
|
209
|
+
| US1 (P1) | Phase 2 のみ | 最初に実装すべき MVP |
|
|
210
|
+
| US2 (P2) | Phase 2 のみ | `find_by_prefix` は US1 の `find_by_code` と独立 |
|
|
211
|
+
| US3 (P3) | US1 | `valid_combination?` が `find_by_code` を内部利用 |
|
|
212
|
+
| US4 (P4) | US3 | Validator が `valid_combination?` を委譲 |
|
|
213
|
+
|
|
214
|
+
### Parallel Opportunities
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Phase 1: 並列実行可能なセットアップタスク
|
|
218
|
+
T002 gemspec 設定 & T003 RSpec 初期化 & T004 Rubocop 設定(同時実行可)
|
|
219
|
+
|
|
220
|
+
# Phase 2: 並列実行可能な基盤タスク
|
|
221
|
+
T007 AddressRecord テスト & T009 Normalizer テスト & T012 Configuration & T014 PostalCode モデル(同時実行可)
|
|
222
|
+
|
|
223
|
+
# Phase 3 (US1): TDD でテストと実装を並列計画
|
|
224
|
+
T016 Repository spec & T017 AR spec & T018 Searcher spec(先に全部 RED にしてから実装)
|
|
225
|
+
|
|
226
|
+
# Phase 4 (US2): Phase 3 と並行可能
|
|
227
|
+
T024-T026 US2 spec(Phase 3 が実装中でも US2 のテストは先行して書ける)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Parallel Example: User Story 1
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Step 1: 全テストを先に書いて RED を確認(並列実行可)
|
|
236
|
+
Task T016: "PostalCodeRepository インターフェーステスト"
|
|
237
|
+
Task T017: "ActiveRecordPostalCodeRepository#find_by_code テスト"
|
|
238
|
+
Task T018: "Searcher#search_by_postal_code テスト(FakeRepository 使用)"
|
|
239
|
+
Task T019: "JpAddressComplement.search_by_postal_code 統合テスト"
|
|
240
|
+
|
|
241
|
+
# Step 2: 実装(T016 → T020 → T021 → T022 の順で GREEN にする)
|
|
242
|
+
Task T020: "ActiveRecordPostalCodeRepository#find_by_code 実装"
|
|
243
|
+
Task T021: "Searcher#search_by_postal_code 実装"
|
|
244
|
+
Task T022: "JpAddressComplement.search_by_postal_code 実装"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Implementation Strategy
|
|
250
|
+
|
|
251
|
+
### MVP First(User Story 1 のみ)
|
|
252
|
+
|
|
253
|
+
1. Phase 1: Setup 完了
|
|
254
|
+
2. Phase 2: Foundational 完了(⚠️ 全ストーリーのブロッカー)
|
|
255
|
+
3. Phase 3: User Story 1 完了
|
|
256
|
+
4. **STOP & VALIDATE**: `bundle exec rspec spec/ --tag us1` で独立テスト
|
|
257
|
+
5. quickstart.md の Step 3(Rake タスク)まで動作確認してデモ可能
|
|
258
|
+
|
|
259
|
+
### Incremental Delivery
|
|
260
|
+
|
|
261
|
+
1. Setup + Foundational → 基盤完成
|
|
262
|
+
2. US1(Phase 3)→ 独立テスト → **MVP リリース可能**
|
|
263
|
+
3. US2(Phase 4)→ 独立テスト → デプロイ・デモ
|
|
264
|
+
4. US3(Phase 5)→ 独立テスト → デプロイ・デモ
|
|
265
|
+
5. US4(Phase 6)→ 独立テスト → デプロイ・デモ
|
|
266
|
+
6. Rake タスク(Phase 7)→ データ投入ツール完成
|
|
267
|
+
7. Polish(Phase 8)→ ドキュメント・品質最終仕上げ
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Notes
|
|
272
|
+
|
|
273
|
+
- **TDD(NON-NEGOTIABLE)**: 各実装タスクの前にテストを書き、RED を確認してから GREEN にする
|
|
274
|
+
- **Rubocop(NON-NEGOTIABLE)**: `bundle exec rubocop` は各コミット前に PASS 必須。`disable` コメント禁止
|
|
275
|
+
- **Coverage**: SimpleCov を常時確認し、90% を下回ったらすぐに対処する
|
|
276
|
+
- **FakeRepository 活用**: コアロジック(Searcher 等)のテストは `FakePostalCodeRepository` を注入し DB 不要で高速化する
|
|
277
|
+
- **Repository パターン**: コア実装は `ActiveRecord` を直接 `require` せず、`PostalCodeRepository` 経由のみでアクセスする
|
|
278
|
+
- **コミット粒度**: 各タスク完了時または論理グループ完了時にコミットする
|
|
279
|
+
- `[P]` = 異なるファイル・無依存のため並列実行可能
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Specification Quality Checklist: RBS 型注釈の導入
|
|
2
|
+
|
|
3
|
+
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
|
4
|
+
**Created**: 2026-02-22
|
|
5
|
+
**Feature**: [spec.md](../spec.md)
|
|
6
|
+
|
|
7
|
+
## Content Quality
|
|
8
|
+
|
|
9
|
+
- [x] No implementation details (languages, frameworks, APIs)
|
|
10
|
+
- [x] Focused on user value and business needs
|
|
11
|
+
- [x] Written for non-technical stakeholders
|
|
12
|
+
- [x] All mandatory sections completed
|
|
13
|
+
|
|
14
|
+
## Requirement Completeness
|
|
15
|
+
|
|
16
|
+
- [x] No [NEEDS CLARIFICATION] markers remain
|
|
17
|
+
- [x] Requirements are testable and unambiguous
|
|
18
|
+
- [x] Success criteria are measurable
|
|
19
|
+
- [x] Success criteria are technology-agnostic (no implementation details)
|
|
20
|
+
- [x] All acceptance scenarios are defined
|
|
21
|
+
- [x] Edge cases are identified
|
|
22
|
+
- [x] Scope is clearly bounded
|
|
23
|
+
- [x] Dependencies and assumptions identified
|
|
24
|
+
|
|
25
|
+
## Feature Readiness
|
|
26
|
+
|
|
27
|
+
- [x] All functional requirements have clear acceptance criteria
|
|
28
|
+
- [x] User scenarios cover primary flows
|
|
29
|
+
- [x] Feature meets measurable outcomes defined in Success Criteria
|
|
30
|
+
- [x] No implementation details leak into specification
|
|
31
|
+
|
|
32
|
+
## Notes
|
|
33
|
+
|
|
34
|
+
- SC-002・SC-003 の「エラーゼロ」「全 public メソッドにシグネチャ定義」は明確で測定可能。
|
|
35
|
+
- `# steep:ignore` の乱用を防ぐ旨をキーエンティティに記載済み。
|
|
36
|
+
- `Data.define` の RBS 対応に関する不確実性は Assumptions セクションに記載済み。
|
|
37
|
+
- すべての項目が Pass のため、`/speckit.plan` に進む準備が整っています。
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# RBS Public API Contract: JpAddressComplement
|
|
2
|
+
|
|
3
|
+
**Gem**: `jp_address_complement`
|
|
4
|
+
**Feature**: 002-rbs-type-annotations
|
|
5
|
+
**Contract Type**: 型シグネチャ(RBS)による公開 API 定義
|
|
6
|
+
|
|
7
|
+
このドキュメントは、FR-008 および FR-009 に基づき、`.rbs` に記述すべき**公開メソッドと Repository インターフェース**の型を契約として定義する。実装する RBS はこの契約に従うこと。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. モジュールメソッド(`JpAddressComplement`)
|
|
12
|
+
|
|
13
|
+
以下のメソッドの引数・返り値型が `sig/` に正確に記述されていること。
|
|
14
|
+
|
|
15
|
+
### 1.1 `search_by_postal_code`
|
|
16
|
+
|
|
17
|
+
```rbs
|
|
18
|
+
# シグネチャ
|
|
19
|
+
def search_by_postal_code: (String) -> Array[JpAddressComplement::AddressRecord]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- **引数**: 郵便番号文字列(ハイフン・全角・〒 は正規化のため String のまま受け取り)。
|
|
23
|
+
- **返り値**: 対応する住所レコードの配列。存在しない場合は空配列。
|
|
24
|
+
|
|
25
|
+
### 1.2 `search_by_postal_code_prefix`
|
|
26
|
+
|
|
27
|
+
```rbs
|
|
28
|
+
def search_by_postal_code_prefix: (String) -> Array[JpAddressComplement::AddressRecord]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- **引数**: 郵便番号の先頭部分(4桁以上)。
|
|
32
|
+
- **返り値**: 候補レコードの配列。4桁未満の場合は空配列。
|
|
33
|
+
|
|
34
|
+
### 1.3 `valid_combination?`
|
|
35
|
+
|
|
36
|
+
```rbs
|
|
37
|
+
def valid_combination?: (String, String) -> bool
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- **引数**: `(postal_code, address)`。
|
|
41
|
+
- **返り値**: 整合していれば `true`、そうでなければ `false`。
|
|
42
|
+
|
|
43
|
+
### 1.4 `configure`
|
|
44
|
+
|
|
45
|
+
```rbs
|
|
46
|
+
def configure: () { (JpAddressComplement::Configuration) -> void } -> void
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- ブロックで `Configuration` を受け取り、設定を変更する。
|
|
50
|
+
|
|
51
|
+
### 1.5 `configuration`
|
|
52
|
+
|
|
53
|
+
```rbs
|
|
54
|
+
def configuration: () -> JpAddressComplement::Configuration
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- 現在の設定オブジェクトを返す。
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 2. 値オブジェクト `AddressRecord`
|
|
62
|
+
|
|
63
|
+
`sig/manual/address_record.rbs`(または同等)に、以下の属性型がすべて定義されていること。
|
|
64
|
+
|
|
65
|
+
| 属性 | 型(RBS) |
|
|
66
|
+
|------|-----------|
|
|
67
|
+
| `postal_code` | `String` |
|
|
68
|
+
| `pref_code` | `String` |
|
|
69
|
+
| `pref` | `String` |
|
|
70
|
+
| `city` | `String` |
|
|
71
|
+
| `town` | `String?` |
|
|
72
|
+
| `kana_pref` | `String?` |
|
|
73
|
+
| `kana_city` | `String?` |
|
|
74
|
+
| `kana_town` | `String?` |
|
|
75
|
+
| `has_alias` | `bool` |
|
|
76
|
+
| `is_partial` | `bool` |
|
|
77
|
+
| `is_large_office` | `bool` |
|
|
78
|
+
|
|
79
|
+
- `AddressRecord` は `Data.define` により定義されているため、RBS では `class` または適切な構造で上記メンバを持つ型として定義する。
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 3. Repository インターフェース(`Repositories::PostalCodeRepository`)
|
|
84
|
+
|
|
85
|
+
抽象インターフェースとして、少なくとも以下のメソッドシグネチャを持つこと。
|
|
86
|
+
※ 実装では `find_by_code` / `find_by_prefix` を使用しているため、RBS もこれに合わせる。
|
|
87
|
+
|
|
88
|
+
```rbs
|
|
89
|
+
module JpAddressComplement
|
|
90
|
+
module Repositories
|
|
91
|
+
class PostalCodeRepository
|
|
92
|
+
def find_by_code: (String) -> Array[JpAddressComplement::AddressRecord]
|
|
93
|
+
def find_by_prefix: (String) -> Array[JpAddressComplement::AddressRecord]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- **find_by_code**: 正規化済み 7 桁郵便番号で検索。
|
|
100
|
+
- **find_by_prefix**: 4 桁以上のプレフィックスで前方一致検索。
|
|
101
|
+
|
|
102
|
+
`ActiveRecordPostalCodeRepository` はこのインターフェースに従うクラスとして定義する。
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 4. 検証方法
|
|
107
|
+
|
|
108
|
+
- `bundle exec rake steep` がエラーゼロで終了すること。
|
|
109
|
+
- 利用側で `JpAddressComplement.search_by_postal_code(123)` のように誤った型を渡した場合、Steep が型エラーを報告すること。
|
|
110
|
+
- `sig/` 配下に、上記モジュールメソッド・`AddressRecord`・`PostalCodeRepository` のシグネチャが存在し、未定義の public メソッドがゼロであること(SC-003)。
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 5. 既存 API ドキュメントとの対応
|
|
115
|
+
|
|
116
|
+
Ruby の公開 API 仕様(引数・返り値・挙動)は `specs/001-jp-address-complement-gem/contracts/public-api.md` に定義されている。本契約はその型のみを RBS で表現したものであり、挙動の変更を意味しない。
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Data Model: RBS 型注釈(型構造)
|
|
2
|
+
|
|
3
|
+
**Branch**: `002-rbs-type-annotations`
|
|
4
|
+
**Date**: 2026-02-22
|
|
5
|
+
|
|
6
|
+
本ドキュメントは DB スキーマではなく、**型定義(RBS)の構造**を記述する。エンティティは「どの .rbs に何を定義するか」および「主要な型の形」とする。
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. sig/ ディレクトリ構成
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
sig/
|
|
14
|
+
├── jp_address_complement.rbs # トップレベルモジュール(rbs-inline 生成)
|
|
15
|
+
├── jp_address_complement/ # 名前空間別(rbs-inline 生成)
|
|
16
|
+
│ ├── address_record.rbs # Data の手動補完が必要な場合は manual を参照
|
|
17
|
+
│ ├── configuration.rbs
|
|
18
|
+
│ ├── normalizer.rbs
|
|
19
|
+
│ ├── searcher.rbs
|
|
20
|
+
│ ├── repositories/
|
|
21
|
+
│ │ ├── postal_code_repository.rbs
|
|
22
|
+
│ │ └── active_record_postal_code_repository.rbs
|
|
23
|
+
│ ├── models/
|
|
24
|
+
│ │ └── postal_code.rbs
|
|
25
|
+
│ ├── validators/
|
|
26
|
+
│ │ └── address_validator.rbs
|
|
27
|
+
│ ├── importers/
|
|
28
|
+
│ │ └── csv_importer.rbs
|
|
29
|
+
│ └── generators/
|
|
30
|
+
│ └── ...
|
|
31
|
+
└── manual/ # 手動定義(rbs-inline で上書きされない)
|
|
32
|
+
└── address_record.rbs # AddressRecord の全フィールド型
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- **自動生成**: `rbs-inline --output sig/ lib/` により `sig/` 直下および `sig/jp_address_complement/` 以下が更新される。
|
|
36
|
+
- **手動**: `sig/manual/` のみ開発者が編集する。主に `Data.define` 由来の `AddressRecord` 用。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 2. AddressRecord(手動 RBS)
|
|
41
|
+
|
|
42
|
+
`lib/jp_address_complement/address_record.rb` は `Data.define`(Ruby 3.2+)で定義されている。rbs-inline が十分な型を生成しないため、`sig/manual/address_record.rbs` で全フィールドを明示する。
|
|
43
|
+
|
|
44
|
+
### フィールドと型
|
|
45
|
+
|
|
46
|
+
| フィールド | 型(RBS) | 説明 |
|
|
47
|
+
|-----------|-----------|------|
|
|
48
|
+
| `postal_code` | `String` | 7桁郵便番号(ハイフンなし) |
|
|
49
|
+
| `pref_code` | `String` | 都道府県コード |
|
|
50
|
+
| `pref` | `String` | 都道府県名 |
|
|
51
|
+
| `city` | `String` | 市区町村名 |
|
|
52
|
+
| `town` | `String?` | 町域名(nil 可) |
|
|
53
|
+
| `kana_pref` | `String?` | 都道府県カナ |
|
|
54
|
+
| `kana_city` | `String?` | 市区町村カナ |
|
|
55
|
+
| `kana_town` | `String?` | 町域カナ |
|
|
56
|
+
| `has_alias` | `bool` | 通称フラグ |
|
|
57
|
+
| `is_partial` | `bool` | 丁目区分フラグ |
|
|
58
|
+
| `is_large_office` | `bool` | 大口事業所フラグ |
|
|
59
|
+
|
|
60
|
+
### 定義方針
|
|
61
|
+
|
|
62
|
+
- RBS では `class` または `interface` で上記属性を持つ型を定義する。
|
|
63
|
+
- `sig/jp_address_complement/address_record.rbs` が rbs-inline で生成される場合、`sig/manual/address_record.rbs` を `sig/` から参照する形(例: 型エイリアスや require 相当)で整合させる。Steep が両方を読みにいく設定とする。
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 3. 公開 API の型シグネチャ(要約)
|
|
68
|
+
|
|
69
|
+
以下は FR-008 で要求される「公開メソッドの引数・返り値型」の要約。詳細は `contracts/rbs-public-api.md` を参照。
|
|
70
|
+
|
|
71
|
+
| メソッド | 引数 | 返り値 |
|
|
72
|
+
|----------|------|--------|
|
|
73
|
+
| `JpAddressComplement.search_by_postal_code` | `(code: String)` | `Array[AddressRecord]` |
|
|
74
|
+
| `JpAddressComplement.search_by_postal_code_prefix` | `(prefix: String)` | `Array[AddressRecord]` |
|
|
75
|
+
| `JpAddressComplement.valid_combination?` | `(postal_code: String, address: String)` | `bool` |
|
|
76
|
+
| `JpAddressComplement.configure` | ブロック `(Configuration) -> void` | `void` |
|
|
77
|
+
| `JpAddressComplement.configuration` | なし | `Configuration` |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 4. Repository インターフェース(RBS)
|
|
82
|
+
|
|
83
|
+
`Repositories::PostalCodeRepository` は抽象インターフェースとして RBS で定義する(FR-009)。実装は `find_by_code` / `find_by_prefix`(現行実装に合わせる)。
|
|
84
|
+
|
|
85
|
+
| メソッド | シグネチャ |
|
|
86
|
+
|----------|------------|
|
|
87
|
+
| `find_by_code` | `(code: String) -> Array[AddressRecord]` |
|
|
88
|
+
| `find_by_prefix` | `(prefix: String) -> Array[AddressRecord]` |
|
|
89
|
+
|
|
90
|
+
`ActiveRecordPostalCodeRepository` はこのインターフェースに従うクラスとして定義する。
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 5. その他対象クラス・モジュール
|
|
95
|
+
|
|
96
|
+
以下のすべてに型シグネチャ(rbs-inline または手動)を定義する(FR-007)。
|
|
97
|
+
|
|
98
|
+
- `Configuration` — 設定オブジェクト(`repository` アクセサ等)
|
|
99
|
+
- `Normalizer` — 正規化メソッドの入出力型
|
|
100
|
+
- `Searcher` — `search_by_postal_code` / `search_by_postal_code_prefix` / `valid_combination?`
|
|
101
|
+
- `Repositories::PostalCodeRepository` — 上記インターフェース
|
|
102
|
+
- `Repositories::ActiveRecordPostalCodeRepository` — 上記実装
|
|
103
|
+
- `Validators::AddressValidator` — ActiveModel バリデーター
|
|
104
|
+
- `Importers::CsvImporter` — インポートメソッド
|
|
105
|
+
- Generators — ジェネレータークラス
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 6. Steep からの参照関係
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Steepfile
|
|
113
|
+
└── target :lib
|
|
114
|
+
signature "sig" → sig/*.rbs, sig/jp_address_complement/**/*.rbs, sig/manual/*.rbs
|
|
115
|
+
check "lib" → lib/**/*.rb(rbs-inline コメントは参照しない。生成済み .rbs を参照)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- `sig/manual/` は `signature "sig"` に含まれるため、Steep は `sig/` 全体を読みにいく。
|
|
119
|
+
- rbs-inline は `lib/` のコメントから `sig/` を生成するだけであり、Steep の実行時には生成済み `.rbs` が存在している前提とする。
|