simple-rss 1.3.3 → 2.1.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/LICENSE +79 -7
- data/README.md +321 -0
- data/Rakefile +134 -136
- data/lib/simple-rss.rb +541 -154
- data/simple-rss.gemspec +5 -6
- data/test/base/array_tags_test.rb +37 -0
- data/test/base/base_test.rb +76 -77
- data/test/base/empty_tag_test.rb +56 -0
- data/test/base/encoding_test.rb +87 -0
- data/test/base/enumerable_test.rb +101 -0
- data/test/base/feed_attributes_test.rb +26 -0
- data/test/base/fetch_test.rb +117 -0
- data/test/base/hash_xml_serialization_test.rb +142 -0
- data/test/base/item_attributes_test.rb +26 -0
- data/test/base/json_serialization_test.rb +81 -0
- data/test/data/atom_with_entry_attrs.xml +13 -0
- data/test/data/atom_with_feed_attrs.xml +13 -0
- data/test/data/media_rss.xml +465 -0
- data/test/data/rss20_utf8.xml +61 -0
- data/test/data/rss20_with_channel_attrs.xml +13 -0
- data/test/data/rss20_with_item_attrs.xml +13 -0
- data/test/test_helper.rb +10 -3
- metadata +21 -11
- data/README.markdown +0 -47
- data/install.rb +0 -40
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f7e2a8e829ef0776194df8fd028266dbcecaeb0a5b7b1025aa75b739acbd6ad9
|
|
4
|
+
data.tar.gz: 664c6319db58dab8bf81e9f540afb6f1251997fa669b3e433a30748eb599ac16
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3885af60f0b54553af1e3545a781f6f1179825386cc3edcf1575ed75bb79240eefc83998295d21e842287f1c949a0554918d39d1df1df924701d27f20b26c192
|
|
7
|
+
data.tar.gz: 81394817f11b09d433eb7b07031120661a7de18d678deff66fdbf4f331f8ec6483a155069f36539e4ba6122210ec3c58cf08b67f3a44d9afc582c0952861d073
|
data/LICENSE
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
GNU LESSER GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 2.1, February 1999
|
|
3
3
|
|
|
4
4
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
|
5
|
-
|
|
5
|
+
<https://fsf.org/>
|
|
6
6
|
Everyone is permitted to copy and distribute verbatim copies
|
|
7
7
|
of this license document, but changing it is not allowed.
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
as the successor of the GNU Library Public License, version 2, hence
|
|
11
11
|
the version number 2.1.]
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Preamble
|
|
14
14
|
|
|
15
15
|
The licenses for most software are designed to take away your
|
|
16
16
|
freedom to share and change it. By contrast, the GNU General Public
|
|
@@ -112,7 +112,7 @@ modification follow. Pay close attention to the difference between a
|
|
|
112
112
|
former contains code derived from the library, whereas the latter must
|
|
113
113
|
be combined with the library in order to run.
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
GNU LESSER GENERAL PUBLIC LICENSE
|
|
116
116
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
117
117
|
|
|
118
118
|
0. This License Agreement applies to any software library or other
|
|
@@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based
|
|
|
146
146
|
on the Library (independent of the use of the Library in a tool for
|
|
147
147
|
writing it). Whether that is true depends on what the Library does
|
|
148
148
|
and what the program that uses the Library does.
|
|
149
|
-
|
|
149
|
+
|
|
150
150
|
1. You may copy and distribute verbatim copies of the Library's
|
|
151
151
|
complete source code as you receive it, in any medium, provided that
|
|
152
152
|
you conspicuously and appropriately publish on each copy an
|
|
@@ -426,4 +426,76 @@ the Free Software Foundation.
|
|
|
426
426
|
14. If you wish to incorporate parts of the Library into other free
|
|
427
427
|
programs whose distribution conditions are incompatible with these,
|
|
428
428
|
write to the author to ask for permission. For software which is
|
|
429
|
-
copyrighted by
|
|
429
|
+
copyrighted by the Free Software Foundation, write to the Free
|
|
430
|
+
Software Foundation; we sometimes make exceptions for this. Our
|
|
431
|
+
decision will be guided by the two goals of preserving the free status
|
|
432
|
+
of all derivatives of our free software and of promoting the sharing
|
|
433
|
+
and reuse of software generally.
|
|
434
|
+
|
|
435
|
+
NO WARRANTY
|
|
436
|
+
|
|
437
|
+
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
|
438
|
+
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
|
439
|
+
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
|
440
|
+
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
|
441
|
+
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
|
442
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
443
|
+
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
|
444
|
+
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
|
445
|
+
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
446
|
+
|
|
447
|
+
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
|
448
|
+
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
|
449
|
+
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
|
450
|
+
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
|
451
|
+
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
|
452
|
+
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
|
453
|
+
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
|
454
|
+
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
|
455
|
+
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
456
|
+
DAMAGES.
|
|
457
|
+
|
|
458
|
+
END OF TERMS AND CONDITIONS
|
|
459
|
+
|
|
460
|
+
How to Apply These Terms to Your New Libraries
|
|
461
|
+
|
|
462
|
+
If you develop a new library, and you want it to be of the greatest
|
|
463
|
+
possible use to the public, we recommend making it free software that
|
|
464
|
+
everyone can redistribute and change. You can do so by permitting
|
|
465
|
+
redistribution under these terms (or, alternatively, under the terms of the
|
|
466
|
+
ordinary General Public License).
|
|
467
|
+
|
|
468
|
+
To apply these terms, attach the following notices to the library. It is
|
|
469
|
+
safest to attach them to the start of each source file to most effectively
|
|
470
|
+
convey the exclusion of warranty; and each file should have at least the
|
|
471
|
+
"copyright" line and a pointer to where the full notice is found.
|
|
472
|
+
|
|
473
|
+
<one line to give the library's name and a brief idea of what it does.>
|
|
474
|
+
Copyright (C) <year> <name of author>
|
|
475
|
+
|
|
476
|
+
This library is free software; you can redistribute it and/or
|
|
477
|
+
modify it under the terms of the GNU Lesser General Public
|
|
478
|
+
License as published by the Free Software Foundation; either
|
|
479
|
+
version 2.1 of the License, or (at your option) any later version.
|
|
480
|
+
|
|
481
|
+
This library is distributed in the hope that it will be useful,
|
|
482
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
483
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
484
|
+
Lesser General Public License for more details.
|
|
485
|
+
|
|
486
|
+
You should have received a copy of the GNU Lesser General Public
|
|
487
|
+
License along with this library; if not, see <https://www.gnu.org/licenses/>.
|
|
488
|
+
|
|
489
|
+
Also add information on how to contact you by electronic and paper mail.
|
|
490
|
+
|
|
491
|
+
You should also get your employer (if you work as a programmer) or your
|
|
492
|
+
school, if any, to sign a "copyright disclaimer" for the library, if
|
|
493
|
+
necessary. Here is a sample; alter the names:
|
|
494
|
+
|
|
495
|
+
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
|
496
|
+
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
|
497
|
+
|
|
498
|
+
<signature of Moe Ghoul>, 1 April 1990
|
|
499
|
+
Moe Ghoul, President of Vice
|
|
500
|
+
|
|
501
|
+
That's all there is to it!
|
data/README.md
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# SimpleRSS
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/simple-rss)
|
|
4
|
+
[](https://github.com/cardmagic/simple-rss/actions/workflows/ruby.yml)
|
|
5
|
+
[](https://opensource.org/licenses/LGPL-3.0)
|
|
6
|
+
|
|
7
|
+
A simple, flexible, extensible, and liberal RSS and Atom reader for Ruby. Designed to be backwards compatible with Ruby's standard RSS parser while handling malformed feeds gracefully.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Parses both RSS and Atom feeds
|
|
12
|
+
- Tolerant of malformed XML (regex-based parsing)
|
|
13
|
+
- Built-in URL fetching with conditional GET support (ETags, Last-Modified)
|
|
14
|
+
- JSON and XML serialization
|
|
15
|
+
- Extensible tag definitions
|
|
16
|
+
- Zero runtime dependencies
|
|
17
|
+
|
|
18
|
+
## What's New in 2.0
|
|
19
|
+
|
|
20
|
+
Version 2.0 is a major update with powerful new capabilities:
|
|
21
|
+
|
|
22
|
+
- **URL Fetching** - One-liner feed fetching with `SimpleRSS.fetch(url)`. Supports timeouts, custom headers, and automatic redirect following.
|
|
23
|
+
|
|
24
|
+
- **Conditional GET** - Bandwidth-efficient polling with ETag and Last-Modified support. Returns `nil` when feeds haven't changed (304 Not Modified).
|
|
25
|
+
|
|
26
|
+
- **JSON Serialization** - Export feeds with `to_json`, `to_hash`, and Rails-compatible `as_json`. Time objects serialize to ISO 8601.
|
|
27
|
+
|
|
28
|
+
- **XML Serialization** - Convert any parsed feed to clean RSS 2.0 or Atom XML with `to_xml(format: :rss2)` or `to_xml(format: :atom)`.
|
|
29
|
+
|
|
30
|
+
- **Array Tags** - Collect all occurrences of a tag (like multiple categories) with the `array_tags:` option.
|
|
31
|
+
|
|
32
|
+
- **Attribute Parsing** - Extract attributes from feed, item, and media tags using the `tag#attr` syntax.
|
|
33
|
+
|
|
34
|
+
- **UTF-8 Normalization** - All parsed content is automatically normalized to UTF-8 encoding.
|
|
35
|
+
|
|
36
|
+
- **Modern Ruby** - Full compatibility with Ruby 3.1 through 4.0, with RBS type annotations and Steep type checking.
|
|
37
|
+
|
|
38
|
+
- **Enumerable Support** - Iterate feeds naturally with `each`, `map`, `select`, and all Enumerable methods. Access items by index with `rss[0]` and get the latest items sorted by date with `latest(n)`.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
Add to your Gemfile:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
gem "simple-rss"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or install directly:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
gem install simple-rss
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
require "simple-rss"
|
|
58
|
+
require "uri"
|
|
59
|
+
require "net/http"
|
|
60
|
+
|
|
61
|
+
# Parse from a string or IO object
|
|
62
|
+
xml = Net::HTTP.get(URI("https://example.com/feed.xml"))
|
|
63
|
+
rss = SimpleRSS.parse(xml)
|
|
64
|
+
|
|
65
|
+
rss.channel.title # => "Example Feed"
|
|
66
|
+
rss.items.first.title # => "First Post"
|
|
67
|
+
rss.items.first.pubDate # => 2024-01-15 12:00:00 -0500 (Time object)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
### Fetching Feeds
|
|
73
|
+
|
|
74
|
+
SimpleRSS includes a built-in fetcher with conditional GET support for efficient polling:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
# Simple fetch
|
|
78
|
+
feed = SimpleRSS.fetch("https://example.com/feed.xml")
|
|
79
|
+
|
|
80
|
+
# With timeout
|
|
81
|
+
feed = SimpleRSS.fetch("https://example.com/feed.xml", timeout: 10)
|
|
82
|
+
|
|
83
|
+
# Conditional GET - only download if modified
|
|
84
|
+
feed = SimpleRSS.fetch("https://example.com/feed.xml")
|
|
85
|
+
# Store these for next request
|
|
86
|
+
etag = feed.etag
|
|
87
|
+
last_modified = feed.last_modified
|
|
88
|
+
|
|
89
|
+
# On subsequent requests, pass the stored values
|
|
90
|
+
feed = SimpleRSS.fetch(
|
|
91
|
+
"https://example.com/feed.xml",
|
|
92
|
+
etag:,
|
|
93
|
+
last_modified:
|
|
94
|
+
)
|
|
95
|
+
# Returns nil if feed hasn't changed (304 Not Modified)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Accessing Feed Data
|
|
99
|
+
|
|
100
|
+
SimpleRSS provides both RSS and Atom style accessors:
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
feed = SimpleRSS.parse(xml)
|
|
104
|
+
|
|
105
|
+
# RSS style
|
|
106
|
+
feed.channel.title
|
|
107
|
+
feed.channel.link
|
|
108
|
+
feed.channel.description
|
|
109
|
+
feed.items
|
|
110
|
+
|
|
111
|
+
# Atom style (aliases)
|
|
112
|
+
feed.feed.title
|
|
113
|
+
feed.entries
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Item Attributes
|
|
117
|
+
|
|
118
|
+
Items support both hash and method access:
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
item = feed.items.first
|
|
122
|
+
|
|
123
|
+
# Hash access
|
|
124
|
+
item[:title]
|
|
125
|
+
item[:link]
|
|
126
|
+
item[:pubDate]
|
|
127
|
+
|
|
128
|
+
# Method access
|
|
129
|
+
item.title
|
|
130
|
+
item.link
|
|
131
|
+
item.pubDate
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Date fields are automatically parsed into `Time` objects:
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
item.pubDate.class # => Time
|
|
138
|
+
item.pubDate.year # => 2024
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Iterating with Enumerable
|
|
142
|
+
|
|
143
|
+
SimpleRSS includes `Enumerable`, so you can iterate feeds naturally:
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
feed = SimpleRSS.parse(xml)
|
|
147
|
+
|
|
148
|
+
# Iterate over items
|
|
149
|
+
feed.each { |item| puts item.title }
|
|
150
|
+
|
|
151
|
+
# Use any Enumerable method
|
|
152
|
+
titles = feed.map { |item| item.title }
|
|
153
|
+
tech_posts = feed.select { |item| item.category == "tech" }
|
|
154
|
+
first_five = feed.first(5)
|
|
155
|
+
total = feed.count
|
|
156
|
+
|
|
157
|
+
# Access items by index
|
|
158
|
+
feed[0].title # first item
|
|
159
|
+
feed[-1].title # last item
|
|
160
|
+
|
|
161
|
+
# Get the n most recent items (sorted by pubDate or updated)
|
|
162
|
+
feed.latest(10)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### JSON Serialization
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
feed = SimpleRSS.parse(xml)
|
|
169
|
+
|
|
170
|
+
# Get as hash
|
|
171
|
+
feed.to_hash
|
|
172
|
+
# => { title: "Feed Title", link: "...", items: [...] }
|
|
173
|
+
|
|
174
|
+
# Get as JSON string
|
|
175
|
+
feed.to_json
|
|
176
|
+
# => '{"title":"Feed Title","link":"...","items":[...]}'
|
|
177
|
+
|
|
178
|
+
# Works with Rails/ActiveSupport
|
|
179
|
+
feed.as_json
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### XML Serialization
|
|
183
|
+
|
|
184
|
+
Convert parsed feeds to standard RSS 2.0 or Atom format:
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
feed = SimpleRSS.parse(xml)
|
|
188
|
+
|
|
189
|
+
# Convert to RSS 2.0
|
|
190
|
+
feed.to_xml(format: :rss2)
|
|
191
|
+
|
|
192
|
+
# Convert to Atom
|
|
193
|
+
feed.to_xml(format: :atom)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Extending Tag Support
|
|
197
|
+
|
|
198
|
+
Add support for custom or non-standard tags:
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
# Add a new feed-level tag
|
|
202
|
+
SimpleRSS.feed_tags << :custom_tag
|
|
203
|
+
|
|
204
|
+
# Add item-level tags
|
|
205
|
+
SimpleRSS.item_tags << :custom_item_tag
|
|
206
|
+
|
|
207
|
+
# Parse tags with specific rel attributes (common in Atom)
|
|
208
|
+
SimpleRSS.item_tags << :"link+enclosure"
|
|
209
|
+
# Accessible as: item.link_enclosure
|
|
210
|
+
|
|
211
|
+
# Parse tag attributes
|
|
212
|
+
SimpleRSS.item_tags << :"media:content#url"
|
|
213
|
+
# Accessible as: item.media_content_url
|
|
214
|
+
|
|
215
|
+
# Parse item/entry attributes
|
|
216
|
+
SimpleRSS.item_tags << :"entry#xml:lang"
|
|
217
|
+
# Accessible as: item.entry_xml_lang
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### Tag Syntax Reference
|
|
221
|
+
|
|
222
|
+
| Syntax | Example | Accessor | Description |
|
|
223
|
+
|--------|---------|----------|-------------|
|
|
224
|
+
| `tag` | `:title` | `.title` | Simple element content |
|
|
225
|
+
| `tag#attr` | `:"media:content#url"` | `.media_content_url` | Attribute value |
|
|
226
|
+
| `tag+rel` | `:"link+alternate"` | `.link_alternate` | Element with specific `rel` attribute |
|
|
227
|
+
|
|
228
|
+
### Collecting Multiple Values
|
|
229
|
+
|
|
230
|
+
By default, SimpleRSS returns only the first occurrence of each tag. To collect all values:
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
# Collect all categories for each item
|
|
234
|
+
feed = SimpleRSS.parse(xml, array_tags: [:category])
|
|
235
|
+
|
|
236
|
+
item.category # => ["tech", "programming", "ruby"]
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## API Reference
|
|
240
|
+
|
|
241
|
+
### `SimpleRSS.parse(source, options = {})`
|
|
242
|
+
|
|
243
|
+
Parse RSS/Atom content from a string or IO object.
|
|
244
|
+
|
|
245
|
+
**Parameters:**
|
|
246
|
+
- `source` - String or IO object containing feed XML
|
|
247
|
+
- `options` - Hash of options
|
|
248
|
+
- `:array_tags` - Array of tag symbols to collect as arrays
|
|
249
|
+
|
|
250
|
+
**Returns:** `SimpleRSS` instance
|
|
251
|
+
|
|
252
|
+
### `SimpleRSS.fetch(url, options = {})`
|
|
253
|
+
|
|
254
|
+
Fetch and parse a feed from a URL.
|
|
255
|
+
|
|
256
|
+
**Parameters:**
|
|
257
|
+
- `url` - Feed URL string
|
|
258
|
+
- `options` - Hash of options
|
|
259
|
+
- `:timeout` - Request timeout in seconds
|
|
260
|
+
- `:etag` - ETag from previous request (for conditional GET)
|
|
261
|
+
- `:last_modified` - Last-Modified header from previous request
|
|
262
|
+
- `:follow_redirects` - Follow redirects (default: true)
|
|
263
|
+
- `:headers` - Hash of additional HTTP headers
|
|
264
|
+
|
|
265
|
+
**Returns:** `SimpleRSS` instance, or `nil` if 304 Not Modified
|
|
266
|
+
|
|
267
|
+
### Instance Methods
|
|
268
|
+
|
|
269
|
+
| Method | Description |
|
|
270
|
+
|--------|-------------|
|
|
271
|
+
| `#channel` / `#feed` | Returns self (for RSS/Atom style access) |
|
|
272
|
+
| `#items` / `#entries` | Array of parsed items |
|
|
273
|
+
| `#each` | Iterate over items (includes `Enumerable`) |
|
|
274
|
+
| `#[](index)` | Access item by index |
|
|
275
|
+
| `#latest(n = 10)` | Get n most recent items by date |
|
|
276
|
+
| `#to_json` | JSON string representation |
|
|
277
|
+
| `#to_hash` / `#as_json` | Hash representation |
|
|
278
|
+
| `#to_xml(format:)` | XML string (`:rss2` or `:atom`) |
|
|
279
|
+
| `#etag` | ETag header from fetch (if applicable) |
|
|
280
|
+
| `#last_modified` | Last-Modified header from fetch (if applicable) |
|
|
281
|
+
| `#source` | Original source XML string |
|
|
282
|
+
|
|
283
|
+
## Compatibility
|
|
284
|
+
|
|
285
|
+
- Ruby 3.1+
|
|
286
|
+
- No runtime dependencies
|
|
287
|
+
|
|
288
|
+
## Development
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
# Run tests
|
|
292
|
+
bundle exec rake test
|
|
293
|
+
|
|
294
|
+
# Run linter
|
|
295
|
+
bundle exec rubocop
|
|
296
|
+
|
|
297
|
+
# Type checking
|
|
298
|
+
bundle exec steep check
|
|
299
|
+
|
|
300
|
+
# Interactive console
|
|
301
|
+
bundle exec rake console
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Contributing
|
|
305
|
+
|
|
306
|
+
1. Fork the repository
|
|
307
|
+
2. Create a feature branch (`git checkout -b feature/my-feature`)
|
|
308
|
+
3. Make your changes with tests
|
|
309
|
+
4. Ensure tests pass (`bundle exec rake test`)
|
|
310
|
+
5. Submit a pull request
|
|
311
|
+
|
|
312
|
+
## Authors
|
|
313
|
+
|
|
314
|
+
- [Lucas Carlson](mailto:lucas@rufy.com)
|
|
315
|
+
- [Herval Freire](mailto:hervalfreire@gmail.com)
|
|
316
|
+
|
|
317
|
+
Inspired by [Blagg](http://www.raelity.org/lang/perl/blagg) by Rael Dornfest.
|
|
318
|
+
|
|
319
|
+
## License
|
|
320
|
+
|
|
321
|
+
This library is released under the terms of the [GNU LGPL](LICENSE).
|