Cookie Notice

As far as I know, and as far as I remember, nothing in this page does anything with Cookies.

2010/12/09

The New Telephony meets the New Television: Expensive Remote

I have fairly close to the top end of the smartphones, the HTC Evo Android phone. I have a Vista computer connected to the Internet. I have just installed Boxee on said TV. Thing is, this TV, while it has a big screen and all, is across the room from me, and the keyboard and mouse are corded.

I did say I have an Android phone, right?

While it is a wonder of technology that can talk to WiFi, Bluetooth and 3G/4G mobile networks, and do much cooler stuff. I can watch video. I can make video. It is a cool thing. Being able to control my other computer is minor, but still cool.

I've done something like this before. Years ago, I had a Palm III. You could install an app that made your Palm into a programmable universal remote. That's cool, but that's coolness relying on IR and line-of-site and the IR source was meant for sending electronic business cards 3 feet away, not turning on a DVD player 10 feet away. This works over wireless networking, so line of sight is done, and thanks for that.

I'm also new to virtualizing interfaces. I've been a big fan of Synergy2 to allow you to have one keyboard for two or more computers. I just didn't expect it to touch the smartphone.

I know I'm at best second or third wave on this sort of thing. MythTV's been around for over 5 years, and many of these concepts are very stable. Microsoft's been putting these capabilities as part of their advertisements these days. And I know there are things involved here that I'm taking the goofy way around with. Big example is moving media onto my Android. (I call it RoyBatty, by the way. Considered going with RickDeckard, but no.) The old reliable way to do it would be to plug in the USB/charging cable. The slightly cooler way is via Bluetooth. My way is to use Dropbox. It's a cool and wonderful cross-platform thing. I was expecting full syncing, but it turns out to be pick-and-choose, which is okay, but it means I'm more active.) I think the really cool solution would be to using uPnP/DLNA. This means I have to start learning how to make that stuff work.

But seriously, this really convinces me that the New Television is a big monitor, receiving signal from whatever device you want, and not really the interface device in and of itself.

Now, if you excuse me, I'll watch some Revision3 on my computer as I go to bed.

2010/12/06

Stupid HTML Error

Code Block A.

<form>
<table id="target">
<tr><td> <input type="text" name="foo"></dr></tr>
</table>
</form>

Code Block B.

<table id="target">
<form>
<tr><td><input type="text" name="foo"></dr></tr>
</form>
</table>
Not much difference, is there? In fact, I'd say they're functionally identical, unless you set form as a block in your CSS. But there is a difference if you're going to use Javascript to add to the form. If you append to the table, you'll get something like this.

<table id="target">
<form>
<tr><td> <input type="text" name="foo"></dr></tr>
</form>
<tr><td> <input type="text" name="bar"></dr></tr>
</table>

And this will not be part of the form when you press submit. Which I have conveniently left out of the code blocks. Hrmm. Still, watch for that. It's the problem that hounded me over the weekend.

2010/11/29

First Real Day With Android/EVO

I received the thing Friday and poked at it all weekend. I don't think I'll have to decide to love this thing.

But I have found a problem. Or at least a non-solution.

I work in a sub-basement. This means no cell connection. I mean, if I sat six feet to the south, there's a slight chance I could get a quarter-bar just enough for a ring. But I don't. So, I have been dreaming of ways to get my phone on. My way has been Google Voice and Gizmo. This meant that I cart my netbook with me. I had been hoping that I could answer calls via WiFi, but that seems to be a no-go.

There's also a Voyage app, but it only works on GSM phones, not CDMA phones like mine. What might work is Skype, but that strikes me as a mighty big if.

So, we'll see how things go.

2010/11/17

Have Mercy! A RouteShout Signage Bug

I now live where the buses go, and I've been taking a bus to work for a little over a month. I can't say there haven't been problems, but I can and must admit that they've all been self-inflicted.

They have signs under all the bus stops saying "Text this code to 25252 and find out how long the wait is". I have tried it, and it fails.

Turns out, this is not an in-house thing, but a service provided through a company called RouteShout. There's more than just texting, though. There's an iPhone app, an Android app, a mobile site for those with older, less-smart phones and an API. That's the point where I said "cool" and dove in. And found the problem.

Say you have a bus stop. Say CityBus labelled it BUS666. Well, if you're gonna get on a bus someplace, you'll want to come back near there, too. So, we can think of BUS666 as where you get picked up and where you get let off. Except, sometimes the bus will go up and down the same road. For example, the bus I take from my apartment goes around a mall and comes back. So BUS666 is on both sides of the street. If I'm waiting on the north side of the street, I don't care about buses coming to the south side, except when they're coming around and back to the north side. So, we have BUS666N and BUS666S.

But the signs still say BUS666.

And if you text BUS666 to 25252, rather than BUS666N. Which makes the texting interface useless. And you have to get inside the API to figure out where the problem is.

(As it turns out, BUS666 is one stop, not two, and doesn't have this problem. It also is far away from anywhere I take the bus to, so using it as an example doesn't lead to people knowing where I live.)

Again, this is a signage bug. The RouteShout service works perfectly, as far as I've seen. I think I'll put together a Perl module from the API and put that on CPAN. Yay to them.

2010/11/10

Solving a Brain Teaser with word lists and Perl

I'm a programmer. When I see a problem, I get tempted to code the solution. One place where this temptation comes into play is brain teaser problems. Once, a while ago, a radio show had a brain teaster where you take the phrase PRECHRISTMAS SALE, turn it into a 4x4 block and find the longest word you can without reusing a letter. I used Perl and found six eight-letter words.

And, of course I used my powers on Sudoku.

Now, I've seen a problem that's inspired me again.
By changing one letter at a time to form different English words, and leaving all other letters in their original positions, convert SIXTH into TENTH in the fewest steps possible. Good luck!

S I X T H
_ _ _ _ _
_ _ _ _ _
_ _ _ _ _
_ _ _ _ _
_ _ _ _ _
_ _ _ _ _
T E N T H
There are Spoilers below

As I learned with PRECHRISTMASSALE, which I've since found is Boggle, if you're going to find words, you first need to know words. So, I pulled and adapted my Boggle code for coming up with a dictionary list.
sub dictionary {
    my ( $length ) = @_ ;
    my %done ;
    my %word ;
    my $dir = './Dict' ;
    my $dirhandle ;
    opendir $dirhandle, $dir ;
    my $file ;
    while ( defined( $file = readdir $dirhandle ) ) {
        next if $file =~ m(^\.)mx ;
        open my $filehandle, '<', $dir . q{/} . $file ;
        while ( <$filehandle> ) {
            chomp ;
            $_ = lc $_ ;
            next if $done{ $_ }++ ;
            next if length != $length ;
            $word{ $_ }++ ;
            }
        close $filehandle ;
        }
    closedir $dirhandle ;
    return sort { $a cmp $b } keys %word ;
    }
Of course, you need to be in the same directory as a directory full of dictionaries. I used a few I pulled from the CERIAS FTP site, and probably has some overlap, but better that then missing a word and thus a connection.

I then started poking at the words, trying to find the next. I came up with a way to get all the proper alternatives, given a word. I then pulled it into a subroutine.
sub get_word_choices {
    my ( $word ) = shift ;
    my @word = split m{}mx, $word ;
    my @output ;
    for my $pos ( 0 .. scalar @word - 1 ) {
        my @local = @word ;
        $local[ $pos ] = '.' ;
        my $local = join '', @local ;
        my @words = grep { m{$local}mx } grep { !m{$word}mx } @dictionary ;
        next if scalar @words == 0 ;
        push @output , @words ;
        }
    return @output ;
    }
I should probably pass @dictionary rather than leave it as a global, but certainly, you don't want to re-parse the dictionary file each time.

It struck me about this point that I should have all I need to automatically solve this, not leaving me to use this tool to piece something together.

I am not as happy with this part as I could be.
sub check_words {
    my $word2 = shift ;
    my @words = @_ ;
    return if scalar @words > 7 ;
    my $last = $words[-1] ;
    if ( $last eq $word2 ) {
        say '' ;
        say join ' ' , @words , scalar @words ;
        }
    my @choices = get_word_choices( $last ) ;
    #say join ' ' , @words ;
    for my $choice ( @choices ) {
        next if grep { m{$choice}mx } @words ;
        check_words( $word2 , @words , $choice ) ;
        }
    print join ' ' ,  '' , scalar @words ;
    return ;
    }
It's depth-first search and it's long. Which, I suppose, is unavoidable for depth-first search. Consider this solution:
sixth
sixte
sixty
silty
tilty
tinty
tenty
tenth 
sixth to sixty is just as legal a jump as sixth to sixte. If I used an iterative process with a better data structure, I could use a shortest path algorithm and bypass sixte. And, of course, by putting print to give me some appreciation of progress, I'm bogging the thing way down. So, that's where I'd rewrite to make this better.

