Viking's Ruby Snippets
Here's some Ruby snippets I've written or found. Some of these are here to help me remember how to do certain things, and others are here for no good reason at all.
Simple Buffer
Here's a buffer class I whipped up:
class Buffer
include Enumerable
attr_reader :length
def initialize(capacity)
@buffer = Array.new(capacity)
@capa = capacity
@length = 0
end
def <<(value)
raise "buffer is full" if full?
@buffer[@length] = value
@length += 1
self
end
def each(&block)
@buffer[0, @length].each(&block)
end
def full?
@length == @capa
end
def flush!
@length = 0
end
end
Flickr Interesting Photos
Here's a little script I wrote that downloads some of the latest interesting photos from
Flickr, crops them, scales them, and sets them as your KDE desktop. It's set up for 6 desktops and 1280x1024 resolution. You can change that by using the variables at the top of the script.
Requirements:
imagemagick
,
xmlsimple
(gem)
» Download
A better version of this can be found here:
http://github.com/viking/kawalla
Module Class Methods
I forget how to do this a lot, so here's an example:
module Foo
module ClassMethods
def junk
"junk"
end
end
def self.included(klass)
klass.extend(ClassMethods)
end
end
class Bar
include Foo
end
Bar.junk #=> "junk"
Sudoku Solver
I wrote this sudoku solver during a long road trip because I got frustrated with a sudoku puzzle I was working on.
class Board
class Space
attr_accessor :value
def initialize(value)
@value = value
end
def empty?
@value == 0
end
def to_s
empty? ? " " : @value.to_s
end
alias :inspect :to_s
def <=>(oth)
@value <=> oth.value
end
end
ROWS = (0..8).collect { |i| Array.new(9) { |j| i*9 + j } }
COLUMNS = (0..8).collect { |i| Array.new(9) { |j| j*9 + i } }
BOXES = (0..8).collect do |i|
j = 27 * (i / 3) + 3 * (i % 3) # first row offset
k = j + 9 # second row offset
l = k + 9 # third row offset
[j, j+1, j+2, k, k+1, k+2, l, l+1, l+2]
end
attr_reader :spaces
def initialize(spaces)
raise unless spaces.length == 81
@spaces = spaces.collect { |s| s.is_a?(Space) ? s : Space.new(s) }
end
def row(index)
@spaces.values_at(*ROWS[index])
end
def column(index)
@spaces.values_at(*COLUMNS[index])
end
def box(index)
@spaces.values_at(*BOXES[index])
end
def empty_spaces
empties = []
@spaces.length.times do |i|
empties << i if @spaces[i].empty?
end
empties
end
def clone
Board.new(@spaces.collect { |s| s.dup })
end
def valid?
[:row, :column, :box].collect do |method|
return false unless (0..8).collect { |i|
set = send(method, i).select { |x| x.empty? }
set.collect! { |x| x.value }
set.uniq == set
}.all?
end
true
end
def complete?
@spaces.collect { |s| s.empty? }.all? && valid?
end
def to_s
str = ""
9.times do |i|
row = row(i)
str << "+-------" * 3 + "+\n" if i % 3 == 0
-
- times { |j| str << "| %s %s %s " % row[(j*3)..(j*3+2)] } str << "|\n" end str + "+-------" * 3 + "+" end
end
class Solver
def initialize(board)
@board = board.clone
end
def solve
empties = @board.empty_spaces
empties.each_with_index do |space_i, i|
(1..9).each do |j|
@board.spaces[space_i].value = j
next unless @board.valid?
return @board if i == empties.length - 1
b = Solver.new(@board).solve
return b if b
end
end
nil
end
end
board = Board.new([
3,0,4,5,0,8,0,0,7,
9,8,2,7,4,6,1,3,5,
5,0,7,0,0,9,8,0,4,
8,4,0,9,0,5,6,7,0,
1,7,0,0,6,4,0,0,8,
6,2,0,0,8,7,4,0,0,
2,9,0,8,5,0,7,4,0,
7,3,0,4,9,0,5,8,0,
4,5,8,6,7,0,0,0,0
])
#puts "row 2"
#p board.row(2)
#puts "column 2"
#p board.column(2)
#puts "box 0"
#p board.box(0)
#puts "box 4"
#p board.box(4)
#puts "box 7"
#p board.box(7)
puts "======================"
puts board.to_s
puts "======================"
puts Solver.new(board).solve.to_s
» Download
Bash Progress Meter
class Progress
RESET = "\r\e[0K"
def initialize(total, increment = 1, total_steps = 50)
@total = total.to_f
@count = 0
@increment = increment
@total_steps = total_steps.to_i
end
def next
return if done?
@count += @increment
draw
end
def done?
@count >= @total
end
def reset!
@count = 0
end
private
def draw
perc = @count / @total
steps = (perc * @total_steps).round
left = @total_steps - steps
print "%s[%s>%s] %.2f%" % [RESET, "=" * steps, "_" * left, perc * 100.0]
$stdout.flush
puts if done?
end
end
x = Progress.new(1000)
while (x.done?) do
x.next
sleep(0.01)
end
Based on ideas from
http://snippets.dzone.com/posts/show/3760
Normalizing mp3's
Here's a script to normalize mp3's.
sox
with mp3 encoding support and the
id3lib-ruby
rubygem is required.
#!/usr/bin/ruby
require 'rubygems'
require 'id3lib'
targetdir = `mktemp -d`.chomp
puts "Target directory: #{targetdir}"
Dir["*.mp3"].each do |file|
print "Normalizing #{file}..."
$stdout.flush
file2 = file.gsub(/'/, "\\'")
adjust = %x{sox '#{file2}' -e stat -v 2>&1}.chomp.to_f
if adjust < 1.01
puts "not done."
next
end
adjust -= 0.01
afile = "#{targetdir}/#{file}"
afile2 = afile.gsub(/'/, "\\'")
%x{sox -v #{adjust} '#{file2}' '#{afile2}'}
# assign id3 tags
tag = ID3Lib::Tag.new(file)
atag = ID3Lib::Tag.new(afile)
tag.each { |frame| atag << frame.dup }
atag.update!
puts "done (#{adjust})."
end
IRB echo
To turn off IRB's echo of return values, do this:
conf.return_format = ""
Here's a function in
~/.irbrc
to turn it off and on easily:
def echo(bool)
val = case bool
when Symbol
if bool == :on
true
else
false
end
else
bool
end
conf.return_format = val ? "=> %s\n" : ""
end
Using Proc's in Case Statements
Someone asked about this in #ruby-lang. I came up with this solution:
class Foo
def good?
true
end
end
class Proc
alias :orig_eql :===
def ===(other)
if other.is_a?(Foo)
call(other)
else
orig_eql(other)
end
end
end
f = Foo.new
case f
when proc { |x| x.good? }
puts "yay"
end
Other Bits of Code
A neat way to calculate number of duplications in an array (rickdangerous from #ruby-lang):
a = [1,1,1,1,2,2,2,3,3,3,4,5,5,6]
res = []; a.uniq.each {|x| res << [x, a.size - [a.delete(x), a.size][1]]}
My version:
a.inject(a.uniq.collect{|x| [x, 0]}) {|arr, x| arr.assoc(x)[1] += 1; arr}
Leet Abbreviation Goodness Stuff
Rails Stuff
Generating fixtures from existing data
Something like this (although this is a specific issue):
template = ERB.new(<<EOF)
two_<%= field %><%= suffix %>:
patient: two
field_name: <%= field %>
reason: <%= reason %>
status: todo
created_at: <%%= Time.now.to_s :db %>
updated_at: <%%= Time.now.to_s :db %>
EOF
hsh = Patient.find(:first, :order => "id desc").problem_hash
hsh.keys.sort.each do |field|
probs = hsh[field]
probs.each_with_index do |problem, i|
reason = problem.reason
suffix = probs.length > 1 ? "_#{i}": ""
puts template.result(binding)
end
end
DRY database.yml
I always forget how to write DRY YAML, so here it is for reference:
login: &login
adapter: mysql
encoding: utf8
username: foo
password: bar
socket: /var/run/mysqld/mysqld.sock
development:
database: foo_dev
<<: *login
test:
database: foo_test
<<: *login
production:
database: foo
<<: *login
Development Script
I wrote a script to set up Konsole with several tabs for developing Ruby on Rails applications. It assumes your Rails apps are in
~/rails
, but you can change it via the
RailsDev::BASEDIR
constant. You also need the korundum library (ubuntu package is
libkorundum0-ruby1.8
). It randomly chooses a port for Webrick to use on a per-application basis. That way each application will run its server on a unique port, so you can set up separate saved passwords in Firefox without conflicting with other app's saved passwords, etc. Ain't Ruby fun? You can tweak the
.railsdevrc
file in your home area to change tab setup.
Here is an example configuration file.
If the script segfaults, it's probably the korundum package's fault. Download the korundum source and build it yourself.
» Download