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.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +45 -22
  3. data/.claude/skills/sm-configure/SKILL.md +10 -1
  4. data/.claude/skills/sm-configure/reference/configuration-reference.md +44 -0
  5. data/.claude/skills/sm-host-setup/reference/initializer-template.md +17 -0
  6. data/.claude/skills/sm-host-setup/reference/setup-checklist.md +2 -0
  7. data/.claude/skills/sm-job/reference/job-conventions.md +26 -0
  8. data/.claude/skills/sm-upgrade/reference/version-history.md +22 -0
  9. data/.gitignore +10 -0
  10. data/AGENTS.md +1 -1
  11. data/CHANGELOG.md +56 -0
  12. data/CLAUDE.md +11 -5
  13. data/Gemfile.lock +1 -1
  14. data/README.md +6 -4
  15. data/VERSION +1 -1
  16. data/app/assets/builds/source_monitor/application.css +43 -0
  17. data/app/assets/builds/source_monitor/application.js +127 -0
  18. data/app/assets/builds/source_monitor/application.js.map +3 -3
  19. data/app/assets/javascripts/source_monitor/application.js +2 -0
  20. data/app/assets/javascripts/source_monitor/controllers/notification_container_controller.js +138 -0
  21. data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +11 -0
  22. data/app/controllers/source_monitor/source_favicon_fetches_controller.rb +38 -0
  23. data/app/controllers/source_monitor/sources_controller.rb +11 -0
  24. data/app/helpers/source_monitor/application_helper.rb +51 -0
  25. data/app/jobs/source_monitor/favicon_fetch_job.rb +71 -0
  26. data/app/jobs/source_monitor/import_opml_job.rb +9 -0
  27. data/app/jobs/source_monitor/source_health_check_job.rb +10 -0
  28. data/app/models/source_monitor/source.rb +2 -0
  29. data/app/views/layouts/source_monitor/application.html.erb +23 -2
  30. data/app/views/source_monitor/shared/_toast.html.erb +1 -0
  31. data/app/views/source_monitor/sources/_details.html.erb +34 -5
  32. data/app/views/source_monitor/sources/_row.html.erb +11 -6
  33. data/config/routes.rb +1 -0
  34. data/docs/configuration.md +1 -1
  35. data/docs/upgrade.md +22 -0
  36. data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +15 -1
  37. data/lib/source_monitor/configuration/favicons_settings.rb +42 -0
  38. data/lib/source_monitor/configuration/http_settings.rb +1 -1
  39. data/lib/source_monitor/configuration/scraping_settings.rb +1 -1
  40. data/lib/source_monitor/configuration.rb +3 -1
  41. data/lib/source_monitor/favicons/discoverer.rb +196 -0
  42. data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +21 -0
  43. data/lib/source_monitor/fetching/feed_fetcher.rb +1 -0
  44. data/lib/source_monitor/http.rb +5 -3
  45. data/lib/source_monitor/version.rb +1 -1
  46. data/lib/source_monitor.rb +4 -0
  47. data/lib/tasks/test_fast.rake +11 -0
  48. data/source_monitor.gemspec +1 -1
  49. metadata +7 -93
  50. data/.vbw-planning/PROJECT.md +0 -51
  51. data/.vbw-planning/ROADMAP.md +0 -32
  52. data/.vbw-planning/SHIPPED.md +0 -63
  53. data/.vbw-planning/STATE.md +0 -27
  54. data/.vbw-planning/codebase/ARCHITECTURE.md +0 -147
  55. data/.vbw-planning/codebase/CONCERNS.md +0 -99
  56. data/.vbw-planning/codebase/CONVENTIONS.md +0 -97
  57. data/.vbw-planning/codebase/DEPENDENCIES.md +0 -100
  58. data/.vbw-planning/codebase/INDEX.md +0 -86
  59. data/.vbw-planning/codebase/META.md +0 -42
  60. data/.vbw-planning/codebase/PATTERNS.md +0 -262
  61. data/.vbw-planning/codebase/STACK.md +0 -101
  62. data/.vbw-planning/codebase/STRUCTURE.md +0 -324
  63. data/.vbw-planning/codebase/TESTING.md +0 -154
  64. data/.vbw-planning/config.json +0 -53
  65. data/.vbw-planning/discovery.json +0 -26
  66. data/.vbw-planning/milestones/default/ROADMAP.md +0 -115
  67. data/.vbw-planning/milestones/default/STATE.md +0 -82
  68. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +0 -56
  69. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +0 -187
  70. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +0 -64
  71. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +0 -137
  72. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +0 -67
  73. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +0 -142
  74. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +0 -64
  75. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +0 -138
  76. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +0 -85
  77. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +0 -147
  78. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +0 -63
  79. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +0 -129
  80. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +0 -74
  81. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +0 -154
  82. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +0 -303
  83. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +0 -510
  84. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +0 -61
  85. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +0 -161
  86. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +0 -66
  87. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +0 -132
  88. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +0 -59
  89. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +0 -171
  90. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +0 -56
  91. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +0 -152
  92. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +0 -33
  93. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +0 -42
  94. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +0 -119
  95. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +0 -52
  96. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +0 -195
  97. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +0 -79
  98. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +0 -130
  99. data/.vbw-planning/milestones/generator-enhancements/REQUIREMENTS.md +0 -72
  100. data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +0 -125
  101. data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +0 -40
  102. data/.vbw-planning/milestones/generator-enhancements/STATE.md +0 -43
  103. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +0 -33
  104. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +0 -86
  105. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +0 -61
  106. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +0 -380
  107. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +0 -78
  108. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +0 -46
  109. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +0 -500
  110. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +0 -89
  111. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +0 -48
  112. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +0 -456
  113. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +0 -129
  114. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -70
  115. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +0 -747
  116. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +0 -156
  117. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +0 -69
  118. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +0 -455
  119. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +0 -39
  120. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +0 -488
  121. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +0 -100
  122. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -37
  123. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +0 -345
  124. data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +0 -80
  125. data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +0 -75
  126. data/.vbw-planning/milestones/upgrade-assurance/STATE.md +0 -29
  127. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +0 -144
  128. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +0 -43
  129. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +0 -405
  130. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +0 -27
  131. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +0 -303
  132. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +0 -380
  133. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +0 -36
  134. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +0 -652
  135. data/.vbw-planning/phases/01-aia-certificate-resolution/.context-dev.md +0 -17
  136. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01-SUMMARY.md +0 -26
  137. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01.md +0 -71
  138. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02-SUMMARY.md +0 -16
  139. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02.md +0 -56
  140. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03-SUMMARY.md +0 -17
  141. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03.md +0 -98