2010/11/08

Somehow The New Television is listening to Me

And not in the creepy HAL2000 kind of way.

I was searching for info on Net Neutrality, wrapping my head around it, and found this bit from G4's Attack of the Show on Hulu. Next clip on the channel was on Bluetooth headsets, and it was presented by Dungeons and Dragons. Which was a bit of a wow for me.

I've been clear on this, but let me restate that I am not categorically against advertising because advertising, properly done, is content. Consider the copy of The Productive Programmer on my desk right now. The last page before the back cover is an ad that says you can read this book online for 45 days at Safari Online. If you're reading a book called The Productive Programmer, you're more than likely a programmer. Which means you deal with computers a lot. Reading this book online will make sense to you, as you read much online. It's as much content to you as the book's suggestions for Desktop shortcuts. This doesn't mean that you, as the programmer reading this book, will jump up and start reading online any more than it means that you will immediately pick up Virtual Desktop Manager to get virtual desktops on your XP dev box. But it'll be something at least interesting to you.

However, if the back-of-the-book ad was Chanel #5 or Audis or Levi's jeans, it would be far less clear that there's a connection between the audience and the advertisement. This is where mass-market media starts to fall apart. They make a statistical model of their audience, or who they want their audience to be, and advertisers who want to sell to that audience buy ads. If you are not in that statistical model, or the statistical model is too general, the ads will not speak to you.

I haven't been an RPG gamer in two decades. D&D is not going to sell me, but it seems a more natural fit to the G4 audience than cars.

2010/11/05

The New Television is similar to the (Older) New Telephone

When I was in college about a decade ago, I knew some people who didn't have a "landline", or a traditional wired telephone in their place of residence. It made sense to me for college students. If you're out and studying, going to classes, working between classes, etc., it made sense that you weren't home enough to hear your phone ringing, so it's best to just have the mobile phone and not worry about it. For students, sure that made perfect sense. For adults, however, I wasn't sure. Come ten years after, you have more and more people who are dropping the landline that are out of school. There are sticking points, like how E911 for mobile isn't quite as far as for traditional telephony*, but it's a trade-off more and more making.

I have an adapter for my old-school TV that allows me to plug my netbook's VGA and watch YouTube, or whatever, on the big screen. My Wii can do Netflix, as can my friend's iPhone. Android is coming soon. Hulu can bring most of my favorite shows to me. Not everything, but close and closer all the time. If this is true, if you can get all the moving pictures you need from the internets and don't need the co-ax run. And we're not talking people who can't afford cable. We're talking people who decided that they just didn't need another wire run and time-sink in their lives.

The comments highlight a few of the hinderances with this. First, if you want to watch live events, like sporting events or news, you're out of luck. If there's a way to see Big Bang Theory or The Marty Stuart Show or new episodes of Discovery shows online, I've yet to see it. And this model is more-or-less fine if you know what you watch and watch what you know, but it locks you out of searching through and finding the next cool thing you didn't know existed. So, TV-wise, I'm not a cord-cutter yet. But I'm moving that way.


* The two times I've had to dial 911, I was calling because of a car accident.

2010/10/29

Encrypt The Web

There's a line from Spafford saying that most email was like a postcard. Anybody anywhere involved with moving it around could just read the back of the postcard and tell what the message is. And, honestly, this is true of the web, too. I've heard of stories where they hook a packet sniffer to the WiFi at conventions and such and sent all the images they grabbed to a projector. The stuff is wide open.

There are sites that use HTTPS, which encrypts your traffic. The sites that do tend to be banks and other financial institutions. And there was a time when I dreaded hitting a HTTPS site, because the computer would chug a bit at the decryption. But honestly, that was back when I was hating on Javascript because it did the same thing. I forget when I started saying it, I think it was around 2002, but there's no reason why every site isn't HTTPS. I don't care if it's only kitten videos, there is no reason for anyone between the server and me to know what I'm downloading.

Which is why I'm so happy to see this. The Electronic Frontiers Foundation has created a Firefox extension that pushes whatever traffic you're doing onto the HTTPS server. It doesn't work for everything, and it's in beta, but you can write your own rules. I'm a Chrome guy these days, but this is such a great idea.

2010/10/27

My Life Just Became Easier

I get mail off of three classes of mail server. The first is an old-school, all-in-one server where I can log on, read mail with a built-in mail client like PINE. I can set a .forward file to send all my mail anywhere, or run it through a filter like procmail. With procmail, you can do almost anything. You can configure it so that when you send an email with the subject "reboot webserver", procmail will run a script that shuts down and restarts your webserver. It is an awesomely powerful and useful thing.
Then there's Gmail. I have nothing bad to say about Gmail. You cannot use it to run abstract programs — I've been playing with the idea of using Google Calendar to set up a remotely-configurable cron/at/batch equivalent, but it isn't there yet — but you have great stability, great interface and filtering. At this point. I'm going to state my position: If you can't filter mail, mail is useless. You'll get squashed by the deluge.

Which brings us to non-Gmail webmail. Specifically, my employer's webmail. You have to pay to get IMAP access to Yahoo Mail, which is a dealbreaker for me. My employer's webmail had fine POP and IMAP access, but what you couldn't do is filter on the server. You could configure clients to filter, but that means you have to copy all your filters to each client, whether it's the web interface, Thunderbird on this machine, Thunderbird on that machine, and so on.

My solution was to write my own filters to run on my workstation via crontab. It worked, and it allowed me to develop a means to specifically alert on things I find crucial rather than Thunderbird's "Alert if new mail" approach. But we've moved to a Zimbra-based solution which seems to have everything I'd want. 

2010/10/21

Coder Input Hardware Beg



A while ago at the office, I made the jump from PS/2 to USB for keyboard and mouse. This is related to me going to a USB KVM so I could use two computers without needing two sets of keyboards and mice. I went trackball years ago and far prefer them to mice, but over the years, I came to the conclusion that, following Ricky Bobby, if you don't type on an IBM Model M keyboard, screw you. I wasn't about to spec a big clicky thing that would annoy my coworkers, even if they're perfect otherwise, so I went to the cheapest keyboard I could with a name I like, the Logitech K120.

And, lately I've been experiencing a sticky control or Windows key key. Not physically sticky, so I don't notice anything as I type, but there are types when the mouse click behaves like I'm holding down the Windows key, which in Linux is to grab and move the current window. And when I move over to one of my Windows machines, I start typing and find myself locked out when I get halfway through "halfway".

I spend eight hours a day or so at my keyboard, so these minor things are a big and crucial part of my day. So, what can I do? Get a Unitech or other modern Model M takes? Run my keyboard through the washer to make sure any cruft I forgot I spilled into the thing has been washed out? And, if I do go the replacement route, what is the preferred programming keyboard in 2010?



A Few Words From Larry Wall

2010/10/15

Array of Troubles

This is perfectly legal Perl.
my @x = ( 1 ,2 ,3, 4, ) ;
This can be useful when you have things you want to add to or move around, not having to be sure to remove the last comma and add one if you move up the last element.

Compare this SQL.
SELECT foo , bar , blee  , FROM quuz ;

That'll give you an error. In one sense, well, sure. On the other hand, it sucks because the same dynamic forces that happen as you program in Perl come forth as you develop SQL queries.

I suppose the solution is to put a kind of null into the last field so you can shuffle until the cows come home, but I'm still frustrated.

Dimensions and the New Television

I saw this article from Maximum PC:
While 3D-enabled displays struggle to gain a foothold in mainstream living rooms, Internet-connected televisions are barging through the front door. According to market research firm WitsView, TVs with integrated NICs will balloon to 40 million units in 2010, or roughly 20 percent of the global LCD TV market.
That's a cool thing. I was told years ago on Twitter that I should get a Mac Mini and connect it to my TV. My initial attempt was to put a monitor on top of my TV and connect the audio out of a broken laptop to it. With Hulu and Netflix (and, in my house, That Guy With The Glasses), there's lots of net-available stuff that's set for the whole family to watch. Being broke, I used a broken laptop and moved up to a five-year-old tower rather than a new Mac with the footprint of a Sun IPC, but the ability to see our favorite shows on the big screen of the house still is there.

Which gets to the other thing.

I've tried 3D TVs at Best Buy. Are they dang cool? Yes. They are dang cool. Am I amazed that you can get 3D shots of football games? Yes. Am I going to get a set? Not likely.

Here's the thing. You get a TV, and it's crazy expensive and comes with 2 pairs of 3D glasses. I have five people in the family. I'd have to pay extra for three extra pairs, or more if I want people to come over and try it out. And, lets face it, watching TV in 3D while your friends watch the weird pre-formatted ugly stuff, that's just rude. If TV is for the whole family to watch together, then make it for the whole family to be able to watch together.

