source_monitor 0.3.3 → 0.4.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 +101 -58
- data/.claude/skills/sm-configure/SKILL.md +13 -2
- data/.claude/skills/sm-configure/reference/configuration-reference.md +33 -0
- data/.claude/skills/sm-host-setup/SKILL.md +15 -1
- data/.claude/skills/sm-host-setup/reference/setup-checklist.md +33 -0
- data/.claude/skills/sm-job/SKILL.md +1 -1
- data/.vbw-planning/REQUIREMENTS.md +22 -0
- data/.vbw-planning/ROADMAP.md +125 -0
- data/.vbw-planning/STATE.md +43 -0
- data/.vbw-planning/config.json +3 -1
- data/.vbw-planning/discovery.json +3 -1
- data/.vbw-planning/phases/01-generator-steps/01-CONTEXT.md +33 -0
- data/.vbw-planning/phases/01-generator-steps/01-VERIFICATION.md +86 -0
- data/.vbw-planning/phases/01-generator-steps/PLAN-01-SUMMARY.md +61 -0
- data/.vbw-planning/phases/01-generator-steps/PLAN-01.md +380 -0
- data/.vbw-planning/phases/02-verification/02-VERIFICATION.md +78 -0
- data/.vbw-planning/phases/02-verification/PLAN-01-SUMMARY.md +46 -0
- data/.vbw-planning/phases/02-verification/PLAN-01.md +500 -0
- data/.vbw-planning/phases/03-docs-alignment/03-VERIFICATION.md +89 -0
- data/.vbw-planning/phases/03-docs-alignment/PLAN-01-SUMMARY.md +48 -0
- data/.vbw-planning/phases/03-docs-alignment/PLAN-01.md +456 -0
- data/.vbw-planning/phases/04-dashboard-ux/04-VERIFICATION.md +129 -0
- data/.vbw-planning/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +70 -0
- data/.vbw-planning/phases/04-dashboard-ux/PLAN-01.md +747 -0
- data/.vbw-planning/phases/05-active-storage-images/05-VERIFICATION.md +156 -0
- data/.vbw-planning/phases/05-active-storage-images/PLAN-01-SUMMARY.md +69 -0
- data/.vbw-planning/phases/05-active-storage-images/PLAN-01.md +455 -0
- data/.vbw-planning/phases/05-active-storage-images/PLAN-02-SUMMARY.md +39 -0
- data/.vbw-planning/phases/05-active-storage-images/PLAN-02.md +488 -0
- data/.vbw-planning/phases/06-netflix-feed-fix/06-VERIFICATION.md +100 -0
- data/.vbw-planning/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +37 -0
- data/.vbw-planning/phases/06-netflix-feed-fix/PLAN-01.md +345 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile.lock +1 -1
- data/VERSION +1 -1
- data/app/assets/builds/source_monitor/application.css +9 -0
- data/app/helpers/source_monitor/application_helper.rb +38 -0
- data/app/jobs/source_monitor/download_content_images_job.rb +72 -0
- data/app/models/source_monitor/item_content.rb +2 -0
- data/app/views/source_monitor/dashboard/_recent_activity.html.erb +9 -0
- data/app/views/source_monitor/items/_details.html.erb +2 -2
- data/app/views/source_monitor/logs/index.html.erb +9 -0
- data/app/views/source_monitor/sources/_details.html.erb +2 -2
- data/app/views/source_monitor/sources/_row.html.erb +1 -1
- data/docs/setup.md +10 -1
- data/docs/troubleshooting.md +38 -7
- data/lib/generators/source_monitor/install/install_generator.rb +101 -0
- data/lib/source_monitor/configuration/http_settings.rb +7 -1
- data/lib/source_monitor/configuration/images_settings.rb +37 -0
- data/lib/source_monitor/configuration.rb +3 -1
- data/lib/source_monitor/dashboard/queries/recent_activity_query.rb +16 -7
- data/lib/source_monitor/dashboard/recent_activity.rb +1 -0
- data/lib/source_monitor/dashboard/recent_activity_presenter.rb +15 -2
- data/lib/source_monitor/fetching/feed_fetcher/entry_processor.rb +13 -0
- data/lib/source_monitor/http.rb +23 -0
- data/lib/source_monitor/images/content_rewriter.rb +81 -0
- data/lib/source_monitor/images/downloader.rb +82 -0
- data/lib/source_monitor/logs/table_presenter.rb +25 -0
- data/lib/source_monitor/setup/procfile_patcher.rb +31 -0
- data/lib/source_monitor/setup/queue_config_patcher.rb +84 -0
- data/lib/source_monitor/setup/verification/recurring_schedule_verifier.rb +102 -0
- data/lib/source_monitor/setup/verification/runner.rb +1 -1
- data/lib/source_monitor/setup/verification/solid_queue_verifier.rb +1 -1
- data/lib/source_monitor/setup/workflow.rb +10 -0
- data/lib/source_monitor/version.rb +1 -1
- data/lib/source_monitor.rb +8 -0
- metadata +31 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
phase: 5
|
|
3
|
+
tier: standard
|
|
4
|
+
result: PASS
|
|
5
|
+
passed: 34
|
|
6
|
+
failed: 0
|
|
7
|
+
total: 34
|
|
8
|
+
date: 2026-02-12
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Phase 5 Verification: Active Storage Image Downloads
|
|
12
|
+
|
|
13
|
+
## Must-Have Checks
|
|
14
|
+
|
|
15
|
+
### PLAN-01: images-config-model-rewriter
|
|
16
|
+
|
|
17
|
+
| # | Truth/Condition | Status | Evidence |
|
|
18
|
+
|---|-----------------|--------|----------|
|
|
19
|
+
| 1 | `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/configuration/images_settings_test.rb` exits 0 with 0 failures | PASS | 15 runs, 21 assertions, 0 failures |
|
|
20
|
+
| 2 | `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/images/content_rewriter_test.rb` exits 0 with 0 failures | PASS | 27 runs, 41 assertions, 0 failures |
|
|
21
|
+
| 3 | `PARALLEL_WORKERS=1 bin/rails test test/models/source_monitor/item_content_test.rb` exits 0 with 0 failures | PASS | 6 runs, 10 assertions, 0 failures |
|
|
22
|
+
| 4 | `bin/rubocop` on PLAN-01 files exits 0 with no offenses | PASS | 3 files inspected, 0 offenses |
|
|
23
|
+
| 5 | `SourceMonitor.config.images` returns an ImagesSettings instance | PASS | attr_reader :images in configuration.rb, @images = ImagesSettings.new |
|
|
24
|
+
| 6 | `SourceMonitor.config.images.download_to_active_storage` defaults to `false` | PASS | DEFAULT in ImagesSettings.reset! sets to false |
|
|
25
|
+
| 7 | `SourceMonitor.reset_configuration!` resets images settings to defaults | PASS | Tested in images_settings_test.rb (15 tests pass) |
|
|
26
|
+
| 8 | ContentRewriter.new(html).image_urls returns an array of absolute image URLs | PASS | ContentRewriter#image_urls method verified (27 tests pass) |
|
|
27
|
+
| 9 | ContentRewriter.new(html).rewrite { \|url\| new_url } replaces img src attributes | PASS | ContentRewriter#rewrite method verified (27 tests pass) |
|
|
28
|
+
| 10 | ItemContent responds to `images` (has_many_attached) | PASS | has_many_attached :images in item_content.rb (6 tests pass) |
|
|
29
|
+
|
|
30
|
+
### PLAN-02: download-job-integration-docs
|
|
31
|
+
|
|
32
|
+
| # | Truth/Condition | Status | Evidence |
|
|
33
|
+
|---|-----------------|--------|----------|
|
|
34
|
+
| 11 | `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/images/downloader_test.rb` exits 0 with 0 failures | PASS | 11 runs, 18 assertions, 0 failures |
|
|
35
|
+
| 12 | `PARALLEL_WORKERS=1 bin/rails test test/jobs/source_monitor/download_content_images_job_test.rb` exits 0 with 0 failures | PASS | 10 runs, 29 assertions, 0 failures |
|
|
36
|
+
| 13 | `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/fetching/feed_fetcher/entry_processor_test.rb` exits 0 with 0 failures | PASS | 5 runs, 8 assertions, 0 failures |
|
|
37
|
+
| 14 | `bin/rubocop` on PLAN-02 files exits 0 with no offenses | PASS | 3 files inspected, 0 offenses |
|
|
38
|
+
| 15 | When `config.images.download_to_active_storage` is false (default), no job enqueued | PASS | entry_processor_test.rb verifies default behavior |
|
|
39
|
+
| 16 | When `config.images.download_to_active_storage` is true, job enqueued for new items with HTML | PASS | entry_processor_test.rb verifies enabled behavior |
|
|
40
|
+
| 17 | DownloadContentImagesJob downloads images, attaches to ItemContent, rewrites item.content | PASS | download_content_images_job_test.rb (10 tests verify full pipeline) |
|
|
41
|
+
| 18 | Images that fail to download preserve original URLs (graceful fallback) | PASS | download_content_images_job_test.rb tests individual failure handling |
|
|
42
|
+
| 19 | Images larger than max_download_size are skipped | PASS | downloader_test.rb tests size validation |
|
|
43
|
+
| 20 | Images with disallowed content types are skipped | PASS | downloader_test.rb tests content type validation |
|
|
44
|
+
| 21 | sm-configure skill documents the new config.images section | PASS | config.images in SKILL.md and configuration-reference.md |
|
|
45
|
+
|
|
46
|
+
## Artifact Checks
|
|
47
|
+
|
|
48
|
+
| Artifact | Exists | Contains | Status |
|
|
49
|
+
|----------|--------|----------|--------|
|
|
50
|
+
| `lib/source_monitor/configuration/images_settings.rb` | YES | class ImagesSettings | PASS |
|
|
51
|
+
| `lib/source_monitor/images/content_rewriter.rb` | YES | class ContentRewriter | PASS |
|
|
52
|
+
| `test/lib/source_monitor/configuration/images_settings_test.rb` | YES | class ImagesSettingsTest (15 tests) | PASS |
|
|
53
|
+
| `test/lib/source_monitor/images/content_rewriter_test.rb` | YES | class ContentRewriterTest (27 tests) | PASS |
|
|
54
|
+
| `test/models/source_monitor/item_content_test.rb` | YES | 6 tests for has_many_attached | PASS |
|
|
55
|
+
| `lib/source_monitor/images/downloader.rb` | YES | class Downloader | PASS |
|
|
56
|
+
| `app/jobs/source_monitor/download_content_images_job.rb` | YES | class DownloadContentImagesJob | PASS |
|
|
57
|
+
| `test/lib/source_monitor/images/downloader_test.rb` | YES | 11 tests for Downloader | PASS |
|
|
58
|
+
| `test/jobs/source_monitor/download_content_images_job_test.rb` | YES | 10 tests for job | PASS |
|
|
59
|
+
| `test/lib/source_monitor/fetching/feed_fetcher/entry_processor_test.rb` | YES | 5 tests for integration hook | PASS |
|
|
60
|
+
| `.claude/skills/sm-configure/SKILL.md` | YES | config.images section | PASS |
|
|
61
|
+
| `.claude/skills/sm-configure/reference/configuration-reference.md` | YES | ImagesSettings documentation | PASS |
|
|
62
|
+
|
|
63
|
+
## Key Link Checks
|
|
64
|
+
|
|
65
|
+
| From | To | Via | Status |
|
|
66
|
+
|------|----|----|--------|
|
|
67
|
+
| images_settings.rb#download_to_active_storage | REQ-24 | Configurable option defaults to false | PASS |
|
|
68
|
+
| content_rewriter.rb#image_urls | REQ-24 | Detects inline images in item content | PASS |
|
|
69
|
+
| content_rewriter.rb#rewrite | REQ-24 | Replaces original URLs with Active Storage URLs | PASS |
|
|
70
|
+
| item_content.rb#has_many_attached | REQ-24 | Images attached to ItemContent via Active Storage | PASS |
|
|
71
|
+
| download_content_images_job.rb#perform | REQ-24 | Downloads inline images to Active Storage | PASS |
|
|
72
|
+
| entry_processor.rb#enqueue_image_download | REQ-24 | Enqueues download job when config enabled | PASS |
|
|
73
|
+
| downloader.rb | REQ-24 | Validates size and content type before download | PASS |
|
|
74
|
+
| sm-configure/SKILL.md | REQ-24 | Configuration documented in skill | PASS |
|
|
75
|
+
|
|
76
|
+
## Convention Compliance
|
|
77
|
+
|
|
78
|
+
| Convention | File | Status | Detail |
|
|
79
|
+
|------------|------|--------|--------|
|
|
80
|
+
| frozen_string_literal | images_settings.rb | PASS | Present in line 1 |
|
|
81
|
+
| frozen_string_literal | content_rewriter.rb | PASS | Present in line 1 |
|
|
82
|
+
| frozen_string_literal | downloader.rb | PASS | Present in line 1 |
|
|
83
|
+
| frozen_string_literal | download_content_images_job.rb | PASS | Present in line 1 |
|
|
84
|
+
| Test coverage | ImagesSettings | PASS | 15 tests covering defaults, accessors, reset, download_enabled? |
|
|
85
|
+
| Test coverage | ContentRewriter | PASS | 27 tests covering extraction, rewriting, edge cases |
|
|
86
|
+
| Test coverage | Downloader | PASS | 11 tests covering success/failure modes |
|
|
87
|
+
| Test coverage | DownloadContentImagesJob | PASS | 10 tests covering full pipeline and failures |
|
|
88
|
+
| Test coverage | EntryProcessor integration | PASS | 5 tests covering enabled/disabled/failure modes |
|
|
89
|
+
| Naming | ImagesSettings | PASS | Follows Configuration sub-class pattern |
|
|
90
|
+
| Naming | Images module | PASS | Follows engine module structure |
|
|
91
|
+
| Autoloading | Images::ContentRewriter | PASS | Autoloaded in lib/source_monitor.rb |
|
|
92
|
+
| Autoloading | Images::Downloader | PASS | Autoloaded in lib/source_monitor.rb |
|
|
93
|
+
|
|
94
|
+
## Anti-Pattern Scan
|
|
95
|
+
|
|
96
|
+
| Pattern | Found | Location | Severity |
|
|
97
|
+
|---------|-------|----------|----------|
|
|
98
|
+
| TODO/HACK/FIXME/XXX | NO | lib/source_monitor/images/* | N/A |
|
|
99
|
+
| TODO/HACK/FIXME/XXX | NO | app/jobs/source_monitor/download_content_images_job.rb | N/A |
|
|
100
|
+
| .html_safe | NO | lib/source_monitor/images/* | N/A |
|
|
101
|
+
| Unsafe HTML rendering | NO | All new files | N/A |
|
|
102
|
+
|
|
103
|
+
## Requirement Mapping
|
|
104
|
+
|
|
105
|
+
| Requirement | Plan Ref | Artifact Evidence | Status |
|
|
106
|
+
|-------------|----------|-------------------|--------|
|
|
107
|
+
| REQ-24: Configurable image download option | PLAN-01 | ImagesSettings with download_to_active_storage (default false) | PASS |
|
|
108
|
+
| REQ-24: Image detection in content | PLAN-01 | ContentRewriter#image_urls extracts URLs from HTML | PASS |
|
|
109
|
+
| REQ-24: Active Storage attachment | PLAN-01 | ItemContent has_many_attached :images | PASS |
|
|
110
|
+
| REQ-24: URL rewriting in content | PLAN-01 | ContentRewriter#rewrite replaces src attributes | PASS |
|
|
111
|
+
| REQ-24: Image download service | PLAN-02 | Downloader downloads, validates size/content-type | PASS |
|
|
112
|
+
| REQ-24: Background job orchestration | PLAN-02 | DownloadContentImagesJob performs full pipeline | PASS |
|
|
113
|
+
| REQ-24: Pipeline integration | PLAN-02 | EntryProcessor enqueues job for new items | PASS |
|
|
114
|
+
| REQ-24: Graceful fallback on failures | PLAN-02 | Failed downloads preserve original URLs | PASS |
|
|
115
|
+
| REQ-24: Zero behavior change when disabled | PLAN-02 | Default config (false) = no jobs enqueued | PASS |
|
|
116
|
+
| REQ-24: Documentation | PLAN-02 | sm-configure skill updated with config.images | PASS |
|
|
117
|
+
|
|
118
|
+
## Summary
|
|
119
|
+
|
|
120
|
+
**Tier:** standard
|
|
121
|
+
|
|
122
|
+
**Result:** PASS
|
|
123
|
+
|
|
124
|
+
**Passed:** 34/34
|
|
125
|
+
|
|
126
|
+
**Failed:** None
|
|
127
|
+
|
|
128
|
+
### Highlights
|
|
129
|
+
|
|
130
|
+
- **Configuration layer:** ImagesSettings with 4 configurable attributes (download_to_active_storage defaults to false, max_download_size 10MB, download_timeout 30s, allowed_content_types 5 image MIME types). Fully integrated into SourceMonitor.config.images.
|
|
131
|
+
|
|
132
|
+
- **Model layer:** ItemContent has_many_attached :images via Active Storage. Active Storage tables installed in dummy app for testing.
|
|
133
|
+
|
|
134
|
+
- **HTML processing:** ContentRewriter uses Nokolexbor to extract image URLs and rewrite img src attributes. Handles relative URLs, data: URIs, malformed URLs gracefully.
|
|
135
|
+
|
|
136
|
+
- **Download service:** Downloader validates content type and size, derives filenames, returns nil on any failure for graceful fallback.
|
|
137
|
+
|
|
138
|
+
- **Background job:** DownloadContentImagesJob orchestrates the full pipeline: extract URLs, download images, attach to ItemContent, rewrite item.content with Active Storage blob paths. Idempotent (skips if images already attached). Runs on fetch queue.
|
|
139
|
+
|
|
140
|
+
- **Integration hook:** EntryProcessor calls enqueue_image_download after item creation. Only fires when config enabled and item has HTML content. Wrapped in rescue to prevent feed processing breakage.
|
|
141
|
+
|
|
142
|
+
- **Documentation:** sm-configure skill and reference updated with full config.images section.
|
|
143
|
+
|
|
144
|
+
- **Test coverage:** 74 new tests (15 + 27 + 6 + 11 + 10 + 5) covering all scenarios. Full suite: 967 runs, 3100 assertions, 0 failures.
|
|
145
|
+
|
|
146
|
+
- **Code quality:** RuboCop 389 files, 0 offenses. All files have frozen_string_literal. No anti-patterns detected.
|
|
147
|
+
|
|
148
|
+
- **Zero behavior change:** Default config (download_to_active_storage = false) means no jobs enqueued, no Active Storage calls, no content rewriting. Existing pipelines unaffected.
|
|
149
|
+
|
|
150
|
+
### Deviations
|
|
151
|
+
|
|
152
|
+
None. Both plans executed as specified. All must_haves verified. All artifacts present and tested.
|
|
153
|
+
|
|
154
|
+
### Next Steps
|
|
155
|
+
|
|
156
|
+
Phase 5 complete. REQ-24 fully satisfied. Engine now supports configurable image downloads to Active Storage with graceful fallback on all failure modes.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# PLAN-01 Summary: images-config-model-rewriter
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE
|
|
4
|
+
|
|
5
|
+
## What Was Built
|
|
6
|
+
|
|
7
|
+
### Task 1: Create ImagesSettings config class
|
|
8
|
+
- Added `ImagesSettings` class with `download_to_active_storage` (default false), `max_download_size` (10MB), `download_timeout` (30s), `allowed_content_types` (5 image MIME types)
|
|
9
|
+
- Wired into `Configuration` as `config.images` attr_reader, initialized in constructor
|
|
10
|
+
- Added `require` to configuration.rb and `Images` module autoload to lib/source_monitor.rb
|
|
11
|
+
- 15 tests covering defaults, accessors, reset!, download_enabled?, integration with SourceMonitor.config
|
|
12
|
+
|
|
13
|
+
### Task 2: Install Active Storage and add attachment
|
|
14
|
+
- Created Active Storage migration in dummy app (`test/dummy/db/migrate/20260212000000_create_active_storage_tables.rb`)
|
|
15
|
+
- Active Storage tables (blobs, attachments, variant_records) added to dummy app schema
|
|
16
|
+
- Added `has_many_attached :images` to `ItemContent` model
|
|
17
|
+
- Created 1x1 PNG test fixture at `test/fixtures/files/test_image.png`
|
|
18
|
+
- 6 tests covering belongs_to, validates presence, images attachment, empty collection, single/multiple attach
|
|
19
|
+
|
|
20
|
+
### Task 3: Create ContentRewriter class
|
|
21
|
+
- Added `ContentRewriter` class in `lib/source_monitor/images/content_rewriter.rb` using Nokolexbor
|
|
22
|
+
- `image_urls` method extracts absolute image URLs from `<img>` tags, skipping data: URIs and invalid URLs
|
|
23
|
+
- `rewrite` method yields each downloadable URL to a block and replaces src with return value; preserves original on nil return
|
|
24
|
+
- Handles relative URL resolution via `base_url` parameter, whitespace in src, self-closing tags
|
|
25
|
+
- 27 tests covering extraction, rewriting, and edge cases
|
|
26
|
+
|
|
27
|
+
### Task 4: Update existing configuration tests
|
|
28
|
+
- Added 4 images settings tests to `configuration_test.rb` (accessible, defaults, configure block, reset)
|
|
29
|
+
- Added `ImagesSettingsInSettingsTest` class to `settings_test.rb` with 4 tests (defaults, reset, download_enabled?, type check)
|
|
30
|
+
|
|
31
|
+
### Task 5: Full verification
|
|
32
|
+
- All 941 tests pass (0 failures, 0 errors)
|
|
33
|
+
- RuboCop: 384 files, 0 offenses (12 array bracket spacing issues auto-fixed)
|
|
34
|
+
|
|
35
|
+
## Files Modified
|
|
36
|
+
- `lib/source_monitor/configuration/images_settings.rb` (new -- ImagesSettings class)
|
|
37
|
+
- `lib/source_monitor/configuration.rb` (added images_settings require, attr_reader, initialization)
|
|
38
|
+
- `lib/source_monitor.rb` (added Images module autoload)
|
|
39
|
+
- `app/models/source_monitor/item_content.rb` (added has_many_attached :images)
|
|
40
|
+
- `lib/source_monitor/images/content_rewriter.rb` (new -- ContentRewriter class)
|
|
41
|
+
- `test/lib/source_monitor/configuration/images_settings_test.rb` (new -- 15 tests)
|
|
42
|
+
- `test/models/source_monitor/item_content_test.rb` (new -- 6 tests)
|
|
43
|
+
- `test/lib/source_monitor/images/content_rewriter_test.rb` (new -- 27 tests)
|
|
44
|
+
- `test/lib/source_monitor/configuration_test.rb` (added 4 images tests)
|
|
45
|
+
- `test/lib/source_monitor/configuration/settings_test.rb` (added 4 images tests)
|
|
46
|
+
- `test/dummy/db/migrate/20260212000000_create_active_storage_tables.rb` (new -- Active Storage migration)
|
|
47
|
+
- `test/dummy/db/schema.rb` (updated with Active Storage tables)
|
|
48
|
+
- `test/fixtures/files/test_image.png` (new -- 1x1 PNG fixture)
|
|
49
|
+
|
|
50
|
+
## Commits
|
|
51
|
+
- `884a3f6` feat(05-01): create-images-settings
|
|
52
|
+
- `52f4291` feat(05-01): install-active-storage-and-add-attachment
|
|
53
|
+
- `e199999` feat(05-01): create-content-rewriter
|
|
54
|
+
- `3954cb1` test(05-01): integration-test-and-config-test-update
|
|
55
|
+
- `05705a8` style(05-01): fix array bracket spacing in content_rewriter_test
|
|
56
|
+
|
|
57
|
+
## Requirements Satisfied
|
|
58
|
+
- REQ-24 config: ImagesSettings with download_to_active_storage toggle (default false), size/timeout/content-type limits
|
|
59
|
+
- REQ-24 attachment: ItemContent has has_many_attached :images via Active Storage
|
|
60
|
+
- REQ-24 detection: ContentRewriter.image_urls extracts downloadable image URLs from HTML content
|
|
61
|
+
- REQ-24 URL replacement: ContentRewriter.rewrite replaces img src attributes via block
|
|
62
|
+
- REQ-24 graceful fallback: ContentRewriter preserves original URLs when rewrite block returns nil
|
|
63
|
+
|
|
64
|
+
## Verification Results
|
|
65
|
+
- `bin/rails test`: 941 runs, 3045 assertions, 0 failures, 0 errors
|
|
66
|
+
- `bin/rubocop`: 384 files inspected, 0 offenses
|
|
67
|
+
|
|
68
|
+
## Deviations
|
|
69
|
+
None. All tasks executed as specified in the plan.
|