@@ -1,262 +0,0 @@
1
- # Patterns
2
-
3
- Recurring patterns observed across the SourceMonitor codebase.
4
-
5
- ## 1. Service Object Pattern
6
-
7
- **Where**: `lib/source_monitor/fetching/`, `lib/source_monitor/scraping/`, `lib/source_monitor/health/`, `lib/source_monitor/items/`
8
-
9
- Service objects encapsulate domain operations. They follow a consistent structure:
10
-
11
- ```ruby
12
- class SomeService
13
- def initialize(source:, **deps)
14
- @source = source
15
- end
16
-
17
- def call
18
- # orchestrate operation
19
- # return a Result struct
20
- end
21
- end
22
- ```
23
-
24
- **Examples**:
25
- - `Fetching::FeedFetcher` -- `#call` returns `Result` struct
26
- - `Scraping::ItemScraper` -- `#call` returns `Result` struct
27
- - `Health::SourceHealthMonitor` -- `#call` updates source health
28
- - `Health::SourceHealthCheck` -- `#call` probes source URL
29
- - `Items::RetentionPruner` -- `#call` prunes old items
30
- - `Items::ItemCreator` -- `.call(source:, entry:)` class method
31
-
32
- ## 2. Struct-Based Result Objects
33
-
34
- **Where**: Throughout all service objects
35
-
36
- Operations return typed `Struct` instances rather than raw hashes or arrays:
37
-
38
- ```ruby
39
- Result = Struct.new(:status, :item, :log, :message, :error, keyword_init: true) do
40
- def success?
41
- status.to_s != "failed"
42
- end
43
- end
44
- ```
45
-
46
- **Examples**:
47
- - `Scraping::ItemScraper::Result` -- `:status, :item, :log, :message, :error`
48
- - `Scrapers::Base::Result` -- `:status, :html, :content, :metadata`
49
- - `Fetching::FeedFetcher::Result` -- `:status, :feed, :response, :body, :error, :item_processing, :retry_decision`
50
- - `Fetching::FeedFetcher::EntryProcessingResult` -- `:created, :updated, :failed, :items, :errors`
51
- - `Events::ItemCreatedEvent`, `Events::ItemScrapedEvent`, `Events::FetchCompletedEvent`
52
- - `Setup::Verification::Result` -- verification outcome
53
-
54
- ## 3. Adapter/Strategy Pattern
55
-
56
- **Where**: `lib/source_monitor/scrapers/`, `lib/source_monitor/realtime/`, `lib/source_monitor/items/retention_strategies/`
57
-
58
- Pluggable behavior via abstract base class with `#call` contract:
59
-
60
- ```ruby
61
- class Scrapers::Base
62
- def call
63
- raise NotImplementedError
64
- end
65
- end
66
- ```
67
-
68
- **Instances**:
69
- - **Scraper adapters**: `Scrapers::Base` -> `Scrapers::Readability` (registered in `ScraperRegistry`)
70
- - **Realtime adapters**: `solid_cable`, `redis`, `async` (configured in `RealtimeSettings`)
71
- - **Retention strategies**: `:destroy`, `:soft_delete` (in `Items::RetentionStrategies/`)
72
-
73
- ## 4. Event/Callback System
74
-
75
- **Where**: `lib/source_monitor/events.rb`, `lib/source_monitor/configuration.rb`
76
-
77
- Event-driven communication between engine components:
78
-
79
- ```ruby
80
- # Registration
81
- SourceMonitor.config.events.after_item_created { |event| ... }
82
- SourceMonitor.config.events.after_item_scraped { |event| ... }
83
- SourceMonitor.config.events.after_fetch_completed { |event| ... }
84
-
85
- # Dispatch
86
- SourceMonitor::Events.after_item_created(item:, source:, entry:, result:)
87
- ```
88
-
89
- - Typed event structs carry context
90
- - Error isolation: each handler failure is logged, does not stop other handlers
91
- - Item processor pipeline: `Events.run_item_processors` runs all registered processors
92
- - Used by `Health` module to register fetch completion callback
93
-
94
- ## 5. Configuration DSL with Nested Settings Objects
95
-
96
- **Where**: `lib/source_monitor/configuration.rb`
97
-
98
- Deeply nested configuration with domain-specific settings classes:
99
-
100
- ```ruby
101
- SourceMonitor.configure do |config|
102
- config.http.timeout = 30
103
- config.fetching.min_interval_minutes = 10
104
- config.health.window_size = 50
105
- config.scrapers.register(:custom, MyCustomScraper)
106
- config.models.source.include_concern SomeConcern
107
- config.authentication.authenticate_with :authenticate_admin!
108
- end
109
- ```
110
-
111
- **Pattern traits**:
112
- - Each settings class has `reset!` for test isolation
113
- - Constants for defaults (e.g., `DEFAULT_QUEUE_NAMESPACE`)
114
- - Callable values supported (procs/lambdas) for dynamic resolution
115
- - Validation in setters (e.g., `RealtimeSettings#adapter=` checks `VALID_ADAPTERS`)
116
-
117
- ## 6. Model Extension System
118
-
119
- **Where**: `lib/source_monitor/model_extensions.rb`, `lib/source_monitor/configuration.rb`
120
-
121
- Host apps can dynamically inject concerns and validations into engine models:
122
-
123
- ```ruby
124
- config.models.source.include_concern "MyApp::SourceExtensions"
125
- config.models.source.validate :custom_validator
126
- config.models.source.validate { |record| record.errors.add(:base, "invalid") if ... }
127
- ```
128
-
129
- - `ModelExtensions.register(model_class, key)` called in each model class body
130
- - `ModelExtensions.reload!` re-applies all extensions on configuration change
131
- - Manages table name prefix assignment
132
- - Tracks applied concerns/validations to prevent duplicates
133
- - Removes old extension validations before re-applying
134
-
135
- ## 7. Turbo Stream Response Pattern
136
-
137
- **Where**: `lib/source_monitor/turbo_streams/stream_responder.rb`, controllers
138
-
139
- Controllers build Turbo Stream responses via a `StreamResponder` builder:
140
-
141
- ```ruby
142
- responder = SourceMonitor::TurboStreams::StreamResponder.new
143
- presenter = SourceMonitor::Sources::TurboStreamPresenter.new(source:, responder:)
144
- presenter.render_deletion(metrics:, query:, ...)
145
- responder.toast(message:, level: :success)
146
- render turbo_stream: responder.render(view_context)
147
- ```
148
-
149
- - Accumulates stream actions as an array
150
- - Supports toast notifications, redirects, and custom actions
151
- - Pairs with Stimulus controllers on the frontend
152
-
153
- ## 8. Defensive Logging Guard
154
-
155
- **Where**: All jobs and service objects
156
-
157
- Consistent pattern for safe logging:
158
-
159
- ```ruby
160
- def log(stage, **extra)
161
- return unless defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
162
- Rails.logger.info("[SourceMonitor::...] #{payload.to_json}")
163
- rescue StandardError
164
- nil
165
- end
166
- ```
167
-
168
- This three-part guard (`defined?`, `respond_to?`, truthy check) prevents errors when running outside Rails (e.g., in tests or standalone scripts).
169
-
170
- ## 9. ActiveSupport::Notifications Instrumentation
171
-
172
- **Where**: `lib/source_monitor/instrumentation.rb`, `lib/source_monitor/metrics.rb`
173
-
174
- Standard Rails instrumentation pattern:
175
-
176
- ```ruby
177
- # Emit events
178
- SourceMonitor::Instrumentation.fetch_start(payload)
179
- SourceMonitor::Instrumentation.fetch_finish(payload)
180
-
181
- # Subscribe to events
182
- ActiveSupport::Notifications.subscribe("source_monitor.fetch.finish") do |...|
183
- SourceMonitor::Metrics.increment(:fetch_finished_total)
184
- end
185
- ```
186
-
187
- - Events namespaced as `source_monitor.*`
188
- - Monotonic clock for duration measurement
189
- - Metrics module aggregates counters and gauges in memory
190
-
191
- ## 10. Search/Filter with Ransack
192
-
193
- **Where**: Models (`ransackable_attributes`, `ransackable_associations`), `SanitizesSearchParams` concern
194
-
195
- ```ruby
196
- class Source < ApplicationRecord
197
- def self.ransackable_attributes(_auth_object = nil)
198
- %w[name feed_url website_url created_at ...]
199
- end
200
- end
201
- ```
202
-
203
- - Explicit whitelisting of searchable attributes (required by Ransack 4+)
204
- - `SanitizesSearchParams` controller concern sanitizes search inputs
205
- - Used in `SourcesController#index` and `LogsController#index`
206
-
207
- ## 11. Circuit Breaker / Retry Policy
208
-
209
- **Where**: `lib/source_monitor/fetching/retry_policy.rb`, `lib/source_monitor/fetching/feed_fetcher.rb`, `app/jobs/source_monitor/fetch_feed_job.rb`
210
-
211
- Fetch failures trigger an escalating retry policy:
212
-
213
- 1. **Retry with backoff**: Exponential wait, up to N attempts
214
- 2. **Circuit open**: After exhausting retries, block fetches for a cooldown period
215
- 3. **Circuit close**: Scheduler recovers after cooldown expires
216
-
217
- State stored on `Source` model: `fetch_retry_attempt`, `fetch_circuit_opened_at`, `fetch_circuit_until`, `backoff_until`.
218
-
219
- ## 12. Wizard State Machine (OPML Import)
220
-
221
- **Where**: `app/models/source_monitor/import_session.rb`, `app/controllers/source_monitor/import_sessions_controller.rb`
222
-
223
- Multi-step wizard with explicit step ordering:
224
-
225
- ```ruby
226
- STEP_ORDER = %w[upload preview health_check configure confirm].freeze
227
- ```
228
-
229
- - State persisted in `ImportSession` model with JSONB columns
230
- - Each step has dedicated `handle_*_step` and `prepare_*_context` methods
231
- - Navigation via `next_step`/`previous_step` model methods
232
- - Step transitions guarded by validation (e.g., "select at least one source")
233
-
234
- ## 13. Soft Delete Pattern
235
-
236
- **Where**: `app/models/source_monitor/item.rb`
237
-
238
- Items use soft deletion via `deleted_at` timestamp rather than physical deletion:
239
-
240
- ```ruby
241
- scope :active, -> { where(deleted_at: nil) }
242
- scope :with_deleted, -> { unscope(where: :deleted_at) }
243
- scope :only_deleted, -> { where.not(deleted_at: nil) }
244
-
245
- def soft_delete!(timestamp: Time.current)
246
- update_columns(deleted_at: timestamp, updated_at: timestamp)
247
- Source.decrement_counter(:items_count, source_id)
248
- end
249
- ```
250
-
251
- No `default_scope` is used (explicitly noted as avoiding anti-pattern).
252
-
253
- ## 14. Separate Content Table
254
-
255
- **Where**: `app/models/source_monitor/item.rb`, `app/models/source_monitor/item_content.rb`
256
-
257
- Large scraped content is stored in a separate `ItemContent` model rather than on the `Item` directly:
258
-
259
- - Lazy-loaded via `has_one :item_content`
260
- - Auto-created when content is assigned, auto-destroyed when both fields become blank
261
- - Delegates `scraped_html` and `scraped_content` to `ItemContent`
262
- - Prevents bloating the items table with large text columns
@@ -1,101 +0,0 @@
1
- # Tech Stack
2
-
3
- ## Core Platform
4
-
5
- | Layer | Technology | Version |
6
- |-------|-----------|---------|
7
- | Language | Ruby | >= 3.4.0 (CI uses 3.4.4) |
8
- | Framework | Rails | >= 8.0.3, < 9.0 (locked at 8.1.1) |
9
- | Database | PostgreSQL | 15 (CI service image) |
10
- | Background Jobs | Solid Queue | >= 0.3, < 3.0 (locked at 1.2.4) |
11
- | WebSocket/Realtime | Solid Cable | >= 3.0, < 4.0 (locked at 3.0.12) |
12
- | Frontend Interactivity | Turbo Rails | ~> 2.0 (locked at 2.0.20) |
13
- | JS Framework | Stimulus (Hotwired) | ^3.2.2 |
14
- | CSS Framework | Tailwind CSS | ^3.4.10 |
15
- | Web Server | Puma | 7.1.0 |
16
- | Asset Pipeline | Propshaft | 1.3.1 |
17
-
18
- ## Project Type
19
-
20
- Mountable Rails 8 Engine gem (`source_monitor.gemspec`), distributed as a RubyGem. The engine uses `isolate_namespace SourceMonitor` and provides its own models, controllers, views, jobs, and frontend assets.
21
-
22
- - **Version**: 0.2.1
23
- - **Required Ruby**: >= 3.4.0
24
- - **License**: MIT
25
-
26
- ## Feed Parsing & HTTP
27
-
28
- | Purpose | Gem | Version |
29
- |---------|-----|---------|
30
- | RSS/Atom/JSON feed parsing | Feedjira | >= 3.2, < 5.0 (locked 4.0.1) |
31
- | HTTP client | Faraday | ~> 2.9 (locked 2.14.0) |
32
- | HTTP retry middleware | faraday-retry | ~> 2.2 |
33
- | HTTP redirect following | faraday-follow_redirects | ~> 0.4 |
34
- | HTTP gzip compression | faraday-gzip | ~> 3.0 |
35
-
36
- ## Content Scraping & Parsing
37
-
38
- | Purpose | Gem | Version |
39
- |---------|-----|---------|
40
- | HTML parsing (fast, C-based) | Nokolexbor | ~> 0.5 (locked 0.6.2) |
41
- | HTML parsing (standard) | Nokogiri | 1.18.10 (transitive) |
42
- | Article content extraction | ruby-readability | ~> 0.7 |
43
-
44
- ## Search & Querying
45
-
46
- | Purpose | Gem | Version |
47
- |---------|-----|---------|
48
- | Search/filter forms | Ransack | ~> 4.2 (locked 4.4.1) |
49
-
50
- ## Frontend Build Pipeline
51
-
52
- | Tool | Purpose | Version |
53
- |------|---------|---------|
54
- | esbuild | JS bundling | ^0.23.0 |
55
- | Tailwind CSS | Utility-first CSS | ^3.4.10 |
56
- | PostCSS | CSS processing | ^8.4.45 |
57
- | Autoprefixer | CSS vendor prefixes | ^10.4.20 |
58
- | ESLint | JS linting | ^9.11.0 |
59
- | Stylelint | CSS linting | ^16.8.0 |
60
-
61
- Build orchestration via `package.json` scripts:
62
- - `npm run build` -- builds both CSS (tailwindcss) and JS (esbuild)
63
- - `cssbundling-rails` (~> 1.4) and `jsbundling-rails` (~> 1.3) bridge npm builds into the Rails asset pipeline
64
-
65
- ## JS Dependencies (Runtime)
66
-
67
- | Package | Purpose |
68
- |---------|---------|
69
- | `@hotwired/stimulus` ^3.2.2 | Stimulus controllers for UI interactions |
70
- | `stimulus-use` ^0.52.0 | Stimulus composable behaviors library |
71
-
72
- ## Testing Stack
73
-
74
- | Tool | Purpose |
75
- |------|---------|
76
- | Minitest | Test framework (Rails default) |
77
- | Capybara | System/integration test driver |
78
- | Selenium WebDriver | Browser automation for system tests |
79
- | WebMock | HTTP request stubbing |
80
- | VCR | HTTP interaction recording/playback |
81
- | SimpleCov | Code coverage (branch coverage enabled) |
82
- | test-prof | Test profiling (TagProf, EventProf) |
83
- | StackProf | Sampling profiler for performance analysis |
84
-
85
- ## Code Quality & Security
86
-
87
- | Tool | Purpose |
88
- |------|---------|
89
- | RuboCop (rails-omakase) | Ruby/Rails linting (omakase style) |
90
- | Brakeman | Static security analysis |
91
- | ESLint | JavaScript linting |
92
- | Stylelint | CSS linting |
93
-
94
- ## CI/CD
95
-
96
- - **GitHub Actions** with 5 jobs: `lint`, `security`, `test`, `release_verification`, `profiling` (scheduled nightly)
97
- - Ruby 3.4.4, Node 20
98
- - PostgreSQL 15 as service container
99
- - Diff coverage enforcement via custom `bin/check-diff-coverage`
100
- - Test profiling guardrails via `bin/check-test-prof-metrics`
101
- - Parallel test execution (configurable via `SOURCE_MONITOR_TEST_WORKERS`)
@@ -1,324 +0,0 @@
1
- # Directory Structure
2
-
3
- ## Top-Level Layout
4
-
5
- ```
6
- source_monitor/
7
- app/ # Rails engine application code
8
- bin/ # Scripts (rubocop, test runners, CI checks)
9
- config/ # Engine configuration (routes, tailwind, feedjira initializer)
10
- db/migrate/ # Engine migrations (24 migration files)
11
- docs/ # Documentation
12
- examples/ # Example configurations and adapters
13
- lib/ # Engine library code and rake tasks
14
- test/ # Test suite
15
- tasks/ # Rake task definitions (aliases)
16
- coverage/ # SimpleCov output (gitignored content)
17
- node_modules/ # NPM packages
18
- pkg/ # Gem build output
19
- tmp/ # Temporary files
20
- ```
21
-
22
- ## App Directory (`app/`)
23
-
24
- ```
25
- app/
26
- assets/
27
- builds/source_monitor/ # Pre-built CSS and JS (committed)
28
- application.css # Built Tailwind CSS output
29
- application.js # Built esbuild JS output
30
- config/
31
- source_monitor_manifest.js
32
- images/source_monitor/ # SVGs and icons
33
- javascripts/source_monitor/
34
- application.js # Stimulus app entry point
35
- turbo_actions.js # Custom Turbo Stream actions
36
- controllers/
37
- async_submit_controller.js
38
- confirm_navigation_controller.js
39
- dropdown_controller.js
40
- modal_controller.js
41
- notification_controller.js
42
- select_all_controller.js
43
- stylesheets/source_monitor/
44
- application.tailwind.css # Tailwind input file
45
- svgs/source_monitor/ # SVG assets
46
- controllers/source_monitor/
47
- application_controller.rb
48
- dashboard_controller.rb
49
- fetch_logs_controller.rb
50
- health_controller.rb
51
- import_sessions_controller.rb
52
- items_controller.rb
53
- logs_controller.rb
54
- scrape_logs_controller.rb
55
- source_bulk_scrapes_controller.rb
56
- source_fetches_controller.rb
57
- source_health_checks_controller.rb
58
- source_health_resets_controller.rb
59
- source_retries_controller.rb
60
- source_turbo_responses.rb
61
- sources_controller.rb
62
- concerns/
63
- sanitizes_search_params.rb
64
- jobs/source_monitor/
65
- application_job.rb
66
- fetch_feed_job.rb
67
- import_opml_job.rb
68
- import_session_health_check_job.rb
69
- item_cleanup_job.rb
70
- log_cleanup_job.rb
71
- schedule_fetches_job.rb
72
- scrape_item_job.rb
73
- source_health_check_job.rb
74
- mailers/source_monitor/
75
- application_mailer.rb
76
- models/source_monitor/
77
- application_record.rb
78
- fetch_log.rb
79
- health_check_log.rb
80
- import_history.rb
81
- import_session.rb
82
- item.rb
83
- item_content.rb
84
- log_entry.rb
85
- scrape_log.rb
86
- source.rb
87
- concerns/
88
- loggable.rb
89
- views/source_monitor/
90
- dashboard/
91
- index.html.erb
92
- _fetch_schedule.html.erb
93
- _job_metrics.html.erb
94
- _recent_activity.html.erb
95
- _stat_card.html.erb
96
- _stats.html.erb
97
- fetch_logs/
98
- show.html.erb
99
- import_sessions/
100
- show.html.erb
101
- show.turbo_stream.erb
102
- _header.html.erb
103
- _sidebar.html.erb
104
- health_check/
105
- _progress.html.erb
106
- _row.html.erb
107
- steps/
108
- _configure.html.erb
109
- _confirm.html.erb
110
- _health_check.html.erb
111
- _navigation.html.erb
112
- _preview.html.erb
113
- _upload.html.erb
114
- items/
115
- index.html.erb
116
- show.html.erb
117
- _details.html.erb
118
- _details_wrapper.html.erb
119
- logs/
120
- index.html.erb
121
- scrape_logs/
122
- show.html.erb
123
- shared/
124
- _toast.html.erb
125
- sources/
126
- index.html.erb
127
- show.html.erb
128
- new.html.erb
129
- edit.html.erb
130
- _bulk_scrape_form.html.erb
131
- _bulk_scrape_modal.html.erb
132
- _details.html.erb
133
- _details_wrapper.html.erb
134
- _empty_state_row.html.erb
135
- _fetch_interval_heatmap.html.erb
136
- _form.html.erb
137
- _form_fields.html.erb
138
- _health_status_badge.html.erb
139
- _import_history_panel.html.erb
140
- _row.html.erb
141
- ```
142
-
143
- ## Lib Directory (`lib/source_monitor/`)
144
-
145
- ```
146
- lib/
147
- source_monitor.rb # Main entry point, requires, module definition
148
- source_monitor/
149
- version.rb # VERSION constant (0.2.1)
150
- engine.rb # Rails::Engine with initializers
151
- configuration.rb # Configuration DSL (655 lines)
152
- events.rb # Event system (dispatch, callbacks)
153
- instrumentation.rb # ActiveSupport::Notifications wrapper
154
- metrics.rb # In-memory counters/gauges
155
- http.rb # Faraday client builder
156
- model_extensions.rb # Dynamic model concern/validation injection
157
- scheduler.rb # Source fetch scheduling with SKIP LOCKED
158
- health.rb # Health module setup
159
- realtime.rb # Realtime broadcasting setup
160
- feedjira_extensions.rb # Feedjira customizations
161
- assets.rb # Asset management utilities
162
-
163
- analytics/ # Query objects for dashboard metrics
164
- source_activity_rates.rb
165
- source_fetch_interval_distribution.rb
166
- sources_index_metrics.rb
167
- assets/ # Asset bundling helpers
168
- dashboard/ # Dashboard presenters and queries
169
- queries.rb
170
- quick_action.rb
171
- quick_actions_presenter.rb
172
- recent_activity.rb
173
- recent_activity_presenter.rb
174
- turbo_broadcaster.rb
175
- upcoming_fetch_schedule.rb
176
- fetching/ # Feed fetching pipeline
177
- feed_fetcher.rb # Core fetcher (627 lines)
178
- fetch_error.rb
179
- fetch_runner.rb
180
- retry_policy.rb
181
- stalled_fetch_reconciler.rb
182
- health/ # Health monitoring
183
- import_source_health_check.rb
184
- source_health_check.rb
185
- source_health_monitor.rb
186
- source_health_reset.rb
187
- import_sessions/ # OPML import support
188
- entry_normalizer.rb
189
- items/ # Item management
190
- item_creator.rb
191
- retention_pruner.rb
192
- jobs/ # Job support modules
193
- cleanup_options.rb
194
- fetch_failure_subscriber.rb
195
- solid_queue_metrics.rb
196
- visibility.rb
197
- logs/ # Unified log system
198
- entry_sync.rb
199
- filter_set.rb
200
- query.rb
201
- table_presenter.rb
202
- models/ # Shared model concerns
203
- sanitizable.rb
204
- url_normalizable.rb
205
- pagination/ # Pagination support
206
- paginator.rb
207
- realtime/ # Realtime broadcasting
208
- adapter.rb
209
- broadcaster.rb
210
- release/ # Release management
211
- changelog.rb
212
- runner.rb
213
- scrapers/ # Scraper adapters
214
- base.rb
215
- readability.rb
216
- fetchers/
217
- http_fetcher.rb
218
- parsers/
219
- readability_parser.rb
220
- scraping/ # Scraping orchestration
221
- bulk_result_presenter.rb
222
- bulk_source_scraper.rb
223
- enqueuer.rb
224
- item_scraper.rb
225
- item_scraper/
226
- adapter_resolver.rb
227
- persistence.rb
228
- scheduler.rb
229
- state.rb
230
- security/ # Security modules
231
- authentication.rb
232
- parameter_sanitizer.rb
233
- setup/ # Installation workflow
234
- bundle_installer.rb
235
- cli.rb
236
- dependency_checker.rb
237
- detectors.rb
238
- gemfile_editor.rb
239
- initializer_patcher.rb
240
- install_generator.rb
241
- migration_installer.rb
242
- node_installer.rb
243
- prompter.rb
244
- requirements.rb
245
- shell_runner.rb
246
- workflow.rb
247
- verification/
248
- action_cable_verifier.rb
249
- printer.rb
250
- result.rb
251
- runner.rb
252
- solid_queue_verifier.rb
253
- telemetry_logger.rb
254
- sources/ # Source-specific support
255
- params.rb
256
- turbo_stream_presenter.rb
257
- turbo_streams/ # Turbo Stream helpers
258
- stream_responder.rb
259
- tasks/ # Rake tasks
260
- recover_stalled_fetches.rake
261
- source_monitor_assets.rake
262
- source_monitor_setup.rake
263
- source_monitor_tasks.rake
264
- test_smoke.rake
265
- ```
266
-
267
- ## Test Directory (`test/`)
268
-
269
- ```
270
- test/
271
- test_helper.rb # Test configuration and shared helpers
272
- test_prof.rb # test-prof integration
273
- source_monitor_test.rb # Module-level tests
274
- fixtures/ # Test fixtures
275
- vcr_cassettes/ # VCR recorded HTTP interactions
276
- dummy/ # Full Rails dummy app for testing
277
- app/
278
- bin/
279
- config/
280
- db/
281
- examples/ # Example integration tests
282
- advanced_template_test.rb
283
- basic_template_test.rb
284
- custom_adapter_example_test.rb
285
- docker_config_test.rb
286
- integration/ # Integration tests
287
- engine_mounting_test.rb
288
- host_install_flow_test.rb
289
- navigation_test.rb
290
- release_packaging_test.rb
291
- lib/source_monitor/ # Unit tests mirroring lib/ structure
292
- configuration_test.rb
293
- feedjira_configuration_test.rb
294
- instrumentation_test.rb
295
- health/
296
- pagination/
297
- release/
298
- scraping/
299
- security/ (implicitly tested)
300
- setup/
301
- turbo_streams/
302
- mailers/
303
- models/source_monitor/ # Model tests
304
- system/ # System/browser tests
305
- dashboard_test.rb
306
- dropdown_fallback_test.rb
307
- items_test.rb
308
- logs_test.rb
309
- mission_control_test.rb
310
- sources_test.rb
311
- tasks/ # Rake task tests
312
- ```
313
-
314
- ## Key File Counts
315
-
316
- | Category | Count |
317
- |----------|-------|
318
- | Ruby files (.rb) | ~324 |
319
- | ERB templates (.erb) | ~48 |
320
- | JavaScript files (.js) | ~14 |
321
- | YAML configs (.yml) | ~16 |
322
- | Test files (*_test.rb) | ~124 |
323
- | Migrations | 24 |
324
- | Stimulus controllers | 6 |