Skip to content

Commit a0d620a

Browse files
authored
docs(ruby-gem): collected updates for gem v0.24.0 (#1228)
* docs(infrastructure): botasaurus-first auto strategy for gem 0.24.0 * docs(adapters): FeedResult status and enclosure RSS rules for 0.24.0 * docs(use-cases): 0.24.0 migration notes for auto and enclosures * docs: fix Code sample indentation Prettier was stripping Use string-concat Code props so YAML nesting and Ruby samples survive prettier --write. * docs: prettier-safe Code sample indentation Encode the no-blank-line template rule in AGENTS.md and restore readable multiline Code samples that survive Prettier MDX.
1 parent fe78575 commit a0d620a

7 files changed

Lines changed: 84 additions & 43 deletions

File tree

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ In docs content (`src/content/docs/**`) and docs-supporting components:
9292
`import { Code } from '@astrojs/starlight/components';`
9393
- Do not use:
9494
`import Code from "astro/components/Code.astro";`
95+
- Prefer multiline template literals: `code={\`...\`}`.
96+
- Give every content line the same 2-space base indent (Starlight strips the common indent on render).
97+
- Do not put blank lines inside the template — Prettier MDX strips indentation after blank lines and corrupts nested YAML/Ruby. Separate sections with `#` comment lines instead.
98+
- Do not use `"...\n" +` string concat for snippets unless a concrete Prettier conflict remains after following the no-blank-line rule (should be rare).
9599

96100
### Accuracy Rules
97101

src/content/docs/ruby-gem/guides/backward-compatibility.mdx

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ import { Code } from "@astrojs/starlight/components";
77

88
This page outlines recent breaking changes, purged legacy aliases, and migration steps for older feed configurations.
99

10+
## 0.24.0
11+
12+
Upgrade notes for configs and integrators targeting gem **0.24.0**:
13+
14+
1. **`auto` strategy chain** — Default `strategy: auto` is `faraday``botasaurus` only. Pin `strategy: browserless` (or `--strategy browserless`) when you need Browserless preload/interaction; Browserless is not a fallback tier.
15+
2. **RSS enclosures** — Configure media with the `enclosure` selector. RSS does not promote `image` into `<enclosure>`; images stay on the description / JSON Feed `image`.
16+
3. **Ruby dual-format / telemetry** — Prefer `Html2rss.feed_result` for one scrape that must render RSS and JSON Feed (or be Marshal-cached). Read `result.status.to_h` for scrape telemetry (`selected_strategy`, `attempt_count`, `strategy_attempts` under `auto`).
17+
1018
## Removed Legacy Selector Aliases
1119

1220
In previous versions, `html2rss` accepted legacy selector names with a deprecation warning. These shims have been removed:
@@ -26,14 +34,12 @@ Update any occurrences of `pubDate` or `updated` in your `selectors` block to `p
2634
selectors:
2735
updated:
2836
selector: ".date"
29-
30-
# Current & Required
31-
32-
selectors:
33-
published_at:
34-
selector: ".date"
37+
# Current & Required
38+
selectors:
39+
published_at:
40+
selector: ".date"
3541
`}
36-
lang="yaml"
42+
lang="yaml"
3743
/>
3844

