December 6, 2006

My Ruby Slum
Post by Peter Cooper

I have a special folder on my computer called rubyslum. It's literally a slum where ugly snatches of Ruby code live. Whenever I want to try out an idea, implement something bizarre, or just mess around with some metaprogramming, that's where I play.

I often stumble across code in there I forget ever writing, which makes me cringe with embarrassment, or stuff that reignites ideas in my brain. In the spirit of sharing, here are several of the highlights from my "Ruby Slum". I hope this encourages other people to share scrappy bits of code of their own. I think there's a lot to learn from some of the weird techniques you try out in private.

Slum Inhabitants

Run these snippets at your own risk! Also, yes, many of them use horrible practices and techniques.. that's why they're in the slum ;-)
class TrueClass; def =~(a); a[:ifTrue].call; end; end
class FalseClass; def =~(a); a[:ifFalse].call; end; end
def _(&a); lambda &a; end

(1 + 1 == 2) =~ { 
	:ifTrue => _{ puts "True" },
	:ifFalse => _{ puts "False" },
}
class TrueClass
	def =~(a)
		a[:ifTrue].call
	end

	def >(a)
		a[:ifTrue].call
	end

end

class FalseClass
	def =~(a)
		a[:ifFalse].call
	end
end

def _(&a)
	lambda &a
end

(1 + 1 == 2) > { 
	:ifTrue => _{ puts "True" },
	:ifFalse => _{ puts "False" },
}

puts case (1 + 1 == 32)
	when true: "True"
	when false: "False"
end
require 'rubygems'
require 'hpricot'
require 'open-uri'

doc = Hpricot(open("index.rdf"))

doc.search("item").each do |i|
	puts i.search("link").inner_html
	puts "\n\n"
end
require 'gserver'
require 'socket'

class SMTPProxy < GServer
  def serve(io)
    @@c ||= 0
    @@c += 1
    data = ''
    puts "#{@@c} go"

    smtp = TCPSocket.new("127.0.0.1", 25)

  loop do
    if IO.select([io], nil, nil, 0.1)
	    x = io.readpartial(4096)
		smtp.print x
		    smtp.flush
    elsif IO.select([smtp], nil, nil, 0.1)
	    x = smtp.readpartial(4096)
		io.print x
		    io.flush
    end
    break if io.closed? || smtp.closed?
  end

    io.close
  end
end

a = SMTPProxy.new(1234)
a.start
a.join
require 'rubygems'
require 'inline'
require 'benchmark'

class CFactorial
  class << self
    inline do |b|
      b.c %q{
        long factorial(int value) {
          int result = 1, i = 1;
          for (i = 1; i <= value; i++) {
            result *= i;
          }
          return result;
        }
      }
    end
  end
end

class Fixnum
  def factorial
    (1..self).inject { |a, b| a * b }
  end
end

Benchmark.bm do |bm|
  bm.report('ruby:') do
    100000.times { 8.factorial }
  end

  bm.report('c:') do
    100000.times { CFactorial.factorial(8) }
  end
end
q = %w{11011001110001110100111001110110000111101110111000011000111000011011110000111100111110001111001}
q.each do |a|
  a.scan(/[01]{8}/).each { |b|
	  puts b.to_i(2).chr
  }
end
class PX; def self.method_missing(m, *args); m = m.to_s; eval $c.join if m == '-@'; m.scan(/(\w)(\w)/).each { |t| ($c ||= []) << (((t[0][0] - 97) * 26) + (t[1][0] - 97)).chr }; end; end
#x = { "a" => "b", "k1" => { "x" => "c", "k2" => { "y" => "z" }}, "f" => "x" }
h = {"a"=>"b", "c"=>"d", 1=>{2=>{"e"=>"f", 3=>{4=>"value"}}}} 

class Hash
	def extract_keys
		keys = []
		self.each { |key, value|
			if value.class == Hash
				keys << value.extract_keys
			else
				keys << key
			end
		}
		keys.flatten
	end
end

