Random code sitting around waiting to be blogged...
module Enumerable
def uniq_by
r, s = [], {}
each do |e|
v = yield(e)
next if s[v]
r << e
s[v] = true
end
r
end
end
Random code sitting around waiting to be blogged...
module Enumerable
def uniq_by
r, s = [], {}
each do |e|
v = yield(e)
next if s[v]
r << e
s[v] = true
end
r
end
end
It's fairly common knowledge that you can sort and reverse a collection in one pass by using sort_by with a negated number:
tally = [["a", 2], ["b", 1], ["c", 2]]
p tally.sort_by { |(str, num)| [-num, str] }
# => [["a", 2], ["c", 2], ["b", 1]]
but what happens if you need to reverse something like strings? Strings don't respond to unary minus, so you can't do this:
tally.sort_by { |(str, num)| [-str, num] } rescue nil
# => undefined method `-@' for "a":String (NoMethodError)
What you want to do is make this behavior dynamic and available everywhere:
module Reversed
def <=> target
-super
end
end
class Object
def -@
self.dup.extend Reversed
end
end
p tally.sort_by { |(str, num)| [-str, num] }
# => [["c", 2], ["b", 1], ["a", 2]]
What would be really special is if Ruby allowed you to apply modules multiple times so that -(-a) == a.
This is in my script menu titled "Play iTunes DJ - Bedtime". I have another for Coding that just changes the two properties at the top.
As an aside:
Dear apple... fuck you... This was a serious pain in the ass and it shouldn't be.
Would it have been so hard to add an application attribute for active speakers or make iTunes DJ its own class so it could have *gasp* a source attribute?
Lemme help you out there... Answer: No.
I gave a talk and a lightning talk at cascadia ruby conf 2011 this last weekend. Below are links to the slides:
Presentation: Size Doesn't Matter - or: The ins and outs of MiniTest
Lightning Talk: gem sandbox and omnifocus
Gem command to display the certificate of a gem, if any.
Changes:
1 major enhancement
I often create complex visualizations using graphviz and my graph gem (I still mourn vcg--vastly superior, but unsupported). I often want to use color to help with the visualization, but picking colors that are distinct is hard.
GV by default supports X11 colors, which have a ton of useless gradients like antiquewhite[1-4], aquamarine[1-4], azure[1-4], etc. Further, many of these colors are simply unusable on a white background. Anything pale or yellow just seems to disappear. I decided to see what I could do to fix that.
There are 655 colors in the X11 color list (for GV, at least). The easiest thing to do is to strip all numbered colors. That brings drops me way down to 141 colors. Much more manageable, but many of those are pale weak colors that don't hold up to a white background.
So at this point, the best thing that I could think of was to convert all the RGB values to HSV and strip out all the unsaturated (and all yellow) colors. I also wanted to spread out like colors as much as possible so that I get maximum contrast as I walk through and use the colors in a graph. With those parameters, I wind up with 47 stark spread out colors:
% ./colors.rb
47
%w[black brown mediumblue blueviolet orange magenta darkgreen
maroon violetred purple greenyellow deeppink midnightblue
firebrick darkturquoise mediumspringgreen chartreuse navy
lightseagreen chocolate lawngreen green indigo darkgoldenrod
darkviolet red springgreen saddlebrown mediumvioletred
goldenrod tomato cyan forestgreen darkorchid crimson coral
deepskyblue seagreen peru turquoise orangered dodgerblue sienna
limegreen royalblue darkorange blue]
Here is the code:
def rgb2hsb(r, g, b)
h = 0.0
min = [r, g, b].min
max = [r, g, b].max
delta = (max - min).to_f
v = max # naming v to not collide w/ blue
s = if max != 0 then
255.0 * delta / max
else
0.0
end
h = if s != 0 then
if r == max then
0.0 + (g - b) / delta
elsif g == max then
2.0 + (b - r) / delta
elsif b == max then
4.0 + (r - g) / delta
end
else
-1.0
end
h *= 60
h += 360.0 if h < 0
s *= 100.0 / 255
v *= 100.0 / 255
return [h, s, v]
end
seen = {}
good = {}
DATA.readlines.map { |l| l.split }.each do |rgb, name|
next if name =~ /\d$/
next if seen[rgb]
h, s, v = rgb2hsb(*rgb.scan(/../).map { |s| s.to_i(16) })
next unless s > 66.67 unless v == 0 # skip weak colors, black is not weak
next if (50..80).include? h # skip yellowish colors, weak on white
seen[rgb] = true
good[name] = [h, s, v]
end
names = good.sort_by { |n, (h, s, v)| [v, h] }.map { |n, _| n }
# 6 colors on the color hexagon, but we took out yellow. Spread out
# all colors as evenly as possible. We can't transpose non-rectangular
# matricies, so we have to fill it in with nils and remove them later.
bucket_size, leftover = names.size.divmod 5
groups = names.enum_slice(bucket_size).to_a
filler = [nil] * (bucket_size - leftover) # must be rectangular to transpose
groups.last.push(*filler)
groups = groups.transpose.flatten.compact
p groups.size
puts "%w[#{groups.join(" ")}]"
__END__
# ...huge color list excluded...
Here's a stupid little snippet:
def ENV.method_missing msg, *args
msg = msg.to_s
if msg =~ /=$/ then
self[msg[0..-2]] = args.first.to_s
else
self[msg]
end
end
Changes:
ENV["GEM_HOME"] = ENV["GEM_PATH"] = dir
into:
ENV.GEM_HOME = ENV.GEM_PATH = dir
The #to_s inside the setter changes:
ENV["BOOL"] = "1"
into:
ENV.BOOL = 1
Not terribly useful, but much prettier imo.
rdoc via Apple's Dictionary.app. Automatically builds and installs an Apple Dictionary with all rdoc nicely formatted.
Changes:
4 bug fixes:

rdoc via Apple's Dictionary.app. Automatically builds and installs an Apple Dictionary with all rdoc nicely formatted.
Changes:
1 major enhancement