<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: Schacon's Code Snippets</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Wed, 23 Jul 2008 22:08:56 GMT</pubDate>
    <description>DZone Snippets: Schacon's Code Snippets</description>
    <item>
      <title>Automatic Bank Balance Checking at WaMu</title>
      <link>http://snippets.dzone.com/posts/show/4548</link>
      <description>I bank at Washington Mutual, and I wanted to see when I was spending too much, or get automated updates of what I was spending money on each day and what my balance was.  However, it's really difficult to do and WaMu won't let you use an API or something helpful like that, so I wrote my own little screen-scraper to do it.&lt;br /&gt;&lt;br /&gt;This requires WWW::Mechanize, but it works quite well.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'mechanize'&lt;br /&gt;&lt;br /&gt;agent = WWW::Mechanize.new&lt;br /&gt;&lt;br /&gt;# login to the account&lt;br /&gt;puts "Login"&lt;br /&gt;page = agent.get('https://online.wamu.com/IdentityManagement/Logon.aspx')&lt;br /&gt;login_form = page.forms.with.name("frmLogin").first&lt;br /&gt;login_form.txtUserID = '&lt;UNAME&gt;'&lt;br /&gt;login_form.password = '&lt;PWD&gt;'&lt;br /&gt;page = agent.submit(login_form, login_form.buttons.first)&lt;br /&gt;&lt;br /&gt;# click on the download button&lt;br /&gt;puts "Download"&lt;br /&gt;dl_link = page.links.with.text(/WAMU FREE CHECKING/)&lt;br /&gt;page = agent.click(dl_link)&lt;br /&gt;&lt;br /&gt;line_items = []&lt;br /&gt;&lt;br /&gt;trs = (page/"table#_ctl0_depositTransactionsGrid/tr")&lt;br /&gt;trs.shift&lt;br /&gt;trs.each do |tr|&lt;br /&gt;  tds = (tr/:td)&lt;br /&gt;&lt;br /&gt;  dtd = tds[1].inner_html&lt;br /&gt;  js_call = dtd.match(/showDDATransactionDetails\('(.*)'\);/)[1]&lt;br /&gt;  js_fields = js_call.split("','")&lt;br /&gt;&lt;br /&gt;  item = {}&lt;br /&gt;  item['type'] = js_fields[1]&lt;br /&gt;  item['descr'] = js_fields[3]&lt;br /&gt;  item['amount'] = js_fields[4]&lt;br /&gt;  item['tranid'] = js_fields[6]&lt;br /&gt;  # if !tranid = ovedraft charge / bank fee&lt;br /&gt;&lt;br /&gt;  item['date'] = tds[0].inner_html&lt;br /&gt;  item['debit'] = tds[3].inner_html&lt;br /&gt;  item['credit'] = tds[4].inner_html&lt;br /&gt;  item['balance'] = tds[5].inner_html&lt;br /&gt;&lt;br /&gt;  line_items &lt;&lt; item&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;pp line_items.first&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;</description>
      <pubDate>Sun, 16 Sep 2007 21:18:32 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/4548</guid>
      <author>schacon (Scott Chacon)</author>
    </item>
    <item>
      <title>FizzBuzz</title>
      <link>http://snippets.dzone.com/posts/show/3720</link>
      <description>This is pretty stupid, but it's a response to the article recently Dugg here:&lt;br /&gt;http://www.codinghorror.com/blog/archives/000781.html&lt;br /&gt;&lt;br /&gt;It is about how a lot of job applicants have a hard time actually programming. My friend and I were amused by it and started going back and forth with different implementations until it got to this point.&lt;br /&gt;&lt;br /&gt;The problem is:&lt;br /&gt;&lt;br /&gt;'Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".'&lt;br /&gt;&lt;br /&gt;Now, it can be done like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;1.upto(100) { |n| puts n % 3 == 0 ? n % 5 == 0 ? "fizzbuzz" : "buzz" : n % 5 == 0 ? "fizz" : n }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;But, this is what I ended up with, which turns out actually to be faster and obviously far more flexible:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;class FizzBuzz&lt;br /&gt;&lt;br /&gt;  def initialize(start_number, end_number)&lt;br /&gt;    @starting = start_number&lt;br /&gt;    @ending = end_number&lt;br /&gt;    @phrase_multiples = []&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def add_phrase_multiple(phrase, multiple)&lt;br /&gt;    @phrase_multiples &lt;&lt; [phrase, multiple]&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def print_phrases&lt;br /&gt;    fb_array = process_phrases&lt;br /&gt;    puts fb_array.collect { |e| e[1] || e[0] }.join("\n")&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  private&lt;br /&gt;&lt;br /&gt;  def process_phrases&lt;br /&gt;    rarray = Array.new(@ending - @starting)&lt;br /&gt;    rarray = rarray.each_with_index { |item, i| rarray[i] = [i + @starting, item] }&lt;br /&gt;    @phrase_multiples.each { |pm| fill_multiples(rarray, pm[1], pm[0]) }&lt;br /&gt;    rarray&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def fill_multiples(fill_array, the_int, printed)&lt;br /&gt;    (the_int - (fill_array[0][0] % the_int)).step(fill_array.size - 1, the_int) do |i|&lt;br /&gt;      fill_array[i][1] = fill_array[i][1].to_s + printed.to_s&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;fb = FizzBuzz.new(1,100)&lt;br /&gt;fb.add_phrase_multiple('fizz', 3)&lt;br /&gt;fb.add_phrase_multiple('buzz', 5)&lt;br /&gt;fb.print_phrases&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Fri, 23 Mar 2007 16:51:21 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/3720</guid>
      <author>schacon (Scott Chacon)</author>
    </item>
    <item>
      <title>Garmin Forerunner TCX file processing</title>
      <link>http://snippets.dzone.com/posts/show/3712</link>
      <description>This is a small ruby file I wrote to process the TCX file that I can download from MotionBased website that is processed from the data off my Garmin Forerunner 305.  It processes the XML file and generates a little badge/infographic that I can put on a website.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;// insert code here..&lt;br /&gt;require "date"&lt;br /&gt;require "rexml/document"&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'RMagick'&lt;br /&gt;&lt;br /&gt;class ForeRunner&lt;br /&gt;  &lt;br /&gt;  M2MI = 1609.344 # meters to miles&lt;br /&gt;  MINIMUM_LAP_TIME = 300 # minimum seconds to count as a full lap&lt;br /&gt;  &lt;br /&gt;  attr_accessor :laps, :total_time, :distance, :calories&lt;br /&gt;  &lt;br /&gt;  def initialize(file)&lt;br /&gt;    @source_doc = REXML::Document.new file&lt;br /&gt;    &lt;br /&gt;    @laps = 0&lt;br /&gt;    @lap_times = Array.new&lt;br /&gt;    @lap_bpm = Array.new&lt;br /&gt;    @full_laps = 0&lt;br /&gt;&lt;br /&gt;    @total_time = 0    &lt;br /&gt;    @distance = 0&lt;br /&gt;    @calories = 0&lt;br /&gt;    @map_data = []&lt;br /&gt;    &lt;br /&gt;    self.process_file  &lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def generate_infographic(output_filename)&lt;br /&gt;    canvas = Magick::Image.new(250, 80)&lt;br /&gt;    map_size = 50&lt;br /&gt;    map_color = 'green'&lt;br /&gt;&lt;br /&gt;    max_lap_height = 25&lt;br /&gt;    &lt;br /&gt;    gc = Magick::Draw.new&lt;br /&gt;&lt;br /&gt;    # Draw ellipse&lt;br /&gt;    gc.stroke('grey50')&lt;br /&gt;    gc.stroke_width(2)&lt;br /&gt;    gc.fill_opacity(0)&lt;br /&gt;&lt;br /&gt;    # draw the relative lap times&lt;br /&gt;    lap = 0&lt;br /&gt;    max_time = @lap_times.max&lt;br /&gt;    @lap_times.each do |s|&lt;br /&gt;      lap += 1&lt;br /&gt;      x = 10 + (lap * 5)&lt;br /&gt;      y = 60 - (s / (max_time / max_lap_height))&lt;br /&gt;      gc.line(x, 60, x, y)&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    #draw the heartbeat avg&lt;br /&gt;    gc.stroke('#c9a')&lt;br /&gt;    &lt;br /&gt;    lap = 0&lt;br /&gt;    lx = nil&lt;br /&gt;    ly = nil&lt;br /&gt;    max_bpm = @lap_bpm.max&lt;br /&gt;    min_bpm = @lap_bpm.min&lt;br /&gt;    &lt;br /&gt;    max_bpm_height = 18&lt;br /&gt;    &lt;br /&gt;    @lap_bpm.each do |s|&lt;br /&gt;      lap += 1&lt;br /&gt;      x = 10 + (lap * 5)&lt;br /&gt;      y = 21 - ( (s - min_bpm) / ((max_bpm - min_bpm) / max_bpm_height))&lt;br /&gt;      if !lx&lt;br /&gt;        lx = x&lt;br /&gt;        ly = y&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      gc.line(lx, ly, x, y)&lt;br /&gt;&lt;br /&gt;      lx = x&lt;br /&gt;      ly = y&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    # draw the map&lt;br /&gt;    lat_diff = (@map_data['max_lat'] - @map_data['min_lat']).abs&lt;br /&gt;    lon_diff = (@map_data['max_lon'] - @map_data['min_lon']).abs&lt;br /&gt;&lt;br /&gt;    lat_off = lat_diff / map_size&lt;br /&gt;    lon_off = lon_diff / map_size&lt;br /&gt;    &lt;br /&gt;    gc.fill(map_color)&lt;br /&gt;    @map_data['map_data'].each do |i|&lt;br /&gt;      lt = (map_size - ((i[0] - @map_data['min_lat']) / lat_off)).round&lt;br /&gt;      lg = ((i[1] - @map_data['min_lon']) / lon_off).round&lt;br /&gt;      gc.point((240 - map_size) + lg, (65 - map_size) + lt)&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    # Annotate&lt;br /&gt;    gc.stroke('transparent')&lt;br /&gt;    gc.fill('black')&lt;br /&gt;    gc.text(120, 15, @distance.to_s[0, 5] + ' mi')&lt;br /&gt;    gc.text(120, 30, (@total_time / 60 / 60).to_s[0, 4] + ' hr')&lt;br /&gt;    gc.text(120, 45, @calories.to_s + ' cal')&lt;br /&gt;&lt;br /&gt;    gc.fill('#555')&lt;br /&gt;    total_hr = 0 &lt;br /&gt;    @lap_bpm.each { |hr| total_hr += hr }&lt;br /&gt;    gc.text(15, 32, 'avg bpm: ' + (total_hr / @laps).round.to_s)&lt;br /&gt;    gc.text(15, 75, 'avg pace: ' + ((@total_time / @full_laps) / 60).to_s[0, 4] + ' min/lap')&lt;br /&gt;&lt;br /&gt;    gc.draw(canvas)&lt;br /&gt;    canvas.write(output_filename)&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  protected &lt;br /&gt;  &lt;br /&gt;  def process_file&lt;br /&gt;    @source_doc.elements.each('TrainingCenterDatabase/Activities/Activity/Lap/*') do |element| &lt;br /&gt;      if element.name == "TotalTimeSeconds"&lt;br /&gt;        @lap_times &lt;&lt; element.text.to_f&lt;br /&gt;        if element.text.to_f &gt; 300  # for removing warmup and warmdown laps&lt;br /&gt;          @total_time += element.text.to_f&lt;br /&gt;          @full_laps += 1&lt;br /&gt;        end&lt;br /&gt;        @laps += 1&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      if element.name == "DistanceMeters" &lt;br /&gt;        @distance += (element.text.to_f / M2MI)&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      if element.name == "Calories" &lt;br /&gt;        @calories += element.text.to_f&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      if element.name == "AverageHeartRateBpm" &lt;br /&gt;        element.elements.each('Value') { |v| @lap_bpm &lt;&lt; v.text.to_f }&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    @map_data = self.generate_map_points&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  def generate_map_points  &lt;br /&gt;    map = []&lt;br /&gt;&lt;br /&gt;    max_lat = -300&lt;br /&gt;    max_lon = -300&lt;br /&gt;    min_lat = 300&lt;br /&gt;    min_lon = 300&lt;br /&gt;&lt;br /&gt;    @source_doc.elements.each('TrainingCenterDatabase/Activities/Activity/Lap/Track/Trackpoint/*') do |element|&lt;br /&gt;      if element.elements['LatitudeDegrees']&lt;br /&gt;        lat = element.elements['LatitudeDegrees'].text.to_f&lt;br /&gt;        lon = element.elements['LongitudeDegrees'].text.to_f&lt;br /&gt;&lt;br /&gt;        map &lt;&lt; [lat, lon]&lt;br /&gt;        max_lat = lat if (lat &gt; max_lat) &lt;br /&gt;        max_lon = lon if (lon &gt; max_lon) &lt;br /&gt;        min_lat = lat if (lat &lt; min_lat) &lt;br /&gt;        min_lon = lon if (lon &lt; min_lon) &lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    {'map_data' =&gt; map, 'max_lat' =&gt; max_lat, 'min_lat' =&gt; min_lat, 'max_lon' =&gt; max_lon, 'min_lon' =&gt; min_lon}&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if ARGV[0].nil? &lt;br /&gt;  puts "This program takes one argument, a Garmin Forerunner TCX File"&lt;br /&gt;  puts "Like this: run.rb filename.tcx"&lt;br /&gt;  exit&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;output_filename = ARGV[0].gsub(/\.tcx$/, "-" + Time.now.strftime("%Y%m%d") + ".png")&lt;br /&gt;puts "Generating Graph #{output_filename}"&lt;br /&gt;&lt;br /&gt;fr = ForeRunner.new(File.new(ARGV[0]))&lt;br /&gt;fr.generate_infographic(output_filename)&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Wed, 21 Mar 2007 19:05:29 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/3712</guid>
      <author>schacon (Scott Chacon)</author>
    </item>
    <item>
      <title>Typo export to WordPress WXR</title>
      <link>http://snippets.dzone.com/posts/show/3264</link>
      <description>If you want to move from Typo to Wordpress, you can use this to export the strange Wordpress RSS file format (comments and all).&lt;br /&gt;&lt;br /&gt;Just add this to the XmlController :&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;  def wp&lt;br /&gt;    @articles = Article.find( :all, :include =&gt; [:categories, :tags, :user, :blog])&lt;br /&gt;  end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And then add this file (app/views/xml/wp.rxml):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;// insert code here..&lt;br /&gt;xml.instruct! :xml, :version=&gt;"1.0", :encoding=&gt;"UTF-8"&lt;br /&gt;&lt;br /&gt;xml.rss 'version' =&gt; "2.0", 'xmlns:content' =&gt; "http://purl.org/rss/1.0/modules/content/", 'xmlns:wfw' =&gt; "http://wellformedweb.org/CommentAPI/", 'xmlns:dc' =&gt; "http://purl.org/dc/elements/1.1/", 'xmlns:wp' =&gt; "http://wordpress.org/export/1.0/" do&lt;br /&gt;  xml.channel do&lt;br /&gt;    xml.title @feed_title&lt;br /&gt;    xml.link @link&lt;br /&gt;    xml.language "en-us"&lt;br /&gt;    xml.ttl "40"&lt;br /&gt;    xml.description this_blog.blog_subtitle&lt;br /&gt;&lt;br /&gt;    @articles.each do |a|&lt;br /&gt;        xml.item do&lt;br /&gt;          xml.title post_title(a)&lt;br /&gt;          xml.content(:encoded) { |x| x &lt;&lt; '&lt;![CDATA[' + a.full_html + ']]&gt;' }&lt;br /&gt;          xml.pubDate a.published_at.rfc2822&lt;br /&gt;          xml.guid "urn:uuid:#{a.guid}", "isPermaLink" =&gt; "false"&lt;br /&gt;          author = a.user.name rescue a.author&lt;br /&gt;          xml.author author&lt;br /&gt;          xml.dc :creator, author&lt;br /&gt;          for category in a.categories&lt;br /&gt;            xml.category category.name&lt;br /&gt;          end&lt;br /&gt;          for tag in a.tags&lt;br /&gt;            xml.category tag.display_name&lt;br /&gt;          end&lt;br /&gt;          xml.wp :post_id, a.id&lt;br /&gt;          xml.wp :post_date, a.published_at.strftime("%Y-%m-%d %H:%M:%S")&lt;br /&gt;          xml.wp :comment_status, 'closed'&lt;br /&gt;          xml.wp :ping_status, 'closed'&lt;br /&gt;          xml.wp :post_name, a.permalink&lt;br /&gt;          xml.wp :status, 'publish'&lt;br /&gt;          xml.wp :post_parent, '0'&lt;br /&gt;          xml.wp :post_type, 'post'&lt;br /&gt;          for comment in a.comments&lt;br /&gt;            xml.wp(:comment) do&lt;br /&gt;              xml.wp :comment_id, comment.id&lt;br /&gt;              xml.wp :comment_author, comment.author&lt;br /&gt;              xml.wp :comment_author_email, comment.email&lt;br /&gt;              xml.wp :comment_author_url, comment.url&lt;br /&gt;              xml.wp :comment_author_IP, comment.ip&lt;br /&gt;              xml.wp :comment_author_date, comment.published_at.strftime("%Y-%m-%d %H:%M:%S")&lt;br /&gt;              xml.wp(:comment_content) { |x| x &lt;&lt; comment.full_html }&lt;br /&gt;              xml.wp :comment_approved, '1'&lt;br /&gt;              xml.wp :comment_parent, '0'&lt;br /&gt;            end&lt;br /&gt;          end&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Then hit your typo blog at http://myblog/xml/wp and save the file to import through the WordPress interface.</description>
      <pubDate>Tue, 09 Jan 2007 23:52:47 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/3264</guid>
      <author>schacon (Scott Chacon)</author>
    </item>
  </channel>
</rss>