puts h.extract_keys.inspect
class OrderedHash < Hash
        alias_method :store, :[]=
        alias_method :each_pair, :each

        def initialize
                @keys = []
        end

        def []=(key, val)
                @keys << key
                super
        end

        def delete(key)
                @keys.delete(key)
                super
        end

        def each
                @keys.each { |k| yield k, self[k] }
        end

        def each_key
                @keys.each { |k| yield k }
        end

        def each_value
                @keys.each { |k| yield self[k] }
        end
end

x = OrderedHash.new
x[:y] = 10
x[:z] = 50

x.each { |k|
puts k
}

^^^ I eventually figured how stupid the above was, but it was an early attempt!

# Yet Another Ruby Text Adventure!
# By Peter Cooper

class Item
	attr_accessor :name, :description
	def initialize(params)
		@name = params[:name]
		@description = params[:description]
	end
end

class Location
	attr_accessor :name, :description, :west, :east, :south, :north, :items

	def initialize(params)
		@name = params[:name]
		@description = params[:description]
		@items = []
	end

	# We have to create our own setter methods to do the mirrored east/west north/south relationships
	def west=(loc)
		@west = loc
		@west.east = self unless @west.east
	end

	def east=(loc)
		@east = loc
		@east.west = self unless @east.west
	end

	def south=(loc)
		@south = loc
		@south.north = self unless @south.north
	end

	def north=(loc)
		@north = loc
		@north.south = self unless @north.south
	end

	def exits
		exits = []
		exits << "west" if self.west
		exits << "east" if self.east
		exits << "south" if self.south
		exits << "north" if self.north
		exits
	end

	def contains_items?
		self.items && self.items.length > 0
	end
end

class Player
	attr_accessor :inventory, :location	
end

class World
	attr_accessor :main_location
end



def prepare_world(world)
	# Create a location
	main_room = Location.new( :name => "Central Room" )

	world.main_location = main_room

	main_room.west = Location.new( :name => "West Room" )
	main_room.east = Location.new( :name => "East Room" )
	main_room.west.north = Location.new( :name => "The North of the West Room" )
	main_room.west.north.north = Location.new( :name => "The Really Far North Room" )

	# Create a lamp
	lamp = Item.new( :name => "Magic Lamp", :description => "a golden lamp with magical powers" )

	# Add the lamp to the west room
	main_room.west.items << lamp
end



# Now the game logic really begins!

world = World.new
player = Player.new
prepare_world(world)
player.location = world.main_location
inventory = []


catch :end_of_game do
	loop do
		puts "You are in #{player.location.name}."
		puts "Items here: #{player.location.items.collect{|item| item.name }.join(", ")} " if player.location.contains_items?
		puts "Exits are to the #{player.location.exits.join(", ")}."
		puts "Which direction next?"
		instruction = gets
		case instruction.chomp.downcase
			when "north"
				next_room = player.location.north || false
			when "south"
				next_room = player.location.south || false
			when "west"
				next_room = player.location.west || false
			when "east"
				next_room = player.location.east || false
			when "exit"
				throw :end_of_game
			when /^take (.+)$/
				puts "Taking #{$1}"
				inventory << player.location.items.find {|item| item.name.downcase == $1 }
				player.location.items.delete_if {|item| item.name.downcase == $1 }
			when /^drop (.+)$/
				puts "Dropping #{$1}"
				player.location.items << inventory.find {|item| item.name.downcase == $1 }
		end

		if next_room == false
			puts "No!"
		elsif next_room
			player.location = next_room
		end
	end
end

puts "Bye bye!"

^^^ Horribly inefficient technique for changing room. You can do a lot better than this with some cute meta methods.

ARGV[1].to_i.upto(ARGV[2].to_i) {|x| puts x if (TCPSocket.new(ARGV[0],x) rescue false)  }
class Hash
	def method_missing(meth, *args)
		if meth.to_s =~ /\=$/
			self[meth.to_s[0...-1].to_sym] = args.first
		else
			return self[meth.to_sym]
		end
	end
end

x = {}
x.test = 10
puts x.test

^^^ Before I discovered OpenStruct..