2010/10/13

More on the New Television

I think the last time I relied on over-the-air broadcast for TV was in 1985. I was living in St. Louis, and we did have cable, but not for every TV. I remember using a little plastic tube to turn the knob to get the VHF stations at the top of the dial, and watching Benny Hill and Doctor in the House and Rowan and Martin's Laugh-In. I remember the jokes, how in 1984, Ronald Reagan would be President, ha-ha-hah. 

I don't remember even trying to get a TV signal in North Carolina. In Arizona, we had cable and the only broadcast channel I remember watching is KTLA from Los Angeles. In South Dakota, the day rooms in the residence halls had cable, and I never had my own TV, so I never tried. And here, in Indiana, in a city served by one television station, we didn't get great reception from a station with a broadcast tower three miles away, so I stopped trying.

Until now.

I've just moved, and we have one of two cable boxes. I had bought an antenna for the TV tuner card but had kept the cable plugged in, wanting the wider selection of the cable, even if it was a low-resolution analog signal. Right now, rather than dig through boxes and find coax cable, I tried the antenna. Two takeaways: Of course nothing came in but the local station (now about seven miles away), and man, digital looks so much better than analog. Will have to A/B some screen shots from my recorded shows, but trust me, big difference.

Which is what they've been saying forever, I'm sure.

2010/09/24

Catching Up to the New Television

First, there was broadcasting. This is one-to-many, and because of the costs and because of regulation, there weren't that many "ones" floating around. If you live in a town with an ABC and a CBS but no NBC, for example, that's because they decided years ago that your town only needed two broadcasters. When I was 14, in St Louis, we had ABC, NBC, CBS, PBS, and two or three UHF channels showing syndicated reruns. That's how I came to know Rowan and Martin's Laugh-In. Remember the Weird Al movie, UHF? I can't say that real UHF was like that, but there was more than a little truth to it.

Then came cable. The one-to-many paradigm became some-to-many, but through one wire. "57 channels and nothin' on" was the Boss 20 years ago. Seems kinda quaint today, doesn't it? The TVs were set for UHF and VHF, so special adapters had to be bought so your TV could get the cable channels. Eventually, you started to get "cable-ready" TVs. Right now, we have a TV that was swank in 1988 that has channels up to 60-some and three stereo RCA ins (for VCRs and such) and a few 1990s TVs that have channels up to and past 100 and one mono RCA in. Once, my VCRs were connected daisy-chained over co-ax, but now when I set one up, I always go RCA.

I guess there are two big changes post-1999, and they would be TiVo and YouTube, but to take away the camel caps and brand names, you can say digital video recording and Flash video. Once, when you wanted video online, it opened up Real or Quicktime, and I groaned for all the configuration options. I was applying for a web-dev position and one of the company's sites had video in the page and it amazed me. Since then, it's become a big thing. So big, in fact, that a <VIDEO> tag showed up in HTML5. Clearly, now it makes sense to have a computer connected to your TV, making TVs more and more just monitors.


(I suppose that the move from videotape to DVD and beyond needs a mention. DVDs were clearly better than tape — better image, no rewinding — so the video stores cleared out their tapes. Streaming video is equal in quality and clearly more convenient than DVDs, so strip malls are clearing out their video stores. I think that Blu-Ray is impressive, but like digital broadcast, it came in just in time to herald the end of non-streaming-internet video. Both require a change, and if the world is going to change, why go with the only-vaguely-better?)


The old TV way of scheduling recordings sucked. If you knew the time and channel and duration, you could set it up to record, but you could only watch what you're recording. My 80s videotapes are either Dr Who shown on PBS starting 10pm on Sunday or lots and lots of videos off MTV. (Yes, I still have some VHS tapes. No, I don't watch 'em much. Yes, it's more convenient to watch the same Tom Baker eps off Netflix and searching "Humpty Dance" on YouTube. I'm getting to that.) If you have your VCR inline with your cable box, it got worse, because your VCR couldn't change the channel and you might get several hours of the Weather Channel. With the TiVo, and with the set-top boxes that came after, and the TV tuner cards that cane after that, you have a box that knows the TV channels and schedules, can switch between them,

This is clearly wonderful, as this means that you are no longer bound to be at the TV to watch your favorite show at 9pm on a Tuesday. It also means that there's an explosion of set-top boxes, often doing mostly the same thing. GoogleTV, AppleTV, Boxee, Roku, TiVo, Wii, XBox, PlayStation, whatever came with my Samsung Blu-Ray player, all wanting to cover some subset of Hulu, Netflix, iTunes, Amazon on Demand and YouTube, plus whatever other streaming stuff is available.

I'm slowly trying to get from the 90s model (CRT, cable, VCR, DVD player) to the 2010 model, but I'm not sure what the 2010 model really is. Clearly, going to an HDTV that's inches thick instead of an old screen that's feet thick is a crucial move. I think the general-purpose computer is a dying thing, but a machine running Windows can handle all the video codecs and sites and choices you might want to do. I have an inexpensive VGA-RCA converter and an old desktop running at 800x600 connected to a big monitor right now, and another with a Wooted TV tuner. This gets my toe in, and while the first step is clearly getting a bigger, better, thinner HD screen, I'm not sure what the next step is. A recent Wired article seems to say that you can get everything you want cheaper by renting the shows than by getting cable. I'm thinking that having seventeen boxes connected KVM-like to my HDTV/monitor might be the way, but that seems so wasteful and wrong, too.

2010/09/22

Dropbox v Ubuntu One

Here is a good head-to-head comparison between Dropbox and Ubuntu One. I cannot speak to everything in the article, but I can say that I have Dropbox working on two Linux machines and three Windows machines (2 XP, one Vista) and have always found the installation process extremely fast and painless. I have tried to get Ubuntu One working on my two Linux machines, but for my home machine, it always seems to trigger a problem that kills X, which is not good.

The thing that Ubuntu One does that Dropbox doesn't is sync contacts, but that's not an issue for me because I use Google to store and manage contacts anyway, and Zindus to access them in Thunderbird.

So I'm in agreement with the take-away: Use Dropbox, not Ubuntu One.

That being said, if I invite you to Dropbox and you join in, I get a bump in storage space. So, please, get Dropbox.

2010/09/17

jQuery .css() Performance (1.4.2 vs. 1.4.3)

I like this. I really do. Every improvement to jQuery is a big win to my life as a web developer.

But....

Isn't it best to do your CSS in your .css? If you need to change a stylesheet thing, make a class and .addClass(myClass) to it?

Thoughts about Passwords and their replacments

As I discussed previously, there's a rising use of OAuth as a mechanism to do authentication online. I don't really grok it, but I get it now.

Alice wants to use Bob's website to send her tweets to Twitter. Bob sets up with Twitter to get his Consumer Key and Consumer Secret. This uniquely identifies SuperCoolSiteOfBob as a Twitter client. Alice goes to Bob's site and starts to connect. This means a jump to Twitter to allow/deny SuperCoolSiteOfBob to do what it needs to to Alice's Twitter feed, in the form of giving Bob an Access Token and Secret to Alice's account. Alice can check her settings and see SuperCoolSiteOfBob and all the other folks she let have access to her Twitter account, and Twitter can decide that SuperCoolSiteOfBob is doing the wrong thing and block it. All without giving out the main keys to the city, Alice's Twitter password.

This is a good thing.

If anyone asks you for your login and password, who isn't the one and only service you're trying to access, while you're trying to access, treat them like they are trying to bankrupt you, take all your belongings, sell your children into slavery and do donuts in the Payless parking lot until your tires burst and your rims spark. Go to a site and have it say It will be much cooler if you could talk to all your friends, so give us your Gmail login and password and we'll see if they're already on and I read it as saying I'd like to f*** your wife or something even more offensive. Here's a several-year-old discussion of the problem and why OAuth is needed.

Let's sidestep for a moment. There's DVDs. There's basic encryption so not just anybody can play (meaning rip) DVDs. Yet, you want people to play (meaning watch) DVDs, and here, yes, you want just anybody to do so. Children watching Spongebob Squarepants. The elderly watching I Love Lucy. The stoned watching Spongebob Squarepants. Just put the disc in and go. So, everybody needed the key, but you needed to hide the key. And, people wanted the key so they can rip DVDs. And they got it.

Now, let's switch from SuperCoolSiteOfBob to SuperCoolAppOfBob. This is running on Alice's desktop or her phone. Or, Marcia's. (I mentally think of Alice and Bob as Alice the house keeper and Bobby of the Brady Bunch. Eve the eavesdropper is of course Eve Plumb, who played Jan. I once made mugshot-looking images with the protect-the-innocent black bars across their eyes.) Marcia can easily do what they did to DVDs to Bob's app and come up with a Twitter-bashing tool that claims to be Bob's app, eventually forcing Twitter to disavow and denounce Bob, meaning that Alice has to download another app to do her tweeting. Which is bad.