3945
## Removed Channel Attributes
@@ -48,16 +54,14 @@ lang="yaml"
4854
strategy: browserless
4955
headers:
5056
User-Agent: "CustomAgent/1.0"
51-
52-
# Current & Required
53-
54-
strategy: browserless
55-
headers:
56-
User-Agent: "CustomAgent/1.0"
57-
channel:
58-
url: "https://example.com/articles"
57+
# Current & Required
58+
strategy: browserless
59+
headers:
60+
User-Agent: "CustomAgent/1.0"
61+
channel:
62+
url: "https://example.com/articles"
5963
`}
60-
lang="yaml"
64+
lang="yaml"
6165
/>
6266

6367
## Migration Checklist
@@ -66,4 +70,7 @@ When upgrading to modern `html2rss` releases:
6670

6771
1. **Rename date selectors**: Ensure date selectors use `published_at` rather than `updated` or `pubDate`.
6872
2. **Move channel-level transport keys**: Ensure `strategy` and `headers` are defined at the top level of the YAML file.
69-
3. **Validate configurations**: Run `html2rss validate config.yml` to ensure your YAML conforms to the current schema.
73+
3. **Pin Browserless when needed**: If you relied on `auto` falling through to Browserless, set `strategy: browserless` (or `--strategy browserless`).
74+
4. **Use `enclosure` for RSS media**: Do not rely on `image` becoming an RSS `<enclosure>`; select podcast/media URLs with `enclosure`.
75+
5. **Prefer `feed_result` for dual-format / cache**: Integrators that need RSS + JSON Feed from one scrape (or Marshal caching) should use `Html2rss.feed_result` and `status.to_h`.
76+
6. **Validate configurations**: Run `html2rss validate config.yml` to ensure your YAML conforms to the current schema.

src/content/docs/ruby-gem/guides/handling-dynamic-content.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ Some websites load their content dynamically using JavaScript. Static fetch path
99

1010
## Solution
1111

12-
Use a [browser-based extraction strategy](/ruby-gem/reference/strategy/) when JavaScript-heavy pages do not work with default static fetching.
12+
Default `strategy: auto` already tries `faraday` then `botasaurus` (when `BOTASAURUS_SCRAPER_URL` is configured). That covers many JS-rendered listing pages without pinning a strategy.
1313

14-
`browserless` is common for this workflow, and `botasaurus` is an alternate browser-based strategy when you run a Botasaurus scrape API.
14+
Pin [`browserless`](/ruby-gem/reference/strategy/#browserless) when you need headless Chrome with preload (wait, click, scroll) or other Browserless-only controls — Browserless is not part of the `auto` chain.
1515

1616
Keep the strategy at the top level and put request-specific options under `request`:
1717

src/content/docs/ruby-gem/guides/managing-feed-configs.mdx

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,46 @@ You can define global settings that apply to all feeds, and then define individu
5353
<Code
5454
code={`
5555
require 'html2rss'
56+
# Build a specific feed from the YAML file
57+
my_feed_config = Html2rss.config_from_yaml_file('feeds.yml', 'my-first-feed')
58+
rss = Html2rss.feed(my_feed_config)
59+
puts rss
60+
# If the YAML file contains only one feed, you can omit the feed name
61+
single_feed_config = Html2rss.config_from_yaml_file('single.yml')
62+
rss = Html2rss.feed(single_feed_config)
63+
puts rss
64+
`}
65+
lang="ruby"
66+
/>
67+
68+
### Ruby API: FeedResult
69+
70+
Prefer `Html2rss.feed_result` when one scrape must render as both RSS and JSON Feed, or when you Marshal-cache the scrape and render later. `Html2rss.feed` and `Html2rss.json_feed` remain convenience wrappers over the same path.
5671

57-
# Build a specific feed from the YAML file
72+
`FeedResult` is an opaque handle. Public surface:
5873

59-
my_feed_config = Html2rss.config_from_yaml_file('feeds.yml', 'my-first-feed')
60-
rss = Html2rss.feed(my_feed_config)
61-
puts rss
74+
- `empty?` — whether the scrape produced items
75+
- `channel_title` — channel title string only
76+
- `to_rss` / `to_json_feed(feed_url:)` — render formats
77+
- `status` — scrape telemetry (`Html2rss::Status`)
6278

63-
# If the YAML file contains only one feed, you can omit the feed name
79+
`status.to_h` is the stable observability payload. Always includes `version` and `dedup_dropped`. When present, it may also include `scraper_tallies`, `selected_strategy`, `attempt_count`, and `strategy_attempts` (auto-fallback attempts; empty outside `strategy: auto`).
6480

65-
single_feed_config = Html2rss.config_from_yaml_file('single.yml')
66-
rss = Html2rss.feed(single_feed_config)
67-
puts rss
81+
<Code
82+
code={`
83+
require 'html2rss'
84+
config = Html2rss.config_from_yaml_file('feeds.yml', 'my-first-feed')
85+
result = Html2rss.feed_result(config)
86+
result.to_rss
87+
result.to_json_feed(feed_url: 'https://example.com/feeds/my-first-feed.json')
88+
result.status.to_h
89+
# => { version: "...", dedup_dropped: 0, selected_strategy: :botasaurus, ... }
6890
`}
69-
lang="ruby"
91+
lang="ruby"
7092
/>
7193

94+
Depth and method contracts: [YARD for `html2rss`](https://www.rubydoc.info/gems/html2rss).
95+
7296
### Command Line
7397

7498
<Code

src/content/docs/ruby-gem/reference/cli-reference.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Command: `html2rss auto [URL]`
3636

3737
Available options:
3838

39-
- `--strategy`: Optional request strategy (`auto`, `faraday`, `browserless`, `botasaurus`, `local_file`). Defaults to `auto`, which tries `faraday` -> `botasaurus` -> `browserless`.
39+
- `--strategy`: Optional request strategy (`auto`, `faraday`, `browserless`, `botasaurus`, `local_file`). Defaults to `auto`, which tries `faraday` -> `botasaurus`. Pin `browserless` explicitly when you need headless Chrome (preload/interaction).
4040
- `--format`: Output format for the auto-sourced feed (`rss` or `jsonfeed`). Defaults to `rss`.
4141
- `--items_selector`: Optional CSS selector hint for item extraction.
4242
- `--max-redirects`: Maximum redirects to follow per request.
@@ -77,7 +77,7 @@ If all fallback tiers run but still extract zero items, html2rss raises:
7777

7878
- `No RSS feed items extracted after auto fallback ...`
7979

80-
If failures continue after URL/surface fixes, retry with an explicit browser-based override (`--strategy browserless`), or `--strategy botasaurus` when `BOTASAURUS_SCRAPER_URL` is configured.
80+
If failures continue after URL/surface fixes, ensure `BOTASAURUS_SCRAPER_URL` is set so the `auto` Botasaurus tier can run, or pin `--strategy browserless` when you need Browserless preload/interaction.
8181

8282
Start by changing the input URL to a direct listing/update page, then move to explicit selectors if needed.
8383

src/content/docs/ruby-gem/reference/selectors.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,9 @@ To create a custom GUID for an item, provide a list of selector names to the `gu
288288

