Remote Control Storage Solution Mod

January 29, 2015

Remote controls are amazingly good at being in the wrong place. Either in the way, or nowhere to be found. After decades of research, I've determined the problem isn't the remotes. It's the furniture. Designed before the invention of electronics, it hasn't evolved to accommodate them.

Until furniture design catches up, a few power-tools and some surplus wood flooring can provide a nice modification/solution. Introducing: Custom Remote Storage Nooks.

Besides tools and materials, the only ingredient necessary is willingness to make sawdust from your furniture.

It's clear looking at the underside that the nooks are post-purchase mods. Few people will ever see that. Those that do will be admiring your handiwork.

From start to finish, the mod took half a day. A fun little weekend project that neatly solves one of life's little annoyances.


Echo Bar Studios - Live Session Teaser

January 14, 2015

One of my oldest friends went crazy and built a recording studio in L.A. It's called The Echo Bar. He's starting a video series of live performances from the studio. Based on the teaser, it's gonna be awesome.


Self Portrait at 40

January 09, 2015

On this, the occasion of my 40th birthday, a self-portrait seems appropriate.

What's the difference between a self-portrait and a selfie, you ask? It's the lighting.


Trick question: How many seconds in a year?

January 07, 2015

Time is weird. Or, if you prefer, wibbly-wobbly1.

Take someone who lives on a border between time zones and has a five minute commute. Separate clocks at home and the office show they make it in before waking up and that it takes two hours longer to trek home than it did to come in.

Weird.

A more subtle strangeness is the Leap Second2. Like the days we add for Leap Years, these seconds keep our clocks and calendars from drifting away from their proper seasons. The expected math class answer for determining how many seconds there are in a year looks like this:

365 days * 24 hr * 60 min * 60 sec = 31,536,000 seconds

A year with a positive Leap Second is just slighly longer3.

365 days * 24 hr * 60 min * 60 sec + 1 Leap Sec = 31,536,001 seconds

Notice the explicit use of "positive" above. That's required since Lead Seconds can, in theory, be negative.

365 days * 24 hr * 60 min * 60 sec - 1 Leap Sec = 31,535,999 seconds

Of course, Leap Seconds can happen on Leap Years too. So, based on our current system of time4, a year can have one of six possible total seconds.

Leap Year Leap Second Number of Seconds
no negative 31,535,999
no –none– 31,536,000
no positive 31,536,001
yes negative 31,622,399
yes –none– 31,622,400
yes positive 31,622,401

Our global time keepers came up with the Leap Second in 1972. Since then, 25 have been added. All of the "positive" variety. And, we're ready for another one5. This June will be one second longer than last.

Wibbly-wobbly indeed.