I do have a thought here. Bob as developer needs to have a Consumer Key and Secret (from here on out referred to as Consumer Key) in order to test and know his code works. Bob as website needs the key and can keep it secure. Bob as software vendor doesn't need to distribute his Consumer Key with his software, as long as Alice can easily get one from Twitter, and the process is not too hard. Why don't we go with this?

2010/09/14

The Trojan Phone?

Do you read Eric Raymond's blog? I put it on my Google Reader list, but I haven't hit it in a while, but I read a few in the last few days.

If you've been watching Google lately, with the joint take on Net Neutrality with Verizon, it's easy to wonder "What are they doing?" ESR's take seems to be that, by getting the phones cheaper, they're breaking the hold that the phone providers have. Combine this with Jeff Atwood's take that the fast OODA loop is beating the handset maker and service providers option-breaking "customization" and you begin to see a world with very powerful, very inexpensive and essentially unlocked phones. Which I think is way cool.

2010/09/09

Question to Python Programmers

I am not a Python guy. I'm a Perl guy. I've tried it several times, and each time, I've found something that offended me about it. The first time, yes, was about whitespace. Finding the one SpaceTab intent in a page full of Tab indents took an hour and soured me. But the second time, the way Python's equivalent to LWP worked was seriously counterintuitive.

I've found something exciting and new that bugs me. Take this as a sample chunk of perl.

use This ;
use That ;
use The::Other::Thing ;

a() ;
for my $i ( b() ) {
    c($i)
    }

sub a {
    ...
    }

sub b {
    ...
    }

sub c {
    ...
    }

I edit a program and, after the declarations, all my central logic is up top. Compare a Python program.

import this
import that
import the_other_thing

def a:
    ...

def b:
    ...

def c:
    ...

a()
b = b()
for i in b:
    c(i)

This puts the "main" of the program at the bottom. I couldn't even fake it and make a def main(): and put it somewhere, because it would have to come after all the functions it uses anyway, so it would be useless.

If programs are to be written for other programmers to read and only incidentally for computers to run, it seems like this forced formatting seems wrong. But I'm willing to be taught. Am I missing something on this one?

2010/09/03

More Than Changing Gears, Changing Transmissions

I have a friend. He plays drums. He's a good drummer. We were talking recently about playing, and he said that he plays guitar some, but can't get into it as much as the drums, because he's already hit a certain level with the drums and he can't get to that level with guitar, and he gets frustrated. I'm the same way with non-guitar instruments: when you get past the point where the novelty is amusing, you're stuck at the point where you're struggling to do things you fly through on other instruments, knowing you could be working toward the next level on your primary instrument instead.

I consider myself a Perl programmer. There's interesting things I do with Javascript, but if there's any decision to make on my part on how to implement something, I will choose Perl. I have spent over ten years in computing and there have been few problems where the solution wasn't a sudo cpan away.

There's a program. It handles big data and makes graphs and such out of it. It's a big graphical Java application. There's a lot of things that it does, and if you mean to use it once or twice, there's a lot of powerful parts, but if you need to use it many, many, many times, it gets to be ponderous. There's been requests for a programmer API to be made for this, but that is, at best, still in the works.  So, I've been trying to use Sikuli. Sikuli uses screen captures to find where to click. It's a neat thing, but there are a few issues. It's also written in Java, and it uses Jython, an implementation of Python in Java, for scripting.

Which leaves me having to learn Python. There's significant enough syntax differences that I feel like I'm learning to program from scratch. I've just searched Google for for loop python and python arrays, to show exactly how back-to-the-start I am.

I don't know. There's probably something good about this. I'm being stretched to figure this out. Plus, of course, I get one more name to tag onto the end of my resume. But it doesn't feel like it's a good thing. It feels like a pain, like frustration.

2010/09/01

Surviving the Twitpocalypse

The 2010 Twitpocalypse has come. Buffy didn't save us this time, I guess. And what that means is that Twitter is no longer doing basic authentication for anything but their home page. So, if you have code like this:
my $twit = Net::Twitter->new(
    username => 'varlo',
    password => 'ISoLoveEdward' , #No Jacob
    clientname => $client ,
    ) ;
You're kinda screwed.

It's using OAuth now. I don't fully grok OAuth, so if you want the full deal, you should Google it and all, but in a nutshell, there is a key and secret combination that uniquely identifies my application. You use this application and it says "I don't see a user key" and sends you to Twitter. You get a user key and value, and you can use that user key and secret in conjunction with the app key and secret until you decide to go to Twitter and disassociate your user key with the app key.

This means that at no point does the application have your password, the same password that you might use for your pizza delivery, bank, email, etc. Until I began to understand OAuth, I thought it was a PITA, but I now think it's at least 70% win.

I have discussed Net::Twitter before, so most of the following code has been up before. My preferred way of handling a status is to join STDIN or ARGV with a space, but there should be enough here for you to adapt it to your workflow.

#!/usr/bin/perl

# largely taken verbatim from
# http://search.cpan.org/dist/Net-Twitter/lib/Net/Twitter/Role/OAuth.pm

# Next step is to get the keys and secrets to a config.

use 5.010 ;
use strict ;
use IO::Interactive qw{ interactive } ;
use Net::Twitter ;
use Carp ;

my $status = join ' ', @ARGV ;
if ( length $status < 1 ) {
    while (  ) {
        $status .= $_ ;
        }
    chomp $status ;
    }

if ( length $status > 140 ) {
    say { interactive } 'Too long' ;
    say { interactive } length $status ;
    exit ;
    }
if ( length $status < 1 ) {
    say { interactive } 'No content' ;
    say { interactive } length $status ;
    exit ;
    }

say $status ;

# GET key and secret from http://twitter.com/apps
my $twit = Net::Twitter->new( traits          => [ 'API::REST', 'OAuth' ],
                              consumer_key    => 'GetYorConsumerKeyFromTwitterDotComSlashApps',
                              consumer_secret => 'GetYorConsumerSecretFromTwitterDotComSlashApps',
                              ) ;

# You'll save the token and secret in cookie, config file or session database
my ( $access_token, $access_token_secret ) ;
( $access_token, $access_token_secret ) = restore_tokens() ;

if ( $access_token && $access_token_secret ) {
    $twit->access_token( $access_token ) ;
    $twit->access_token_secret( $access_token_secret ) ;
    }

unless ( $twit->authorized ) {

    # You have no auth token
    # go to the auth website.
    # they'll ask you if you wanna do this, then give you a PIN
    # input it here and it'll register you.
    # then save your token vals.

    say "Authorize this app at ", $twit->get_authorization_url, ' and enter the PIN#' ;
    my $pin =  ;    # wait for input
    chomp $pin ;
    my ( $access_token, $access_token_secret, $user_id, $screen_name ) =
      $twit->request_access_token( verifier => $pin ) ;
    save_tokens( $access_token, $access_token_secret ) ;    # if necessary
    }

if ( $twit->update( $status ) ) {
    say { interactive } 'OK' ;
    }
else {
    say { interactive } 'FAIL' ;
    }

#========= ========= ========= ========= ========= ========= =========

# Docs-suggested
sub restore_tokens {
    my $access_token        = 'GetYrOwnAccessToken' ;
    my $access_token_secret = 'GetYrOwnAccessTokenSecret' ;
    return $access_token, $access_token_secret ;
    }

sub save_tokens {
    my ( $access_token, $access_token_secret ) = @_ ;
    say 'access_token: ' . $access_token ;
    say 'access_token_secret: ' . $access_token_secret ;
    return 1 ;
    }
And if this has helped anyone drive their pet dingo and XB Falcon into the post-twitpocalyptic wasteland, remember, I'm just here for the gasoline.

First Pass on Javascript: The Good Parts


I read some of Douglas Crockford's JavaScript: The Good Parts on Safari, using the school's site license. I saw some of the talk videos on YouTube and Yahoo Video. I now have a copy on my desk.

I've even been putting some of my code into JSLint. It has a warning — JSLint will hurt your feelings — which is exactly right. I mean, ow!

I'll say more on this book the more I integrate it into my headspace. I've been getting serious about Javascript over the last few years, after over a decade of being vocally dismissive of it, because changes have made the languages run faster, as has Moore's Law. The first machines I did web development on would be seriously outclassed by first-generation iPhones. I take that back — even the second machines I did web development on are seriously outclassed by first-generation iPhones. And as a programmer getting serious about Javascript, I want my code to be serious, too, not just lame hack stuff. Which is where having JSLint comes in handy.

And where my issues start to come in.

You can see this as having a similar purpose as Damian Conway's Perl Best Practices and the connected Perl::Tidy, where you use the tool to outline the changes the book suggests you make. Except, Perl::Tidy gives you chapter and verse as to where to look up the dink and allow you to learn. As I put more code into JSLint, the more I wonder "Why is that?" and so far, I'm not finding the answers in The Good Parts.

