MacBook, defective by design banner

title:
Put the knife down and take a green herb, dude.


descrip:

One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com

Using 89% of the same design the blog had in 2001.

FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike, always wear white.

As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.

x

MarkUpDown is the best Markdown editor for professionals on Windows 10.

It includes two-pane live preview, in-app uploads to imgur for image hosting, and MultiMarkdown table support.

Features you won't find anywhere else include...

You've wasted more than $15 of your time looking for a great Markdown editor.

Stop looking. MarkUpDown is the app you're looking for.

Learn more or head over to the 'Store now!

Friday, January 30, 2015

Some details: February: worked on Dash 2.0 May: mostly played Hearthstone :( August: 3-week holiday September-October: worked on Dash for iOS November: 2-week holiday December: mostly played Hearthstone :(

Wow. For all but one month, the Dash coder is working less than eight hours a day. And he's still pulling in $271,500 in 2014. That's some serious dinky.

Labels: , ,


posted by ruffin at 1/30/2015 08:17:00 PM

It's funny.

13. I would tell you a UDP joke, but you might not get it.

As you might have guessed, there are more.

posted by ruffin at 1/30/2015 07:43:00 PM
Thursday, January 29, 2015

iTunes Connect bug logs developers in to other developersโ€™ accounts at random | Ars Technica:

This morning, a number of developers signed in to Apple's iTunes Connect service only to be greeted by a list of apps that didn't belong to them. TechCrunch has a good roundup of tweets from affected developersโ€”it seems that whenever developers signed in with their credentials, they were being granted access to other developers' accounts at random.

This is perhaps the most serious Apple Fail I've seen to date. Not only could you see other developers' info, I assume you could also edit it. That's a serious financial and fairly proprietary business info breach.

Labels:


posted by ruffin at 1/29/2015 08:51:00 PM
Monday, January 26, 2015

I wanted to see if JSHint had a provision for checking imported packages for property names in a file it was, um, "hint"ing, and ran into a link to the, "Why I forked JSLint" post (which was down -- domain now points to github? -- but lives at archive.org) again. The post is interesting. The representative example given to fork JSLint is a horrible one, imo. Kovalyov argues that he should be able to put a var before a iterator variable, like this...

for ( var name in obj ) { ...

That's really not a good reason to leave JSLint. Forcing you to acknowledge variable hoisting doesn't seem that objectionable. There are other things that it forces you to do that I don't see as much wisdom in, but this really isn't one of them. And that his snippet has "Example taken from jQuery 1.4.2 source" as if that made it automatically something worth emulating also gnaws at my craw a bit.

But the post's epigraph from Crockford does resonate:

Your sadly pathetic bleatings are harshing my mellow. โ€”Douglas Crockford.

Wow. Reminds me of the link I use in my StackOverflow profile to undercut where I quote, "You should take JSLint's advice," a bit. The guy's a great coder, and I enjoy his very strict approach to JavaScript 99.44% of the time, but Kovalyov is right: Crockford's social skills (and, from what I've seen, hubris) are awful.

But again, bad social skills don't make the tool just as toxic. Kovalyov links to Brenan Eich, "creator of JavaScript", where Eich says, "JS Lint can suck it" That's said, sure (Eich is using automatic semicolon insertion, though he's doing it to make his code look like CoffeeScript, so Kovalyov has already taken the quote well out of context), but there's an interesting counterpoint Kovalyov misses in that same blog post from Eich:

Here's my pitch: committees do not design well, period. Given a solid design, a committee with fundamental disagreements can stall or eviscerate that design out of conservatism or just nay-saying, until the proposal is hardly worth the trouble. At best, the language grows larger more quickly, with conservative add-ons instead of holistic rethinkings.

What I like most about JSLint is that I know I'm getting similar code across the board. JSLint honestly enforces well-considered conventions. When Kovalyov says, "It is quickly transforming from a tool that helps developers to prevent bugs to a tool that makes sure you write your code like Douglas Crockford," my reply would be, "And you think your committee is going to do better?"

So many questions about JSLint on StackOverflow get answered with, "You should use JSHint." But if we all use JSHint to turn off whatever bugs us, as we'd do if we followed that advice each time, what do we leave turned on? Just the things that aren't troublesome? Isn't our code getting lint-i-er each time? Show me an example of where what Crockford requires from you is wrong -- not just annoying, but undeniably worse than an alternative. I don't think it exists, yet.

Isn't the point of a linter to challenge us all to write better code?

Labels: , , ,


posted by ruffin at 1/26/2015 12:33:00 PM
Wednesday, January 21, 2015

I've been using Thunderbird again recently because Outlook 2010 wasn't able to connect to the company's Exchange server (although I guess Office365 with OWA might be something different? Maybe Exchange in the cloud? I didn't track things down too far... Outlook, even after updating with a download that the OWA interface said I needed, didn't work), but somehow Thunderbird with the ExQuilla extension ($10 a year) does. Go figure.

I don't hate Thunderbird, but I can see why folks aren't drawn to it. It's very Netscape 3.0-ish and its interface is, at times, horribly unintuitive. It's got *everything*, but everything isn't particularly accessible. I can't imagine if I was coming into Thunderbird cold and hadn't used since, well, early Netscape.

Here's an example.

If you want to set the display font for "plain text" emails in Thunderbird, you have to go to Tools >>> Options, Display, Formatting, click the Advanced button, then click the Monospace drop-down and select a font and size. But I kept getting what many of what were obviously plain text emails in a much larger font than what I'd set.

Turns out that the "Fonts for [Western]" drop-down at the top of the Advanced fonts interface only works for ASCII (I'm assuming) emails. If you want plain text emails in UTF-8 to have a different font than the default as well, you have to choose "Fonts for [Unicode]" from that drop-down. This bugzilla thread helped. Not sure why there's a distinction. Perhaps other languages have different fonts for different 7-bit encodings? But shouldn't there just be a concept of a "default" display?

So right. Guess I should've figured that out. /sigh Why you can't just right-click the message you're looking at and set your font (for the whole app) there, I don't know.

Labels: ,


posted by ruffin at 1/21/2015 09:55:00 AM
Tuesday, January 20, 2015

Today's headline is essentially the flip-side of a a post by Release Notes' Charles Perry that Marco shared yesterday. The post's conclusions on app revenue would make that Jump to Conclusions guy from Office Space proud:

In fact all apps above number 3,175 on the U.S. Top Grossing list produce enough revenue to at least make its developer [<<< sic!!] the United States household median income for 2014 ($53,891). And this is just for a single app.

Let's pretend his calculations are on the money. I don't know if they are or not. He mentions "Pareto distribution" with the requisite link to wikipedia, and, well, his n equals 1, so it seems legit. ;^)

It seems that even with the revenue curve tilted so heavily towards the big hits, the shape of the App Store still allows room for sustainable businesses to develop in the long tail. It seems that developers who work hard, mind the details, and treat their business like a business have a real chance of making it.

Really? That's Perry's take-home? "A real chance" as in, "if you don't buy a lottery ticket, you don't have a real chance to win"? I really enjoy Perry's podcast, but this post is horribly rose-colored.

Take another look. If your app is top 3175 for an entire year (which it very likely won't be, okay? Check out Exhibit Jared Sinclair), you'll have median income -- about what a reasonably experienced teacher makes, afaict. You know, those folks we always say are underpaid, largely because they deal with our brats day-in and day-out, but let's face it, unlike that of most key-masher engineers, it's not an unseemly, brat-resisting, over-the-top salary.

Again, that's iff your app doesn't fall out of the top 3175 for an entire year. Essentially Perry's pie-ish in the sky-ish numbers don't factor in app ranking churn. What you really need to know is how long it takes for an app like yours to drop from, say, 500 to 4000. I bet it's not very long.

Look, let's put this another way. Let's say that app ranking churn isn't nearly as volatile as I'm betting it is (even though, let's face it, I'm not wrong). If we define median income as our goal, if these numbers are accurate, only 3175 developers worldwide made median income or above on the app store for however long these revenue numbers are accurate. And that's only if each of those apps was written by a one-person shop! Perry does put in a caveat, implicitly asking, "What if you had two apps in there?!" Are we assuming they're both in the top 3175 for a year? Really? Guess what that means for the number of viable professional indie app developers?

And look, let's assume he does has access to non-median revenue fail apps. What did they make? Do they fit the curve and his predictions? What does our ranking over time look like? How long does an app sustain a "median wage" ranking if it breaks into the top 3175?

Each successful app on the way down but still in the rankings is another spot you can't get on your way up. I'd almost rather grow hand-cultivated wheat. Less risk.

These numbers are awful. That's not even a large company's worth of people. Ouch.

Good luck, everyone, in this year's hunger games. Or Thunderdome, or whatever your version of "scarce resources favor predators" metaphor is.

Labels: , , , ,


posted by ruffin at 1/20/2015 12:57:00 PM
Monday, January 19, 2015

The right way to make cash on an app.
  1. Table stakes (do what everyone else does)
  2. Killer feature
  3. Lucky launch
  4. Profit
William Wilkinson on Manual, an app that gives your iPhone's camera a "manual" setting on the dial:

I thought Manual was way too niche. How many people actually wanna set ISO on their smartphone? It never crossed my mind that it could exceed Everyday's launch. The launch went so unbelievably well. I was blown away. People were tweeting, the press was great, Apple featured it immediately. Shock and awe. I will toot my own horn and say that the video I made with Oliver helped a lot.
Manual was available for 100 days in 2014. Hereโ€™s my dinky:

95,621 total sales.
$123,413 USD total revenue (after Appleโ€™s 30%, before taxes).
$91,773 USD in the first month.

Yeah it was [screwed] up. In terms of up-front expenses it was minimal, the investment is our time. I spent $1,500 on the website, video, sound design, domain, and stock photos. There are no substantial monthly expenses.

Not sure what a "dinky" is, but it seems to have worked out fairly well. ;^)

Though it's worth mentioning he did have a decent team put together to get design done and marketing out. Looks like a little less than three months of work from "noticing" the new camera API to release.
Note that there's lots of luck making evident at that last post too. Timing and targetting new APIs is pretty important. Note that that's _David Smith's MO too.

I submitted Manual a week before the iOS 8 release from Phoot Camp over shaky wifi. Including a fun last minute sprint to create new screenshot sizes for the iPhone 6 and plus. We got lucky and Apple approved Manual a few days later.

Labels: , ,


posted by ruffin at 1/19/2015 11:11:00 AM

A happy coincidence of events -- that Unison, the far and away best OS X usenet client, went free and Google Groups' digest emails started working again -- has me using usenet a little again. Then last week, we have a sign of life on rec.games.video.classic beyond Robert Bernardo's C= user group announcements, which I admittedly do enjoy seeing, and posts by Bravo Sierra Computers, which I might find spammy iff there were real discussions going on. So that's awesome.

But when I go to sign in, of course Teranews, the "free" usenet server that only runs you $3 to sign-up and then promises free access for life, is down. Its web site, rarely updated since it appears to be the early 90s, even recognizes the outage, with a somewhat ominous message that there's no ETA on when it'll be back up. A quickly googled thread on alt.free.newsservers shows it's been down for a week or more.

Time for a fallback, at least for when Teranews is down (just in case it comes back up, which it apparently does each time folks think it's finally given up).

The Netherlands in particular seem to have a number of free options (a list that I haven't vetted is here), but it seemed somewhat convoluted and often not in English. The most popular option doesn't allow posting. I'm going to shy away from that.

I did bump into Astraweb selling blocks of access that don't expire, which looked promising. Since I'm just pulling text on two or three groups I feel nostalgic about, like rgvc, even 5 gigs should be nearly limitless access. I doubt I've pulled that much down from TeraNews -- heck, I wonder if I've pulled in 200 megs -- since I got it in 2007.

While searching for reviews of Astraweb, I ran into this post on reddit that was awfully helpful as it listed a number of block usenet vendors. Seemed time to find out what I could about buying blocks of usenet access rather than just checking out Astraweb in detail.

After stumbling over a usenet review site linked on slickdeals a while back looks like the most important consideration past price is expiration. Atranet, TheCubeNet and NewsDemon, eg, don't have an expiration on their blocks. NewsgroupReviews.com has a listing of blocks with prices that claims that all of the providers listed don't have an expiration here.

So then it's simply a quick check to make sure there isn't an expiration, and then a price-check for their smallest blocks. Here's my random sample.

TheCubeNet: 5 gigs for $3
NewsDemon: 10 gigs for $2
Astraweb: 25 gigs for $10

The NewsDemon FAQ says the block doesn't expire, and I've seen some not uncomplimentary reviews, so I think I'll give that a shot. We'll see what happens.

Labels:


posted by ruffin at 1/19/2015 10:12:00 AM
Sunday, January 18, 2015

After thirty years, my stereo amp finally gave up the ghost. I might eventually fix it; I really like its sound. But for now, toast.

Now that I have a new one, I've learned that having a separate component equalizer is a thing of the past. That's really too bad. And I don't have my new amp hooked up to a TV, which is what it apparently now uses to view all your equalizer settings quickly. Not to mention all but one preset assume you have four to five speakers. I should add I'm mostly using this as an output for my iMac and to play vinyl on my 30 year-old turntable (which now needs its own pre-amp for this amp to play it).

I really want to be able to reach over, grab a knob, push up/down a specific band, tweak the sound a touch, and go back to work. I don't want to have to hit seventeen different buttons in a row on my stupid remote (remote?! For a stereo amp? You've got to be kidding me) to make one album sound a little better.

Look, kids, sometimes nostalgia for the past really is remembering a better way of doing things. When we couldn't make everything work through the same digital interface, we could often access different settings with ease. /sigh

Labels: ,


posted by ruffin at 1/18/2015 08:00:00 AM
Saturday, January 17, 2015

I (he said uncontroversially) agree with Jon Skeet -- I'm not sure why converting to quoted printable isn't already in C# somewhere. It's old as the hills, and email seems it'll keep it going for the foreseeable future.

I've seen some wacky ways of trying to do this, but it's really pretty easy. There are only two rules. You take a collection of bytes, here an array, and in the first rule, you only have two options. Either it's a "normal" byte -- which is between 33 and 127, inclusive, but exclusive of 61 (decimal) (since that's the =) -- or it's not. If it is, you add its ASCII char equivalent to the string. If it isn't, you add = plus the string representation of the byte as a two-digit hexadecimal number.

The only monkey wrench is the second rule: that no line can be more than 76 characters, though apparently you can cut any line you want shorter than that. So if the line you're on is 75 characters long already & you're adding a "normal" or if it's 73 characters or greater and you're adding a "non-normal", you need to add a = to the end of the line to let a parser know it's not really a new line of text yet. Then you add (always) a carriage return and line feed. That is, in another [more complicated] manner of speaking, package your data with a \r\n between every 75 characters (treating =$$ as a unit) except, if you're encoding data that is text, you don't escape your new line bytes, and the counting starts from zero after a textual new line.

Simple, right? Either/or check for byte values with a line length check.

public static string ToQuotedPrintable(this string self)
{
    byte[] bytes = System.Text.Encoding.UTF8.GetBytes(self);
    StringBuilder sbInner = new StringBuilder();
    StringBuilder sbOuter = new StringBuilder();

    foreach (byte byt in bytes)
    {
        int charLenQP = (byt >= 33 && byt <= 126 && byt != 61) ? 1 : 3;
        if (sbInner.Length + charLenQP > 75)
        {
            sbOuter.Append(sbInner + "=\r\n");
            sbInner = new StringBuilder();
        }

        if (1 == charLenQP)
        {
            sbInner.Append((char)byt);
        }
        else
        {
            sbInner.Append("=" + byt.ToString("X2"));
        }
    }
    sbOuter.Append(sbInner);
    return sbOuter.ToString();
}

I think that's right.

QUICK NOTE: I am encoding in UTF-8 every time right now. You could take that in as a parameter (maybe with a UTF-8 default), but the best case would be to send in a byte array from the start, which would let you decide your own encoding, but then we couldn't use it as a fancy string extension. Though perhaps it should be a byte array extension, eh? That might make more sense.

Labels: , , ,


posted by ruffin at 1/17/2015 10:00:00 PM

Jared Sinclair responds to Marco Arment's Overcast sales numbers, and essentially says what I said in "Your App's Not Marco's", but gets a little closer to putting a number of what Marco's "Most Valuable Geek" (I read something like that somewhere else -- can't find it now -- but the intent was to say that there's a community of Apple coders that have a certain social cache. It's true, and it's A Good Thing. Who wouldn't want to get Gruber, _David, and Marco in a room to talk Apple? Maybe Gruber separately...) status buys him. Emphasis is mine.

His list of expenses leave out one really big thing: marketing. Through hard work and good fortune, Marco is a well-known figure among many of Overcastโ€™s potential customers. This kind of exposure would be expensive for the average indie developer to replicate. For example, ongoing discussions of Overcast on the ATP podcast (before, during, and after the 1.0 launched [sic -mfn]) would have cost thousands of dollars โ€” at least $3750 per episode. ... Promoted tweets that reach 78,000 interested Twitter followers wouldnโ€™t be cheap, either, relative to $160K in revenue. Overcast also benefitted from numerous conversational mentions from friends of Marcoโ€™s like John Gruber, who carry a lot of weight behind their recommendations.

That's a great point -- Marco has built enough "good will inertia" (sequel to Damon & Affleck's flick) that he can "advertise disproportionally" (my words) for $160k of revenue. That's a good point.

But, admittedly, as I read, Jared's full post sounded a lot like sour grapes, which is too bad. He admits as much, but I'm ultimately not sure he understands how apples to oranges Unread is to Overcast.

Perhaps Marco intentionally left out these things because he was worried about sounding like a jerk. Iโ€™m having a hard enough time writing this article as a third-party without sounding like a [punk], so I can appreciate the backlash he might endure if he mentioned them directly.

Look, Marco's earned the "free" advertising. The difference is that his advertising is a residual, not a capital expense. He's partially living (and I know he hates this suggestion, but he can't live in a vacuum; people like him) off the interest of the work he already accomplished. Marco's socially rich in the Apple community in a way a new app developer can't be.

There's no reason to think him mentioning his social richness would make him sound a like a jerk. "Admittedly, I have a number of 'innate' advantages when it comes to advertising, not least of which is the time it got on ATP. I can't say I feel badly about that, but it is an advantage..." No problems by me. It is curious he leaves the help he's gotten from his friends and community out, to the point he's at the very least not explicitly mentioning them, but we can't expect to have a career's worth of goodwill starting from square one.

More importantly, as I said earlier, Unread is not Overcast. Even within its target technology (RSS), Unread's not a mass market app. That's not really controversial. Jared says as much constantly, and goes so far as to use "comfortable reading" (iirc) in his marketing as his killer feature. "Don't triage RSS feeds, enjoy them," was the feel I got.

Guess what? It turned out that I don't want to read slowly and carefully when I'm consuming RSS (unless it's for Medium). I want to triage and skim and consume like crazy. I bought Unread. I liked Unread. But it's no longer on my iPhone.

Overcast does everything Downcast did, but with a slightly nicer interface, that Smart Speed (catchy, no? See?) feature where I don't waste time listening to silence (listening time is my scarce resource; I'm already listening at nearly 2x speed), and it even syncs beautifully when you log in on another device. Overcast is as good in conventional use cases as what came before it, and then, in a few key ways, better. As the designer of the failed .Mail app says, "[W]e need to deliver a[...] client that works at least just as good as [popular clients], otherwise it would be a step backwards. We just can't deliver something that isn't running at least at the same performance as the examples I mentioned above." Unread tries not to match the status quo. That's not wrong, but it's risky. So don't tell me a lot of Marco's success didn't come from a more successful market positioning. His app, I think, turns out to be conventionally competitive, and, in non-destructive ways, better.

My question, and ultimately probably Jared's too, if he gives it more thought, is what I would have had to do if I'd written Overcast instead of Marco to get the same success. That's an interesting thought experiment.

Labels: , , ,


posted by ruffin at 1/17/2015 10:15:00 AM
Friday, January 16, 2015

If I'm reading this right, Apple had to pay damages because it had secure p2p video connections </ full stop>.

VirnetX had alleged that two of its patents - registered in 2002 and 2009 - had been infringed by Apple's iPhones, iPod Touches and iPads.

These referred to ways to establishing a secure communication link between different types of computers using a protocol referred to as TARP (Tunneled Agile Routing Protocol). ... "Apple says they don't infringe, but Apple developers testified that they didn't pay any attention to anyone's patents when developing their system," a lawyer for VirnetX was quoted as saying by the Bloomberg news agency.

from here:

The VirnetX patents cover the use of a domain-name service to set up virtual private networks, through which a website owner can interact with customers in a secure way or an employee can work at home and get access to a company's electronic files.

Look, if you can invent the same thing without having any idea the other existed, I'd say you haven't [in any society run by common sense] run afoul of a patent. And unless this predated Gnutella (it doesn't, I don't believe), you've got prior art, right?

Apparently some of the decision was "vacated", but I think we can agree the patent system isn't optimal. I hate that I can't even understand the argument after reading two articles. When the crux of the issue is this obfuscated, you're just playing what Gygax called (iirc) "semantic gymnastics" at this point.

Patents in software drive me crazy. Too much is painfully obvious. I understanding protecting something limited and genuinely new, but this is insane.

(sourced from reddit)

PS -- Manatees? Sauce:

Cartman is introduced to the Family Guy writing staff, who turn out to be a group of manatees. The staff, who live in a large tank, pick up "idea balls" from a large pile of them, each of which has a different noun, a verb or a pop culture reference written on it, and deliver them, five at a time, to a machine that then forms a Family Guy cutaway gag based on those ideas.

Labels: , ,


posted by ruffin at 1/16/2015 02:49:00 PM

Marco Arment very helpfully released Overcastโ€™s 2014 sales numbers:

$164,134 total revenue after Appleโ€™s 30% but before any taxes or expenses.

The question for me is if we can really buy Marco's "take home":

For most people, the App Store wonโ€™t be a lottery windfall, but making a decent living is within reach for many.

It's difficult to jive his numbers with a fantasy-land where he doesn't write Overcast and you write exactly the same app instead. There is a natural advantage for him when it comes to marketing, because he has your ear. I can't recall what the Accidental Tech Podcast's numbers are (where Marco is a co-host), but it's easy to see that his blog has 23,000 readers on Feedly alone.
For comparison, Daring Fireball, a blog that also cites Marco with some frequency, currently has 105k on Feedly. That cuts both ways -- to show you how big 23k is, and to show you that Arment's app has a heck of a broad broadcast reach. Even if we pretend that everyone subscribed to Marco's feed is on Daring Fireball's, and even if we pretend 20% of those are duplicate or dead subscriptions, once you add non-Feedly subscribers back in, I bet you're still over 100k high-tech, Apple-using schmoes know about his app out of the gate.

And that bears out -- he gives you the numbers if you can open the calculator or have paper handy. August through December downloads are 139,760 total, and he's had 318,996 total downloads, so he had 179,236 downloads the first month. Let's call those "launch hype" (that's a good thing to have; no derogatory connotation on "hype" here) downloads.

It had a perfect launch that far exceeded my expectations โ€” it was the best launch an indie developer could possibly hope for, with tons of great press, a mid-level App Store feature, and thousands of tweets on launch day.

<envy />

I don't think an unknown could get that kine a launch. I just don't. I hope I'm wrong, but man, that'd take some hustling. I don't think Sinclair gave out downloads for Unread in his now famous post, and his numbers are a little wack b/c I believe he changed prices at one point (EDIT: Looks like about 2700 [obviously paid] downloads at launch at $3 each), and Unread is a paid app that doesn't play into the freemium model Overcast uses, largely because Unread is a new "experience" for RSS, not a solid-but-conventional RSS reader with a few killer features (which means Unread is not, I should say, playing into what I bet the formula to development success is).

If I was going to be blunt, I also don't think Unread has quite as much market appeal as Overcast. Overcast has some serious bugs -- I've had it display many and even download one latest podcast from feeds I've listened to and am not actively subscribed (no response yet from support@overcast.fm) -- but it's arguably a better podcast app bar none than, say, Downcast. I stopped using Unread a few months after I purchased. Even in the non-IAP state, Overcast is a pretty good app. I had to have faster playback speeds, so I shelled out the cash. It hasn't given me a reason to stop using it yet.

That makes Unread to Overcast as apples to oranges, which stinks, because if Marco has years of marketing build-up behind him, Jared marks, I think, high-water for self-promotion from zero. It's like what the Unicorn Free people keep saying (and proving): If you have a huge email list, success follows. Give people something to be excited about, make them feel like part of a community, and you'll have sustainable success.

But 179k downloads your first month, with a sustained conversion rate of over 14%? You're not getting that without a heck of a lot of marketing. Even if you had the same app quality, you're going to be hustling like Jared or more to sniff those numbers.

I just wish I knew how important that huge launch and sustained chatter (look, Marco posts sales numbers! Oh yeah, we remember that app. Let's try it again! Sales bump time!) is to keeping the long tail of your sales curve taller.

Labels: , , , ,


posted by ruffin at 1/16/2015 07:58:00 AM
Tuesday, January 13, 2015

Look Ma! More Perl!

;_; (aka QQ)

Again from Learn Perl in about 2 hours 30 minutes:

Unlike almost every other major programming language, Perl calls by reference. This means that the variables or values available inside the body of a subroutine are not copies of the originals. They are the originals.

You're killing me, Perl.

(Bee tea dub: Nice doc on perl command line options here -- -l is a nice alternative to say)

Padre fails

In the "won't behave" category, add still being unable to install Padre, the at least implicitly recommended IDE for Perl, with cpanm or cpan. And I'm not the only person. What a versioning mess.

Loops are fun!

And finally, this jive on loops is more note-to-self, but isn't especially intuitive either:

Basic C-style for loops are available too. Notice how we put a my inside the for statement, declaring $i only for the scope of the loop:

for(my $i = 0; $i < scalar @array; $i++) {
    print $i, ": ", $array[$i];
}
# $i has ceased to exist here, which is much tidier.

This kind of for loop is considered old-fashioned and should be avoided where possible. Native iteration over a list is much nicer. Note: unlike PHP, the for and foreach keywords are synonyms. Just use whatever looks most readable:

foreach my $string ( @array ) {
    print $string;
}

... and it's intuitive, too!

Remembering that $#array gets you the array's length (argh. Of course)...

If you do need the indices, the range operator .. creates an anonymous list of integers:

foreach my $i ( 0 .. $#array ) {
    print $i, ": ", $array[$i];
}

You can't iterate over a hash. However, you can iterate over its keys. Use the keys built-in function to retrieve an array containing all the keys of a hash. Then use the foreach approach that we used for arrays:

foreach my $key (keys %scientists) {
    print $key, ": ", $scientists{$key};
}

Since a hash has no underlying order, the keys may be returned in any order. Use the sort built-in function to sort the array of keys alphabetically beforehand:

foreach my $key (sort keys %scientists) {
    print $key, ": ", $scientists{$key};
}

If you don't provide an explicit iterator, Perl uses a default iterator, $_. $_ is the first and friendliest of the built-in variables:

foreach ( @array ) {
    print $_;
}

Packages are also fun!!!

Packages and modules are two completely separate and distinct features of the Perl programming language. The fact that they both use the same double colon delimiter is a huge red herring. It is possible to switch packages multiple times over the course of a script or module, and it is possible to use the same package declaration in multiple locations in multiple files.

You can see where this is going, right? I was already loling in my head at how convoluted the two concepts could get by this point. -mfn

Calling require Foo::Bar does not look for and load a file with a package Foo::Bar declaration somewhere inside it, nor does it necessarily load subroutines in the Foo::Bar namespace. Calling require Foo::Bar merely loads a file called Foo/Bar.pm, which need not have any kind of package declaration inside it at all, and in fact might declare package Baz::Qux and other nonsense inside it for all you know.

Likewise, a subroutine call Baz::Qux::processThis() need not necessarily have been declared inside a file named Baz/Qux.pm. It could have been declared literally anywhere. This next bit at the close of the packages/modules section is probably best considered a universally applicable tenet of programming:

However, it is important that you do not take [any convention or best practice] for granted, because one day you will meet code produced by a madman.

I don't think I'd ever recommend Perl for someone's first programming language. Probably not even their second (I think I'd recommend 6502 assembler (because you should know, in at least a simplest case, how the things work) and then C# at this point).

VIm is not the IDE you were looking for

And here's a VIm related issue...

I'm trying to trick to run :!perl % to run what I'm editing in a shell, but right now VIm is opening PowerShell, which is executing my PowerShell profile and dropping me into the directory I have PowerShell navigate to by default. When it's there, it can't find the file in %, of course.

I tried Open Perl IDE, which was rough running, and there's some sentiment that it's not working well with recent versions of Windows.

Seems it would be insanely easy (all things considered) to write a Perl IDE for Windows (and Open Perl IDE had 131 downloads from SourceForge (that we know is evil now) this week) since Perl provides its own debugger for goodness' sake, but it doesn't appear to have been done.

WHAT A PAIN.

Others feel your pain.

from here, and the first quote is from the page's author:

2014-09-08 09:20:04 by Sam:

PHP is a minor evil perpetrated and created by incompetent amateurs, whereas Perl is a great and insidious evil perpetrated by skilled but perverted professionals.

... or, better yet...

2014-09-07 05:37:51 by David Mitchell:

Dear God. Not[e] to self: never use perl.

Labels:


posted by ruffin at 1/13/2015 08:26:00 PM
Monday, January 12, 2015

Again from Learn Perl in about 2 hours 30 minutes:

Perl variables come in three types: scalars, arrays and hashes. Each type has its own sigil: $, @ and % respectively.

I started dropping which was which in my head trying to remember what the character was for grabbing a reference, and decided it was mnemonic time.

Taking a closer look, the mnemonic's already there, and is painfully obvious. For those even thicker than me (ha), they are...

  • $ is for S is for Scalar.
  • @ is for A is for Array
  • % is for, well, I guess H, since it's Hash.

Okay, the "H" isn't quite as obvious as it could be (# would probably be more obvious, and then we could use // for comments, but I digress ;^D), but the first two are as obvious as they can get.

Duh.

And as for grabbing references, that's a backlash. Because why not, I guess.

A reference is created using a backslash.

my $colour = "Indigo";
my $scalarRef = \$colour;

Any time you would use the name of a variable, you can instead just put some braces in, and, within the braces, put a reference to a variable instead.

print $colour; # "Indigo"
print $scalarRef; # e.g. "SCALAR(0x182c180)"
print ${ $scalarRef }; # "Indigo"

As long as the result is not ambiguous, you can omit the braces too:

print $$scalarRef; # "Indigo"

or, for an array...

my @owners = @{ $ownersRef };

In other news, I was trying to install Padre with Strawberry Perl, and was following the apparently years old suggestions to the letter. After trying to cpanm Wx, however, I got this encouraging bit in a failed build log:

Test Summary Report
    t/04_userdata.t (Wstat: 0 Tests: 65 Failed: 0)
    TODO passed: 25, 33, 41, 57
    t/14_eh_die.t (Wstat: 0 Tests: 5 Failed: 0)
    Parse errors: Bad plan. You planned 6 tests but ran 5.
    Files=23, Tests=772, 444 wallclock secs ( 0.27 usr + 0.34 sys = 0.61 CPU)
    Result: FAIL

This sort of thing has apparently happened before and may be some sort of version conflict.

Idk, when I was using fink on OS X (I think brew is the more popular package manager for OS X now), I never hit this sort of issue, which was pretty impressive. You'd think Perl on Windows would be popular enough to be pretty painless. It's not.

Labels: ,


posted by ruffin at 1/12/2015 09:19:00 AM
Friday, January 09, 2015

Having managed to dodge Perl for the first 16 years of his professional life, somehow, in 2014 [sic], it creeps up and takes him down. "Just when I thought I was out..."

From Learn Perl in about 2 hours 30 minutes:

Perl has no boolean data type. A scalar in an if statement evaluates to boolean "false" if and only if it is one of the following:

undef
number 0
string ""
string "0".

The Perl documentation repeatedly claims that functions return "true" or "false" values in certain situations. In practice, when a function is claimed to return "true" it usually returns 1, and when it is claimed to return false it usually returns the empty string, "".

RLY Perl? You return truth and falsity with two different paradigms? `string "0"` is *false*?

We're not getting off on the right foot. And I thought I'd unfairly disliked you for all these years.


Backstory:

Third party to direct client and me (and a few others):

I'd like to engage [mfn author] and have him work the actual deployment there. He can also help us troubleshoot scripting issues that arise when the scripts run and work directly with [Perl guy] here. How is [mfn author's] Perl scripting?

My reply to direct client:

> How is his Perl scripting?

Unfortunately nonexistent.

"Unfortunately" said not wholly without jest, unfortunately.

Direct client says:

Oh, I am sure you will be great at it. :)

;^D And with that, we start installing Strawberry Perl and reading free intro books. YAY.

Labels:


posted by ruffin at 1/09/2015 09:24:00 AM
Wednesday, January 07, 2015

From this StackOverflow answer:

P.S. Unfortunately, it is not that easy to simulate break in ForEach-Object.

Argh. That's putting it mildly.

Here's some slightly amended code from the StackOverflow question to which the above quote is responding:

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

ForEach (1..100 in $range){
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

Guess which one works and which doesn't?

The crux seems to be that the ForEach loop, where ForEach leads the line, is a conventional loop, but the Foreach-Object that you so commonly see with the % shorthand, but also when ForEach is used ANYWHERE other than the start of a line is a "looping cmdlet" that's going to freight-train from the start to the finish of your range.

How complicated is this? Check this blog post (that I stole the phrase "looping cmdlet" from)...

First it is important to understand that foreach -ne foreach-object unless used in the pipeline.

(emphasis is mine)

ForEach should be ForEach, if you ask me, but luckily nobody did, as I think the point is that when you're mid-pipeline, there's no real way to refer to what came down from the pipeline into your command, afaik.

That is, this is meaningless...

1..100 | ForEach ($myInt in $PIPELINE_VALUE) { if ($myInt % 2) { $myInt } }

... because $PIPELINE_VALUE doesn't exist. Pipeline values are only implicitly sent along -- unless, of course, you're pulling them up as a parameter in a module or function. Creating a $PIPELINE_VALUE variable, though horribly useful for .NET-threaded heads like mine, would break [2] PowerShell's useful pipeline paradigm.

The limitation with the lack of break, of course, is that you can't do this...

1..100 | % { if ($_ % 2) { $_ }; if ($_ % 30) STOP_EVERYTHING!!!1! }

You're going to run through all 1 to 100, afaict. breaking here would drop the whole pipeline clanging on the floor. And that's my beef, I guess. Why not change the usage of break in this specific context? It seems to be the exception that would prove the [usefulness] of the [general] rule.

So to ForEach proper, you need a two-stepped process (or you have to use a Where-Object cleverly as described in the SO answers, above).

Fun. I should rename this blog "PowerShell Commando".

[2] Okay, literally that's wrong. It would "encourage breaking from PowerShell's pipeline paradigm for a more .NET-ish one".

Labels:


posted by ruffin at 1/07/2015 10:33:00 AM
Tuesday, January 06, 2015

From a reddit on PowerShell:

Can you show the code that you are using? It sounds like you are using [pscustomobject] in V3+ which turns into the System.Collections.Hashtable object you are seeing in V2.

Argh.โ€‹ In my case, yes, yes that is what happened.

PowerShell -- Almost a fun scripting language.

Labels:


posted by ruffin at 1/06/2015 05:32:00 PM

Coming from JavaScript, it feels like adding a function to a hashtable in PowerShell shouldn't be that complicated.

Took a bit of googling, but it's not. For PS 3+...

$h = @{ spam = "spiced"; ham = "green" };
$obj = [PSCustomObject]$h | 
Add-Member -MemberType ScriptMethod -Value { $this.spam } -Name foo -PassThru

Create hashtable. Cast to PSCustomObject (this is the same as saying New-Object psobject -Property $h, which I might end up preferring for backwards compat -- and for the consistency of using a cmdlet). Pipe to Add-Member where you add your .NET method. Profit.

You can kinda tell it's [C#-ish] .NET because you're into the this paradigm inside of the scriptblock.

Want a function with params? Can do.

$h = @{ spam = "spiced"; ham = "green" };
$obj = [PSCustomObject]$h | 
Add-Member -MemberType ScriptMethod -Value { $this.spam } -Name foo -PassThru | 
Add-Member -MemberType ScriptMethod -Value { 
    param( $first, $second) 
    "first is $first, second is $second" 
} -Name DoStuff -PassThru;

That adds two methods, the second with params.

Calling the function is also painfully .NET-ese, right back to the parens that your first few weeks of PowerShelling taught you to avoid (original, pre-fiddled sauce):

PS> $obj.foo()
spiced
PS> $obj.DoStuff("first", "second")
first is first, second is second

I really dislike how that paradigm shift (from PowerShell modules to unabashedly .NET functions) operates, but I guess the point is that a Verb-Description is never going to belong to an object. They are tied to no specific noun. Yet PowerShell still feels a little like a kludge that too often gives up and dumps its problems directly into .NET. That's good, since I'm a .NET programmer, but bad in that PowerShell feels imcomplete and kludgey, and its culture (I think even the new-as-of-PS 3 [PSCustomObject] cast is kludgey, and that's straight from the Scripting Guy as the preferred method of doing this) is wrapping itself around these sorts of kludges instead of pushing forward PS development and best practices.

Maybe dumping into .NET is good. Idk. But it's sure grafty, grafty like the Tree of 40 Fruit.

Thanks to these links:

Labels: ,


posted by ruffin at 1/06/2015 01:54:00 PM
Monday, January 05, 2015

As a recently relapsed cord-cutter (as in, I'm subscription free again), today's CET announcement by Dish about Sling TV (ESPN, Disney, TNT, TBS, et al for $20 a month) will probably suck me back in to spending some "cable" cash, at least during the NBA playoffs and the NFL season.

I bagged a ChannelMaster DVR+ as part of my cutting, and though it's a little steep ($175 even on deep Black Friday discount), it's wonderful. I don't have the WiFi module, so no streaming services or "extended scheduling", yet just the stock DTV OTA schedule metadata is enough for it to feel like I'm back in subscription-supported DVR land. (And, admittedly, I enjoy almost everything MeTV is playing -- most everything but MASH and a few others.) There's absolutely no reason to shell out for TiVo if you're OTA.

So it's humorous to see the boilerplate Sling TV announcement include...

Also missing are broadcast networks ABC, CBS, NBC, and Fox. According to Sling TV CEO Roger Lynch, thatโ€™s because most consumers are able to find programming from those networks elsewhere โ€” like on Hulu, for instance.

When did Hulu become the go-to source for network TV? Do these folks even use a TV set? I can understand if you're on your laptop/mobile device all the time -- as many college students seem to be -- but once you're employed, I've got to think most shell out for a real tube. Sometimes I think my 32"er puts me in the television hardware poorhouse. And doesn't Hulu have nearly unskippable commercials? Seems it'd be worse than a good DVR and [great] OTA antenna.

In any event, now that we're getting real "Over The Top" options for internet TV that don't include the big four, I'm hoping this means those major networks keep investing in OTA infrastructure. They've benefitted enough from the "must carry" laws, and are have probably been running a bit of a deficit in the "public service" category for a while now.

Labels: ,


posted by ruffin at 1/05/2015 07:50:00 PM

What? You're trying to open an html file with Excel that you just made from scratch [with another app]? Why would you do that? Well, for one, this seems to be the preferred way to use PowerShell to make Excel files when you want to control colors and other UI-specific information along with data. It's kind of strange that, when one of PowerShell's most useful group of functions is centered around creating and manipulating CSV files, you wouldn't have similar modules for Excel, but fine (Scripting Guy says it's because of Excel's heavy use of COM [1]).

So you get a nice html template, you have PowerShell data-i-fy it all up, you go to Excel to open it, and then BAM.

"You are attempting to open a file type (Web Pages and Excel 2003 XML Spreadsheets) that has been blocked by your File Block settings in the Trust Center."

Wait, what? The Trust Center? I mean, I understand when I have to Unblock-File on ps files I've sourced from the Net, but an html file I created (yes, "all by myself") in SeaMonkey [sic]? RLY, Excel?

You can follow the instructions and wade into your Trust Center to say, "Forget it. I want Excel to open ANY html file, whether I created it or not, in spite of the fact that you could've just checked to see if I'd created it and only stopped me if things were much more suspicious." Or you can try to do that and find your company's IT dept has made it impossible to change your Trust Center settings even though you have admin access for nearly anything else.

Well, if you find you're in the second group (the one with not exactly consistently enforced IT security policies) there's a workaround: Put your html file that you want Excel to open in a "trusted location". Who knew? Good thing the bad guys will never learn where the locations are and exploit this extra trust.

Here's a list of Trusted Locations for Excel 2010 -- a list that will look like crud until I monospace it (sauce: [3]):

Default trusted locations                               | Folder desc    | Trusted subfolders
--------------------------------------------------------+----------------+-------------------
Program Files\Microsoft Office\Templates                | App templates  | Allowed
Users\user_name\Appdata\Roaming\Microsoft\Templates     | User templates | Not allowed
Program Files\Microsoft Office\Office14\XLSTART         | Excel startup  | Allowed
Users\user_name\Appdata\Roaming\Microsoft\Excel\XLSTART | User startup   | Not allowed
Program Files\Microsoft Office\Office14\STARTUP         | Office startup | Allowed
Program Files\Microsoft Office\Office14\Library         | Add-ins        | Allowed

If you're not using 2010 or are looking for something similar for Word, etc, that info is here: http://support.microsoft.com/kb/922849

And voila. Look at that html file you painstakingly created and filled with data, now mangled beyond belief by that bastion of spreadsheet integrity, Excel! You're welcome. ;^)

[1] http://blogs.technet.com/b/heyscriptingguy/archive/2014/01/10/powershell-and-excel-fast-safe-and-reliable.aspx [2] http://technet.microsoft.com/en-us/library/hh849924.aspx [3] http://technet.microsoft.com/en-us/library/cc179039(office.14).aspx

Labels: ,


posted by ruffin at 1/05/2015 01:11:00 PM
Friday, January 02, 2015

I'm not sure how I was supposed to tease it out of the help for Sort-Object, but you can apparently just put in a script-block for Sort-Object that'll operate on each object in an array just like any other pipeline function. Painfully difficult to google up, for some reason, though I eventually found it here:

So if I have an array of hashtables called $ahash, and each hashtable in $ahash has a property called `name`, I can just do this...

$sorted = $ahash | Sort-Object { $_.name };
$sorted | % { $_.name };

Voila.

Maybe that's in the help, but I didn't see it. There some script block stuff described under `-Property<Object[]>`, but that's specifically for the Property param, which isn't exactly what I think we want. Lots easier than writing an IComparer and .NETting around your elbow to get back to your nose. /shrug

(I don't know why the p tags are so goofy in this post.)

Labels: ,


posted by ruffin at 1/02/2015 10:58:00 AM

<< Older | Newer >>


Support freedom
All posts can be accessed here:


Just the last year o' posts:

URLs I want to remember:
* Atari 2600 programming on your Mac
* joel on software (tip pt)
* Professional links: resume, github, paltry StackOverflow * Regular Expression Introduction (copy)
* The hex editor whose name I forget
* JSONLint to pretty-ify JSON
* Using CommonDialog in VB 6 * Free zip utils
* git repo mapped drive setup * Regex Tester
* Read the bits about the zone * Find column in sql server db by name
* Giant ASCII Textifier in Stick Figures (in Ivrit) * Quick intro to Javascript
* Don't [over-]sweat "micro-optimization" * Parsing str's in VB6
* .ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); (src) * Break on a Lenovo T430: Fn+Alt+B
email if ya gotta, RSS if ya wanna RSS, (?_?), ยข, & ? if you're keypadless


Powered by Blogger etree.org Curmudgeon Gamer badge
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity.