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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ec1f1ac7917068016cb104c8167d66cfe171ea4d1abc6275b3691d4cfc7609c
4
- data.tar.gz: 81320090aa73a8befaf55ed41ac80cc19f11ebdd7e1aeb733f016f1617281e93
3
+ metadata.gz: f7e2a8e829ef0776194df8fd028266dbcecaeb0a5b7b1025aa75b739acbd6ad9
4
+ data.tar.gz: 664c6319db58dab8bf81e9f540afb6f1251997fa669b3e433a30748eb599ac16
5
5
  SHA512:
6
- metadata.gz: c95d7d0111de45e671d01a2e05b14548e7d4e4d0152c2ff1e4f5f1eca8c7d5fbb9b184eca08da0439cd6322513fdc370102df6931ea26f795c447b6fdeb9553c
7
- data.tar.gz: 628901aedd08ff41428e44eb37e79fc1b5ed3d77ddc00613d0a3b2c4d0b0c1c6f663b724a1048fbd297bc84943ba3ce2f8ce0e80fb67d153a19e94f4eae6d7df
6
+ metadata.gz: 3885af60f0b54553af1e3545a781f6f1179825386cc3edcf1575ed75bb79240eefc83998295d21e842287f1c949a0554918d39d1df1df924701d27f20b26c192
7
+ data.tar.gz: 81394817f11b09d433eb7b07031120661a7de18d678deff66fdbf4f331f8ec6483a155069f36539e4ba6122210ec3c58cf08b67f3a44d9afc582c0952861d073
data/LICENSE CHANGED
@@ -1,8 +1,8 @@
1
- GNU LESSER GENERAL PUBLIC LICENSE
2
- Version 2.1, February 1999
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
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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
- Preamble
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
- GNU LESSER GENERAL PUBLIC LICENSE
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
+ [![Gem Version](https://badge.fury.io/rb/simple-rss.svg)](https://badge.fury.io/rb/simple-rss)
4
+ [![CI](https://github.com/cardmagic/simple-rss/actions/workflows/ruby.yml/badge.svg)](https://github.com/cardmagic/simple-rss/actions/workflows/ruby.yml)
5
+ [![License: LGPL](https://img.shields.io/badge/License-LGPL-blue.svg)](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).