Still, if you're a Javascript programmer, you can probably help yourself out a lot by getting this book.

2010/08/30

My Facebook Status Solution

You might remember me posting how to post status updates to Buzz, albeit via Perl. I mentioned how I wanted to do the same or similar with Facebook. I got nice suggestions of how to handle it via WWW::Facebook::API, and I tried to wrestle with Facebook's OAuth or similar implementation to get the access I need to do what I want. And then I figured out another way.

#!/usr/bin/perl

# usage: echo big long string of text | facebook.pl

# returns "big long string of text"

use 5.010 ;
use strict ;
use warnings ;
use Carp ;
use Data::Dumper ;
use Net::SMTP::TLS ;
use Getopt::Long ;
use IO::Interactive qw{ interactive } ;
use subs qw{ send_mail get_credentials } ;

my $status = join ' ', @ARGV ;
if ( length $status < 1 ) {
    while (  ) {
        $status .= $_ ;
        }
    chomp $status ;
    }

my $identity = 'MY_IDENTITY_STRING' ;
my @to ;
push @to, 'GETYOUROWNSPECIALEMAILFROM@m.facebook.com' ;
my $body = $status ;

my %msg ;
$msg{ body }    = $body ;
$msg{ to }      = \@to ;
$msg{ cc }      = \@cc ;

say send_mail %msg ;

exit ;