Footnotes

  1. Season three, episode ten of the modern Doctor Who is titled "Blink". It's a masterpiece. It's also where this wonderful 14 second description of time comes from. (If you've not yet tuned into Doctor Who, do yourself a favor and start from the first episode of the modern reboot.)
  2. To quote the Wikipedia entry for Leap Second: A leap second is a one-second adjustment that is occasionally applied to Coordinated Universal Time (UTC) in order to keep its time of day close to the mean solar time. Without such a correction, time reckoned by Earth's rotation drifts away from atomic time because of irregularities in the Earth's rate of rotation.
  3. I trust some clever, smart-ass student will get the "How many seconds in a year" question and use a Leap Second in the calculation to see if they can argue the point.
  4. Anyone else remember when, in 1998, Swatch tried to redefine time with their '.beats' Internet Time? Saying: Internet Time exists so that we do not have to think about timezones. For example, if a New York web-supporter makes a date for a chat with a cyber friend in Rome, they can simply agree to meet at an "@ time" - because internet time is the same all over the world. I was a little surprised that Beats is still around, though, I haven't heard it mentioned since the '98.
  5. Here's the leap second announcement from the International Earth Rotation And Reference Systems Service. It contains the line, "To authorities responsible for the measurement and distribution of time." That's just begging to be the opening of a sci-fi novel.

Full Length Software Licenses

November 22, 2014

Even lawyers don't read software agreements. It's no wonder. Here's the three different agreements that must be accepted before installing an iPad software update. The first image in each set shows what fits on the screen. The next one assembles the entire scrolling text and shows how long it actually is.

Most amusing is the dozen or so paragraphs that are written in all caps to show how important they are.


Self Portrait - November 2014

November 20, 2014

Carrying on the long standing tradition of photographers photographing themselves.


Don't Trust PhotoShop's JavaScript ColorSampler

November 16, 2014

Beware the ColorSampler in PhotoShop's JavaScript (aka ExtendScript) implementation. In CS6, it doesn't always pick the color from the coordinates assigned to it. Problematic since that's the purpose of the tool.

To illustrate, take this image:

It's ten pixels high with alternating rows of red and green. The hex colors are #990000 and #005500, respectively. Now, take this script:

app.preferences.rulerUnits = Units.PIXELS;

for (var row=0; row <=9; row = row + 1) {
  app.activeDocument.colorSamplers.removeAll();
  app.activeDocument.colorSamplers.add([0, row]);
  $.writeln(app.activeDocument.colorSamplers[0].color.rgb.hexValue);
}

It makes sure PhotoShop is using pixels and then samples the first pixel of each row. If the ColorSampler worked as expected, the result would be a list of ten lines alternating between '990000' and '005500'. Instead, running it in PhotoShop CS6 (Version 13.0.6) on a MacBook with OS X 10.10 (14A389) produces this:

990000
005500
990000
005500
990000
005500
990000
990000
990000
005500

Everything looks good until just before the end where '990000' occurs three times in a row. That shows the ColorSampler is pulling the color from either row 7 or 9 even though it was assigned to row 8. It's only one error but in programming that's all it takes to render something useless.

I've found a work around to make the ColorSampler trustworthy again. Instead of using integers (e.g. [10, 0]) for the coordinates, use floats that have an extra tenth of a pixel added on them (e.g. [10.1, 0.1]). In theory, this doesn't make sense because a pixel is supposed to be the smallest possible unit. Regardless, it works. For example, here's an updated version of the script that adds "0.1" to every row except the last one. (If you add it to the last one it pushes off the canvas and PhotoShop throws an error.)

app.preferences.rulerUnits = Units.PIXELS;

for (var row=0; row <=9; row = row + 1) {
  var adjustedRow = (row == app.activeDocument.height) ? row : row + 0.1;
  app.activeDocument.colorSamplers.removeAll();
  app.activeDocument.colorSamplers.add([0, adjustedRow]);
  $.writeln(app.activeDocument.colorSamplers[0].color.rgb.hexValue);
}

Running this on the same test image produces the expected output:

990000
005500
990000
005500
990000
005500
990000
005500
990000
005500

And with that, ColorSampler is trustworthy enough to use again. (Though, I'll certainly be keeping a close eye on it.)

Anecdotal evidence suggest very few folks automate PhotoShop with JavaScript. Seeing a bug like this in mature software is more evidence. Kudos to Adobe for continuing to invest resources in something so few use.


Stay One Undo Away from Green

November 14, 2014

I recently had the good fortune of attending Practical Object-Oriented Design with Sandi Metz and Katrina Owen. It was fantastic. Three days with them did more to improve my object-oriented thinking than months of reading and experimentation. I can't recommended the course highly enough.

Sandi and Katrina provided an elegant refactoring guideline on the first day:

Stay one undo away from green.

It was a key take-away and it's as simple as it sounds. Make one small change at a time during refactoring. If the tests go red, hit undo and immediately get back to green. If you're like me, and easily venture a dozen or more changes away from a passing test suite, this approach is transformative.

To illustrate, they walked the class through their refined, systematic process for extracting a conditional into a method. Two examples of their approach are below. The first lays out the steps used when starting with an 'else' branch. The second starts with an explicit case. The number of steps may appear tedious but each change is tiny. The entire process goes very quickly. As we discovered in class, picking a good name for the method takes longer than typing the code.


Extracting a conditional starting with the 'else' branch

Step 0 - Initial Code

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..." end end end

Step 1 - Identify the things that are most alike.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..." end end end

Step 2 - Identify the smallest difference in the things that are most alike.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..."</span> end end end

Step 3 - Make a method to return the value needed by the 'else' case but don't actually call it. Just make sure it parses.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..." end end def container "bottles" end end

Step 4 - Execute the method without actually using the value.

class Bottles def verse_for(number) container if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..." end end def container "bottles" end end

Step 5 - Used the new method in the 'else' case.

class Bottles def verse_for(number) # container if number == 1 "#{number} bottle of beer on the wall..." else "#{number} #{container} of beer on the wall..." # Replaced 'bottles' end end def container "bottles" end end

Step 6 - Shim on an argument and run the tests to make sure it still passes.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} #{container} of beer on the wall..." end end def container(bottle_count=:FIXME) "bottles" end end

Step 7 - Send an argument to the method even though it's not used.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} #{container(number)} of beer on the wall..." end end def container(bottle_count=:FIXME) "bottles" end end

Step 8 - Remove the default shim for the argument.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} #{container(number)} of beer on the wall..." end end def container(bottle_count) # Removed =:FIXME "bottles" end end

