Recently in Toys Category

Enumerable#uniq_by

| | Comments (1)

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

Reverse Sort

| | Comments (0)

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.

property myPlaylist : "Mellow"
property myVolume : 25

tell application "iTunes"
  activate
  
  set dj to playlist "iTunes DJ"
  stop application
  reveal dj
  
  my setSource()
  my setSpeakers()
  
  set sound volume to myVolume
  play dj
  
  set minimized of front browser window to true
end tell

on setSource()
  tell application "System Events"
    tell application process "iTunes"
      tell window "iTunes"
        if value of pop up button 1 is not myPlaylist then
          -- this is so stupid... apple fucked up the UI and detaches the menu so we can't access it
          -- so we must set the value and then... UGH... _type_ to select the value we just set
          set focused of pop up button 1 to true
          set value of pop up button 1 to myPlaylist
          delay 0.5
          keystroke " "
          delay 0.5
          keystroke " "
          delay 0.5
          click button "Refresh"
        end if
      end tell
    end tell
  end tell
end setSource

on setSpeakers()
  tell application "System Events"
    tell application process "iTunes"
      -- stable:
      -- set airplayButton to the first button of window "iTunes" whose description contains "AirPlay"
      -- fast(er):
      set airplayButton to button 12 of window "iTunes" -- changes on updates :(
      -- lame... when it is an icon, it doesn't have a title attribute... at all... so you must do this:
      set thetitle to get value of attributes of airplayButton whose name is "AXTitle"
      
      if thetitle is not {"2 Speakers"} then
        try
          window "Multiple Speakers" -- error if not open
        on error
          tell airplayButton
            delay 0.1
            click
            delay 0.1
            keystroke (ASCII character 31) -- down arrow key
            delay 0.1
            keystroke (ASCII character 31) -- down arrow key
            delay 0.1
            keystroke " "
          end tell
        end try
        delay 0.5
        set value of every checkbox of group 1 of every row of table 1 of scroll area 1 of window "Multiple Speakers" whose value is 0 to 1
      end if
    end tell
  end tell
end setSpeakers

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.0.0 / 2010-12-01

Bold Colors

| | Comments (0)

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:

1.0.1 / 2010-01-28

  • 4 bug fixes:

    • Added known bad XML to the exclude list.
    • Added missing dependency on rdoc 2 gem. Old rdoc won't cut it.
    • Fixed rsync command
    • Terminal.app in UTF8 breaks on some input. Fix by clearing $LANG.
  • http://rubyforge.org/projects/seattlerb

RDoc via OSX's Dictionary.app

| | Comments (0)

rdoc_osx_dictionary.png

sudo gem install rdoc_osx_dictionary

rdoc via Apple's Dictionary.app. Automatically builds and installs an Apple Dictionary with all rdoc nicely formatted.

Changes:

1.0.0 / 2010-01-27

About this Archive

This page is a archive of recent entries in the Toys category.

Thoughts / Misc is the previous category.

ZenObfuscate is the next category.

Find recent content on the main index or look in the archives to find all content.

Pages

Powered by Movable Type 4.32-en