source_monitor 0.7.1 → 0.8.1
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 +18 -6
- 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 +45 -0
- data/CLAUDE.md +24 -3
- data/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/Rakefile +0 -2
- 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/import_sessions/steps/_preview.html.erb +7 -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/source_monitor.gemspec +1 -1
- metadata +6 -106
- data/.vbw-planning/PROJECT.md +0 -51
- data/.vbw-planning/ROADMAP.md +0 -53
- 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
- data/.vbw-planning/phases/02-test-performance/.context-dev.md +0 -75
- data/.vbw-planning/phases/02-test-performance/.context-lead.md +0 -89
- data/.vbw-planning/phases/02-test-performance/.context-qa.md +0 -23
- data/.vbw-planning/phases/02-test-performance/02-RESEARCH.md +0 -56
- data/.vbw-planning/phases/02-test-performance/02-VERIFICATION.md +0 -51
- data/.vbw-planning/phases/02-test-performance/PLAN-01-SUMMARY.md +0 -37
- data/.vbw-planning/phases/02-test-performance/PLAN-01.md +0 -156
- data/.vbw-planning/phases/02-test-performance/PLAN-02-SUMMARY.md +0 -33
- data/.vbw-planning/phases/02-test-performance/PLAN-02.md +0 -120
- data/.vbw-planning/phases/02-test-performance/PLAN-03-SUMMARY.md +0 -30
- data/.vbw-planning/phases/02-test-performance/PLAN-03.md +0 -154
- data/.vbw-planning/phases/02-test-performance/PLAN-04-SUMMARY.md +0 -28
- data/.vbw-planning/phases/02-test-performance/PLAN-04.md +0 -133
data/.vbw-planning/ROADMAP.md
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# Roadmap
|
|
2
|
-
|
|
3
|
-
## Milestone: aia-ssl-fix
|
|
4
|
-
|
|
5
|
-
### Phases
|
|
6
|
-
|
|
7
|
-
1. [x] **AIA Certificate Resolution** -- Fix SSL failures for feeds with missing intermediate certificates by implementing AIA (Authority Information Access) resolution
|
|
8
|
-
2. [x] **Test Performance** -- Reduce test suite runtime from ~133s to ~50s by splitting monolithic test classes, enabling parallelism, reducing log IO, and adopting before_all
|
|
9
|
-
|
|
10
|
-
### Phase Details
|
|
11
|
-
|
|
12
|
-
#### Phase 1: AIA Certificate Resolution
|
|
13
|
-
|
|
14
|
-
**Goal:** Implement automatic AIA intermediate certificate fetching so feeds like netflixtechblog.com (served via Medium/AWS with wrong intermediates) succeed without manual cert configuration.
|
|
15
|
-
|
|
16
|
-
**Requirements:**
|
|
17
|
-
- REQ-AIA-01: Create AIAResolver module with thread-safe cache and 1-hour TTL
|
|
18
|
-
- REQ-AIA-02: Add cert_store: parameter to HTTP.client for custom cert stores
|
|
19
|
-
- REQ-AIA-03: On Faraday::SSLError, attempt AIA resolution before failing
|
|
20
|
-
- REQ-AIA-04: Best-effort only -- never make things worse (rescue StandardError -> nil)
|
|
21
|
-
|
|
22
|
-
**Success Criteria:**
|
|
23
|
-
- [ ] AIAResolver.resolve(hostname) fetches leaf cert, extracts AIA URL, downloads intermediate
|
|
24
|
-
- [ ] HTTP.client(cert_store:) accepts and uses custom cert stores
|
|
25
|
-
- [ ] FeedFetcher retries once with AIA-resolved cert store on SSL failure
|
|
26
|
-
- [ ] All existing tests pass (1003+), new tests cover AIA paths
|
|
27
|
-
- [ ] RuboCop zero offenses, Brakeman zero warnings
|
|
28
|
-
|
|
29
|
-
#### Phase 2: Test Performance
|
|
30
|
-
|
|
31
|
-
**Goal:** Reduce test suite wall-clock time from ~133s to ~50s through structural optimizations. The 3-agent investigation identified that FeedFetcherTest (71 tests, 84.8s, 64% of total) is a monolithic class that cannot be parallelized, integration tests add 31s, and 95MB of debug logging adds 5-15s.
|
|
32
|
-
|
|
33
|
-
**Requirements:**
|
|
34
|
-
- REQ-PERF-01: Split FeedFetcherTest into 5+ smaller classes by concern (success paths, error handling, adaptive interval, dirty-check, content fingerprint, utilities)
|
|
35
|
-
- REQ-PERF-02: Set test log level to :warn in test/dummy/config/environments/test.rb (eliminates 95MB log IO)
|
|
36
|
-
- REQ-PERF-03: Tag integration tests (host_install_flow, release_packaging) so they can be excluded during dev with --exclude-pattern
|
|
37
|
-
- REQ-PERF-04: Switch default parallelism from forks to threads (avoids PG segfault, enables splitting benefit)
|
|
38
|
-
- REQ-PERF-05: Adopt before_all/setup_once in top DB-heavy test files (dashboard/queries_test.rb, etc.)
|
|
39
|
-
|
|
40
|
-
**Success Criteria:**
|
|
41
|
-
- [ ] FeedFetcherTest split into 5+ files, each independently runnable
|
|
42
|
-
- [ ] All 1031+ tests pass with PARALLEL_WORKERS=1 and default workers
|
|
43
|
-
- [ ] Test suite completes in <70s locally (down from 133s)
|
|
44
|
-
- [ ] `bin/rails test --exclude-pattern="**/integration/**"` runs <50s
|
|
45
|
-
- [ ] RuboCop zero offenses, Brakeman zero warnings
|
|
46
|
-
- [ ] No test isolation regressions (parallel runs still green)
|
|
47
|
-
|
|
48
|
-
### Progress
|
|
49
|
-
|
|
50
|
-
| Phase | Status | Plans | Completed |
|
|
51
|
-
|-------|--------|-------|-----------|
|
|
52
|
-
| 1. AIA Certificate Resolution | Complete | 3 | 3 |
|
|
53
|
-
| 2. Test Performance | Complete | 4 | 4 |
|
data/.vbw-planning/SHIPPED.md
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# Shipped Milestones
|
|
2
|
-
|
|
3
|
-
## default (2026-02-09 to 2026-02-10)
|
|
4
|
-
|
|
5
|
-
**Core value:** Drop-in Rails engine for feed monitoring, content scraping, and operational dashboards.
|
|
6
|
-
|
|
7
|
-
### Metrics
|
|
8
|
-
|
|
9
|
-
| Metric | Value |
|
|
10
|
-
|--------|-------|
|
|
11
|
-
| Phases | 4 |
|
|
12
|
-
| Plans completed | 14 |
|
|
13
|
-
| Commits | 18 |
|
|
14
|
-
| Requirements satisfied | 15/15 |
|
|
15
|
-
| Test runs | 841 (up from 473) |
|
|
16
|
-
| Coverage | 86.97% line (510 uncovered, down from 2117) |
|
|
17
|
-
|
|
18
|
-
### Phases
|
|
19
|
-
|
|
20
|
-
1. Coverage Analysis & Quick Wins (2 plans)
|
|
21
|
-
2. Critical Path Test Coverage (5 plans)
|
|
22
|
-
3. Large File Refactoring (4 plans)
|
|
23
|
-
4. Code Quality & Conventions Cleanup (3 plans)
|
|
24
|
-
|
|
25
|
-
### Archive
|
|
26
|
-
|
|
27
|
-
Location: `.vbw-planning/milestones/default/`
|
|
28
|
-
Tag: `milestone/default`
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## upgrade-assurance (2026-02-12 to 2026-02-13)
|
|
33
|
-
|
|
34
|
-
**Goal:** Give host app developers confidence that gem updates go smoothly -- automated migration detection, upgrade command, config validation, and AI-assisted upgrade guidance.
|
|
35
|
-
|
|
36
|
-
### Metrics
|
|
37
|
-
|
|
38
|
-
| Metric | Value |
|
|
39
|
-
|--------|-------|
|
|
40
|
-
| Phases | 3 |
|
|
41
|
-
| Plans completed | 3 |
|
|
42
|
-
| Tasks completed | 14 |
|
|
43
|
-
| Commits | 12 |
|
|
44
|
-
| Requirements satisfied | 5/5 (REQ-26 through REQ-30) |
|
|
45
|
-
| Tests | 1003 (up from 973) |
|
|
46
|
-
|
|
47
|
-
### Phases
|
|
48
|
-
|
|
49
|
-
1. Upgrade Command & Migration Verifier (1 plan, 5 tasks)
|
|
50
|
-
2. Configuration Deprecation Framework (1 plan, 4 tasks)
|
|
51
|
-
3. Upgrade Skill & Documentation (1 plan, 5 tasks)
|
|
52
|
-
|
|
53
|
-
### Key Decisions
|
|
54
|
-
|
|
55
|
-
- 3 phases: command, config, skill -- each independently valuable
|
|
56
|
-
- `.source_monitor_version` marker file for version tracking
|
|
57
|
-
- Deprecation registry with :warning and :error severities
|
|
58
|
-
- sm-upgrade as a consumer skill (installed by default)
|
|
59
|
-
|
|
60
|
-
### Archive
|
|
61
|
-
|
|
62
|
-
Location: `.vbw-planning/milestones/upgrade-assurance/`
|
|
63
|
-
Tag: `milestone/upgrade-assurance`
|
data/.vbw-planning/STATE.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# State
|
|
2
|
-
|
|
3
|
-
## Current Position
|
|
4
|
-
|
|
5
|
-
- **Milestone:** aia-ssl-fix
|
|
6
|
-
- **Phase:** 2 -- Test Performance
|
|
7
|
-
- **Status:** Complete
|
|
8
|
-
- **Progress:** 100%
|
|
9
|
-
|
|
10
|
-
## Decisions
|
|
11
|
-
|
|
12
|
-
| Decision | Date | Context |
|
|
13
|
-
|----------|------|---------|
|
|
14
|
-
| Single-phase milestone for AIA fix | 2026-02-17 | Complete plan already validated; no scoping needed |
|
|
15
|
-
| 3 plans with wave parallelism | 2026-02-17 | Plans 01+02 (wave 1, disjoint files), Plan 03 (wave 2, integration) |
|
|
16
|
-
|
|
17
|
-
## Todos
|
|
18
|
-
|
|
19
|
-
## Metrics
|
|
20
|
-
|
|
21
|
-
- **Started:** 2026-02-17
|
|
22
|
-
- **Phases:** 1
|
|
23
|
-
- **Plans:** 3
|
|
24
|
-
- **Tests at start:** 1003
|
|
25
|
-
- **Tests at end:** 1025
|
|
26
|
-
- **Commits:** 4 (f60e9bf, 4c9568a, 9c38bc3, e68a6b0)
|
|
27
|
-
- **Plans completed:** 3/3
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
# Architecture
|
|
2
|
-
|
|
3
|
-
## System Overview
|
|
4
|
-
|
|
5
|
-
SourceMonitor is a mountable Rails 8 engine that ingests RSS, Atom, and JSON feeds, scrapes full article content, and surfaces Solid Queue-powered dashboards for monitoring and remediation. It is packaged as a RubyGem and mounted into a host Rails application.
|
|
6
|
-
|
|
7
|
-
## Architecture Pattern
|
|
8
|
-
|
|
9
|
-
**Mountable Rails Engine** with `isolate_namespace SourceMonitor`. The engine:
|
|
10
|
-
- Provides its own models, controllers, views, jobs, and frontend assets
|
|
11
|
-
- Uses its own `ApplicationRecord`, `ApplicationController`, and `ApplicationJob` base classes
|
|
12
|
-
- Namespaces all database tables under a configurable prefix (default: `sourcemon_`)
|
|
13
|
-
- Registers its own routes, asset paths, and initializers
|
|
14
|
-
|
|
15
|
-
## Core Domain Modules
|
|
16
|
-
|
|
17
|
-
### 1. Feed Fetching (`lib/source_monitor/fetching/`)
|
|
18
|
-
The primary data ingestion pipeline:
|
|
19
|
-
- `FeedFetcher` -- Orchestrates HTTP request, feed parsing via Feedjira, item creation, adaptive interval scheduling, and retry policies
|
|
20
|
-
- `FetchRunner` -- Entry point for enqueuing fetch jobs; handles concurrency control
|
|
21
|
-
- `FetchError` hierarchy -- Typed error classes (TimeoutError, ConnectionError, HTTPError, ParsingError)
|
|
22
|
-
- `RetryPolicy` -- Exponential backoff with circuit breaker pattern
|
|
23
|
-
- `StalledFetchReconciler` -- Recovers stalled fetch jobs
|
|
24
|
-
|
|
25
|
-
### 2. Content Scraping (`lib/source_monitor/scraping/` + `lib/source_monitor/scrapers/`)
|
|
26
|
-
Pluggable content extraction system:
|
|
27
|
-
- `Scrapers::Base` -- Abstract adapter contract; subclasses implement `#call` returning a `Result` struct
|
|
28
|
-
- `Scrapers::Readability` -- Default adapter using ruby-readability
|
|
29
|
-
- `Scraping::ItemScraper` -- Orchestrator that resolves adapter, executes scrape, persists results
|
|
30
|
-
- `Scraping::BulkSourceScraper` -- Batch scraping across all items for a source
|
|
31
|
-
- `Scraping::Enqueuer` -- Manages scrape job queuing with in-flight throttling
|
|
32
|
-
- `Scraping::State` -- Tracks in-flight scrape state via cache/memory
|
|
33
|
-
|
|
34
|
-
### 3. Health Monitoring (`lib/source_monitor/health/`)
|
|
35
|
-
Source health tracking system:
|
|
36
|
-
- `SourceHealthMonitor` -- Computes health status from recent fetch history (sliding window)
|
|
37
|
-
- `SourceHealthCheck` -- One-off health probe for a source URL
|
|
38
|
-
- `ImportSourceHealthCheck` -- Variant for import wizard health checks
|
|
39
|
-
- `SourceHealthReset` -- Resets health status for a source
|
|
40
|
-
- Configurable thresholds: healthy (0.8), warning (0.5), auto-pause (0.2)
|
|
41
|
-
|
|
42
|
-
### 4. Scheduling (`lib/source_monitor/scheduler.rb`)
|
|
43
|
-
- `Scheduler` -- Periodic job that finds sources due for fetch using `FOR UPDATE SKIP LOCKED`
|
|
44
|
-
- Adaptive fetch interval algorithm (increase/decrease factors, jitter)
|
|
45
|
-
- Integrates with `StalledFetchReconciler` for recovery
|
|
46
|
-
|
|
47
|
-
### 5. Event System (`lib/source_monitor/events.rb`)
|
|
48
|
-
- Typed event structs: `ItemCreatedEvent`, `ItemScrapedEvent`, `FetchCompletedEvent`
|
|
49
|
-
- Callback-based dispatch with error isolation per handler
|
|
50
|
-
- Item processor pipeline for custom host-app processing
|
|
51
|
-
- Registration via `SourceMonitor.config.events.after_*` DSL
|
|
52
|
-
|
|
53
|
-
### 6. Configuration (`lib/source_monitor/configuration.rb`)
|
|
54
|
-
Rich nested configuration object with sub-configs:
|
|
55
|
-
- `HTTPSettings` -- timeout, retries, user agent, proxy, headers
|
|
56
|
-
- `ScraperRegistry` -- pluggable adapter registration
|
|
57
|
-
- `RetentionSettings` -- item retention days, max items, strategy (destroy/soft_delete)
|
|
58
|
-
- `RealtimeSettings` -- adapter selection (solid_cable/redis/async)
|
|
59
|
-
- `FetchingSettings` -- adaptive interval tuning
|
|
60
|
-
- `HealthSettings` -- health window and threshold configuration
|
|
61
|
-
- `AuthenticationSettings` -- pluggable authentication/authorization handlers
|
|
62
|
-
- `ScrapingSettings` -- concurrency limits
|
|
63
|
-
- `Events` -- callback registration
|
|
64
|
-
- `Models` -- concern injection and custom validation registration
|
|
65
|
-
|
|
66
|
-
### 7. Model Extensions (`lib/source_monitor/model_extensions.rb`)
|
|
67
|
-
Dynamic model customization system:
|
|
68
|
-
- Host apps can inject concerns and validations into engine models at configuration time
|
|
69
|
-
- `ModelExtensions.register(model_class, key)` -- called in each model class body
|
|
70
|
-
- `ModelExtensions.reload!` -- re-applies all extensions (called on configuration change)
|
|
71
|
-
- Manages table name assignment from configurable prefix
|
|
72
|
-
|
|
73
|
-
### 8. Real-time Broadcasting (`lib/source_monitor/realtime/`)
|
|
74
|
-
- `Realtime::Adapter` -- Configures Action Cable based on selected adapter
|
|
75
|
-
- `Realtime::Broadcaster` -- Broadcasts source/item updates and toast notifications
|
|
76
|
-
- `Dashboard::TurboBroadcaster` -- Wires dashboard stat updates to Turbo Streams
|
|
77
|
-
|
|
78
|
-
### 9. Setup/Installation System (`lib/source_monitor/setup/`)
|
|
79
|
-
Comprehensive host-app installation workflow:
|
|
80
|
-
- `Setup::CLI` -- Command-line interface for setup
|
|
81
|
-
- `Setup::Workflow` -- Orchestrates multi-step installation
|
|
82
|
-
- `Setup::Requirements` / `Setup::Detectors` -- System requirement checks
|
|
83
|
-
- `Setup::GemfileEditor` / `Setup::BundleInstaller` / `Setup::NodeInstaller` -- Dependency installation
|
|
84
|
-
- `Setup::InstallGenerator` -- Rails generator for migrations, routes, initializer
|
|
85
|
-
- `Setup::Verification::Runner` -- Post-install verification (Solid Queue, Action Cable)
|
|
86
|
-
|
|
87
|
-
### 10. OPML Import Wizard (`app/controllers/source_monitor/import_sessions_controller.rb`)
|
|
88
|
-
Multi-step wizard for bulk feed import:
|
|
89
|
-
- 5-step flow: upload -> preview -> health_check -> configure -> confirm
|
|
90
|
-
- State persisted in `ImportSession` model with JSONB columns
|
|
91
|
-
- Health check jobs run asynchronously with Turbo Stream progress updates
|
|
92
|
-
- Bulk settings applied to all imported sources
|
|
93
|
-
|
|
94
|
-
## Data Model
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
Source (sourcemon_sources)
|
|
98
|
-
|-- has_many Item (sourcemon_items)
|
|
99
|
-
| |-- has_one ItemContent (sourcemon_item_contents) [separate table for large content]
|
|
100
|
-
| |-- has_many ScrapeLog (sourcemon_scrape_logs)
|
|
101
|
-
| +-- has_many LogEntry (sourcemon_log_entries) [polymorphic]
|
|
102
|
-
|-- has_many FetchLog (sourcemon_fetch_logs)
|
|
103
|
-
|-- has_many HealthCheckLog (sourcemon_health_check_logs)
|
|
104
|
-
+-- has_many LogEntry (sourcemon_log_entries)
|
|
105
|
-
|
|
106
|
-
LogEntry (sourcemon_log_entries)
|
|
107
|
-
|-- delegated_type :loggable -> FetchLog | ScrapeLog | HealthCheckLog
|
|
108
|
-
|
|
109
|
-
ImportSession (sourcemon_import_sessions)
|
|
110
|
-
+-- JSONB columns for wizard state
|
|
111
|
-
|
|
112
|
-
ImportHistory (sourcemon_import_histories)
|
|
113
|
-
+-- Records completed imports
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## Job Architecture
|
|
117
|
-
|
|
118
|
-
All jobs inherit from `SourceMonitor::ApplicationJob` which inherits from the host app's `ApplicationJob` (or `ActiveJob::Base`).
|
|
119
|
-
|
|
120
|
-
| Job | Queue | Purpose |
|
|
121
|
-
|-----|-------|---------|
|
|
122
|
-
| `ScheduleFetchesJob` | fetch | Recurring: triggers Scheduler to find and enqueue due sources |
|
|
123
|
-
| `FetchFeedJob` | fetch | Fetches a single source's feed, creates items |
|
|
124
|
-
| `ScrapeItemJob` | scrape | Scrapes content for a single item |
|
|
125
|
-
| `SourceHealthCheckJob` | fetch | Runs health check for a source |
|
|
126
|
-
| `ImportSessionHealthCheckJob` | fetch | Health checks during OPML import wizard |
|
|
127
|
-
| `ImportOpmlJob` | fetch | Bulk-creates sources from OPML import |
|
|
128
|
-
| `LogCleanupJob` | fetch | Recurring: prunes old log entries |
|
|
129
|
-
| `ItemCleanupJob` | fetch | Recurring: prunes old items per retention policy |
|
|
130
|
-
|
|
131
|
-
Queue names are configurable via `config.fetch_queue_name` and `config.scrape_queue_name`.
|
|
132
|
-
|
|
133
|
-
## Security Architecture
|
|
134
|
-
|
|
135
|
-
- `Security::Authentication` -- Pluggable authentication via handler callbacks (symbol method names or callables)
|
|
136
|
-
- `Security::ParameterSanitizer` -- HTML sanitization of all user inputs via `ActionView::Base.full_sanitizer`
|
|
137
|
-
- `Models::Sanitizable` -- Concern that sanitizes string and hash model attributes before validation
|
|
138
|
-
- `Models::UrlNormalizable` -- URL normalization and validation concern
|
|
139
|
-
- `SanitizesSearchParams` -- Controller concern for search parameter sanitization
|
|
140
|
-
- CSRF protection enabled (`protect_from_forgery with: :exception`)
|
|
141
|
-
- No built-in user model -- delegates to host app
|
|
142
|
-
|
|
143
|
-
## Instrumentation & Metrics
|
|
144
|
-
|
|
145
|
-
- `Instrumentation` -- Emits `ActiveSupport::Notifications` events for fetch lifecycle
|
|
146
|
-
- `Metrics` -- In-memory counters and gauges, populated via notification subscribers
|
|
147
|
-
- Events: `source_monitor.fetch.start`, `source_monitor.fetch.finish`, `source_monitor.scheduler.run`, `source_monitor.items.duplicate`, `source_monitor.items.retention`
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
# Concerns
|
|
2
|
-
|
|
3
|
-
## Technical Debt
|
|
4
|
-
|
|
5
|
-
### 1. Large Files
|
|
6
|
-
- `lib/source_monitor/fetching/feed_fetcher.rb` (627 lines) -- The core fetch pipeline handles HTTP, parsing, item creation, adaptive intervals, retry strategies, and source updates all in one class. This is the largest single-responsibility violation.
|
|
7
|
-
- `lib/source_monitor/configuration.rb` (655 lines) -- Contains ~12 nested configuration classes in a single file. Each settings class could be extracted.
|
|
8
|
-
- `app/controllers/source_monitor/import_sessions_controller.rb` (792 lines) -- The OPML import wizard controller handles all 5 wizard steps, file parsing, health checks, selection management, and bulk operations.
|
|
9
|
-
- `lib/source_monitor/items/item_creator.rb` -- Large item creation file (based on coverage baseline entry count).
|
|
10
|
-
- `config/coverage_baseline.json` (2329 lines) -- This file itself is very large, indicating significant uncovered code.
|
|
11
|
-
|
|
12
|
-
### 2. Coverage Baseline Gaps
|
|
13
|
-
The `config/coverage_baseline.json` catalogs uncovered lines. Particularly notable gaps:
|
|
14
|
-
- `lib/source_monitor/items/item_creator.rb` -- Hundreds of uncovered branch lines
|
|
15
|
-
- `lib/source_monitor/fetching/feed_fetcher.rb` -- Extensive uncovered branches
|
|
16
|
-
- `lib/source_monitor/configuration.rb` -- Many configuration edge cases untested
|
|
17
|
-
- `lib/source_monitor/dashboard/queries.rb` -- Dashboard query logic largely uncovered
|
|
18
|
-
- `lib/source_monitor/realtime/broadcaster.rb` -- Broadcasting logic has gaps
|
|
19
|
-
- `lib/source_monitor/scraping/bulk_source_scraper.rb` -- Bulk scraping coverage gaps
|
|
20
|
-
- `lib/source_monitor/analytics/sources_index_metrics.rb` -- Analytics largely uncovered
|
|
21
|
-
|
|
22
|
-
### 3. LogEntry Hard-coded Table Name
|
|
23
|
-
In `app/models/source_monitor/log_entry.rb` line 6: `self.table_name = "sourcemon_log_entries"` -- This bypasses the configurable table name prefix system that all other models use via `ModelExtensions.register`. The table name is hard-coded despite the engine supporting custom prefixes.
|
|
24
|
-
|
|
25
|
-
### 4. `lib/source_monitor.rb` Entry Point Complexity
|
|
26
|
-
The main entry point has 102+ require statements loaded eagerly. This means all engine code is loaded at boot regardless of what features the host app uses. No autoloading or lazy-loading strategy.
|
|
27
|
-
|
|
28
|
-
## Architectural Risks
|
|
29
|
-
|
|
30
|
-
### 1. PostgreSQL Lock-in
|
|
31
|
-
The `Scheduler` uses `FOR UPDATE SKIP LOCKED` (PostgreSQL-specific locking), and queries use `NULLS FIRST`/`NULLS LAST` ordering. The engine will not work with MySQL or SQLite. This is documented nowhere in the gemspec constraints.
|
|
32
|
-
|
|
33
|
-
### 2. In-Memory Metrics Without Persistence
|
|
34
|
-
`SourceMonitor::Metrics` stores counters and gauges in module-level instance variables (`@counters`, `@gauges`). These are:
|
|
35
|
-
- Lost on process restart
|
|
36
|
-
- Not shared across workers/processes
|
|
37
|
-
- Not suitable for production monitoring
|
|
38
|
-
|
|
39
|
-
### 3. Scraping State Management
|
|
40
|
-
`SourceMonitor::Scraping::State` tracks in-flight scrapes but the mechanism (cache/memory) is fragile. In multi-process deployments, this could lead to over-enqueuing or under-enqueuing of scrape jobs.
|
|
41
|
-
|
|
42
|
-
### 4. Optional Dependency Loading
|
|
43
|
-
Solid Queue, Solid Cable, Turbo, and Ransack are loaded with `rescue LoadError`. This means the engine could silently fail to load core functionality without clear error messages to the host app developer.
|
|
44
|
-
|
|
45
|
-
### 5. No Database Index Verification
|
|
46
|
-
24 migration files create the schema incrementally. There is no single-schema check to verify all expected indexes exist after migration.
|
|
47
|
-
|
|
48
|
-
## Security Considerations
|
|
49
|
-
|
|
50
|
-
### 1. No Built-in Authentication
|
|
51
|
-
The engine relies entirely on the host app for authentication. The `Security::Authentication` module provides hooks but no default protection. An unconfigured engine is accessible to anyone who can reach the mount path.
|
|
52
|
-
|
|
53
|
-
### 2. Fallback User Creation in Import Controller
|
|
54
|
-
`ImportSessionsController#create_guest_user` (lines 416-429) can create a `User` record in the host app's database when no authentication is configured. This is a defensive fallback but could be unexpected behavior.
|
|
55
|
-
|
|
56
|
-
### 3. Parameter Sanitization Scope
|
|
57
|
-
`Security::ParameterSanitizer` uses `ActionView::Base.full_sanitizer` which strips all HTML. This is broad but may not catch all injection vectors (e.g., URL-based attacks, header injection via custom_headers).
|
|
58
|
-
|
|
59
|
-
### 4. Custom Headers Passthrough
|
|
60
|
-
Source `custom_headers` are stored as JSONB and passed to HTTP requests (`FeedFetcher#request_headers`). Malicious header values could potentially be used for SSRF or header injection if not properly validated.
|
|
61
|
-
|
|
62
|
-
## Performance Considerations
|
|
63
|
-
|
|
64
|
-
### 1. N+1 Query Risk in Dashboard
|
|
65
|
-
`Dashboard::Queries` performs multiple database queries for stats, recent activity, quick actions, job metrics, and fetch schedules. The controller assembles all of these synchronously.
|
|
66
|
-
|
|
67
|
-
### 2. Large OPML File Handling
|
|
68
|
-
The OPML import parses entire files in memory (`Nokogiri::XML(content)`). Large OPML files with thousands of entries could cause memory pressure.
|
|
69
|
-
|
|
70
|
-
### 3. Bulk Scrape Operations
|
|
71
|
-
`BulkSourceScraper` enqueues individual `ScrapeItemJob` for each item. For sources with thousands of items, this could flood the job queue.
|
|
72
|
-
|
|
73
|
-
### 4. Coverage Baseline File
|
|
74
|
-
The `config/coverage_baseline.json` at 2329 lines is parsed at test time and represents significant technical debt in test coverage.
|
|
75
|
-
|
|
76
|
-
## Operational Risks
|
|
77
|
-
|
|
78
|
-
### 1. No Health Check Endpoint Documentation
|
|
79
|
-
The `/health` endpoint exists but its response format and behavior are not documented.
|
|
80
|
-
|
|
81
|
-
### 2. Stalled Fetch Recovery
|
|
82
|
-
The `StalledFetchReconciler` runs within the `Scheduler`, but if the scheduler itself stalls, there is no external recovery mechanism.
|
|
83
|
-
|
|
84
|
-
### 3. Circuit Breaker State in Database
|
|
85
|
-
Fetch circuit breaker state (`fetch_circuit_opened_at`, `fetch_circuit_until`) is stored on the `Source` model. This means circuit breaker resets require database writes, and a failing database connection prevents circuit recovery.
|
|
86
|
-
|
|
87
|
-
## Dependency Risks
|
|
88
|
-
|
|
89
|
-
### 1. ruby-readability (~> 0.7)
|
|
90
|
-
This gem appears to be minimally maintained. The `0.7.x` line is old. It depends on Nokogiri which receives frequent security updates, creating a transitive dependency management burden.
|
|
91
|
-
|
|
92
|
-
### 2. Nokolexbor (~> 0.5)
|
|
93
|
-
Native extension gem with C bindings to the Lexbor HTML parser. Platform-specific builds could cause deployment issues, especially on less common architectures.
|
|
94
|
-
|
|
95
|
-
### 3. Feedjira Wide Version Range
|
|
96
|
-
`>= 3.2, < 5.0` allows major version bumps that could introduce breaking API changes.
|
|
97
|
-
|
|
98
|
-
### 4. Ruby >= 3.4.0 Minimum
|
|
99
|
-
This is an aggressive minimum that excludes many production Ruby installations still on 3.2.x or 3.3.x.
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# Conventions
|
|
2
|
-
|
|
3
|
-
## Ruby Code Style
|
|
4
|
-
|
|
5
|
-
- **Style Guide**: Rails Omakase via `rubocop-rails-omakase` gem
|
|
6
|
-
- **Frozen String Literals**: Consistently used across all app/ and lib/ Ruby files (`# frozen_string_literal: true`)
|
|
7
|
-
- **No frozen_string_literal**: Absent from some test files and the main `lib/source_monitor.rb` entry point
|
|
8
|
-
|
|
9
|
-
## Naming Conventions
|
|
10
|
-
|
|
11
|
-
### Module/Class Naming
|
|
12
|
-
- All engine code namespaced under `SourceMonitor::`
|
|
13
|
-
- Sub-modules use descriptive domain names: `Fetching`, `Scraping`, `Health`, `Security`, `Dashboard`, `Realtime`
|
|
14
|
-
- Service objects named as nouns (e.g., `FeedFetcher`, `ItemScraper`, `Scheduler`, `RetentionPruner`)
|
|
15
|
-
- Presenters suffixed with `Presenter` (e.g., `BulkResultPresenter`, `RecentActivityPresenter`, `TurboStreamPresenter`)
|
|
16
|
-
- Query objects in `analytics/` and `dashboard/` (e.g., `Queries`, `SourcesIndexMetrics`)
|
|
17
|
-
|
|
18
|
-
### File Organization
|
|
19
|
-
- One class per file, file path mirrors class namespace
|
|
20
|
-
- Nested classes extracted to sub-directories (e.g., `item_scraper/adapter_resolver.rb`, `item_scraper/persistence.rb`)
|
|
21
|
-
- Concerns placed in `concerns/` subdirectories
|
|
22
|
-
|
|
23
|
-
### Database Naming
|
|
24
|
-
- Table prefix: `sourcemon_` (configurable via `config.models.table_name_prefix`)
|
|
25
|
-
- Tables: `sourcemon_sources`, `sourcemon_items`, `sourcemon_fetch_logs`, etc.
|
|
26
|
-
- Foreign keys follow Rails conventions: `source_id`, `item_id`
|
|
27
|
-
- Timestamps use `started_at`/`completed_at` pattern for log records
|
|
28
|
-
|
|
29
|
-
### Method Naming
|
|
30
|
-
- Bang methods (`!`) for operations that raise on failure: `soft_delete!`, `mark_processing!`
|
|
31
|
-
- Predicate methods: `fetch_circuit_open?`, `auto_paused?`, `deleted?`, `success?`
|
|
32
|
-
- `call` method pattern for service objects and scraper adapters
|
|
33
|
-
- Private `set_*` for controller before_actions: `set_source`, `set_import_session`
|
|
34
|
-
|
|
35
|
-
### Job Naming
|
|
36
|
-
- Jobs suffixed with `Job`: `FetchFeedJob`, `ScrapeItemJob`, `ScheduleFetchesJob`
|
|
37
|
-
- Queue assignment via `source_monitor_queue :fetch` class method
|
|
38
|
-
|
|
39
|
-
### Controller Naming
|
|
40
|
-
- Resource controllers follow Rails conventions: `SourcesController`, `ItemsController`
|
|
41
|
-
- Singular nested resource controllers: `SourceFetchesController`, `SourceRetriesController`
|
|
42
|
-
- Action-specific controllers for non-RESTful actions: `SourceBulkScrapesController`, `SourceHealthChecksController`
|
|
43
|
-
|
|
44
|
-
## Configuration Patterns
|
|
45
|
-
|
|
46
|
-
- Configuration via `SourceMonitor.configure { |config| ... }` block DSL
|
|
47
|
-
- Nested configuration objects with `reset!` methods for testing
|
|
48
|
-
- Settings classes with explicit defaults defined as constants
|
|
49
|
-
- Callable values supported (procs/lambdas) for dynamic configuration
|
|
50
|
-
|
|
51
|
-
## Error Handling
|
|
52
|
-
|
|
53
|
-
- Custom error hierarchy: `FetchError` base with typed subclasses (`TimeoutError`, `ConnectionError`, `HTTPError`, `ParsingError`)
|
|
54
|
-
- Errors carry structured data: `http_status`, `response`, `original_error`
|
|
55
|
-
- Defensive `rescue StandardError` wrappers around logging calls
|
|
56
|
-
- `Rails.logger` usage guarded with `defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger`
|
|
57
|
-
|
|
58
|
-
## Struct Usage
|
|
59
|
-
|
|
60
|
-
- `Struct.new(..., keyword_init: true)` extensively used for result/event objects
|
|
61
|
-
- Named structs: `Result`, `ResponseWrapper`, `EntryProcessingResult`
|
|
62
|
-
- Event structs: `ItemCreatedEvent`, `ItemScrapedEvent`, `FetchCompletedEvent`
|
|
63
|
-
|
|
64
|
-
## Frontend Conventions
|
|
65
|
-
|
|
66
|
-
- CSS scoped under `.fm-admin` class with Tailwind `important` selector
|
|
67
|
-
- Stimulus controllers registered on `window.SourceMonitorStimulus`
|
|
68
|
-
- Controller naming: kebab-case in HTML (`async-submit`, `confirm-navigation`)
|
|
69
|
-
- ERB partials prefixed with underscore, organized per resource
|
|
70
|
-
- Turbo Stream responses via `SourceMonitor::TurboStreams::StreamResponder`
|
|
71
|
-
|
|
72
|
-
## View Conventions
|
|
73
|
-
|
|
74
|
-
- Layouts: Engine uses host app layout; views are ERB
|
|
75
|
-
- Partials extensively used: `_row.html.erb`, `_details.html.erb`, `_form_fields.html.erb`
|
|
76
|
-
- Wizard steps as separate partials: `steps/_upload.html.erb`, `steps/_preview.html.erb`
|
|
77
|
-
- Toast notifications broadcast via realtime system
|
|
78
|
-
|
|
79
|
-
## Test Conventions
|
|
80
|
-
|
|
81
|
-
- Minitest with Rails test helpers (not RSpec)
|
|
82
|
-
- Test files mirror source structure: `test/lib/source_monitor/scraping/item_scraper_test.rb`
|
|
83
|
-
- Test helper methods: `create_source!` factory method in `ActiveSupport::TestCase`
|
|
84
|
-
- Parallel test execution by default
|
|
85
|
-
- Configuration reset in `setup` block: `SourceMonitor.reset_configuration!`
|
|
86
|
-
- WebMock disables external connections
|
|
87
|
-
- VCR for HTTP interaction recording
|
|
88
|
-
- test-prof `before_all` for expensive shared setup
|
|
89
|
-
- `with_inline_jobs` and `with_queue_adapter` helpers for job testing
|
|
90
|
-
|
|
91
|
-
## Documentation Conventions
|
|
92
|
-
|
|
93
|
-
- YARD-style comments in some service classes
|
|
94
|
-
- Inline comments explain "why" not "what"
|
|
95
|
-
- `# :nocov:` markers for untestable/defensive code paths
|
|
96
|
-
- CHANGELOG maintained in conventional format
|
|
97
|
-
- CONTRIBUTING.md present
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# Dependencies
|
|
2
|
-
|
|
3
|
-
## Runtime Dependencies (gemspec)
|
|
4
|
-
|
|
5
|
-
These are declared in `source_monitor.gemspec` and required by any host app using the engine.
|
|
6
|
-
|
|
7
|
-
| Gem | Constraint | Purpose | Risk |
|
|
8
|
-
|-----|-----------|---------|------|
|
|
9
|
-
| rails | >= 8.0.3, < 9.0 | Core framework | Major version lock |
|
|
10
|
-
| cssbundling-rails | ~> 1.4 | CSS asset bundling bridge | Low |
|
|
11
|
-
| jsbundling-rails | ~> 1.3 | JS asset bundling bridge | Low |
|
|
12
|
-
| turbo-rails | ~> 2.0 | Hotwire Turbo for real-time updates | Medium - optional but recommended |
|
|
13
|
-
| feedjira | >= 3.2, < 5.0 | Feed parsing (RSS/Atom) | Core functionality |
|
|
14
|
-
| faraday | ~> 2.9 | HTTP client | Core functionality |
|
|
15
|
-
| faraday-retry | ~> 2.2 | Retry middleware | Low |
|
|
16
|
-
| faraday-follow_redirects | ~> 0.4 | Redirect handling | Low |
|
|
17
|
-
| faraday-gzip | ~> 3.0 | Compression support | Low |
|
|
18
|
-
| nokolexbor | ~> 0.5 | Fast HTML parsing (lexbor engine) | Medium - native extension |
|
|
19
|
-
| ruby-readability | ~> 0.7 | Article content extraction | Medium - older gem |
|
|
20
|
-
| solid_queue | >= 0.3, < 3.0 | Background job processing | Core functionality |
|
|
21
|
-
| solid_cable | >= 3.0, < 4.0 | Action Cable adapter | Real-time features |
|
|
22
|
-
| ransack | ~> 4.2 | Search/filter form builder | Medium |
|
|
23
|
-
|
|
24
|
-
## Optional Runtime Dependencies
|
|
25
|
-
|
|
26
|
-
Loaded with rescue from LoadError in `lib/source_monitor.rb`:
|
|
27
|
-
- `solid_queue` -- optional if host uses different Active Job backend
|
|
28
|
-
- `solid_cable` -- optional if host uses Redis or another Action Cable adapter
|
|
29
|
-
- `turbo-rails` -- optional but recommended for real-time updates
|
|
30
|
-
- `ransack` -- powers search forms when available
|
|
31
|
-
|
|
32
|
-
## Development Dependencies (Gemfile)
|
|
33
|
-
|
|
34
|
-
| Gem | Purpose |
|
|
35
|
-
|-----|---------|
|
|
36
|
-
| puma | Development web server |
|
|
37
|
-
| pg | PostgreSQL adapter |
|
|
38
|
-
| propshaft | Asset pipeline |
|
|
39
|
-
| rubocop-rails-omakase | Linting (Rails omakase style) |
|
|
40
|
-
| brakeman | Security scanning |
|
|
41
|
-
|
|
42
|
-
## Test Dependencies (Gemfile)
|
|
43
|
-
|
|
44
|
-
| Gem | Purpose |
|
|
45
|
-
|-----|---------|
|
|
46
|
-
| simplecov | Code coverage reporting |
|
|
47
|
-
| test-prof | Test profiling toolkit |
|
|
48
|
-
| stackprof | Stack sampling profiler |
|
|
49
|
-
| capybara | System test framework |
|
|
50
|
-
| webmock | HTTP stubbing |
|
|
51
|
-
| vcr | HTTP recording/playback |
|
|
52
|
-
| selenium-webdriver | Browser driver for system tests |
|
|
53
|
-
|
|
54
|
-
## JavaScript Dependencies (package.json)
|
|
55
|
-
|
|
56
|
-
### Runtime
|
|
57
|
-
|
|
58
|
-
| Package | Version | Purpose |
|
|
59
|
-
|---------|---------|---------|
|
|
60
|
-
| @hotwired/stimulus | ^3.2.2 | Stimulus JS framework |
|
|
61
|
-
| stimulus-use | ^0.52.0 | Composable Stimulus behaviors |
|
|
62
|
-
|
|
63
|
-
### Development
|
|
64
|
-
|
|
65
|
-
| Package | Version | Purpose |
|
|
66
|
-
|---------|---------|---------|
|
|
67
|
-
| esbuild | ^0.23.0 | JS bundling |
|
|
68
|
-
| tailwindcss | ^3.4.10 | CSS framework |
|
|
69
|
-
| postcss | ^8.4.45 | CSS transformation |
|
|
70
|
-
| autoprefixer | ^10.4.20 | CSS vendor prefixes |
|
|
71
|
-
| eslint | ^9.11.0 | JS linting |
|
|
72
|
-
| @eslint/js | ^9.11.0 | ESLint core config |
|
|
73
|
-
| stylelint | ^16.8.0 | CSS linting |
|
|
74
|
-
| stylelint-config-standard | ^36.0.0 | Stylelint standard config |
|
|
75
|
-
|
|
76
|
-
## Dependency Coupling Analysis
|
|
77
|
-
|
|
78
|
-
### Tight Coupling (hard to replace)
|
|
79
|
-
- **Rails 8.x** -- entire engine is built on Rails conventions
|
|
80
|
-
- **Feedjira** -- core feed parsing logic, deeply integrated in `Fetching::FeedFetcher`
|
|
81
|
-
- **Faraday** -- HTTP client used throughout `SourceMonitor::HTTP`, configurable via middleware stack
|
|
82
|
-
- **Solid Queue** -- integrated in engine initializer, scheduler, and job visibility system
|
|
83
|
-
- **PostgreSQL** -- uses `FOR UPDATE SKIP LOCKED`, `NULLS FIRST/LAST` SQL syntax
|
|
84
|
-
|
|
85
|
-
### Moderate Coupling (replaceable with effort)
|
|
86
|
-
- **Nokolexbor/Nokogiri** -- HTML parsing in scrapers and OPML import
|
|
87
|
-
- **Ransack** -- used in model `ransackable_attributes` declarations and search forms
|
|
88
|
-
- **Tailwind CSS** -- all views use Tailwind utility classes scoped under `.fm-admin`
|
|
89
|
-
|
|
90
|
-
### Loose Coupling (easily replaceable)
|
|
91
|
-
- **Solid Cable** -- configurable via `config.realtime.adapter`, supports `:solid_cable`, `:redis`, `:async`
|
|
92
|
-
- **ruby-readability** -- wrapped in `Scrapers::Readability` adapter behind pluggable adapter interface
|
|
93
|
-
- **Turbo Rails** -- optional, loaded conditionally
|
|
94
|
-
|
|
95
|
-
## Version Constraints of Note
|
|
96
|
-
|
|
97
|
-
- Ruby >= 3.4.0 is a relatively aggressive minimum requirement
|
|
98
|
-
- Rails >= 8.0.3 pins to the latest major, narrowing host app compatibility
|
|
99
|
-
- Solid Queue has a wide range (0.3 to 3.0), suggesting early adoption with forward-looking flexibility
|
|
100
|
-
- PostgreSQL is the only supported database (uses PG-specific SQL features)
|