289289
### Enclosures
290290

291-
To add an enclosure (e.g., an image, audio, or video file) to an item, use the `enclosure` selector to specify the URL of the file.
291+
Use the `enclosure` selector to attach media (audio, video, or other non-image files) to an item. The selector is wired into each article and rendered into the feed.
292+
293+
RSS `<enclosure>` uses the first **non-image** enclosure. Images stay on the item description (and on JSON Feed `image` / attachments) — they are not promoted into RSS `<enclosure>`. For podcast/media RSS, select a non-image resource with `enclosure`.
292294

293295
<Code
294296
code={`

src/content/docs/ruby-gem/reference/strategy.mdx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,27 @@ import { Code } from "@astrojs/starlight/components";
77

88
The `strategy` key defines how `html2rss` fetches a website's content.
99

10-
- **`auto`** (default): Tries concrete strategies in order: `faraday` -> `botasaurus` -> `browserless`.
10+
- **`auto`** (default): Tries concrete strategies in order: `faraday` -> `botasaurus`.
1111
- **`faraday`**: Makes a direct HTTP request. It is fast but does not execute JavaScript.
12-
- **`browserless`**: Renders the website in a headless Chrome browser, which is necessary for JavaScript-heavy sites.
13-
- **`botasaurus`**: Delegates fetching to a Botasaurus scrape API. This is opt-in and requires `BOTASAURUS_SCRAPER_URL`.
12+
- **`botasaurus`**: Delegates fetching to a Botasaurus scrape API. Included in the `auto` chain; requires `BOTASAURUS_SCRAPER_URL` when that tier runs (or when you pin `strategy: botasaurus`).
13+
- **`browserless`**: Renders the website in a headless Chrome browser. **Explicit only** — set `strategy: browserless` or `--strategy browserless` (not part of `auto`).
1414
- **`local_file`**: Reads HTML content directly from a local file on disk without making network requests.
1515

1616
`strategy` is a top-level config key. Request-specific controls live under `request`.
1717

18-
`auto` falls back to the next strategy when the current attempt errors or extracts zero items. Use explicit `--strategy ...` only when you need to force a specific transport for troubleshooting or reproducibility.
18+
`auto` falls back to the next strategy when the current attempt errors or extracts zero items. Pin a concrete strategy when you need a specific transport (for example Browserless preload/interaction, or a forced Botasaurus-only run).
1919

2020
## `auto` (default)
2121

2222
The default strategy chain is:
2323

24-
`faraday` -> `botasaurus` -> `browserless`
24+
`faraday` -> `botasaurus`
25+
26+
`browserless` is not in this chain. Pin it when you need headless Chrome with preload (wait/click/scroll) or other Browserless-only controls.
2527

2628
Auto fallback shares one request budget across all strategy attempts. For pagination-heavy or dynamic pages, increase `request.max_requests` (or `--max-requests`) when retries exhaust the budget.
2729

28-
Auto fallback decisions are hidden at the default `LOG_LEVEL=warn`; run with `LOG_LEVEL=info` to include them in CLI output.
30+
Under `auto`, `Html2rss.feed_result(...).status` exposes scrape telemetry: `selected_strategy`, `attempt_count`, and `strategy_attempts` (see [Managing Feed Configs](/ruby-gem/guides/managing-feed-configs/#ruby-api-feedresult)). Auto fallback decisions are also visible at `LOG_LEVEL=info` (hidden at the default `LOG_LEVEL=warn`).
2931

3032
## `faraday`
3133

@@ -204,12 +206,14 @@ For custom Browserless websocket endpoints, `BROWSERLESS_IO_API_TOKEN` is mandat
204206

205207
## `botasaurus`
206208

207-
`botasaurus` delegates page fetching to a Botasaurus scrape API endpoint. This strategy is explicit opt-in and requires:
209+
`botasaurus` delegates page fetching to a Botasaurus scrape API endpoint. It runs as the second tier of `auto`, or when you pin `strategy: botasaurus`.
210+
211+
Requirements:
208212

209-
- `strategy: botasaurus`
210213
- `BOTASAURUS_SCRAPER_URL` set to your Botasaurus scrape API base URL (for example `http://localhost:4010`)
214+
- pin `strategy: botasaurus` only when you want to skip Faraday and force this transport
211215

212-
html2rss still enforces local request policy preflight and timeout budget. Botasaurus handles browser navigation/rendering internals, so some policy details are delegated to upstream execution.
216+
html2rss still enforces local request policy preflight and timeout budget. When a total request timeout remains, Botasaurus `max_retries` and `wait_timeout_seconds` are clamped so upstream work fits the remaining budget. Botasaurus handles browser navigation/rendering internals, so some policy details are delegated to upstream execution.
213217

214218
### Configuration
215219

@@ -222,7 +226,7 @@ html2rss still enforces local request policy preflight and timeout budget. Botas
222226
request:
223227
botasaurus:
224228
navigation_mode: auto
225-
max_retries: 2
229+
max_retries: 1
226230
headless: false
227231
`}
228232
lang="yml"
@@ -231,7 +235,7 @@ html2rss still enforces local request policy preflight and timeout budget. Botas
231235
Supported `request.botasaurus` options:
232236

233237
- `navigation_mode` (`auto`, `get`, `google_get`, `google_get_bypass`; default `auto`)
234-
- `max_retries` (`0..3`; default `2`)
238+
- `max_retries` (`0..3`; default `1`)
235239
- `wait_for_selector` (string)
236240
- `wait_timeout_seconds` (integer)
237241
- `block_images` (boolean)
@@ -252,7 +256,7 @@ Example scrape-API payload shape:
252256
{
253257
"url": "https://example.com",
254258
"navigation_mode": "auto",
255-
"max_retries": 2,
259+
"max_retries": 1,
256260
"headless": false
257261
}
258262
`}

0 commit comments

Comments
 (0)