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
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..