Class: SimpleRSS

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/simple-rss.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

VERSION =

@rbs! include Enumerable[Hash[Symbol, untyped]]

"2.2.0".freeze
DATE_TAGS =
i[pubDate lastBuildDate published updated expirationDate modified dc:date].freeze
STRIP_HTML_TAGS =
i[author contributor skipHours skipDays].freeze
@@feed_tags =
i[
  id
  title subtitle link
  description
  author webMaster managingEditor contributor
  pubDate lastBuildDate updated dc:date
  generator language docs cloud
  ttl skipHours skipDays
  image  icon rating
  rights copyright
  textInput feedburner:browserFriendly
  itunes:author itunes:category
]
@@item_tags =
i[
  id
  title link link+alternate link+self link+edit link+replies
  author contributor
  description summary content content:encoded comments
  pubDate published updated expirationDate modified dc:date
  category guid
  trackback:ping trackback:about
  dc:creator dc:title dc:subject dc:rights dc:publisher
  feedburner:origLink
  media:content#url media:content#type media:content#height media:content#width media:content#duration
  media:title media:thumbnail#url media:thumbnail#height media:thumbnail#width
  media:credit media:credit#role
  media:category media:category#scheme
  media:description
  enclosure#url enclosure#type enclosure#length
  itunes:duration itunes:image#href
]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, options = {}) ⇒ SimpleRSS

Returns a new instance of SimpleRSS.



61
62
63
64
65
66
67
68
# File 'lib/simple-rss.rb', line 61

def initialize(source, options = {})
  @source = source.respond_to?(:read) ? source.read.to_s : source.to_s
  @items = [] #: Array[Hash[Symbol, untyped]]
  @options = {} #: Hash[Symbol, untyped]
  @options.update(options)

  parse
end

Instance Attribute Details

#etagObject (readonly)

: String?



23
24
25
# File 'lib/simple-rss.rb', line 23

def etag
  @etag
end

#itemsObject (readonly) Also known as: entries

Returns the value of attribute items.



21
22
23
# File 'lib/simple-rss.rb', line 21

def items
  @items
end

#last_modifiedObject (readonly)

: String?



24
25
26
# File 'lib/simple-rss.rb', line 24

def last_modified
  @last_modified
end

#sourceObject (readonly)

: String



22
23
24
# File 'lib/simple-rss.rb', line 22

def source
  @source
end

Class Method Details

.feed_tagsObject



231
232
233
# File 'lib/simple-rss.rb', line 231

def feed_tags
  @@feed_tags
end

.feed_tags=(ft) ⇒ Object



236
237
238
# File 'lib/simple-rss.rb', line 236

def feed_tags=(ft)
  @@feed_tags = ft
end

.fetch(url, options = {}) ⇒ Object

Fetch and parse a feed from a URL Returns nil if conditional GET returns 304 Not Modified

Raises:



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/simple-rss.rb', line 277

def fetch(url, options = {})
  require "net/http"
  require "uri"

  uri = URI.parse(url)
  response = perform_fetch(uri, options)

  return nil if response.is_a?(Net::HTTPNotModified)

  raise SimpleRSSError, "HTTP #{response.code}: #{response.message}" unless response.is_a?(Net::HTTPSuccess)

  body = response.body.force_encoding(Encoding::UTF_8)
  feed = parse(body, options)
  feed.instance_variable_set(:@etag, response["ETag"])
  feed.instance_variable_set(:@last_modified, response["Last-Modified"])
  feed
end

.item_tagsObject



241
242
243
# File 'lib/simple-rss.rb', line 241

def item_tags
  @@item_tags
end

.item_tags=(it) ⇒ Object



246
247
248
# File 'lib/simple-rss.rb', line 246

def item_tags=(it)
  @@item_tags = it
end

.merge(*feeds) ⇒ Object



266
267
268
269
270
271
# File 'lib/simple-rss.rb', line 266

def merge(*feeds)
  first_feed = feeds.first
  return [] if first_feed.nil?

  first_feed.merge(*feeds.drop(1))
end

.parse(source, options = {}) ⇒ Object

The strict attribute is for compatibility with Ruby's standard RSS parser



253
254
255
# File 'lib/simple-rss.rb', line 253

def parse(source, options = {})
  new source, options
end

.valid?(source, options = {}) ⇒ Boolean

Returns:

  • (Boolean)


258
259
260
261
262
263
# File 'lib/simple-rss.rb', line 258

def valid?(source, options = {})
  parse(source, options)
  true
rescue StandardError
  false
end

Instance Method Details

#[](index) ⇒ Object

Access an item by index



90
91
92
# File 'lib/simple-rss.rb', line 90

def [](index)
  items[index]
end

#as_json(_options = {}) ⇒ Object Also known as: to_hash



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/simple-rss.rb', line 196

