Whyday 2011

Happy Whyday! We’re doing a last minute contest that will end next Monday. This weekend, put your best practices away and celebrate Why’s contributions to our unique culture. Create something wonderful. If you’re still looking for something to build, check out some of the example ideas on the Whyday website.

Go wild! Put your work in a Gist and include a README explaining what it does and how it works. Again, you only have until Monday, so get started as soon as you can!

Happy hacking!

Prize

This week’s winner will receive a code for $25 worth of credit for the 6sync hosting platform!

This contest is finished

Congratulations to this week's winners! The entries and the contestant names are shown below.

  • foxes
    #!/usr/bin/env ruby
    
    require File.join(File.dirname(__FILE__), 'foxes.rb')
    
    unless ARGV.length == 1
      $stderr.puts "Usage: #{$0} input.png"
      exit 1
    end
    
    begin
      input = File.open(ARGV[0], 'r')
    
      foxes = Foxes.new input
    rescue
      $stderr.puts "Argh! #{$!}"
      exit 1
    end
    
    begin
      foxes.execute
    rescue Foxes::NotFoxified
      $stderr.puts "#{ARGV[0]} is not yet foxified!"
      exit 1
    end
    
    foxify
    #!/usr/bin/env ruby
    
    require File.join(File.dirname(__FILE__), 'foxes.rb')
    
    unless ARGV.length == 3
      $stderr.puts "Usage: #{$0} input.png code.rb output.png"
      exit 1
    end
    
    begin
      modes = %w{r r w+}
      input, code, output = *ARGV.map { |a| File.open(a, modes.shift) }
    
      foxes = Foxes.new input
      foxes.foxify code
      foxes.png output
    rescue
      $stderr.puts "Argh! #{$!}"
      exit 1
    end
    
    
    README.md

    Foxes

    Adds Ruby code to a PNG file. This allows foxes to run a PNG file as Ruby code, while keeping the PNG file intact and viewable. It basically just adds the "ruby" ancillary FourCC to the PNG spec and throws the code in.

    If you read the PNG spec, you'll notice PNG is just like bacon.

    The foxes are licensed by why the lucky stiff under the CC BY-SA 2.0 license, and are part of why's (poignant) guide to Ruby.

    Usage

    $ ./foxify foxes.png bacon.rb output.png
    $ ./foxes output.png
    Chunky bacon!!
    Chunky bacon!!
    
    foxes.png
    bacon.rb
    2.times do
      puts "Chunky bacon!!"
    end
    
    foxes.rb
    require 'zlib'
    
    class Foxes
      PNG_HEADER = "\x89PNG\r\n\x1A\n"
      class NotFoxified < RuntimeError; end
      
      # Foxes accepts a File pointing to a PNG image.
      def initialize(file)
        @file = file
        @file.seek 0
    
        if @file.read(8) != PNG_HEADER
          raise ArgumentError, 'Not a PNG file!'
        end
    
        # Simple way to read the PNG file.
        @chunks = {}
        while !@file.eof?
          length, type = *@file.read(8).unpack('NA*')
          data = @file.read length
          crc = @file.read(4).unpack('N').first
    
          if Zlib::crc32(type + data) != crc
            raise RuntimeError, "CRC checksum error on #{type}"
          end
          
          @chunks[type] ||= []
          @chunks[type] << data
        end
      end
    
      # Adds to code in the file to the PNG file.
      def foxify(file)
        @chunks['ruby'] = [Zlib::Deflate.deflate(file.read)]
      end
    
      # Executes the Ruby code in the PNG file.
      def execute
        raise NotFoxified unless @chunks['ruby']
        Object.class_eval(Zlib::Inflate.inflate(@chunks['ruby'].join),
                          @file.path)
      end
    
      # Writes the PNG data to file.
      def png(file)
        file.write PNG_HEADER
    
        # Make sure IEND is actually at the end (Ruby 1.9).
        iend = @chunks.delete 'IEND'
        @chunks['IEND'] = iend
    
        @chunks.each do |type, data|
          data.each do |data_part|
            file.write [data_part.length, type].pack('NA*')
            file.write data_part
            file.write [Zlib::crc32(type + data_part)].pack('N')
          end
        end
      end
    end
    
    View full entry
    Finished in 1st place with a final score of 4.0/5. (View the Gist)
  • README.md

    Binary Clock

    A simple and concise (474 bytes minified, 1KB normal) binary clock in Shoes.

    Compatible with Shoes 2 (Raisins) and 3 (Policeman).

    Design inspired by The Game of Life on the ShoeBox.

    capture.png
    binary_clock.rb
    margin, space, width = 30, 20, 24
    with_help = true
    
    Shoes.app :title => "Binary Clock",
              :width =>  6*width+5*space+2*margin,
              :height => 3*width+2*space+2*margin,
              :resizable => false do
    
      on, off = rgb(0, 0, 255, 0.45), gray(0,0)
    
      if Shoes::VERSION == 'Policeman'
        background rgb(6, 6, 89)
      else
        background gradient(rgb(0, 0, 128), rgb(12, 12, 50))
      end
      stroke gray(1.0, 0.5)
      strokewidth 2.5
      fill off
    
      lights = Array.new(3) { |i|
        Array.new(6) { |j|
          next if i == 0 and j == 5 # The first light is not needed for hours
          x, y = margin + (5-j)*(space+width), margin + i*(space+width)
          para 2**j, :size => 'xx-small', :left => x+20, :top => y+20, :stroke => gray if with_help
          oval x, y, width
        }
      } and lights.first.compact!
    
      every 1 do
        now = Time.now
        [now.hour, now.min, now.sec].zip(lights) { |v, row|
          row.each_with_index { |light, i|
            light.style :fill => (v[i] == 1 ? on : off)
          }
        }
      end
    end
    
    binary_clock.minified.rb
    Shoes.app(:title=>"Binary Clock",:width=>304,:height=>172,:resizable=>false){
    on,off=rgb(0,0,255,0.45),gray(0,0);background rgb(6,6,89)
    stroke gray(1.0,0.5);strokewidth 2.5;fill off
    lights=Array.new(3){|i|Array.new(6){|j|next if j-i==5;x,y=250-j*44,30+i*44
    para 2**j,:size=>'xx-small',:left=>x+20,:top=>y+20,:stroke=>gray;oval x,y,24}}
    lights[0].pop;every(1){now=Time.now;[now.hour,now.min,now.sec].zip(lights){|v,r|
    r.each_with_index{|l,j|l.style:fill=>(v[j]==1?on:off)}}}}
    
    View full entry
    Finished in 2nd place with a final score of 3.4/5. (View the Gist)
  • README.md

    IRB implemented in single line of ruby code:

    $ ruby i.rb

    > a = "Simple"

    => Simple

    > b = "IRB"

    => IRB

    > a + ' ' + b

    => Simple IRB

    > IO.read($0)

    => until(puts(%Q[=> #{eval("#{print('> ')}#{gets.chomp}")}])) do end

    > exit

    $

    No exception handling, no line navigation, no history, no multi-line support :-(

    i.rb
    until(puts(%Q[=> #{eval("#{print('> ')}#{gets.chomp}")}])) do end
    
    View full entry
    Finished in 3rd place with a final score of 2.8/5. (View the Gist)