Step 9 - Update the method to have the new functionality without actually calling it in the new location.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} #{container(number)} of beer on the wall..." end end def container(bottle_count) if bottle_count == 1 "bottle" else "bottles" end end end

Step 10 - Call the method in the new location.

class Bottles def verse_for(number) if number == 1 "#{number} #{container(number)} of beer on the wall..." # Replaced 'bottle' else "#{number} #{container(number)} of beer on the wall..." end end def container(bottle_count) if bottle_count == 1 "bottle" else "bottles" end end end

Step 11 - Comment out unused cases.

class Bottles def verse_for(number) # if number == 1 # "#{number} #{container(number)} of beer on the wall..." # else "#{number} #{container(number)} of beer on the wall..." # end end def container(bottle_count) if bottle_count == 1 "bottle" else "bottles" end end end

Step 12 - Remove comments and you're done.

class Bottles def verse_for(number) "#{number} #{container(number)} of beer on the wall..." end def container(bottle_count) if bottle_count == 1 "bottle" else "bottles" end end end


Extracting a conditional starting with a specific case

Step 0 - Initial Code

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..." end end end

Step 1 - Identify the things that are most alike.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..." end end end

Step 2 - Identify the smallest difference in the things that are most alike.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..."</span> end end end

Step 3 - Make a method that takes an unused argument and returns the value needed but don't actually call it. Just make sure it parses.

class Bottles def verse_for(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..." end end def container(bottle_count) "bottle" end end

Step 4 - Execute the method without actually using the value.

class Bottles def verse_for(number) container(number) if number == 1 "#{number} bottle of beer on the wall..." else "#{number} bottles of beer on the wall..." end end def container(bottle_count) "bottle" end end

Step 5 - Use the new method

class Bottles def verse_for(number) # container(number) if number == 1 "#{number} #{container(number)} of beer on the wall..." # Replaced 'bottle' else "#{number} bottles of beer on the wall..." end end def container(bottle_count) "bottle" end end

Step 6 - Update the method to have the new functionality without actually calling it in the new location.

class Bottles def verse_for(number) if number == 1 "#{number} #{container(number)} of beer on the wall..." else "#{number} bottles of beer on the wall..." end end def container(bottle_count) if bottle_count == 1 "bottle" else "bottles" end end end

Step 7 - Call the method in the 'else' case.

class Bottles def verse_for(number) if number == 1 "#{number} #{container(number)} of beer on the wall..." else "#{number} #{container(number)} of beer on the wall..." # Replaced 'bottles' end end def container(bottle_count) if bottle_count == 1 "bottle" else "bottles" end end end

Step 8 - Comment out unused case.

class Bottles def verse_for(number) # if number == 1 "#{number} #{container(number)} of beer on the wall..." # else # "#{number} #{container(number)} of beer on the wall..." # end end def container(bottle_count) if bottle_count == 1 "bottle" else "bottles" end end end

Step 9 - Remove comments and you're done.

class Bottles def verse_for(number) "#{number} #{container(number)} of beer on the wall..." end def container(bottle_count) if bottle_count == 1 "bottle" else "bottles" end end end

Don't let the terse nature of the examples fool you. They are simplified versions of a 99 Bottles exercise used throughout the class. The full problem has a lot more going on and the actual extraction provides benefits that aren't evident in this demonstration. If you want to learn more, definitely attend the class. Sandi and Katrina also have a '99 Bottles Of OOP' book coming out soon. Based on the class, I expect it'll be great. Sign up for info about it if you're interested.


Go To Index Page: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106


© Alan W. Smith
RSS Feed