Ruby spreadsheet hack for whyday
Like many others, my introduction to ruby was Why’s (poignant) guide to ruby. Why’s attitude towards programming emphasized fun, learning, and creativity above generally regarded “best practices”.
On august 19th, the Ruby community throws caution and best practices to the wind to celebrate Whyday.
I hoped to capture this programming spirit with this ~30 line Spreadsheet class which can have procs as cells. This is adapted from an idea I’ve seen a couple times in python. It supports access through array both array access (ss['a0']
) and methods (ss.a0
), ranges (ss.a[0..10]
) are also supported.
class Spreadsheet
def initialize
@hash = Hash.new(0)
end
class Subset < Struct.new(:spreadsheet, :row, :col)
def [] k
return k.map{|x| self[x]} if k.respond_to? :map
spreadsheet[cell k]
end
def []= k, v
return k.map{|x| self[x] = v} if k.respond_to? :map
spreadsheet[cell k] = v
end
def cell k; "#{self.col || k}#{self.row || k}"; end
end
def method_missing cell, *args
if cell =~ /\A([a-z]+)([0-9]+)=\Z/
@hash[[$1, $2]] = args.first
elsif cell =~ /\A([a-z]+)([0-9]+)\Z/
v = @hash[[$1, $2]]
v.is_a?(Proc) ? self.instance_eval(&v) : v
elsif cell =~ /\A([a-z]+)\Z/
Subset.new(self, nil, $1)
elsif cell =~ /\A_?([0-9]+)\Z/
Subset.new(self, $1, nil)
else
super
end
end
def []= key, value; method_missing("#{key}=", value); end
def [] key; method_missing(key.to_s); end
def to_s; @hash.to_s; end
end
ss = Spreadsheet.new
ss.a1 = Proc.new { a2 + a3 }
ss['a'][2..10] = 50 # assign a2 through a10
puts ss.a1
# => 100
ss['a']['1'] = Proc.new { a[2..20].inject(:+) }
puts ss['a1']
# => 450
ss.b1 = 'hello, world'
ss.d[1..20] = 'chunky bacon'
puts ss['1']['a'..'e'].inspect # print a1 through e1
# => [450, "hello, world", 0, "this fills row d", 0]
Hope you enjoyed.