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,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 |
|