Class: Atom::Feed

Inherits:
Element show all
Includes:
Enumerable
Defined in:
lib/atom/feed.rb

Overview

A feed of entries. As an Atom::Element, it can be manipulated using accessors for each of its child elements. You can set them with any object that makes sense; they will be returned in the types listed.

Feeds have the following children:

id

a universally unique IRI which permanently identifies the feed

title

a human-readable title (Atom::Text)

subtitle

a human-readable description or subtitle (Atom::Text)

updated

the most recent Time the feed was modified in a way the publisher considers significant

generator

the agent used to generate a feed

icon

an IRI identifying an icon which visually identifies a feed (1:1 aspect ratio, looks OK small)

logo

an IRI identifying an image which visually identifies a feed (2:1 aspect ratio)

rights

rights held in and over a feed (Atom::Text)

There are also links, categories, authors, contributors and entries, each of which is an Array of its respective type and can be used thusly:

entry = feed.entries.new
entry.title = "blah blah blah"

Direct Known Subclasses

Collection

Instance Attribute Summary collapse

Attributes inherited from Element

#base, #extensions

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Element

#[], #[]=, attrb, attrs, define_accessor, element, elements, inherited, #local_name, required, #taguri, #to_element, #to_s, #to_xml, #to_yaml, #to_yaml_properties

Constructor Details

#initialize(feed_uri = nil, http = Atom::HTTP.new) ⇒ Feed

Create a new Feed that can be found at feed_uri and retrieved using an Atom::HTTP object http



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/atom/feed.rb', line 82

def initialize feed_uri = nil, http = Atom::HTTP.new
  @entries = []
  @http = http

  if feed_uri
    @uri = feed_uri.to_uri
    self.base = feed_uri
  end

  super "feed"
end

Instance Attribute Details

#etagObject (readonly)

conditional get information from the last fetch



41
42
43
# File 'lib/atom/feed.rb', line 41

def etag
  @etag
end

#last_modifiedObject (readonly)

conditional get information from the last fetch



41
42
43
# File 'lib/atom/feed.rb', line 41

def last_modified
  @last_modified
end

#nextObject (readonly)

the Atom::Feed pointed to by link



38
39
40
# File 'lib/atom/feed.rb', line 38

def next
  @next
end

#prevObject (readonly)

the Atom::Feed pointed to by link



36
37
38
# File 'lib/atom/feed.rb', line 36

def prev
  @prev
end

#uriObject (readonly)

Returns the value of attribute uri.



33
34
35
# File 'lib/atom/feed.rb', line 33

def uri
  @uri
end

Class Method Details

.parse(xml, base = "") ⇒ Object

parses XML fetched from base into an Atom::Feed



70
71
72
73
74
75
76
77
78
# File 'lib/atom/feed.rb', line 70

def self.parse xml, base = ""
  if xml.respond_to? :to_atom_entry
    xml.to_atom_feed(base)
  elsif xml.respond_to? :read
    self.parse(xml.read)
  else
    REXML::Document.new(xml.to_s).to_atom_feed(base)
  end
end

Instance Method Details

#<<(entry) ⇒ Object

adds an entry to this feed. if this feed already contains an entry with the same id, the newest one is used.



218
219
220
221
222
223
224
225
226
227
228
# File 'lib/atom/feed.rb', line 218

def << entry
  existing = entries.find do |e|
    e.id == entry.id
  end

  if not existing
    @entries << entry
  elsif not existing.updated or (existing.updated and entry.updated and entry.updated >= existing.updated)
    @entries[@entries.index(existing)] = entry
  end
end

#each(&block) ⇒ Object

iterates over a feed’s entries



95
96
97
# File 'lib/atom/feed.rb', line 95

def each &block
  @entries.each &block
end

#get_everything!Object

gets everything in the logical feed (could be a lot of stuff) (see <www.ietf.org/internet-drafts/draft-nottingham-atompub-feed-history-05.txt>)



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/atom/feed.rb', line 101

def get_everything!
  self.update!
  
  prev = @prev
  while prev
    prev.update!

    self.merge_entries! prev
    prev = prev.prev
  end

  nxt = @next
  while nxt
    nxt.update!

    self.merge_entries! nxt
    nxt = nxt.next
  end

  self
end

#inspectObject

:nodoc:



65
66
67
# File 'lib/atom/feed.rb', line 65

def inspect # :nodoc:
  "<#{@uri} entries: #{entries.length} title='#{title}'>"
end

#merge(other_feed) ⇒ Object

merges “important” properties of this feed with another one, returning a new feed



148
149
150
151
152
153
154
# File 'lib/atom/feed.rb', line 148

def merge other_feed
  feed = self.clone

  feed.merge! other_feed
  
  feed
end

#merge!(other_feed) ⇒ Object

like #merge, but in place



132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/atom/feed.rb', line 132

def merge! other_feed
  [:id, :title, :subtitle, :updated, :rights].each { |p|
    self.send("#{p}=", other_feed.send("#{p}"))
  }

  [:links, :categories, :authors, :contributors].each do |p|
    other_feed.send("#{p}").each do |e|
      self.send("#{p}") << e
    end
  end

  merge_entries! other_feed
end

#merge_entries!(other_feed) ⇒ Object

merges the entries from another feed into this one



124
125
126
127
128
129
# File 'lib/atom/feed.rb', line 124

def merge_entries! other_feed
  other_feed.each do |entry|
    # TODO: add atom:source elements
    self << entry
  end
end

#update!Object

fetches this feed’s URL, parses the result and #merge!s changes, new entries, &c.

(note that this is different from Atom::Entry#updated!

Raises:

  • (RuntimeError)


160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/atom/feed.rb', line 160

def update!
  raise(RuntimeError, "can't fetch without a uri.") unless @uri
 
  headers = {}
  headers["Accept"] = "application/atom+xml"
  headers["If-None-Match"] = @etag if @etag
  headers["If-Modified-Since"] = @last_modified if @last_modified

  res = @http.get(@uri, headers)

  if res.code == "304"
    # we're already all up to date
    return self
  elsif res.code == "410"
    raise Atom::FeedGone, "410 Gone (#{@uri})"
  elsif res.code != "200"
    raise Atom::HTTPException, "Unexpected HTTP response code: #{res.code}"
  end
   
  # we'll be forgiving about feed content types.
  res.validate_content_type(["application/atom+xml", 
                              "application/xml", 
                              "text/xml"])

  @etag = res["ETag"] if res["ETag"]
  @last_modified = res["Last-Modified"] if res["Last-Modified"]

  xml = res.body

  coll = REXML::Document.new(xml)

  update_el = REXML::XPath.first(coll, "/atom:feed/atom:updated", { "atom" => Atom::NS } )

  # the feed hasn't been updated, don't do anything.
  if update_el and self.updated and self.updated >= Time.parse(update_el.text)
    return self
  end

  coll = Atom::Feed.parse(coll, self.base.to_s)
  merge! coll
 
  link = coll.links.find { |l| l["rel"] = "next" and l["type"] == "application/atom+xml" }
  if link
    abs_uri = @uri + link["href"]
    @next = Feed.new(abs_uri.to_s, @http)
  end

  link = coll.links.find { |l| l["rel"] = "previous" and l["type"] == "application/atom+xml" } 
  if link
    abs_uri = @uri + link["href"]
    @prev = Feed.new(abs_uri.to_s, @http)
  end

  self
end