class RSS2Mail::Feed

Constants

KEEP

Attributes

content[R]
debug[R]
feed[R]
reload[R]
rss[R]
simple[R]
transport[R]
updated[R]
verbose[R]

Public Class Methods

new(feed, options = {}) click to toggle source
   # File lib/rss2mail/feed.rb
35 def initialize(feed, options = {})
36   raise TypeError, "Hash expected, got #{feed.class}" unless feed.is_a?(Hash)
37 
38   required = [:url, :to, :title].delete_if { |key| feed.key?(key) }
39 
40   unless required.empty?
41     raise ArgumentError, "Feed incomplete: #{required.join(', ')} missing"
42   end
43 
44   @feed    = feed
45   @simple  = feed[:simple]
46   @updated = feed[:updated]
47 
48   @reload  = options[:reload]
49   @verbose = options[:verbose]
50   @debug   = options[:debug]
51 
52   klass, opt = transport_from(options)
53   @transport = "#{klass.name.split('::').last} @ #{opt}"
54 
55   extend klass
56 end

Public Instance Methods

deliver(templates) click to toggle source
    # File lib/rss2mail/feed.rb
 62 def deliver(templates)
 63   check_deliver_requirements if respond_to?(:check_deliver_requirements)
 64 
 65   if (to = Array(feed[:to])).empty?
 66     log 'No one to send to'
 67     return
 68   end
 69 
 70   unless get && parse
 71     log 'Nothing to send'
 72     return
 73   end
 74 
 75   if (items = rss.items).empty?
 76     log 'No new items'
 77     return
 78   end
 79 
 80   type = feed[:content_type] || 'text/html'
 81 
 82   if template = templates[type[/\/(.*)/, 1]]
 83     type = "Content-type: #{type}; charset=#{feed[:encoding] || 'UTF-8'}"
 84   else
 85     log "Template not found: #{type}"
 86     return
 87   end
 88 
 89   sent = feed[:sent] ||= []
 90   count, title = 0, feed[:title]
 91 
 92   items.each { |item|
 93     link, subject, body = render(feed, item, template)
 94 
 95     send_mail(to, "[#{title}] #{subject}", body, type) {
 96       sent << link
 97       count += 1
 98     } if link
 99   }
100 
101   sent.uniq!
102   sent.slice!(0...-KEEP)
103 
104   log "#{count} items sent"
105   count
106 end

Private Instance Methods

error(err = nil, occasion = nil, extra = nil) click to toggle source
    # File lib/rss2mail/feed.rb
239 def error(err = nil, occasion = nil, extra = nil)
240   msg = 'Error'
241 
242   msg << " #{occasion}"            if occasion
243   msg << ": #{err} (#{err.class})" if err
244   msg << " [#{extra}]"             if extra
245 
246   msg = [msg, *err.backtrace].join("\n    ") if debug
247 
248   log msg, true
249 end
get(reload = reload()) click to toggle source
    # File lib/rss2mail/feed.rb
133 def get(reload = reload())
134   conditions = {}
135 
136   if reload
137     @content = nil
138   else
139     if etag = feed[:etag]
140       conditions['If-None-Match'] = etag
141     end
142 
143     if mtime = feed[:mtime]
144       conditions['If-Modified-Since'] = mtime
145     end
146   end
147 
148   log conditions.inspect, debug
149 
150   begin
151     open_feed(feed[:url], conditions) { |uri|
152       if etag = uri.meta['etag']
153         feed[:etag] = etag
154       else
155         feed.delete(:etag)
156       end
157 
158       if mtime = begin; uri.last_modified; rescue ArgumentError; end
159         feed[:mtime] = mtime.rfc822
160       else
161         feed.delete(:mtime)
162       end
163 
164       unless etag || mtime
165         feed[:updated] = Time.now
166       else
167         feed.delete(:updated)
168       end
169 
170       @content ||= uri.read
171     }
172 
173     log feed.values_at(:etag, :mtime, :updated).inspect, debug
174   rescue OpenURI::HTTPError => err
175     log "Feed not found or unchanged: #{err} (#{err.class})"
176   rescue Exception => err
177     error err, 'while getting feed'
178   end
179 
180   content
181 end
log(msg, verbose = verbose()) click to toggle source
    # File lib/rss2mail/feed.rb
235 def log(msg, verbose = verbose())
236   warn "[#{feed[:title]}] #{msg}" if verbose
237 end
parse(reload = reload()) click to toggle source
    # File lib/rss2mail/feed.rb
183 def parse(reload = reload())
184   @rss = nil if reload
185 
186   if content
187     @rss ||= RSS.feed(content, simple) { |err|
188       error err, 'while parsing feed'
189     }
190 
191     if rss && !reload
192       if update = updated
193         rss.items.delete_if { |item| (date = item.date) && date <= update }
194       end
195 
196       if sent = feed[:sent]
197         set = Set.new(sent)
198         rss.items.delete_if { |item| set.include?(item.link) }
199       end
200     end
201   else
202     log 'Nothing to parse'
203   end
204 
205   rss
206 end
render(feed, item, template) click to toggle source
    # File lib/rss2mail/feed.rb
208 def render(feed, item, template)
209   title       = item.title
210   link        = item.link
211   description = item.description(feed[:unescape_html])
212   date        = item.date
213   author      = item.author
214   body        = item.body(feed[:body])
215   subject     = item.subject
216 
217   log "#{title} / #{date} [#{author}]", debug
218   log "<#{link}>", debug
219 
220   [link, subject, ERB.new(template).result(binding)]
221 rescue => err
222   error err, 'while rendering feed', title
223 end
send_mail(*args) { || ... } click to toggle source
    # File lib/rss2mail/feed.rb
225 def send_mail(*args)
226   return if debug
227 
228   deliver_mail(*args)
229 
230   yield if block_given?
231 rescue Exception => err
232   error err, 'while sending mail', transport
233 end
transport_from(options) click to toggle source
    # File lib/rss2mail/feed.rb
110 def transport_from(options)
111   if lmtp = options[:lmtp] or smtp = options[:smtp]
112     klass = smtp ? Transport::SMTP : Transport::LMTP
113 
114     case @smtp = lmtp || smtp
115       when Array  # ok
116       when true   then @smtp = []
117       when Fixnum then @smtp = [nil, @smtp]
118       when String then @smtp = @smtp.split(':')
119       else raise TypeError, "Array expected, got #{@smtp.class}"
120     end
121 
122     host, port = @smtp.shift, @smtp.shift
123 
124     host = klass::DEFAULT_HOST if host.nil? || host.empty?
125     port = klass::DEFAULT_PORT if port.nil?
126 
127     [klass, @smtp.unshift(port.to_i).unshift(host)[0, 4].join(':')]
128   else
129     [klass = Transport::Mail, "#{klass::CMD} = #{klass::BIN.inspect}"]
130   end
131 end