sub get_credentials {

    #this is the point where my setup sucks
    my %creds ;
    my $config = '/home/jacoby/.smtp_identities' ;
    if ( open my $fh, '<', $config ) {
        while ( <$fh> ) {
            my $line = $_ ;
            chomp $line ;
            $line = ( split m{\#}mx )[ 0 ] ;
            next if length $line < 1 ;
            $line =~ s{\s}{}gmx ;
            my @line = split m{,}mx, $line ;
            next if $line[ 0 ] ne $identity ;
            $creds{ from }     = $line[ 1 ] ;
            $creds{ server }   = $line[ 2 ] ;
            $creds{ username } = $line[ 3 ] ;
            $creds{ password } = $line[ 4 ] ;
            }
        close $fh ;
        }
    if ( defined $creds{ username } ) {
        return %creds ;
        }
    say 'No identity chosen' ;
    exit 1 ;

    }

sub send_mail {
    my %msg     = @_ ;
    my %creds   = get_credentials ;
    my $body    = $msg{ body } ;
    my $to      = $msg{ to } ;
    my @to      = @$to ;
    my $mailer = new Net::SMTP::TLS( $creds{ server },
                                     Hello    => $creds{ server },
                                     Port     => 587,
                                     User     => $creds{ username },
                                     Password => $creds{ password }, ) or croak $!;
    $mailer->mail( $creds{ from } ) ;
    $mailer->to( @to ) ;
    $mailer->data ;
    $mailer->datasend( 'Subject: ' . $body . "\n" ) ;
    $mailer->datasend( 'From: ' . $creds{ from } . "\n" ) ;
    $mailer->datasend( 'To: ' . ( join ',', @to ) . "\n" ) ;
    $mailer->datasend( "\n" ) ;
    $mailer->datasend( '' ) ;
    $mailer->dataend ;
    $mailer->quit ;
    return $msg{ body } ;
    }

I already had it, so I just had to modify it slightly, but it came to me. You can set status and upload images via email. I just hand to double-check the right way — statuses in subject, not body — and came right up.

get_credentials is my method for not hard-coding information into the code, and here I do exactly that for the $to email and the $identity string.

I could see using the big and scary Facebook API if I wanted to code up the next Farmville. I know my wife wants me to do that. But for this small purpose, there's little need to go through all that.

2010/08/27

I Have Brilliant Friends

Take overclocking. You have a processor, and if you tell it to run at 1.5x the given clock speed, you get 1.5 the performance. And a gob of heat. Which you then get a better heat sink to take care of.

Let's consider the opposite. You're there, you're doing work, but your workspace is too frakin' hot! What's your alternative? Underclocking. But don't you have to poke at the BIOS for that? Maybe mobo jumpers? Certainly reboot?

Maybe not.

PC World has an article on a software tool to handle just that situation, turning down the CPU speed when the server room heat becomes too much. (Yeah, it's a server thing. It isn't something that makes too much sense unless you have huge rooms full of servers, each full of long-running jobs.)

And I was in a Linux User Group with both guys in the article. So cool.

A Squeeze of Python to give me a Buzz

I've been learning Python.

I know, I know....

It starts with Google Buzz. I accept web pages and window apps as the only choices to see social media output from places like Identica, Twitter, Facebook, etc. But my preference is to type things on the command line for most of my content-generating needs. If I wanted to send the same thought to most of my microblogging sites, I just pipe it together.

echo My oh-so-crucial thought | twitter.pl | identi.pl | wiki.pl

So far, two things have been standing in my way. Facebook and Google Buzz. I have yet to find ways to use my beloved Perl to write to either. But I poked around and found some sample code on the Google Buzz API page. Specifically, I found make_post.py, which does write to Buzz, but it will not easily integrate to my preferred way. Specifically, it takes in your key and secret as command-line flags and accepts your Buzz update via STDIN. If that's how you roll, that's fine, but for me, the message is join ' ' , @ARGV . So, I recoded it, taking more out than I put in, and using some of my very slight knowledge of Python, to make this. I have the key and secret hard-coded, which is kinda acceptable for personal use but still not to be accepted. You should use the make_post.py that comes with buzz-python-client to set up your OAuth if you use this, because anything that would do that has been brutally ripped from the original code.

Expect Perl code based on this to come around eventually. For now, enjoy buzz-python-client and my addition.

# Copyright 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import sys
import traceback
import getopt

#
# Load Buzz library (if available...)
#

try:
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import buzz
except ImportError:
print 'Error importing Buzz library!!!'

token = verification_code = buzz_client = ''

#
# Function to obtain login data
#

def GetLoginData():
'Obtains login information from either the command line or by querying the user'

global token, verification_code, buzz_client
key = 'KEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEYKEY'
secret = 'SECRETSECRETSECRETSECRETSECRETSECRET'
buzz_client.build_oauth_access_token(key, secret)

#
# Main program starts here
#

try:
buzz_client = buzz.Client()
buzz_client.oauth_scopes=[buzz.FULL_ACCESS_SCOPE]
buzz_client.use_anonymous_oauth_consumer()
GetLoginData()
args = sys.argv
del args[0]
message = ' '.join(args)
message = message.strip()
post = buzz.Post(content=message)
buzz_client.create_post(post)
print message
except:
print '\nBzzzz! Something broke!!!'
print '-' * 50
traceback.print_exc()
print '-' * 50

2010/08/26

Quick Google Bookmarks Tutorial/Reminder

This is a question that came up on one of the new StackExchange betas, WebApps.
There appears to be two distinct sets of bookmarks maintained by Google. If you visit Google Notebook or Google Bookmarks you get one set. And if you sync Chrome accounts you get another. My question is: how can I get one set of bookmarks maintained on Google's web apps and through Chrome? I want to be able to access the bookmarks I have synced with Chrome from a web app when I'm using a public computer.
I answered it, but I'll hit the topic here, because I can.


This is Google Bookmarks. It seemed to be a response to Delicious, which turns the need for persistent and distributed bookmarks into a social media application. It wasn't imagined to be part of the browser. This was before Chrome was started, or at least before it was far along.

It was also before Google Docs was far along, because rather than working with Google Bookmarks, Google Chrome decided to work with Google Docs for bookmark storage. There's a folder under My Folders marked Google Chrome, which is where it stores your bookmarks.


So, guy on StackExchange, you CAN use a web app to access your bookmarks. It's just which one that's the question. And, for all the things you can say about Chrome, this feature, the easy integration of bookmarks syncing across platforms, is the one that sold me on Chrome. If all your bookmarks are in Google Bookmarks, it's dead easy to export them and import them into via your Bookmarks Manager.

Now, how do I sync bookmarks with a mobile Opera browser?

2010/08/24

Overdue Book Review: Higher Order Perl

Ever promise to do something and not remember it?

That's kinda how it is here. I thought I was being given a copy of Higher Order Perl, while not understanding or promptly forgetting that I had incurred the responsibility of writing a review. Which is now way late, after far better reviews have already been writtten, and now that you can freely download it from the author's website.

I'll start by giving you the takeaway: Right now, there are two books that a Perl programmer should have and should read and should integrate into their coding style. The first is Perl Best Practices by Damian Conway, and this is there to raise the minimum level of your programs. If you want your code to be readable and reusable, read this book and get it to this level. The other is Higher Order Perl. This is because, while you may be leveraging the Awesome Power of Perl, you don't know the half of it. Mark Jason Dominus knows, and this book tells you.

I first learned about MJD when years ago I was going to defend Perl to my Linux User Group. One of my defense points was from MJD, who said of Norvig's seven features that make Lisp different, Perl shares six. Not that I really used it like that. I saw the bit about fibonacci sequences that shows up here in page 33, coded my own iterative and recursive implementations, became convinced that yes, I see the problem here, and wrote my version that used a hash or array to fix it. I didn't hit the Memoize part, which is where the awesome power comes in.

I knew there was awesome power there for a long time. I just failed to learn about it, because it was beyond what I needed at the time. Until, of course, it became part of what I needed. I needed it because I was creating a stack of horrible copy-and-paste code that really needed a dispatch table. I needed it because I needed a better and more adaptable filesystem spider than I had.

I am, for the first time, going chapter-by-chapter through Higher Order Perl and I like to think that my code will be better for it. I know two programs that's better for it.

Douglas Crockford talking about Javascript: The Good parts


Javascript - The Good parts on O'Reilly.

2010/08/23

Think Globally, Play With Hashes Locally

Reading a bit from Effective Perl Programming, I found out that Perl 5.12 supports deleting an element in a hash in a local scope. I was looking up from Higher Order Perl for a moment, so you might guess that my thoughts went to manipulating dispatch tables.



my %subs = ( do => sub { return 'c' },
rey => sub { return 'd' },
mi => sub { return 'e' },
fa => sub { return 'f' },
so => sub { return 'g' },
la => sub { return 'a' },
ti => sub { return 'b' }, ) ;

my @notes = qw( do rey mi fa so la ti do rum fmep agog ) ;

{
delete local $subs{ ti } ;
say '=' x 20 ;
for my $note ( @notes ) {
if ( $subs{ $note } ) {
my $sub = $subs{ $note } ;
say join "\t", '', $note, &$sub ;
}
}
}

say '-' x 20 ;
for my $note ( @notes ) {
if ( $subs{ $note } ) {
my $sub = $subs{ $note } ;
say join "\t", '', $note, &$sub ;
}
}

====================
do c
rey d
mi e
fa f
so g
la a
do c
--------------------
do c
rey d
mi e
fa f
so g
la a
do c

Yeah, I'm still on 5.10. But that made me think.
Can you add to a hash only in the local scope?

my %subs = ( do => sub { return 'c' },
rey => sub { return 'd' },
mi => sub { return 'e' },
fa => sub { return 'f' },
so => sub { return 'g' },
la => sub { return 'a' },
ti => sub { return 'b' }, ) ;

my @notes = qw( do rey mi fa so la ti do rum fmep agog ) ;

{
local $subs{ rum } = sub { return 'one' } ;
say '=' x 20 ;
for my $note ( @notes ) {
if ( $subs{ $note } ) {
my $sub = $subs{ $note } ;
say join "\t", '', $note, &$sub ;
}
}
}

say '-' x 20 ;
for my $note ( @notes ) {
if ( $subs{ $note } ) {
my $sub = $subs{ $note } ;
say join "\t", '', $note, &$sub ;
}
}

====================
do c
rey d
mi e
fa f
so g
la a
ti b
do c
rum one
--------------------
do c
rey d
mi e
fa f
so g
la a
ti b
do c


Yeah, I'm still on 5.10. But that made me think.
And you might guess that it's a foregone conclusion that you can modify a hash element locally.



my %subs = ( do => sub { return 'c' },
rey => sub { return 'd' },
mi => sub { return 'e' },
fa => sub { return 'f' },
so => sub { return 'g' },
la => sub { return 'a' },
ti => sub { return 'b' }, ) ;

my @notes = qw( do rey mi fa so la ti do rum fmep agog ) ;

{
#delete local $subs{ ti } ;
#local $subs{ rum } = sub { return 'one' } ;
local $subs{ ti } = sub { return 'FMEP' } ;
say '=' x 20 ;
for my $note ( @notes ) {
if ( $subs{ $note } ) {
my $sub = $subs{ $note } ;
say join "\t", '', $note, &$sub ;
}
}
}

say '-' x 20 ;
for my $note ( @notes ) {
if ( $subs{ $note } ) {
my $sub = $subs{ $note } ;
say join "\t", '', $note, &$sub ;
}
}

====================
do c
rey d
mi e
fa f
so g
la a
ti FMEP
do c
--------------------
do c
rey d
mi e
fa f
so g
la a
ti b
do c

The more I play with dispatch tables, the more I imagine practical uses of this. I love cool stuff!

The Community is Solving My Problems!

The Stack Exchange Crew is pushing a few domain-specific Stack sites for beta-testing. The one I've been most involved in is the Ubuntu one. I had two specific problems which I think I have complained about there. The first is how, for no reason I could identify, I would lose the ability to view the contents of my ~ folder. The other is that I used to be able to connect my netbook's LINE OUT to my desktop's LINE IN and get both audio streams in one set of headphones.

The second one was the easiest. Simply using gst-launch to bridge the in and out of my desktop. I'm running media on my netbook right now, with my headphones plugged into my desktop. It was one simple apt-get install to implement.

The second one took a little more talking, but the top contender is that unstable symbolic links bringing down /usr/bin/ls but not /bin/ls, and my 12 sshfs mount points which I keep in /sshmounts to keep them where du won't search them, that's where the instability is. I'm waiting for the next time problems on my home box or one of the other mount points causes the problem again before I mark it answered, but I think it is.

I have logged into a few others, dealing with computer science and user interfaces, but Ubuntu is the one where I have had the most questions and found the most answers.

2010/08/19

Copy/Paste: The First Step Is To Admit You Have A Problem

Thanks to Juster, I have come up with a better way to handle it.


my %functions = ( 'status' => \&create_new_request_status,
'barcode' => \&update_request_barcode,
'lab_director' => \&update_request_lab_director,
'library_type' => \&update_request_library_type,
'request_name' => \&update_request_name,
'source' => \&update_request_source,
'species' => \&update_request_species, ) ;

my $out_url = 'request_info.cgi' ;
$out_url .= '?test=1' if defined $cgi->param( 'test' ) ;
$output .= $cgi->h2( $cgi->a( { href => $out_url }, 'Request Table' ) ) ;

## show specific run info
if ( 0 ) { } # BLANK to make adding and changing elsifs easier
elsif ( defined $cgi->param( 'request_id' ) ) {
for my $key ( $cgi->param() ) {
if ( $functions{ $key } ) {
my $data ;
$data->{ request_id } = $cgi->param( 'request_id' ) ;
if ( $key eq 'status' ) { # status is different
for my $word ( qw{ status program notes } ) {
$data->{ $word } = $cgi->param( $word ) ;
}
$functions{ $key }->( $data ) ;
}
else {
$data->{ val } = $cgi->param( $key ) ;
$functions{ $key }->( $data ) ;
}
}
}
my $request_id = $cgi->param( 'request_id' ) ;
$output .= $cgi->h2( 'Request #' . $request_id ) ;
$output .= $cgi->div( show_request( $request_id ) ) ;
}
else {
$output .= $cgi->div( show_list() ) ; # show full table
}
Of course, if I had another table showing what fields I need to put into each input hash, that would make the code even simpler.

Maybe quite obvious: using CGI to make a Javascript test page

I've been reading Javascript:The Good Parts on Safari, and looking through the code examples. When doing the same for Perl, I toss test.pl into an editor and add to it over and over. I suppose I could have a web page with an ever-growing SCRIPT section, but I had a more clever idea.

I made a CGI where the body is just a FORM with a TEXTAREA and a submit button, and I set it up so that when you clicked submit, the code is sent via POST (so you can be verbose) and put into the TEXTAREA and the SCRIPT block. I have a code sample, but it's as close to a textbook use of CGI.pm that I don't think there's much use to posting it.

And I could've put in a test.js and run spidermonkey, but it seems like you can't apt-get install it anymore. I've tried rhino, and just installed it again, but I didn't like it as much as spidermonkey. But if you're writing Javascript, you're likely coding for the web.

2010/08/18

The Heartbreak of Copy/Paste Code

This is actual CGI code that I'm working on.

    if ( 0 ) { }    # BLANK to make adding and changing elsifs easier
    elsif (    defined $cgi->param( 'request_id' )
            && defined $cgi->param( 'status' ) ) {
        my @keys ;
        my @vals ;
        my $data ;
        $data->{ request_id } = $cgi->param( 'request_id' ) ;
        $data->{ status }     = $cgi->param( 'status' ) ;
        $data->{ program }    = $cgi->param( 'program' ) ;
        $data->{ notes }      = $cgi->param( 'notes' ) ;
        create_new_request_status $data ;

        my $request_id = $cgi->param( 'request_id' ) ;
        $output .= $cgi->h2( 'Request #' . $request_id ) ;
        $output .= $cgi->div( show_request( $request_id ) ) ;
        }
    elsif (    defined $cgi->param( 'request_id' )
            && defined $cgi->param( 'barcode' ) ) {
        my $data ;
        $data->{ request_id } = $cgi->param( 'request_id' ) ;
        $data->{ val }        = $cgi->param( 'barcode' ) ;
        my $err        = update_request_barcode( $data ) ;
        my $request_id = $cgi->param( 'request_id' ) ;
        $output .= $cgi->h2( 'Request #' . $request_id ) ;
        $output .= $cgi->div( show_request( $request_id ) ) ;
        }
    elsif (    defined $cgi->param( 'request_id' )
            && defined $cgi->param( 'request_name' ) ) {
        my $data ;
        $data->{ request_id } = $cgi->param( 'request_id' ) ;
        $data->{ val }        = $cgi->param( 'request_name' ) ;
        my $err        = update_request_name( $data ) ;
        my $request_id = $cgi->param( 'request_id' ) ;
        $output .= $cgi->h2( 'Request #' . $request_id ) ;
        $output .= $cgi->div( show_request( $request_id ) ) ;
        }
    elsif (    defined $cgi->param( 'request_id' )
            && defined $cgi->param( 'species' ) ) {
        my $data ;
        $data->{ request_id } = $cgi->param( 'request_id' ) ;
        $data->{ val }        = $cgi->param( 'species' ) ;
        my $err        = update_request_species( $data ) ;
        my $request_id = $cgi->param( 'request_id' ) ;
        $output .= $cgi->h2( 'Request #' . $request_id ) ;
        $output .= $cgi->div( show_request( $request_id ) ) ;
        }
    elsif (    defined $cgi->param( 'request_id' )
            && defined $cgi->param( 'source' ) ) {
        my $data ;
        $data->{ request_id } = $cgi->param( 'request_id' ) ;
        $data->{ val }        = $cgi->param( 'source' ) ;
        my $err        = update_request_source( $data ) ;
        my $request_id = $cgi->param( 'request_id' ) ;
        $output .= $cgi->h2( 'Request #' . $request_id ) ;
        $output .= $cgi->div( show_request( $request_id ) ) ;
        }
    elsif (    defined $cgi->param( 'request_id' )
            && defined $cgi->param( 'library_type' ) ) {
        my $data ;
        $data->{ request_id } = $cgi->param( 'request_id' ) ;
        $data->{ val }        = $cgi->param( 'library_type' ) ;
        my $err        = update_request_library_type( $data ) ;
        my $request_id = $cgi->param( 'request_id' ) ;
        $output .= $cgi->h2( 'Request #' . $request_id ) ;
        $output .= $cgi->div( show_request( $request_id ) ) ;
        }
    elsif (    defined $cgi->param( 'request_id' )
            && defined $cgi->param( 'lab_director' ) ) {
        my $data ;
        $data->{ request_id } = $cgi->param( 'request_id' ) ;
        $data->{ val }        = $cgi->param( 'lab_director' ) ;
        my $err        = update_request_lab_director( $data ) ;
        my $request_id = $cgi->param( 'request_id' ) ;
        $output .= $cgi->h2( 'Request #' . $request_id ) ;
        $output .= $cgi->div( show_request( $request_id ) ) ;
        }
    elsif ( defined $cgi->param( 'request_id' ) ) {
        my $request_id = $cgi->param( 'request_id' ) ;
        $output .= $cgi->h2( 'Request #' . $request_id ) ;
        $output .= $cgi->div( show_request( $request_id ) ) ;
        }
There's value-scrubbing on the other side of all the update_request_* stuff. At one point, there were four cases here, and now there's nine. The if (0) {} bit at front was there to make it easy for me to juggle order if I want, but the rest ....

I'm Dave, and I'm a Copy/Paste Coder.

If I was tossing to the same function at the end, it would be one thing. I can imagine having something like the following function to fix things, but the big elsif chunk in the center seems only marginally better.
sub un_copy-paste {
    my $output ;
    my ( $request_id , $key , $val ) = @_ ;
    $data->{ request_id } = $cgi->param( 'request_id' ) ;
    $data->{ val }        = $cgi->param( 'source' ) ;
    my $err ;
    if ( 0 ) {}
    elsif ( $key eq 'source' ) { update_request_source( $data ) ; }
    elsif ( $key eq 'species' ) { update_request_species( $data ) ; }
    # and more like this
    $output .= $cgi->h2( 'Request #' . $request_id ) ;
    $output .= $cgi->div( show_request( $request_id ) ) ;
    return $output ;
    }
I think reading more Higher Order Perl could help me come up with something non-stupid.

2010/08/17

Every Now And Then, I Turn It On Again....


Slashdot reports that broadcasters and the RIAA are considering getting Congress to order FM radio into cellphones. On a tech level, I'm surprisingly OK with it. Cellphones already have GPS, cameras, MP3 players, keyboards, Wi-Fi, texting and email, plus a billion apps. I wonder if I can get an iPhone cover that makes it look like a Swiss Army knife.

And there was a time, once, long ago, where I would've loved to been able to have a programmable recording interface to radio. Something like this in my crontab.
0 17 * * 0 /usr/bin/radio-record -freq 98.9 -band fm -duration 60 # Blues hour of 98.9FM
But that was years ago. Honestly, the ONLY time I listen to radio these days is when my wife is driving, she only listens to talk radio and mostly listens to AM stations on the fuzzy edge of reception so that the experience is indistinguishable from white noise. And, notice, they want to have FM on phones. Not AM. There may be a technical reason. I kinda doubt it.

I have two issues with radio. First, there's variety. Within the classes of music I am interested in, radio gives me few choices. I have a greater chance of hearing a cool song I've never heard before in my MP3 directory than on the radio. And for many whole classes of music I like, there's no chance I can find it in my dial.

Then there's the advertising. I don't object categorically to advertising. I once did, but I don't anymore. I was taking an advanced editing course in J-school and was told that advertising was as much newspaper content as the news stories. I balked. Afterward, I had some downtime, opened up a copy of Maximum Rock'n'Roll and saw a Dischord Records ad showing that Fugazi had a new album coming out, and I was enlightened.

Taking it into a more computer-related forum, I can see on the back cover of the August issue of Maximum PC that you can get an Antec case that would allow you to hot-swap laptop-sized SATA drives out the front. We've hit drive sizes and data sizes where any personal backup choice besides putting it on a hard drive is too slow and too small to be practical, and here's a solution. This is Antec trying to sell me something. This is also news.

Radio is filled with ads trying to sell someone something, but it stopped selling stuff to me a long time ago.

The Slashdot audience dragged out the buggy-whip analogy, but I don't think that radio has no place in the 21st century. I think that cellphones would already have radios if there was a market for them. I think that Pandora on a smartphone will always sound better than an FM radio on a smartphone. It is the forcing of the issue that most bugs me, but I'm not

The thing I'd put into a cellphone if I had a choice is an LED flashlight. I've used the screens of my Palms and phones as a field-expedient light through many power outages before but would like more candlepower.

2010/08/11

Something Now Sucks Like A Vax


vax11 is a friend of mine. Well, acquaintance. I'm not sure that we shared words in the last three years. We used to be part of the local Linux Users Group together. I used to call his apartment the Museum for Retro-Computation. He thought the room we used for install-fests looked like NASA Mission Control circa Apollo, especially with all the monitors up, so he wore white short-sleeve shirts and skinny ties so he looked he belonged in NASA Mission Control circa Apollo. You can tell he's old-school by the fact his handle is vax11.

That's an image of his VT125 terminal, connecting to the VAX emulator on his HTC EVO phone, hanging from his VT125. Now, I'm a big geek — 6'4", 250lbs+, Plus all the coder, reads O'Reilly books for fun, read LoTR, saw Star Wars for his eighth birthday, owns a 20ft scarf stuff, has the Daredevil comic where Bullseye kills Elektra, blogs about frustration with electronic esoterica, etc — but this is way next level. Isn't it so cool?

2010/08/06

Rhythmbox Problems

That sure tells me what the problem is, doesn't it?

Poor Planning Prevents Proper Performance: An SQL Tale

I'm making an application that handles workflow for a gene sequencer. What's on my plate right now is creating a record online that we can then feed into the machine. The process starts with a request, and the names of all the tables related to the request match /^request/. Each request has one or more accessions (you can think of them as samples), and the name of each table related to accession matches match /^accession/. Not everything involved with this is perfect but not bad for a project that I wrote everything for.

But we don't handle requests. We don't load requests into the sequencer. We put samples on a chip and run the process on that. A chip can have 1-n regions with 1-n samples in each region, using barcoding, which I'll have to explain sometime.

What's the term for this? What did I use? "Chip"? "Run"?

Sequence.

And for a unique sample used in the run?

Barcode.

Let me metaphor for a moment to explain why this is so hateful. Imagine you're an official for Imaginary Elementary School, about to take the 2nd grade class to the Zoo for a field trip. Problem is, the 2nd grade class of Theoretical Elementary is going to. As is the 2nd grade of Potential Elementary, etc. So, you get a bunch of bright yellow shirts in 2nd-grader sizes that say "Imaginary Elementary Class Trip 2010". Then Theoretical gets a bunch of bright blue shirts saying "Theoretical Elem", etc., and now you know if that group of eight-year-olds running around is Imaginary or Theoretical.

Now, instead of 2nd graders, we're talking DNA samples, and instead of t-shirts, we have chemicals. When I think "barcoding", I think of putting barcodes on products so I know what price they are. I think "tagging" (like they do with wild animals to track their movements) is a more fitting metaphor and I like my extended T-shirt thing. And, right now, I'm wanting to make a table where I can say that, on August 6, the Imaginary Elementary kids wore red, and the obvious table name would be sequence_barcodes and the functions I need to write should be get_sequence_barcode() and set_sequence_barcode(), but because I'm using "barcode" as the generic term for the 2nd-grader and not the shirt the child wears, that table name and those function names are already used.

So I can't use the right names now because the wrong names were used in the past. I might just call the table keaton.

2010/08/05

More on Passwords

In the comments of a previous post on creating passwords, xenoterracide mentioned his method of creating more secure passwords.

I say "more secure" rather than secure because, as mentioned in an exchange in xeno's comments section that got so heated xeno locked the comments, the entropy could be better, plus if the desire for the passworded information gets big enough, they'll just use legal or extralegal methods rather than crack your password. Basically, we're trying to make it easier to have passwords that are better than average.

The Xeno method is as follows.

echo -n SOME_STRING_YOU_WILL_REMEMBER | sum | base64 

or

echo -n SOME_STRING_YOU_WILL_REMEMBER | sha1sum | base64 

I wrote code to automate it somewhat. I display the hexdigest and base64 digest of the string.

#!/usr/bin/perl

# turns the input into a string and puts out SHA1 digests in both hex and base64

use 5.010 ;
use strict ;
use warnings ;
use Digest::SHA1 ;

my $sha = Digest::SHA1->new;
$sha->add( lc join ' ' , @ARGV ) ;
say $sha->hexdigest;
say $sha->b64digest; 

But I have realized a problem with this. Let me give you the use case.

You have otherwise had your account hacked. You do this on a machine where someone else has root, or you have an ancient version of sendmail or you use .authorized_keys and someone came in free from another machine. They don't need to be root, they just need to be you.

grep password .bash_history
grep sha1sum .bash_history

So, writing it up as a tool that takes the string from STDIN while running, not from the command line so it ends up in the .history, that's how it should be.

I haven't written it up like that yet, but it should be easy. I mean, the above example is only 4 lines of real code, right?

My Wireless Networking, Plus


In the foreground is my netbook, connected to the campus wireless network with barely one bar of connectivity.

In the background is the antenna for the access point for the campus wireless network.

Yes, I can physically touch my netbook to the antenna and not get better than low connectivity. Elsewhere it's OK.

I have sent a help ticket on. I haven't sent this picture. Yet.


This one's just a bit silly. I realized that the foliage (which I think is for sale) is covering the hours. If it wasn't open 24 hours, covering up when you're open could be a problem. Still, it's kinda FAIL, don't you think?

2010/08/04

I Was Born A REPL

I have seen the Perl module Devel::REPL a few times. I haven't seen much use for it, thinking I can write a quick Perl program if I need to and a one-liner if necessary. Who needs a Perl shell?

Then I had reason to learn Python, or actually Jython, or a tool that used Jython for scripting. I went to the O'Reilly, and it started with using Python as a shell. (Or, Jython. Or ... whatever.) Then the oddest thing happened.

I used the Python shell as a calculator.

More than once.

That wasn't the push that made me consider re.pl. It was this helpful .rc file from recent blog commenter xenoterracide.

I don't use it often yet. I keep test.pl with the current test code at the top and most everything but the module declarations commented out, just for this purpose. And, as a purely aesthetic issue, I want to munge the way the prompt looks. But I'm sure I will use it.

2010/08/02

More Fun with Ubuntu upgraded to Lucid

I have a Linux box. I installed a 500GB drive and put on Ubuntu Jaunty. I left it at Jaunty through Karmic, thinking that I didn't want or need any of the diffs. But I got to Lucid and I thought I should move up.

I used the built-in upgrade method, rather than pulling out a live CD and reinstalling. I've come up with two problems. First, it comes up without a mouse pointer. I can mouse, but I just don't know where I'm mousing. I can fix that short-term — open the Appearance module, change the pointer, lock screen, unlock — but the short term fix is all I can do, because not long after, I lock up with the screen blinking Checking Battery Status. I remind you that this is on a desktop machine. No battery to check. All powered from the hole in the wall.

But that's not the annoying part.

The annoying part is that none of these issues come up when I log into another account I never used before. I logged in on Saturday. It's running fine still.

I don't want to have to rename and move lots of crap. I also don't want to reinstall from liveCD or live thumbdrive. But I don't want to keep it as-is, and it's hard to know which I want less.

2010/07/30

A Cute Bit Of Talking Code

As I've previously mentioned, I have problems talking to computers but have few problems having them talk to me. I have a crontab on my work machine that runs every 15 minutes to tell me the time of day. ( Yes, my computer will give me the time of day. )

I found out quickly that just piping date into Festival won't make you happy. I wrote this to force a pronunciation. It's simple Perl, and it doesn't deal with Festival directly. But it gives me the pronunciation I want.

If it's useful to you, use in good health.

#!/usr/bin/perl

# */15 8-17 * * 1-5 ~/bin/curr_time.pl | /usr/bin/festival --tts &> /dev/null

use 5.010 ;
use strict ;
use warnings ;
use Data::Dumper ;
use subs qw( hour minute ampm ) ;
my @time = localtime ;
my @output ;

my @hour =  qw{
    Twelve One Two Three Four Five Six Seven Eight Nine Ten Eleven Twelve
    } ;

push @output , hour(@time) ;
push @output , minute(@time) ;
push @output , ampm(@time) ;
say join ' ' , @output ;

# -- hour -------------------
sub hour {
    my @time = @_ ;
    my $hour ;
    if    ( $time[2] % 12 == 0 ) { $hour = 12 ; }
    elsif ( $time[2] > 12 )      { $hour = $time[2] - 12 ; }
    else                         { $hour = $time[2] ; }
    return $hour[ $hour ];
    }

# -- minute -----------------
sub minute {
    my @time = @_ ;
    my $minute = sprintf '%d' , $time[1] ;
    if    ( $time[1] < 10 ) { $minute = 'oh ' . $minute ; }
    if    ( $time[1] == 0 ) { $minute = '' ; }
    return $minute;
    }

# -- a.m. - p.m. ------------
sub ampm {
    my @time = @_ ;
    my $m ;
    if    ( $time[2] == 0  && $time[1] == 0 )  { $m = 'Midnight' ; }
    elsif ( $time[2] == 12  && $time[1] == 0 ) { $m = 'Noon' ; }
    elsif ( $time[2] <  12   )                 { $m = 'A M' ; }
    else                                       { $m = 'P M' ; }
    return $m;
    }

Check My Math

There's a function. getJSON. It could be easily presumed to just get (in the colloquial sense) a JSON object. It doesn't. It uses the HTTP GET method.

As it turns out, it is just a rebadge of the more generic GET.

$.getJSON( url , query_object , function( data ) { ... } ) ; 

Is equivalent to this.

$.get( url , query_object , function( data ) { ... } , 'json'  ) ; 

And, of course, this is generic. If you want to POST and get JSON back, you do this.

$.post( url , query_object , function( data ) { ... } , 'json'  ) ; 

But they will not not not not NOT do this.

$.postJSON( url , query_object , function( data ) { ... } ) ; 

Because that function name assumes you've put together a JSON object and are posting it, not downloading it with the POST method.

Assuming that I was just getting JSON and not GETting JSON, as jQuery seems to want me to assume, has caused me several days of problems. Am I right in thinking that their rational encourages people to have problems?

I'm So Stupid!



I have just moved my application from getJSON with me handling the data by creating the URL by myself to getJSON by creating an object for the function to handle. And did it work? No it didn't work. I spent much of yesterday trying to think myself around the problem. And this morning, I started again with the jQuery AJAX documentation.

"Load JSON-encoded data from the server using a GET HTTP request."

I'm using getJSON and wondering why I get the GET-related problem.

I chose the empty box! I'm so stupid!

I will now proceed to write postJSON. After, of course, I bash my head against the wall over and over again.