From: Ruby Quiz Date: 2007-03-02T00:40:20+09:00 Subject: [SUMMARY] Mailing List Files (#115) I've been playing a little with TMail lately, which is what really inspired this quiz. I thought that a simple solution to this problem would be to pull the pages down with open-uri and then dump them into TMail and just pull the attachments from that. It turns out to be a bit harder to do that than I expected, but one solution did follow that path. What I love about this plan is the fact that you are just stitching the real tools together. I like leaning on libraries to get tons of functionality with just a few lines of code. Apparently, so does Louis J Scoras! Check out this list of dependencies that kick-starts his solution (I've removed the excellent comments in the code to save space): #!/usr/bin/env ruby require 'action_mailer' require 'cgi' require 'delegate' require 'elif' require 'fileutils' require 'hpricot' require 'open-uri' require 'tempfile' # ... Wow. Let's start with the standard libraries. Louis pulls in cgi to handle HTML escapes, delegate to wrap existing classes, fileutils for easy directory creation, open-uri to fetch web pages with, and tempfile for creating temporary files, of course. That's an impressive set of tools all of which ship with Ruby. The other three dependancies are external. You can get them all as gems. action_mailer is a component of the Rails framework used to handle email. Louis doesn't actually use the action_mailer part, just the bundled TMail dependency. This is a trick for getting TMail as a gem. elif is a little library I wrote as a solution to an earlier quiz (#64). It reads files line by line, but in reverse order. In other words, you get the last line first, then the next to last line, all the way up to the first line. hpricot is a fun little HTML parser from Why the Lucky Stiff. It has a very unique interface that makes it popular for web scraping usage. Now that Louis has imported all the tools he could find, he's ready to do some fetching. Here's the start of that code: module Quiz115 class QuizMail < DelegateClass(TMail::Mail) class << self attr_reader :archive_base_url def archive_base_url @archive_base_url || "http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/" end def solutions(quiz_number) doc = Hpricot( open("http://www.rubyquiz.com/quiz#{quiz_number}.html") ) (doc/'#links'/'li/a').collect do |link| [CGI.unescapeHTML(link.inner_text), link['href']] end end end # ... This object we are examining now is a TMail enhancement, via delegation. This section has some class methods added for easy usability. I believe the attr_reader line is actually intended to be attr_writer though, giving you a way to override the base URL. The reader is defined manually and just defaults to the Ruby Talk mailing list. The solutions() method is a neat added feature of the code which will allows you to pass in a Ruby Quiz number in order to fetch all the solution emails for that quiz. Here you can see some Hpricot parsing. Its XPath-in-Ruby style syntax is used to pull the solution links off of the quiz page at rubyquiz.com. Let's get to the real meat of this class now: # ... def initialize(mail) temp_path = to_temp_file(mail) boundary = MIME::BoundaryFinder.new(temp_path).find_boundary @tmail = TMail::Mail.load(temp_path) @tmail.set_content_type 'multipart', 'mixed', 'boundary' => boundary if boundary super(@tmail) end private def to_temp_file(mail) temp = Tempfile.new('qmail') temp.write(if (Integer(mail) rescue nil) url = self.class.archive_base_url + mail open(url) { |f| x = cleanse_html f.read } else web = URI.parse(mail).scheme == 'http' open(mail) { |m| web ? cleanse_html(m.read) : m.read } end) temp.close temp.path end def cleanse_html(str) CGI.unescapeHTML( str.gsub(/\A.*?