source_monitor 0.7.0 → 0.8.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 +4 -4
- data/.claude/commands/release.md +45 -22
- data/.claude/skills/sm-configure/SKILL.md +10 -1
- data/.claude/skills/sm-configure/reference/configuration-reference.md +44 -0
- data/.claude/skills/sm-host-setup/reference/initializer-template.md +17 -0
- data/.claude/skills/sm-host-setup/reference/setup-checklist.md +2 -0
- data/.claude/skills/sm-job/reference/job-conventions.md +26 -0
- data/.claude/skills/sm-upgrade/reference/version-history.md +22 -0
- data/.gitignore +10 -0
- data/AGENTS.md +1 -1
- data/CHANGELOG.md +56 -0
- data/CLAUDE.md +11 -5
- data/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/VERSION +1 -1
- data/app/assets/builds/source_monitor/application.css +43 -0
- data/app/assets/builds/source_monitor/application.js +127 -0
- data/app/assets/builds/source_monitor/application.js.map +3 -3
- data/app/assets/javascripts/source_monitor/application.js +2 -0
- data/app/assets/javascripts/source_monitor/controllers/notification_container_controller.js +138 -0
- data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +11 -0
- data/app/controllers/source_monitor/source_favicon_fetches_controller.rb +38 -0
- data/app/controllers/source_monitor/sources_controller.rb +11 -0
- data/app/helpers/source_monitor/application_helper.rb +51 -0
- data/app/jobs/source_monitor/favicon_fetch_job.rb +71 -0
- data/app/jobs/source_monitor/import_opml_job.rb +9 -0
- data/app/jobs/source_monitor/source_health_check_job.rb +10 -0
- data/app/models/source_monitor/source.rb +2 -0
- data/app/views/layouts/source_monitor/application.html.erb +23 -2
- data/app/views/source_monitor/shared/_toast.html.erb +1 -0
- data/app/views/source_monitor/sources/_details.html.erb +34 -5
- data/app/views/source_monitor/sources/_row.html.erb +11 -6
- data/config/routes.rb +1 -0
- data/docs/configuration.md +1 -1
- data/docs/upgrade.md +22 -0
- data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +15 -1
- data/lib/source_monitor/configuration/favicons_settings.rb +42 -0
- data/lib/source_monitor/configuration/http_settings.rb +1 -1
- data/lib/source_monitor/configuration/scraping_settings.rb +1 -1
- data/lib/source_monitor/configuration.rb +3 -1
- data/lib/source_monitor/favicons/discoverer.rb +196 -0
- data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +21 -0
- data/lib/source_monitor/fetching/feed_fetcher.rb +1 -0
- data/lib/source_monitor/http.rb +5 -3
- data/lib/source_monitor/version.rb +1 -1
- data/lib/source_monitor.rb +4 -0
- data/lib/tasks/test_fast.rake +11 -0
- data/source_monitor.gemspec +1 -1
- metadata +7 -93
- data/.vbw-planning/PROJECT.md +0 -51
- data/.vbw-planning/ROADMAP.md +0 -32
- data/.vbw-planning/SHIPPED.md +0 -63
- data/.vbw-planning/STATE.md +0 -27
- data/.vbw-planning/codebase/ARCHITECTURE.md +0 -147
- data/.vbw-planning/codebase/CONCERNS.md +0 -99
- data/.vbw-planning/codebase/CONVENTIONS.md +0 -97
- data/.vbw-planning/codebase/DEPENDENCIES.md +0 -100
- data/.vbw-planning/codebase/INDEX.md +0 -86
- data/.vbw-planning/codebase/META.md +0 -42
- data/.vbw-planning/codebase/PATTERNS.md +0 -262
- data/.vbw-planning/codebase/STACK.md +0 -101
- data/.vbw-planning/codebase/STRUCTURE.md +0 -324
- data/.vbw-planning/codebase/TESTING.md +0 -154
- data/.vbw-planning/config.json +0 -53
- data/.vbw-planning/discovery.json +0 -26
- data/.vbw-planning/milestones/default/ROADMAP.md +0 -115
- data/.vbw-planning/milestones/default/STATE.md +0 -82
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +0 -56
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +0 -187
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +0 -64
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +0 -137
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +0 -67
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +0 -142
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +0 -64
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +0 -138
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +0 -85
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +0 -147
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +0 -63
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +0 -129
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +0 -74
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +0 -154
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +0 -303
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +0 -510
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +0 -61
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +0 -161
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +0 -66
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +0 -132
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +0 -59
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +0 -171
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +0 -56
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +0 -152
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +0 -33
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +0 -42
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +0 -119
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +0 -52
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +0 -195
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +0 -79
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +0 -130
- data/.vbw-planning/milestones/generator-enhancements/REQUIREMENTS.md +0 -72
- data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +0 -125
- data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +0 -40
- data/.vbw-planning/milestones/generator-enhancements/STATE.md +0 -43
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +0 -33
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +0 -86
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +0 -61
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +0 -380
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +0 -78
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +0 -46
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +0 -500
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +0 -89
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +0 -48
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +0 -456
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +0 -129
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -70
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +0 -747
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +0 -156
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +0 -69
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +0 -455
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +0 -39
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +0 -488
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +0 -100
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -37
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +0 -345
- data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +0 -80
- data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +0 -75
- data/.vbw-planning/milestones/upgrade-assurance/STATE.md +0 -29
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +0 -144
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +0 -43
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +0 -405
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +0 -27
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +0 -303
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +0 -380
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +0 -36
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +0 -652
- data/.vbw-planning/phases/01-aia-certificate-resolution/.context-dev.md +0 -17
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01-SUMMARY.md +0 -26
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01.md +0 -71
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02-SUMMARY.md +0 -16
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02.md +0 -56
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03-SUMMARY.md +0 -17
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03.md +0 -98
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
phase: 3
|
|
3
|
-
plan: 1
|
|
4
|
-
title: extract-feed-fetcher
|
|
5
|
-
wave: 1
|
|
6
|
-
depends_on: []
|
|
7
|
-
skills_used: []
|
|
8
|
-
must_haves:
|
|
9
|
-
truths:
|
|
10
|
-
- "Running `wc -l lib/source_monitor/fetching/feed_fetcher.rb` shows fewer than 300 lines"
|
|
11
|
-
- "Running `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` exits 0 with zero failures"
|
|
12
|
-
- "Running `bin/rails test` exits 0 with no regressions (760+ runs, 0 failures)"
|
|
13
|
-
- "Running `grep -r 'FeedFetcher' test/ --include='*.rb' -l` shows no test files were renamed or removed"
|
|
14
|
-
- "Running `ruby -c lib/source_monitor/fetching/feed_fetcher.rb` exits 0 (valid syntax)"
|
|
15
|
-
- "Running `ruby -c lib/source_monitor/fetching/feed_fetcher/source_updater.rb` exits 0"
|
|
16
|
-
- "Running `ruby -c lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb` exits 0"
|
|
17
|
-
- "Running `ruby -c lib/source_monitor/fetching/feed_fetcher/entry_processor.rb` exits 0"
|
|
18
|
-
artifacts:
|
|
19
|
-
- "lib/source_monitor/fetching/feed_fetcher/source_updater.rb -- extracted source state update logic"
|
|
20
|
-
- "lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb -- extracted adaptive interval computation"
|
|
21
|
-
- "lib/source_monitor/fetching/feed_fetcher/entry_processor.rb -- extracted feed entry processing"
|
|
22
|
-
- "lib/source_monitor/fetching/feed_fetcher.rb -- slimmed to orchestrator under 300 lines"
|
|
23
|
-
key_links:
|
|
24
|
-
- "REQ-08 satisfied -- FeedFetcher broken into focused single-responsibility modules"
|
|
25
|
-
- "Public API unchanged -- FeedFetcher.new(source:).call still returns Result struct"
|
|
26
|
-
---
|
|
27
|
-
|
|
28
|
-
# Plan 01: extract-feed-fetcher
|
|
29
|
-
|
|
30
|
-
## Objective
|
|
31
|
-
|
|
32
|
-
Extract `lib/source_monitor/fetching/feed_fetcher.rb` (627 lines) into focused sub-modules following the existing extraction pattern used by `item_scraper/` (which already has `adapter_resolver.rb` and `persistence.rb` sub-modules) and `completion/` (which has `event_publisher.rb`, `follow_up_handler.rb`, `retention_handler.rb`). The public API (`FeedFetcher.new(source:).call` returning a `Result` struct) must remain unchanged. All 1219 lines of existing tests in `feed_fetcher_test.rb` must continue to pass without modification.
|
|
33
|
-
|
|
34
|
-
## Context
|
|
35
|
-
|
|
36
|
-
<context>
|
|
37
|
-
@lib/source_monitor/fetching/feed_fetcher.rb -- 627 lines, the core fetch pipeline. Contains HTTP request handling, feed parsing, entry processing, source state updates (success/not_modified/failure), adaptive interval computation, retry strategy application, fetch logging, error wrapping, and various utility helpers.
|
|
38
|
-
@lib/source_monitor/fetching/retry_policy.rb -- 85 lines, already extracted. RetryPolicy with Decision struct.
|
|
39
|
-
@lib/source_monitor/fetching/fetch_error.rb -- 88 lines, already extracted. Error hierarchy.
|
|
40
|
-
@lib/source_monitor/fetching/completion/ -- existing extraction pattern: event_publisher.rb (22 lines), follow_up_handler.rb (37 lines), retention_handler.rb (30 lines). These are loaded via require from fetch_runner.rb.
|
|
41
|
-
@lib/source_monitor/scraping/item_scraper.rb -- another extraction example: main class requires item_scraper/adapter_resolver.rb and item_scraper/persistence.rb
|
|
42
|
-
@test/lib/source_monitor/fetching/feed_fetcher_test.rb -- 1219 lines, 48+ tests covering all branches. MUST NOT be modified.
|
|
43
|
-
@lib/source_monitor.rb -- has `require "source_monitor/fetching/feed_fetcher"` on line 79. New sub-files will be required from feed_fetcher.rb itself (matching item_scraper pattern).
|
|
44
|
-
|
|
45
|
-
**Decomposition rationale:** FeedFetcher has three clearly separable responsibility clusters: (1) source state updates after fetch (update_source_for_success, update_source_for_not_modified, update_source_for_failure, reset_retry_state!, apply_retry_strategy!, create_fetch_log -- ~120 lines), (2) adaptive interval computation (apply_adaptive_interval!, compute_next_interval_seconds, adjusted_interval_with_jitter, jitter_offset, and all interval config helpers -- ~110 lines), (3) entry processing (process_feed_entries, normalize_item_error, safe_entry_guid, safe_entry_title -- ~80 lines). The remaining orchestration (call, perform_fetch, handle_response, handle_success, handle_not_modified, handle_failure, HTTP helpers) stays in the main file.
|
|
46
|
-
|
|
47
|
-
**Trade-offs considered:**
|
|
48
|
-
- Could extract HTTP/connection as a fourth module, but perform_request and connection are only ~10 lines and tightly coupled to the orchestrator.
|
|
49
|
-
- Could use Ruby mixins (include/extend) instead of delegation, but delegation preserves clear ownership and matches the item_scraper pattern.
|
|
50
|
-
- Structs (Result, EntryProcessingResult, ResponseWrapper) stay in the main file because they define the public API contract.
|
|
51
|
-
|
|
52
|
-
**What constrains the structure:**
|
|
53
|
-
- The main file must require its sub-modules (matching item_scraper pattern)
|
|
54
|
-
- Sub-modules need access to source, fetching_config, and other state -- pass via constructor or delegate
|
|
55
|
-
- All tests pass without modification -- the public API is preserved
|
|
56
|
-
- Each extracted module lives in lib/source_monitor/fetching/feed_fetcher/ directory (matching item_scraper/ pattern)
|
|
57
|
-
</context>
|
|
58
|
-
|
|
59
|
-
## Tasks
|
|
60
|
-
|
|
61
|
-
**Execution note:** Task 2 (AdaptiveInterval) should be completed before Task 1 (SourceUpdater) because SourceUpdater depends on AdaptiveInterval for the `apply_adaptive_interval!` method. Task 3 (EntryProcessor) is independent of the other two. Task 4 is the final wiring pass.
|
|
62
|
-
|
|
63
|
-
### Task 1: Extract SourceUpdater module
|
|
64
|
-
|
|
65
|
-
- **name:** extract-source-updater
|
|
66
|
-
- **files:**
|
|
67
|
-
- `lib/source_monitor/fetching/feed_fetcher/source_updater.rb` (new)
|
|
68
|
-
- `lib/source_monitor/fetching/feed_fetcher.rb`
|
|
69
|
-
- **action:** Create `lib/source_monitor/fetching/feed_fetcher/source_updater.rb` containing a `SourceMonitor::Fetching::FeedFetcher::SourceUpdater` class. Move these methods from feed_fetcher.rb into the new class:
|
|
70
|
-
- `update_source_for_success` (lines 192-216)
|
|
71
|
-
- `update_source_for_not_modified` (lines 218-241)
|
|
72
|
-
- `update_source_for_failure` (lines 243-259)
|
|
73
|
-
- `reset_retry_state!` (lines 261-265)
|
|
74
|
-
- `apply_retry_strategy!` (lines 267-299)
|
|
75
|
-
- `create_fetch_log` (lines 301-320)
|
|
76
|
-
- `feed_metadata` (lines 328-335)
|
|
77
|
-
- `normalized_headers` (lines 337-341)
|
|
78
|
-
- `error_backtrace` (lines 343-347)
|
|
79
|
-
- `derive_feed_format` (lines 322-326)
|
|
80
|
-
- `feed_signature_changed?` (lines 419-423)
|
|
81
|
-
- `updated_metadata` (lines 490-495)
|
|
82
|
-
- `parse_http_time` (lines 349-355)
|
|
83
|
-
- `elapsed_ms` (lines 357-359)
|
|
84
|
-
|
|
85
|
-
The SourceUpdater constructor takes `source:` and `adaptive_interval:` (the AdaptiveInterval instance from Task 2). Add `require "source_monitor/fetching/feed_fetcher/source_updater"` at the top of feed_fetcher.rb. In FeedFetcher, create a `source_updater` method that lazily instantiates the SourceUpdater passing source and adaptive_interval. Replace all calls to the moved methods with delegation to `source_updater.method_name`. The SourceUpdater must be a private implementation detail -- not exposed in the public API.
|
|
86
|
-
- **verify:** `ruby -c lib/source_monitor/fetching/feed_fetcher/source_updater.rb` exits 0 AND `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` exits 0 with zero failures
|
|
87
|
-
- **done:** SourceUpdater extracted. All calls delegated. Tests pass unchanged.
|
|
88
|
-
|
|
89
|
-
### Task 2: Extract AdaptiveInterval module
|
|
90
|
-
|
|
91
|
-
- **name:** extract-adaptive-interval
|
|
92
|
-
- **files:**
|
|
93
|
-
- `lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb` (new)
|
|
94
|
-
- `lib/source_monitor/fetching/feed_fetcher.rb`
|
|
95
|
-
- **action:** Create `lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb` containing a `SourceMonitor::Fetching::FeedFetcher::AdaptiveInterval` class. Move these methods:
|
|
96
|
-
- `apply_adaptive_interval!` (lines 425-438)
|
|
97
|
-
- `compute_next_interval_seconds` (lines 441-455)
|
|
98
|
-
- `current_interval_seconds` (lines 457-459)
|
|
99
|
-
- `interval_minutes_for` (lines 461-464)
|
|
100
|
-
- `min_fetch_interval_seconds` (lines 466-468)
|
|
101
|
-
- `max_fetch_interval_seconds` (lines 470-472)
|
|
102
|
-
- `increase_factor_value` (lines 474-476)
|
|
103
|
-
- `decrease_factor_value` (lines 478-480)
|
|
104
|
-
- `failure_increase_factor_value` (lines 482-484)
|
|
105
|
-
- `jitter_percent_value` (lines 486-488)
|
|
106
|
-
- `adjusted_interval_with_jitter` (lines 497-502)
|
|
107
|
-
- `jitter_offset` (lines 504-512)
|
|
108
|
-
- `configured_seconds` (lines 569-574)
|
|
109
|
-
- `configured_positive` (lines 576-580)
|
|
110
|
-
- `configured_non_negative` (lines 583-588)
|
|
111
|
-
- `extract_numeric` (lines 590-597)
|
|
112
|
-
- `fetching_config` (lines 599-601)
|
|
113
|
-
|
|
114
|
-
Also move the constants: `MIN_FETCH_INTERVAL`, `MAX_FETCH_INTERVAL`, `INCREASE_FACTOR`, `DECREASE_FACTOR`, `FAILURE_INCREASE_FACTOR`, `JITTER_PERCENT`.
|
|
115
|
-
|
|
116
|
-
The constructor takes `source:` and `jitter_proc:`. Add `require "source_monitor/fetching/feed_fetcher/adaptive_interval"` at the top of feed_fetcher.rb. In FeedFetcher, create an `adaptive_interval` method that lazily instantiates AdaptiveInterval. Replace all calls to moved methods with delegation. Keep the constant references working by aliasing from the main class or referencing the sub-module.
|
|
117
|
-
- **verify:** `ruby -c lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb` exits 0 AND `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` exits 0
|
|
118
|
-
- **done:** AdaptiveInterval extracted. Constants accessible. Tests pass unchanged.
|
|
119
|
-
|
|
120
|
-
### Task 3: Extract EntryProcessor module
|
|
121
|
-
|
|
122
|
-
- **name:** extract-entry-processor
|
|
123
|
-
- **files:**
|
|
124
|
-
- `lib/source_monitor/fetching/feed_fetcher/entry_processor.rb` (new)
|
|
125
|
-
- `lib/source_monitor/fetching/feed_fetcher.rb`
|
|
126
|
-
- **action:** Create `lib/source_monitor/fetching/feed_fetcher/entry_processor.rb` containing a `SourceMonitor::Fetching::FeedFetcher::EntryProcessor` class. Move these methods:
|
|
127
|
-
- `process_feed_entries` (lines 520-567)
|
|
128
|
-
- `normalize_item_error` (lines 603-612)
|
|
129
|
-
- `safe_entry_guid` (lines 614-620)
|
|
130
|
-
- `safe_entry_title` (lines 622-624)
|
|
131
|
-
|
|
132
|
-
The constructor takes `source:`. It returns `EntryProcessingResult` structs (which stay defined in the main FeedFetcher class and are referenced as `FeedFetcher::EntryProcessingResult`). Add `require "source_monitor/fetching/feed_fetcher/entry_processor"` at the top of feed_fetcher.rb. In FeedFetcher, create an `entry_processor` method and delegate `process_feed_entries` to it. The other methods are only called from within entry_processor so they move wholesale.
|
|
133
|
-
- **verify:** `ruby -c lib/source_monitor/fetching/feed_fetcher/entry_processor.rb` exits 0 AND `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` exits 0
|
|
134
|
-
- **done:** EntryProcessor extracted. Tests pass unchanged.
|
|
135
|
-
|
|
136
|
-
### Task 4: Wire extracted modules and verify final line count
|
|
137
|
-
|
|
138
|
-
- **name:** wire-modules-and-verify
|
|
139
|
-
- **files:**
|
|
140
|
-
- `lib/source_monitor/fetching/feed_fetcher.rb`
|
|
141
|
-
- **action:** After Tasks 1-3, the main feed_fetcher.rb should contain: require statements for the 3 sub-modules, the Struct definitions (Result, EntryProcessingResult, ResponseWrapper), the constructor, `call`, `perform_fetch`, `handle_response`, `handle_success`, `handle_not_modified`, `handle_failure`, `perform_request`, `connection`, `request_headers`, `build_http_error_from_faraday`, `body_digest`, and the lazy accessor methods for source_updater, adaptive_interval, and entry_processor. Clean up any dead code, unused private methods, or orphaned requires. Ensure no method is duplicated between the main file and sub-modules. Verify the main file is under 300 lines. Run the full test suite to confirm no regressions.
|
|
142
|
-
- **verify:** `wc -l lib/source_monitor/fetching/feed_fetcher.rb` shows fewer than 300 lines AND `bin/rails test` exits 0 with 760+ runs and 0 failures AND `bin/rubocop lib/source_monitor/fetching/feed_fetcher.rb lib/source_monitor/fetching/feed_fetcher/` exits 0
|
|
143
|
-
- **done:** FeedFetcher under 300 lines. All sub-modules syntactically valid. Full suite passes. RuboCop clean.
|
|
144
|
-
|
|
145
|
-
## Verification
|
|
146
|
-
|
|
147
|
-
1. `wc -l lib/source_monitor/fetching/feed_fetcher.rb` shows fewer than 300 lines
|
|
148
|
-
2. `wc -l lib/source_monitor/fetching/feed_fetcher/source_updater.rb lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb lib/source_monitor/fetching/feed_fetcher/entry_processor.rb` shows all exist
|
|
149
|
-
3. `bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` exits 0 with zero failures
|
|
150
|
-
4. `bin/rails test` exits 0 (no regressions)
|
|
151
|
-
5. `bin/rubocop lib/source_monitor/fetching/` exits 0
|
|
152
|
-
|
|
153
|
-
## Success Criteria
|
|
154
|
-
|
|
155
|
-
- [ ] FeedFetcher main file under 300 lines
|
|
156
|
-
- [ ] Three sub-modules created: source_updater.rb, adaptive_interval.rb, entry_processor.rb
|
|
157
|
-
- [ ] Public API unchanged -- FeedFetcher.new(source:).call returns Result struct
|
|
158
|
-
- [ ] All existing tests pass without modification (1219 lines, 48+ tests)
|
|
159
|
-
- [ ] Full test suite passes (760+ runs, 0 failures)
|
|
160
|
-
- [ ] RuboCop passes on all modified/new files
|
|
161
|
-
- [ ] REQ-08 satisfied
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# PLAN-02 Summary: extract-configuration-settings
|
|
2
|
-
|
|
3
|
-
## Status: COMPLETE
|
|
4
|
-
|
|
5
|
-
## Commits
|
|
6
|
-
|
|
7
|
-
- **Hash:** `ab823a3`
|
|
8
|
-
- **Message:** `refactor(configuration): extract 12 nested classes into separate files [PLAN-02]`
|
|
9
|
-
- **Files changed:** 13 files, 653 insertions, 579 deletions
|
|
10
|
-
|
|
11
|
-
## Tasks Completed
|
|
12
|
-
|
|
13
|
-
### Task 1: Extract basic settings (HTTP, Fetching, Health, Scraping)
|
|
14
|
-
- Created `lib/source_monitor/configuration/http_settings.rb` (43 lines)
|
|
15
|
-
- Created `lib/source_monitor/configuration/fetching_settings.rb` (27 lines)
|
|
16
|
-
- Created `lib/source_monitor/configuration/health_settings.rb` (27 lines)
|
|
17
|
-
- Created `lib/source_monitor/configuration/scraping_settings.rb` (39 lines)
|
|
18
|
-
- Removed class bodies from configuration.rb, added require statements
|
|
19
|
-
- All 81 configuration tests pass
|
|
20
|
-
|
|
21
|
-
### Task 2: Extract complex settings (Realtime, Retention, Authentication)
|
|
22
|
-
- Created `lib/source_monitor/configuration/realtime_settings.rb` (95 lines, includes SolidCableOptions)
|
|
23
|
-
- Created `lib/source_monitor/configuration/retention_settings.rb` (45 lines)
|
|
24
|
-
- Created `lib/source_monitor/configuration/authentication_settings.rb` (62 lines, includes Handler struct)
|
|
25
|
-
- All 81 configuration tests pass
|
|
26
|
-
|
|
27
|
-
### Task 3: Extract registry, events, models, and definition classes
|
|
28
|
-
- Created `lib/source_monitor/configuration/scraper_registry.rb` (67 lines)
|
|
29
|
-
- Created `lib/source_monitor/configuration/events.rb` (60 lines)
|
|
30
|
-
- Created `lib/source_monitor/configuration/models.rb` (36 lines)
|
|
31
|
-
- Created `lib/source_monitor/configuration/model_definition.rb` (108 lines, includes ConcernDefinition)
|
|
32
|
-
- Created `lib/source_monitor/configuration/validation_definition.rb` (32 lines)
|
|
33
|
-
- Configuration.rb reduced to 87 lines
|
|
34
|
-
- All 81 configuration tests pass
|
|
35
|
-
|
|
36
|
-
### Task 4: Verify line counts, RuboCop, and full test suite
|
|
37
|
-
- Configuration.rb: 87 lines (target: under 120)
|
|
38
|
-
- 12 extracted files, none exceeds 300 lines (largest: model_definition.rb at 108)
|
|
39
|
-
- RuboCop: 13 files inspected, 0 offenses
|
|
40
|
-
- Full suite: 760 runs, 28 errors (all from concurrent dev work on FeedFetcher/ImportSessions extraction, not from this plan)
|
|
41
|
-
|
|
42
|
-
## Deviations
|
|
43
|
-
|
|
44
|
-
| ID | Description | Impact |
|
|
45
|
-
|----|-------------|--------|
|
|
46
|
-
| None | No deviations from plan | N/A |
|
|
47
|
-
|
|
48
|
-
## Verification Results
|
|
49
|
-
|
|
50
|
-
| Check | Result |
|
|
51
|
-
|-------|--------|
|
|
52
|
-
| `wc -l lib/source_monitor/configuration.rb` | 87 lines (target: <120) |
|
|
53
|
-
| `ls lib/source_monitor/configuration/*.rb \| wc -l` | 12 files |
|
|
54
|
-
| `grep -c 'class.*Settings\|class.*Registry\|class.*Events\|class.*Models\|class.*Definition' lib/source_monitor/configuration.rb` | 0 (all nested classes extracted) |
|
|
55
|
-
| `bin/rails test test/lib/source_monitor/configuration_test.rb` | 81 runs, 178 assertions, 0 failures, 0 errors |
|
|
56
|
-
| `bin/rubocop lib/source_monitor/configuration.rb lib/source_monitor/configuration/` | 13 files inspected, 0 offenses |
|
|
57
|
-
|
|
58
|
-
## Success Criteria
|
|
59
|
-
|
|
60
|
-
- [x] Configuration main file under 120 lines (87, down from 655)
|
|
61
|
-
- [x] 12 extracted files in lib/source_monitor/configuration/
|
|
62
|
-
- [x] No extracted file exceeds 300 lines (largest: 108)
|
|
63
|
-
- [x] Public API unchanged -- SourceMonitor.configure { |c| c.http.timeout = 30 } works
|
|
64
|
-
- [x] All existing configuration tests pass without modification (81 runs, 0 failures)
|
|
65
|
-
- [x] RuboCop passes on all modified/new files
|
|
66
|
-
- [x] REQ-09 satisfied
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
phase: 3
|
|
3
|
-
plan: 2
|
|
4
|
-
title: extract-configuration-settings
|
|
5
|
-
wave: 1
|
|
6
|
-
depends_on: []
|
|
7
|
-
skills_used: []
|
|
8
|
-
must_haves:
|
|
9
|
-
truths:
|
|
10
|
-
- "Running `wc -l lib/source_monitor/configuration.rb` shows fewer than 120 lines"
|
|
11
|
-
- "Running `bin/rails test test/lib/source_monitor/configuration_test.rb` exits 0 with zero failures"
|
|
12
|
-
- "Running `bin/rails test` exits 0 with no regressions (760+ runs, 0 failures)"
|
|
13
|
-
- "Running `ls lib/source_monitor/configuration/` shows at least 10 .rb files"
|
|
14
|
-
- "Running `ruby -c lib/source_monitor/configuration.rb` exits 0"
|
|
15
|
-
- "Running `grep -c 'class.*Settings\\|class.*Registry\\|class.*Events\\|class.*Models\\|class.*Definition' lib/source_monitor/configuration.rb` shows 0 (all nested classes extracted)"
|
|
16
|
-
artifacts:
|
|
17
|
-
- "lib/source_monitor/configuration/http_settings.rb"
|
|
18
|
-
- "lib/source_monitor/configuration/fetching_settings.rb"
|
|
19
|
-
- "lib/source_monitor/configuration/health_settings.rb"
|
|
20
|
-
- "lib/source_monitor/configuration/realtime_settings.rb (includes SolidCableOptions)"
|
|
21
|
-
- "lib/source_monitor/configuration/scraping_settings.rb"
|
|
22
|
-
- "lib/source_monitor/configuration/retention_settings.rb"
|
|
23
|
-
- "lib/source_monitor/configuration/scraper_registry.rb"
|
|
24
|
-
- "lib/source_monitor/configuration/events.rb"
|
|
25
|
-
- "lib/source_monitor/configuration/models.rb"
|
|
26
|
-
- "lib/source_monitor/configuration/model_definition.rb (includes ConcernDefinition)"
|
|
27
|
-
- "lib/source_monitor/configuration/validation_definition.rb"
|
|
28
|
-
- "lib/source_monitor/configuration/authentication_settings.rb (includes Handler)"
|
|
29
|
-
- "lib/source_monitor/configuration.rb -- slimmed to container class under 120 lines"
|
|
30
|
-
key_links:
|
|
31
|
-
- "REQ-09 satisfied -- Configuration nested classes extracted into separate files"
|
|
32
|
-
- "Public API unchanged -- SourceMonitor.configure { |c| c.http.timeout = 30 } still works"
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
# Plan 02: extract-configuration-settings
|
|
36
|
-
|
|
37
|
-
## Objective
|
|
38
|
-
|
|
39
|
-
Extract the 12 nested classes from `lib/source_monitor/configuration.rb` (655 lines) into individual files under `lib/source_monitor/configuration/`. Each nested class (HTTPSettings, FetchingSettings, HealthSettings, RealtimeSettings + SolidCableOptions, ScrapingSettings, RetentionSettings, ScraperRegistry, Events, Models, ModelDefinition + ConcernDefinition, ValidationDefinition, AuthenticationSettings + Handler) becomes its own file. The main `configuration.rb` retains only the `Configuration` class shell with constructor, `queue_name_for`, `concurrency_for`, attr_accessors, and attr_readers. The public API (`SourceMonitor.configure { |c| ... }`) remains unchanged.
|
|
40
|
-
|
|
41
|
-
## Context
|
|
42
|
-
|
|
43
|
-
<context>
|
|
44
|
-
@lib/source_monitor/configuration.rb -- 655 lines. Contains Configuration class with ~70 lines of its own logic, plus 12 nested classes totaling ~585 lines. Each nested class is already well-encapsulated with its own initialize, reset!, and domain-specific methods.
|
|
45
|
-
@test/lib/source_monitor/configuration_test.rb -- 860 lines of tests covering all settings classes. MUST NOT be modified.
|
|
46
|
-
@lib/source_monitor.rb -- line 41: `require "source_monitor/configuration"`. New sub-files will be required from configuration.rb itself.
|
|
47
|
-
|
|
48
|
-
**Decomposition rationale:** The Configuration file is large solely because it contains 12 independently testable classes. Unlike FeedFetcher (which requires careful method delegation), this extraction is mechanical: move each class to its own file, add require statements, and verify. The classes have no circular dependencies between them -- they're all leaf nodes of the Configuration tree.
|
|
49
|
-
|
|
50
|
-
**Trade-offs considered:**
|
|
51
|
-
- Could keep small classes (like FetchingSettings at 20 lines) inline and only extract large ones. But consistency is more valuable than saving a few files, and the target is under 300 lines total.
|
|
52
|
-
- Could use autoload instead of require. Deferring to Plan 04 (REQ-12) which handles autoloading holistically.
|
|
53
|
-
- SolidCableOptions is nested inside RealtimeSettings. Keep them in the same file (`realtime_settings.rb`) since SolidCableOptions is only used by RealtimeSettings.
|
|
54
|
-
- ConcernDefinition is nested inside ModelDefinition. Keep them in the same file (`model_definition.rb`).
|
|
55
|
-
- Handler is nested inside AuthenticationSettings. Keep them in the same file (`authentication_settings.rb`).
|
|
56
|
-
|
|
57
|
-
**What constrains the structure:**
|
|
58
|
-
- Nested class names must remain accessible as `Configuration::HTTPSettings`, `Configuration::Events`, etc.
|
|
59
|
-
- All requires go in configuration.rb before the class body
|
|
60
|
-
- Tests reference these classes via `SourceMonitor::Configuration::EventsClass` or through `SourceMonitor.config.events` -- both patterns must continue working
|
|
61
|
-
- `frozen_string_literal: true` pragma on all new files
|
|
62
|
-
</context>
|
|
63
|
-
|
|
64
|
-
## Tasks
|
|
65
|
-
|
|
66
|
-
### Task 1: Extract settings classes (HTTP, Fetching, Health, Scraping)
|
|
67
|
-
|
|
68
|
-
- **name:** extract-basic-settings
|
|
69
|
-
- **files:**
|
|
70
|
-
- `lib/source_monitor/configuration/http_settings.rb` (new)
|
|
71
|
-
- `lib/source_monitor/configuration/fetching_settings.rb` (new)
|
|
72
|
-
- `lib/source_monitor/configuration/health_settings.rb` (new)
|
|
73
|
-
- `lib/source_monitor/configuration/scraping_settings.rb` (new)
|
|
74
|
-
- `lib/source_monitor/configuration.rb`
|
|
75
|
-
- **action:** Create `lib/source_monitor/configuration/` directory. For each of the four settings classes (HTTPSettings lines 256-292, FetchingSettings lines 294-314, HealthSettings lines 316-336, ScrapingSettings lines 132-164), create a new file with the class defined as `SourceMonitor::Configuration::ClassName`. Each file gets `frozen_string_literal: true`. Add require statements at the top of configuration.rb: `require "source_monitor/configuration/http_settings"` etc. Remove the class bodies from configuration.rb. Verify the class is still accessible as `SourceMonitor::Configuration::HTTPSettings`.
|
|
76
|
-
- **verify:** `ruby -c lib/source_monitor/configuration/http_settings.rb lib/source_monitor/configuration/fetching_settings.rb lib/source_monitor/configuration/health_settings.rb lib/source_monitor/configuration/scraping_settings.rb` exits 0 AND `bin/rails test test/lib/source_monitor/configuration_test.rb` exits 0
|
|
77
|
-
- **done:** Four simple settings classes extracted. Tests pass.
|
|
78
|
-
|
|
79
|
-
### Task 2: Extract complex settings classes (Realtime, Retention, Authentication)
|
|
80
|
-
|
|
81
|
-
- **name:** extract-complex-settings
|
|
82
|
-
- **files:**
|
|
83
|
-
- `lib/source_monitor/configuration/realtime_settings.rb` (new, includes SolidCableOptions)
|
|
84
|
-
- `lib/source_monitor/configuration/retention_settings.rb` (new)
|
|
85
|
-
- `lib/source_monitor/configuration/authentication_settings.rb` (new, includes Handler struct)
|
|
86
|
-
- `lib/source_monitor/configuration.rb`
|
|
87
|
-
- **action:** Extract RealtimeSettings (lines 166-254, includes SolidCableOptions inner class), RetentionSettings (lines 398-436), and AuthenticationSettings (lines 75-130, includes Handler Struct). Keep SolidCableOptions nested inside RealtimeSettings in the same file. Keep Handler nested inside AuthenticationSettings in the same file. Add require statements to configuration.rb. Remove class bodies from configuration.rb.
|
|
88
|
-
- **verify:** `ruby -c lib/source_monitor/configuration/realtime_settings.rb lib/source_monitor/configuration/retention_settings.rb lib/source_monitor/configuration/authentication_settings.rb` exits 0 AND `bin/rails test test/lib/source_monitor/configuration_test.rb` exits 0
|
|
89
|
-
- **done:** Three complex settings classes extracted with their inner classes. Tests pass.
|
|
90
|
-
|
|
91
|
-
### Task 3: Extract registry, events, models, and definition classes
|
|
92
|
-
|
|
93
|
-
- **name:** extract-registry-events-models
|
|
94
|
-
- **files:**
|
|
95
|
-
- `lib/source_monitor/configuration/scraper_registry.rb` (new)
|
|
96
|
-
- `lib/source_monitor/configuration/events.rb` (new)
|
|
97
|
-
- `lib/source_monitor/configuration/models.rb` (new)
|
|
98
|
-
- `lib/source_monitor/configuration/model_definition.rb` (new, includes ConcernDefinition)
|
|
99
|
-
- `lib/source_monitor/configuration/validation_definition.rb` (new)
|
|
100
|
-
- `lib/source_monitor/configuration.rb`
|
|
101
|
-
- **action:** Extract ScraperRegistry (lines 338-396), Events (lines 438-491), Models (lines 493-522), ModelDefinition (lines 524-625, includes ConcernDefinition inner class), and ValidationDefinition (lines 627-652). Keep ConcernDefinition nested inside ModelDefinition. Add require statements to configuration.rb. Remove all remaining nested class bodies from configuration.rb. The main configuration.rb should now contain only: require statements, the Configuration class with attr_accessor/attr_reader declarations, initialize (instantiating all settings objects), queue_name_for, and concurrency_for.
|
|
102
|
-
- **verify:** `ruby -c lib/source_monitor/configuration/scraper_registry.rb lib/source_monitor/configuration/events.rb lib/source_monitor/configuration/models.rb lib/source_monitor/configuration/model_definition.rb lib/source_monitor/configuration/validation_definition.rb` exits 0 AND `bin/rails test test/lib/source_monitor/configuration_test.rb` exits 0 AND `wc -l lib/source_monitor/configuration.rb` shows fewer than 120 lines
|
|
103
|
-
- **done:** All nested classes extracted. Configuration.rb under 120 lines. Full configuration test suite passes.
|
|
104
|
-
|
|
105
|
-
### Task 4: Verify line counts and full suite
|
|
106
|
-
|
|
107
|
-
- **name:** verify-extraction-complete
|
|
108
|
-
- **files:**
|
|
109
|
-
- `lib/source_monitor/configuration.rb`
|
|
110
|
-
- `lib/source_monitor/configuration/*.rb`
|
|
111
|
-
- **action:** Run `wc -l` on all extracted files and the main configuration.rb. Verify no extracted file exceeds 300 lines. Verify the main file is under 120 lines. Run `bin/rubocop lib/source_monitor/configuration.rb lib/source_monitor/configuration/` to verify style compliance. Run `bin/rails test` for the full suite. Fix any RuboCop issues (likely just the frozen_string_literal pragma which should already be present).
|
|
112
|
-
- **verify:** `wc -l lib/source_monitor/configuration.rb` shows fewer than 120 lines AND no file in `lib/source_monitor/configuration/` exceeds 300 lines AND `bin/rails test` exits 0 with 760+ runs AND `bin/rubocop lib/source_monitor/configuration.rb lib/source_monitor/configuration/` exits 0
|
|
113
|
-
- **done:** Configuration extraction complete. All files under limits. Full suite passes. RuboCop clean.
|
|
114
|
-
|
|
115
|
-
## Verification
|
|
116
|
-
|
|
117
|
-
1. `wc -l lib/source_monitor/configuration.rb` shows fewer than 120 lines
|
|
118
|
-
2. `ls lib/source_monitor/configuration/*.rb | wc -l` shows 12 files
|
|
119
|
-
3. `bin/rails test test/lib/source_monitor/configuration_test.rb` exits 0
|
|
120
|
-
4. `bin/rails test` exits 0 (no regressions)
|
|
121
|
-
5. `bin/rubocop lib/source_monitor/configuration.rb lib/source_monitor/configuration/` exits 0
|
|
122
|
-
|
|
123
|
-
## Success Criteria
|
|
124
|
-
|
|
125
|
-
- [ ] Configuration main file under 120 lines (down from 655)
|
|
126
|
-
- [ ] 12 extracted files in lib/source_monitor/configuration/
|
|
127
|
-
- [ ] No extracted file exceeds 300 lines
|
|
128
|
-
- [ ] Public API unchanged -- SourceMonitor.configure { |c| c.http.timeout = 30 } works
|
|
129
|
-
- [ ] All existing configuration tests pass without modification (860 lines)
|
|
130
|
-
- [ ] Full test suite passes (760+ runs, 0 failures)
|
|
131
|
-
- [ ] RuboCop passes on all modified/new files
|
|
132
|
-
- [ ] REQ-09 satisfied
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# PLAN-03 Summary: extract-import-sessions-controller
|
|
2
|
-
|
|
3
|
-
## Status: COMPLETE
|
|
4
|
-
|
|
5
|
-
## Commits
|
|
6
|
-
|
|
7
|
-
- **Hash:** `9dce996`
|
|
8
|
-
- **Message:** `refactor: extract import-sessions-controller into 4 concerns [PLAN-03]`
|
|
9
|
-
- **Files changed:** 5 files (4 new concerns + slimmed controller)
|
|
10
|
-
|
|
11
|
-
## Tasks Completed
|
|
12
|
-
|
|
13
|
-
### Task 1: Extract OpmlParser concern
|
|
14
|
-
- Created `app/controllers/source_monitor/import_sessions/opml_parser.rb` (130 lines)
|
|
15
|
-
- Moved OPML parsing methods: parse_opml_file, build_entry, malformed_entry, validate_upload!, etc.
|
|
16
|
-
- Moved constants: ALLOWED_CONTENT_TYPES, GENERIC_CONTENT_TYPES, UploadError class
|
|
17
|
-
- All 29 controller tests pass
|
|
18
|
-
|
|
19
|
-
### Task 2: Extract EntryAnnotation concern
|
|
20
|
-
- Created `app/controllers/source_monitor/import_sessions/entry_annotation.rb` (187 lines)
|
|
21
|
-
- Moved entry annotation methods: annotated_entries, normalize_entry, filter_entries, selectable_entries, build_selection_from_params, etc.
|
|
22
|
-
- All 29 controller tests pass
|
|
23
|
-
|
|
24
|
-
### Task 3: Extract HealthCheckManagement concern
|
|
25
|
-
- Created `app/controllers/source_monitor/import_sessions/health_check_management.rb` (112 lines)
|
|
26
|
-
- Moved health check methods: start_health_checks_if_needed, reset_health_results, enqueue_health_check_jobs, health_check_progress, etc.
|
|
27
|
-
- All 29 controller tests pass
|
|
28
|
-
|
|
29
|
-
### Task 4: Extract BulkConfiguration concern
|
|
30
|
-
- Created `app/controllers/source_monitor/import_sessions/bulk_configuration.rb` (106 lines)
|
|
31
|
-
- Moved bulk config methods: build_bulk_source, sample_identity_attributes, persist_bulk_settings_if_valid!, bulk_settings_payload, etc.
|
|
32
|
-
- Controller reduced to 295 lines (target: <300)
|
|
33
|
-
- All 29 controller tests pass
|
|
34
|
-
|
|
35
|
-
## Deviations
|
|
36
|
-
|
|
37
|
-
| ID | Description | Impact |
|
|
38
|
-
|----|-------------|--------|
|
|
39
|
-
| None | No deviations from plan | N/A |
|
|
40
|
-
|
|
41
|
-
## Verification Results
|
|
42
|
-
|
|
43
|
-
| Check | Result |
|
|
44
|
-
|-------|--------|
|
|
45
|
-
| `wc -l import_sessions_controller.rb` | 295 lines (target: <300) |
|
|
46
|
-
| `ls import_sessions/*.rb \| wc -l` | 4 concern files |
|
|
47
|
-
| `bin/rails test test/controllers/.../import_sessions_controller_test.rb` | 29 runs, 133 assertions, 0 failures, 0 errors |
|
|
48
|
-
| `bin/rubocop import_sessions_controller.rb import_sessions/` | 5 files inspected, 0 offenses |
|
|
49
|
-
|
|
50
|
-
## Success Criteria
|
|
51
|
-
|
|
52
|
-
- [x] ImportSessionsController main file under 300 lines (295, down from 792)
|
|
53
|
-
- [x] Four concern modules created in app/controllers/source_monitor/import_sessions/
|
|
54
|
-
- [x] No concern file exceeds 300 lines (largest: entry_annotation.rb at 187)
|
|
55
|
-
- [x] All wizard routes and step handling preserved
|
|
56
|
-
- [x] All existing controller tests pass without modification (29 runs, 0 failures)
|
|
57
|
-
- [x] Full test suite passes (760 runs, 0 failures)
|
|
58
|
-
- [x] RuboCop passes on all modified/new files
|
|
59
|
-
- [x] REQ-10 satisfied
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
phase: 3
|
|
3
|
-
plan: 3
|
|
4
|
-
title: extract-import-sessions-controller
|
|
5
|
-
wave: 1
|
|
6
|
-
depends_on: []
|
|
7
|
-
skills_used: []
|
|
8
|
-
must_haves:
|
|
9
|
-
truths:
|
|
10
|
-
- "Running `wc -l app/controllers/source_monitor/import_sessions_controller.rb` shows fewer than 300 lines"
|
|
11
|
-
- "Running `bin/rails test test/controllers/source_monitor/import_sessions_controller_test.rb` exits 0 with zero failures"
|
|
12
|
-
- "Running `bin/rails test` exits 0 with no regressions (760+ runs, 0 failures)"
|
|
13
|
-
- "Running `ls app/controllers/source_monitor/import_sessions/` shows at least 4 .rb files"
|
|
14
|
-
- "Running `ruby -c app/controllers/source_monitor/import_sessions_controller.rb` exits 0"
|
|
15
|
-
- "Running `grep -r 'ImportSessionsController' test/ --include='*.rb' -l` shows no test files were renamed or removed"
|
|
16
|
-
artifacts:
|
|
17
|
-
- "app/controllers/source_monitor/import_sessions/opml_parser.rb -- OPML file parsing and validation"
|
|
18
|
-
- "app/controllers/source_monitor/import_sessions/entry_annotation.rb -- entry annotation, filtering, selection logic"
|
|
19
|
-
- "app/controllers/source_monitor/import_sessions/health_check_management.rb -- health check lifecycle"
|
|
20
|
-
- "app/controllers/source_monitor/import_sessions/bulk_configuration.rb -- bulk source configuration and settings"
|
|
21
|
-
- "app/controllers/source_monitor/import_sessions_controller.rb -- slimmed to orchestrator under 300 lines"
|
|
22
|
-
key_links:
|
|
23
|
-
- "REQ-10 satisfied -- ImportSessionsController broken into focused concerns"
|
|
24
|
-
- "Public API unchanged -- all wizard routes and step handling preserved"
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
# Plan 03: extract-import-sessions-controller
|
|
28
|
-
|
|
29
|
-
## Objective
|
|
30
|
-
|
|
31
|
-
Extract `app/controllers/source_monitor/import_sessions_controller.rb` (792 lines) into focused concerns under `app/controllers/source_monitor/import_sessions/`. The controller has five distinct responsibility clusters beyond the core CRUD: (1) OPML file parsing and validation, (2) entry annotation/filtering/selection, (3) health check lifecycle management, (4) bulk source configuration. Each becomes a concern module included in the controller. The controller retains the action methods (new, create, show, update, destroy), step handlers, and before_action filters.
|
|
32
|
-
|
|
33
|
-
## Context
|
|
34
|
-
|
|
35
|
-
<context>
|
|
36
|
-
@app/controllers/source_monitor/import_sessions_controller.rb -- 792 lines. The OPML import wizard controller. 5-step flow: upload -> preview -> health_check -> configure -> confirm. Contains file parsing, entry annotation, health check management, bulk configuration, selection management, and user authentication fallback.
|
|
37
|
-
@test/controllers/source_monitor/import_sessions_controller_test.rb -- 572 lines, integration tests for the wizard flow. MUST NOT be modified.
|
|
38
|
-
@app/controllers/concerns/source_monitor/sanitizes_search_params.rb -- existing controller concern pattern in this codebase
|
|
39
|
-
@lib/source_monitor/import_sessions/entry_normalizer.rb -- existing extracted support class for import sessions
|
|
40
|
-
@lib/source_monitor/sources/params.rb -- source parameter handling, used by the controller
|
|
41
|
-
@config/routes.rb -- routes for import_sessions (RESTful + step param)
|
|
42
|
-
|
|
43
|
-
**Decomposition rationale:** ImportSessionsController has four clearly separable responsibility clusters:
|
|
44
|
-
1. **OPML parsing** (parse_opml_file, build_entry, malformed_entry, outline_attribute, valid_feed_url?, validate_upload!, content_type_allowed?, generic_content_type?, build_file_metadata, uploading_file?, UploadError) -- ~100 lines, pure data transformation
|
|
45
|
-
2. **Entry annotation** (annotated_entries, normalize_entry, filter_entries, selectable_entries_from, selectable_entries, build_selection_from_params, health_check_selection_from_params, advancing_from_health_check?, advancing_from_preview?, normalize_page_param) -- ~100 lines, query/presentation logic
|
|
46
|
-
3. **Health check management** (start_health_checks_if_needed, reset_health_results, enqueue_health_check_jobs, deactivate_health_checks!, health_check_entries, health_check_progress, health_check_complete?, health_check_targets) -- ~100 lines, async job orchestration
|
|
47
|
-
4. **Bulk configuration** (build_bulk_source, build_bulk_source_from_session, build_bulk_source_from_params, sample_identity_attributes, selected_entries_for_identity, fallback_identity, configure_source_params, strip_identity_attributes, persist_bulk_settings_if_valid!, bulk_settings_payload, bulk_setting_keys) -- ~90 lines, source configuration logic
|
|
48
|
-
|
|
49
|
-
The remaining ~400 lines (action methods, step handlers, prepare_* context methods, user auth, step navigation) stay in the controller.
|
|
50
|
-
|
|
51
|
-
**Trade-offs considered:**
|
|
52
|
-
- Could use service objects instead of concerns. But these methods need controller context (params, render, redirect_to) or instance variables (@import_session, @current_step). Concerns are the idiomatic Rails pattern for extracting controller private methods.
|
|
53
|
-
- Could extract each wizard step into its own concern. But that fragments the step-handling logic too much and makes the flow harder to follow. Grouping by responsibility (parsing, annotation, health, config) is more cohesive.
|
|
54
|
-
- The user auth fallback methods (current_user_id, ensure_current_user!, fallback_user_id, create_guest_user, guest_value_for) are already inside a :nocov: block and are short (~60 lines). Leave them in the main controller -- they're security-sensitive and shouldn't be in a shared concern.
|
|
55
|
-
|
|
56
|
-
**What constrains the structure:**
|
|
57
|
-
- Concerns are placed in app/controllers/source_monitor/import_sessions/ (not in app/controllers/concerns/) because they're specific to this one controller. This follows the convention of co-locating private concerns with their controller.
|
|
58
|
-
- Each concern is `extend ActiveSupport::Concern` and `included in` the controller
|
|
59
|
-
- The controller must `include` the concerns and delegate appropriately
|
|
60
|
-
- All wizard step handling (handle_upload_step, handle_preview_step, etc.) stays in the main controller because they orchestrate across concerns
|
|
61
|
-
</context>
|
|
62
|
-
|
|
63
|
-
## Tasks
|
|
64
|
-
|
|
65
|
-
### Task 1: Extract OpmlParser concern
|
|
66
|
-
|
|
67
|
-
- **name:** extract-opml-parser
|
|
68
|
-
- **files:**
|
|
69
|
-
- `app/controllers/source_monitor/import_sessions/opml_parser.rb` (new)
|
|
70
|
-
- `app/controllers/source_monitor/import_sessions_controller.rb`
|
|
71
|
-
- **action:** Create `app/controllers/source_monitor/import_sessions/` directory. Create `opml_parser.rb` containing a `SourceMonitor::ImportSessions::OpmlParser` module using `extend ActiveSupport::Concern`. Move these methods into the concern as private methods:
|
|
72
|
-
- `parse_opml_file` (lines 305-327)
|
|
73
|
-
- `build_entry` (lines 329-353)
|
|
74
|
-
- `malformed_entry` (lines 355-367)
|
|
75
|
-
- `outline_attribute` (lines 369-372)
|
|
76
|
-
- `valid_feed_url?` (lines 374-379)
|
|
77
|
-
- `validate_upload!` (lines 282-295)
|
|
78
|
-
- `content_type_allowed?` (lines 297-299)
|
|
79
|
-
- `generic_content_type?` (lines 301-303)
|
|
80
|
-
- `build_file_metadata` (lines 255-264)
|
|
81
|
-
- `uploading_file?` (lines 266-268)
|
|
82
|
-
|
|
83
|
-
Move the constants `ALLOWED_CONTENT_TYPES`, `GENERIC_CONTENT_TYPES`, and the `UploadError` class into the concern as well. In the controller, add `include SourceMonitor::ImportSessions::OpmlParser`. Remove the moved methods and constants from the controller. Add `require "source_monitor/import_sessions/opml_parser"` or rely on Rails autoloading in the app/ directory (prefer autoloading since this is in app/).
|
|
84
|
-
- **verify:** `ruby -c app/controllers/source_monitor/import_sessions/opml_parser.rb` exits 0 AND `bin/rails test test/controllers/source_monitor/import_sessions_controller_test.rb` exits 0
|
|
85
|
-
- **done:** OpmlParser concern extracted. Controller includes it. Tests pass.
|
|
86
|
-
|
|
87
|
-
### Task 2: Extract EntryAnnotation concern
|
|
88
|
-
|
|
89
|
-
- **name:** extract-entry-annotation
|
|
90
|
-
- **files:**
|
|
91
|
-
- `app/controllers/source_monitor/import_sessions/entry_annotation.rb` (new)
|
|
92
|
-
- `app/controllers/source_monitor/import_sessions_controller.rb`
|
|
93
|
-
- **action:** Create `entry_annotation.rb` containing a `SourceMonitor::ImportSessions::EntryAnnotation` module using `extend ActiveSupport::Concern`. Move these methods as private:
|
|
94
|
-
- `annotated_entries` (lines 501-523)
|
|
95
|
-
- `normalize_entry` (lines 561-564)
|
|
96
|
-
- `filter_entries` (lines 566-575)
|
|
97
|
-
- `selectable_entries_from` (lines 557-559)
|
|
98
|
-
- `selectable_entries` (lines 607-609)
|
|
99
|
-
- `build_selection_from_params` (lines 577-592)
|
|
100
|
-
- `health_check_selection_from_params` (lines 594-605)
|
|
101
|
-
- `advancing_from_health_check?` (lines 611-613)
|
|
102
|
-
- `advancing_from_preview?` (lines 615-617)
|
|
103
|
-
- `normalize_page_param` (lines 619-625)
|
|
104
|
-
- `permitted_filter` (lines 778-783)
|
|
105
|
-
- `preview_per_page` (lines 785-787)
|
|
106
|
-
|
|
107
|
-
In the controller, add `include SourceMonitor::ImportSessions::EntryAnnotation`. Remove moved methods.
|
|
108
|
-
- **verify:** `ruby -c app/controllers/source_monitor/import_sessions/entry_annotation.rb` exits 0 AND `bin/rails test test/controllers/source_monitor/import_sessions_controller_test.rb` exits 0
|
|
109
|
-
- **done:** EntryAnnotation concern extracted. Tests pass.
|
|
110
|
-
|
|
111
|
-
### Task 3: Extract HealthCheckManagement concern
|
|
112
|
-
|
|
113
|
-
- **name:** extract-health-check-management
|
|
114
|
-
- **files:**
|
|
115
|
-
- `app/controllers/source_monitor/import_sessions/health_check_management.rb` (new)
|
|
116
|
-
- `app/controllers/source_monitor/import_sessions_controller.rb`
|
|
117
|
-
- **action:** Create `health_check_management.rb` containing a `SourceMonitor::ImportSessions::HealthCheckManagement` module. Move these methods as private:
|
|
118
|
-
- `start_health_checks_if_needed` (lines 627-660)
|
|
119
|
-
- `reset_health_results` (lines 753-761)
|
|
120
|
-
- `enqueue_health_check_jobs` (lines 763-767)
|
|
121
|
-
- `deactivate_health_checks!` (lines 769-776)
|
|
122
|
-
- `health_check_entries` (lines 525-532)
|
|
123
|
-
- `health_check_progress` (lines 534-545)
|
|
124
|
-
- `health_check_complete?` (lines 547-549)
|
|
125
|
-
- `health_check_targets` (lines 551-555)
|
|
126
|
-
|
|
127
|
-
In the controller, add `include SourceMonitor::ImportSessions::HealthCheckManagement`. Remove moved methods.
|
|
128
|
-
- **verify:** `ruby -c app/controllers/source_monitor/import_sessions/health_check_management.rb` exits 0 AND `bin/rails test test/controllers/source_monitor/import_sessions_controller_test.rb` exits 0
|
|
129
|
-
- **done:** HealthCheckManagement concern extracted. Tests pass.
|
|
130
|
-
|
|
131
|
-
### Task 4: Extract BulkConfiguration concern
|
|
132
|
-
|
|
133
|
-
- **name:** extract-bulk-configuration
|
|
134
|
-
- **files:**
|
|
135
|
-
- `app/controllers/source_monitor/import_sessions/bulk_configuration.rb` (new)
|
|
136
|
-
- `app/controllers/source_monitor/import_sessions_controller.rb`
|
|
137
|
-
- **action:** Create `bulk_configuration.rb` containing a `SourceMonitor::ImportSessions::BulkConfiguration` module. Move these methods as private:
|
|
138
|
-
- `build_bulk_source` (lines 675-682)
|
|
139
|
-
- `build_bulk_source_from_session` (lines 662-665)
|
|
140
|
-
- `build_bulk_source_from_params` (lines 667-673)
|
|
141
|
-
- `sample_identity_attributes` (lines 684-694)
|
|
142
|
-
- `selected_entries_for_identity` (lines 696-702)
|
|
143
|
-
- `fallback_identity` (lines 704-709)
|
|
144
|
-
- `configure_source_params` (lines 711-715)
|
|
145
|
-
- `strip_identity_attributes` (lines 717-719)
|
|
146
|
-
- `persist_bulk_settings_if_valid!` (lines 721-727)
|
|
147
|
-
- `bulk_settings_payload` (lines 729-734)
|
|
148
|
-
- `bulk_setting_keys` (lines 736-751)
|
|
149
|
-
|
|
150
|
-
In the controller, add `include SourceMonitor::ImportSessions::BulkConfiguration`. Remove moved methods. After this task, verify the controller is under 300 lines. It should contain: before_actions, new, create, show, update, destroy, set_import_session, set_wizard_step, persist_step!, the 5 handle_*_step methods, the 4 prepare_*_context methods, state_params, permitted_step, target_step, session_attributes, auth methods, and the include statements. If the controller is between 300-320 lines, move `state_params`, `session_attributes`, `permitted_step`, and `target_step` into the EntryAnnotation concern (they're helper methods that support step navigation and parameter handling).
|
|
151
|
-
- **verify:** `ruby -c app/controllers/source_monitor/import_sessions/bulk_configuration.rb` exits 0 AND `wc -l app/controllers/source_monitor/import_sessions_controller.rb` shows fewer than 300 lines AND `bin/rails test test/controllers/source_monitor/import_sessions_controller_test.rb` exits 0 AND `bin/rails test` exits 0 with 760+ runs AND `bin/rubocop app/controllers/source_monitor/import_sessions_controller.rb app/controllers/source_monitor/import_sessions/` exits 0
|
|
152
|
-
- **done:** BulkConfiguration extracted. Controller under 300 lines. Full suite passes. RuboCop clean. REQ-10 satisfied.
|
|
153
|
-
|
|
154
|
-
## Verification
|
|
155
|
-
|
|
156
|
-
1. `wc -l app/controllers/source_monitor/import_sessions_controller.rb` shows fewer than 300 lines
|
|
157
|
-
2. `ls app/controllers/source_monitor/import_sessions/*.rb | wc -l` shows 4 files
|
|
158
|
-
3. `bin/rails test test/controllers/source_monitor/import_sessions_controller_test.rb` exits 0
|
|
159
|
-
4. `bin/rails test` exits 0 (no regressions)
|
|
160
|
-
5. `bin/rubocop app/controllers/source_monitor/import_sessions_controller.rb app/controllers/source_monitor/import_sessions/` exits 0
|
|
161
|
-
|
|
162
|
-
## Success Criteria
|
|
163
|
-
|
|
164
|
-
- [ ] ImportSessionsController main file under 300 lines (down from 792)
|
|
165
|
-
- [ ] Four concern modules created in app/controllers/source_monitor/import_sessions/
|
|
166
|
-
- [ ] No concern file exceeds 300 lines
|
|
167
|
-
- [ ] All wizard routes and step handling preserved
|
|
168
|
-
- [ ] All existing controller tests pass without modification (572 lines)
|
|
169
|
-
- [ ] Full test suite passes (760+ runs, 0 failures)
|
|
170
|
-
- [ ] RuboCop passes on all modified/new files
|
|
171
|
-
- [ ] REQ-10 satisfied
|