def as_json(_options = {})
  hash = {} #: Hash[Symbol, untyped]

  @@feed_tags.each do |tag|
    tag_cleaned = clean_tag(tag)
    value = instance_variable_get("@#{tag_cleaned}")
    hash[tag_cleaned] = serialize_value(value) if value
  end

  hash[:items] = items.map do |item|
    item.transform_values { |v| serialize_value(v) }
  end

  hash
end

#channelObject Also known as: feed



71
72
73
# File 'lib/simple-rss.rb', line 71

def channel
  self
end

#dedupeObject



170
171
172
173
# File 'lib/simple-rss.rb', line 170

def dedupe
  @items = dedupe_items(items)
  self
end

#diff(other) ⇒ Object



159
160
161
162
163
164
165
166
167
# File 'lib/simple-rss.rb', line 159

def diff(other)
  other_keys = keyed_item_set(other.items)
  current_keys = keyed_item_set(items)

  {
    added: select_new_keyed_items(other.items, current_keys),
    removed: select_new_keyed_items(items, other_keys)
  }
end

#each(&block) ⇒ Object

Iterate over all items in the feed



80
81
82
83
84
85
# File 'lib/simple-rss.rb', line 80

def each(&block)
  return enum_for(:each) unless block

  items.each(&block)
  self
end

#enclosuresObject



176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/simple-rss.rb', line 176

def enclosures
  items.filter_map do |item|
    enclosure_url = item[:enclosure_url]
    next if blank_value?(enclosure_url)

    {
      url: enclosure_url,
      type: item[:enclosure_type],
      length: item[:enclosure_length],
      item: item
    }
  end
end

#feed_typeObject



102
103
104
105
106
107
108
109
110
# File 'lib/simple-rss.rb', line 102

def feed_type
  atom_namespaced_feed = source.match?(/<(atom:)?feed\b[^>]*xmlns(:\w+)?=['"][^'"]*atom/i)
  return :atom if atom_namespaced_feed
  return :rss2 if source.match?(/<rss[^>]*version=['"]2/i)
  return :rss1 if source.match?(/<rdf:RDF/i)
  return :rss09 if source.match?(/<rss[^>]*version=['"]0\.9/i)

  :unknown
end

#imagesObject



191
192
193
# File 'lib/simple-rss.rb', line 191

def images
  items.flat_map { |item| item_image_urls(item) }.uniq
end

#items_by_category(name) ⇒ Object



132
133
134
135
136
137
138
139
140
141
# File 'lib/simple-rss.rb', line 132

def items_by_category(name)
  query = name.to_s.downcase

  items.select do |item|
    category = item[:category]
    next false if category.nil?

    category_matches_query?(category, query)
  end
end

#items_since(time) ⇒ Object



124
125
126
127
128
129
# File 'lib/simple-rss.rb', line 124

def items_since(time)
  items.select do |item|
    item_date = item[:pubDate] || item[:updated] || item[:published]
    item_date.is_a?(Time) && item_date > time
  end
end

#latest(count = 10) ⇒ Object

Get the n most recent items, sorted by date



97
98
99
# File 'lib/simple-rss.rb', line 97

def latest(count = 10)
  items.sort_by { |item| item[:pubDate] || item[:updated] || Time.at(0) }.reverse.first(count)
end

#merge(*feeds) ⇒ Object



153
154
155
156
# File 'lib/simple-rss.rb', line 153

def merge(*feeds)
  all_items = [items, *feeds.map(&:items)].flatten
  dedupe_items(sorted_items_by_date(all_items))
end

#search(query) ⇒ Object



144
145
146
147
148
149
150
# File 'lib/simple-rss.rb', line 144

def search(query)
  pattern = Regexp.new(Regexp.escape(query.to_s), Regexp::IGNORECASE)

  items.select do |item|
    searchable_fields(item).any? { |field| field.to_s.match?(pattern) }
  end
end

#to_jsonObject



213
214
215
216
# File 'lib/simple-rss.rb', line 213

def to_json(*)
  require "json"
  JSON.generate(as_json)
end

#to_xml(format: :rss2) ⇒ Object



221
222
223
224
225
226
227
# File 'lib/simple-rss.rb', line 221

def to_xml(format: :rss2)
  case format
  when :rss2 then to_rss2_xml
  when :atom then to_atom_xml
  else raise ArgumentError, "Unknown format: #{format}. Supported: :rss2, :atom"
  end
end

#valid?Boolean

Returns:

  • (Boolean)


113
114
115
116
117
118
119
120
121
# File 'lib/simple-rss.rb', line 113

def valid?
  return false if items.empty?

  title_value = instance_variable_get(:@title)
  link_value = instance_variable_get(:@link)
  return true if title_value || link_value

  false
end