Writing About Things

I pushed v1 of a little project up to a new puddingbowl subdomain this evening.

Things is meant to scratch a few itches:

I’ve been meaning to redo this site as “a real site with a blog attached,” I’ve been wanting to not use WordPress anymore, and I’ve been wanting to write things that aren’t blog entries. Things only satisfies one of those criteria right now: It’s not WordPress. There’s not much content on it at all, and of what there is, one of the things is a repurposed blog entry.

Anyhow, here’s why I think I’ll like working on it:


I wanted to start writing reviews about things, and I wanted to try to do better than a lot of informal reviews I end up stumbling across when I’m trying to learn more about something. When I was trying to learn about standing desks a few years ago, I kept coming across all these reviews written from the perspective of someone who’d been using one for all of a week. That didn’t help me understand the products, and it really didn’t help me understand what you’re in for with a standing desk.

Reviews in general are kind of like that. I’m really thrilled when I come across one where the writer is circling back on their initial review months later, or simply hadn’t bothered to write about the thing at all until they’d had some time with it. Most of the time, though, you’re getting initial impressions. It’s that way from commercial sites because they’re under pressure to have those early impressions out. It’s that way for personal sites because people mostly seem to want you to know they have a thing, and they don’t often care to come back around and note that it was a bad purchase, or that it broke and they threw it out, or that they just don’t care to use it anymore.

So, the core of Things will be me trying to improve the situation for anyone considering purchasing a thing I happen to own and wanted to write about: I want to come back to each one I write later on and offer the long-haul perspective on it.

The reviews I write get a score based on how long I’ve owned a thing. So, for instance, my initial review of a Schwinn Loop folding bike—which I’ve owned for less than a month—gets a lower score than my review of the Quicksilver, which I’ve owned for just shy of a year. I could figure that out for myself and periodically update the site by hand, but it’d be better to automate that.

That’s where the backend comes in:


I was introduced to Jekyll as “a static site generator built on Ruby.” If you’re like me and maybe had several Rails projects behind you when you first heard that, you might think things like “oh, cool … I bet it uses ERB or maybe even HAML for templating!” and you got a little excited over that.

Jekyll very pointedly does not use Erb or HAML, and after almost three years of coming in and out of contact with it in a professional capacity, I don’t really want much to do with it in my spare time.

Middleman is a static site generator built on Ruby, and it does use HAML right out of the box, along with a few other pretty nice things. I’m having fun with it.

One of the ways I’m taking advantage of having HAML templating is in the review templates: I put the date I bought something I’m reviewing in the metadata of a review, and that changes the visual presentation of the review. So that Loop review has a red warning box, and the Quicksilver review is a sooting blue.


So, that’s that. The reviews (such as they are) are the featured content. I’d like to move some of my tool- and things-related blogging from here over to there, and I’d like to start building sections about tools I use a lot over time.

I also want to use what I’m learning building out little features here and there to redo this site. That’s gonna take some time because I don’t want to use somebody else’s pre-packaged theme.

That’s kind of what got me on this kick: The theme for this blog dates back to when Blueprint was the new hotness in CSS frameworks. Someone bundled it up for WordPress, I poked at it a little to personalize it, and here we are. I’d like to climb just one level up the ladder, to using Bootstrap and HAML to put things together. Still someone else’s CSS framework and all the rest, but it’s just a hair bit more I can play with.

There’s a Human Tooth Where ESC Used to Be, or: Playing With a Chromebook

We have this promotion going on at work where you can get a free Dell Chromebook if you buy a Linux training course. I got a sample of one of the Chromebooks and did a little writeup about putting a minimal Linux distribution on the machine via Crouton.

Crouton’s kind of neat: If you stick your Chromebook in developer mode, it opens up a minimal Linux environment you can further leverage to install a full Linux distro on the machine, side-by-side with ChromeOS. You can choose Debian, Ubuntu, or Kali.

My writeup was about Crouton’s two CLI environments, which let you run a “real” Linux without the overhead of a GUI. The Chromebooks are there to help you learn Linux so you can get a certification, and the certification testing is conducted in a command line environment: Might as well get used to it.

The initial writeup was supposed to be about booting the machine into Linux from an SD card or USB stick while retaining ChromeOS. This particular model doesn’t allow that, and there was the looming possibility of screwing up the instructions and bricking the machine; or, worse, doing a fine writeup of the process that would lead someone less careful to blithely brick their own machine. So I settled on “minimal CLI Crouton.”

Anyhow, a few thoughts:

Dell Chromebook 11 (Hardware)

It’s not bad! It weighs a bit over three pounds, has a comfortable, spill-resistant keyboard and a rubber bumper. It feels very sturdy and substantial.

The one thing I’m not a fan of is the 11″ display. I thought maybe it was me being spoiled by Retina displays, but it’s not: I compared it to an 11″ MacBook Air and it’s simply not that crisp. That’s exacerbated by Chrome/ChromeOS, which doesn’t have great type options and tends toward tiny type in the UI.

The keyboard is a little different from standard laptops. Google preferred to pretend function keys don’t exist, so you don’t see any “f1” etc. on the function key row. It’s all ChromeOS functions (forward/backward, reload, fullscreen, tile windows) and standard laptop controls (volume, brightness). On the other hand, those really are function keys. When you have the machine in developer mode, you can switch between ttys with them.

In lieu of a caps lock key, there’s a search key. In ChromeOS, it toggles a popup Google search/Chrome app launcher. There’s no command or Win key, either, so you get an extra large ctrl key. That’s a bit cruel to an Emacs user: ChromeOS uses the more Win-style “CUA” bindings, so I spent a day or two using that giant ctrl key to accidentally “select all” rather than going to the beginning of a line.

When I compare the machine to the ASUS eeePC I bought several years back, it’s very nice. The keyboard is superior, the machine is more rugged, there’s more RAM and storage, and 11″ of screen feels capacious compared to the 8″ the eeePC had. It’s way easier to imagine this machine filling the use-case I had in mind when I bought the eeePC, which was “portable text-editing rig.”


I was a skeptic going in, but I gave myself a few days to try it out, and I’m beginning to get it. I can’t honestly consider adopting a Chromebook as my sole machine, but I can easily imagine taking one along for business travel in a Google-centric work environment.

I think squeezing the iPad into work situations made me more amenable to the Chromebook. You have to adopt a certain minimalist mindset to deal with their respective UIs. Pick up that skill for one, you can slip into it with the other.

So by the end of a week with ChromeOS, I was “just working.” The basic Google apps don’t change at all. There are a few things you can have on a Chromebook that you might be used to on a desktop machine in one form or another. A few things I found:

  • There’s an Evernote app that works pretty well. I think it’s an Android app running on top of ChromeOS, so it doesn’t behave like a browser window. Drawback: Tiny type you can’t adjust.
  • There’s a web-based Markdown editor called StackEdit that’s really well done. It can save to Google Drive, GitHub, WordPress, and Dropbox. It has a live preview and different themes. I’d have no qualms using it on the road.
  • Skype and Slack have web apps that serve for the basics. Combined with Google’s chat app, I had a way to talk to everything in use at work.

I came to appreciate the limitations by the time it was all over. I could do just enough and not much more. We can take it as a measure of how much I liked it that I considered spending the computer allowance work gave me on a Google Chromebook Pixel. Knowing I could put Crouton on it to have a development environment was encouraging, but two things stopped me:

  1. Having a Chromebook in developer mode is a little inelegant. The manufacturer does not want you to do that, so every reboot greets you with a warning and you have this nagging sense that you’re an automatic update away from the stuff you did to build a Linux environment being taken away.
  2. I was able to actually play with a 12″ MacBook, and it won me over. I couldn’t find a Pixel anywhere in Portland, so it never had a chance to make its case in person.

In its own way, ChromeOS is Google making a comment about computing the way Apple does with iOS: Both would like you to consider the hardware comfortable and pleasant to use, but neither want you to really care about the hardware. They want you to care about the experience the hardware delivers.

“But Mike, Apple is a hardware company!”

Yeah, yeah … right. They sell hardware. I get that. But they don’t want you to care about what’s inside the case in terms of RAM or clock speed or whatever any more than it takes to help their customers spend as much as they’re willing to optimize the experience.

Google’s approach is maybe more utilitarian:

They want you to realize you could take your Chromebook down to the river, toss it in, order up another one from Amazon Prime Now, have it delivered to your door two hours later, and pick up where you left off. Where the most sensible accessories for MacBooks are padded cases and maybe a grippy protective shell, I can imagine a future where the most common accessory for a Chromebook is a little holder for disinfectant wipes so that people can swab down the one they found left out on a picnic table at a public park or next to a puddle of vomit in an Old Town alley.

“How’s that Chromebook you found?”

“Oh, it’s cool. The last guy replaced the escape key with a human tooth and there was some hair and clotted blood in the USB port, but when’s the last time I needed a thumb drive, right?”

“Ha ha right … Hold it … a tooth for an escape key? Oh shit! That’s the one I lost after Murray’s bachelor party last year! Man. Wonder where the hell it’s been?”

“Oh yeah? No telling. Want it back? I found another one out on the curb this morning.”

“No, it’s cool … that one was just my clubbing one.”

Okay, that’s overstating the case. Dell just released a 13″ Chromebook that could properly be called a “business machine.” Nobody’s IT department will be happy if one ends up in the river, or left on a playground. But the i3 model is pretty inexpensive and very capable. The i5 model is pricier, but I bet it’s way less frustrating than a comparably priced Windows machine in terms of perceived speed. Maybe more importantly to an IT department, nothing lives on those things. So if one does end up in the river or gets left behind on the bus, it’s a little less costly to replace the hardware and the data doesn’t have to be “put back.” Just pull one off the shelf and slide it across the counter.

Linux on the Dell Chromebook 11

I was feeling a little poorly over not being able to try out the (somewhat scary) “flash a more flexible BIOS, turn it into a ‘real’ Linux machine” approach. I mean, it wasn’t something I wanted someone trying on my advice. I felt poorly about it because my curiosity was thwarted. I set about to fix that this morning and it was pretty simple!

  1. Pick the distro you’ll want to stick on the machine. I went with Ubuntu and used the UNetbootIn app to burn the install CD to an SD card.
  2. Crack open the case and void your warranty to remove a write-protect screw that’s designed to keep you from overwriting the BIOS. See the picture above for where to find it. Put it somewhere safe, I guess, against the day you want to restore the Chromebook to its pristine state. I kind of love the physical hardware aspect of the operation. There’s something deeply psychologically unsettling about knowing the machine you’re holding is literally missing a screw that you took out of it. I like to think they settled on the write protect screw after testing “make the user crush an ampule of cat urine over the keyboard” and deciding that sourcing cat urine would be cost prohibitive.
  3. Put the machine in developer mode. There’s a little fussing and it takes some time (they just don’t want you to do it), but it’s a simple process. At this point, if you reboot the machine it will give you this warning screen and provide you with an easy way to get back to “normal.” The screen is a clean white, and there are no sounds of screaming at bootup because that would be bad UX, and because it reduces the chances of you loaning the machine to someone who literally just taps the spacebar at boot to wipe out all your customizations.
  4. This step is dangerous. If opening the case and removing a screw didn’t deter you, this is probably the best point at which to pause and ask whether turning your Chromebook into a dedicated Linux machine really matters. Download and run a shell script that flashes the ROM with SeaBIOS. That will allow you to boot from a USB stick or SD card to run a Linux installer. With the Dell Chromebook 11, this is an all-or-nothing situation. Some Chromebooks let you dual-boot between ChromeOS and something else; this one does not. If you screw it up, you’ll risk bricking the machine.
  5. Reboot from the SD card or USB stick you burned in step 1 and run the installer.

Once you do all that, you have the basics: The machine boots into Linux, can talk to the network, and generally “works.” There’s no sound, the trackpad is sort of weird, and the special function keys don’t work. You can take steps to fix all that. Here’s a starting point, none of which have I attempted: I made the machine boot into Linux because I wanted to see what would happen.

What happened was pretty anticlimactic:

rbenv, Emacs, zsh and git are all just an apt-get install away. GNOME is GNOME. Firefox was just sitting there, and I used it to Google “john lewis restore chromebook stock ROM.” Then I downloaded the script I used to flash a new BIOS in the first place, told it to please restore me to the stock ROM, and let the Chromebook boot into its recovery media (which I made last week before I started screwing around with the machine). I’m pretty sure I’m not even going to bother putting it into developer mode again, and I need to put that screw back in place once it’s done reinstalling ChromeOS … It’s sitting there on my desk bothering the hell out of me.

Linux Anywhere But the Dell Chromebook 11

Why not even put it in developer mode?

Because I don’t have to. I’ve already got two Linux machines (one I own, one I rent via shared hosting). It turns out Chrome has pretty decent ssh client you can use, and I did for the week I was playing around with ChromeOS and doing that writeup. Stick the ssh client tab in full-screen mode, and it looks about like the ssh client in full-screen mode looks under GNOME, Unity, OS X, or my iPad.

The thing you get from developer mode/Crouton is offline availability of that more robust environment. If I traveled a lot, that would matter. As it is, I’m almost never disconnected. If there’s not Wi-Fi, my phone is a serviceable hotspot. The “little CLI workstation with Emacs and Ruby” is never far away. It just happens to be running somewhere else. That’s kind of the point of the thing.

You can’t say what you are, but you should try anyhow.

I say ‘I consider myself a feminist,’ because I really do. But I always feel like I’m taking a big risk when I say ‘I AM a feminist,’ because there is always, always some other feminist out there who will show you that you’re wrong. Usually they’ll also show you that you’re awful for it.
— Someone somewhere I visit regularly

Another feminist here. That’s an understandable sentiment.

Personally, I hate calling myself anything at all, ever. I spent four years trying to reconcile what I thought I was, what I wanted to say to people I was, what I wanted people to think I was underneath, and what I wanted to be with what I was being every single day by just waking up where I was waking up and doing what I was doing.

I spent even more years after that trying to work through whether I’d ever known or could ever know what I was: Maybe I’d stopped listening to my better angels. Maybe the better angels had never been real. Gandhi had suggested that nonviolent behavior could be motivated (and tainted) by cowardice, so I wondered to myself if what I’d thought had been a nonviolent worldview hadn’t actually been a sort of cowardice, and that by enlisting maybe I’d just embraced what I’d always been.

Some understandings about myself and the world around me crystallized, some things just got more complicated:

Could I jump out of an airplane at night? Yes. And for the last year I was jumping out of airplanes, it’s fair to say I was frightened every time. By the time I got to that point, I’d healed up a lot. I wasn’t who I’d been when I walked into the recruiter’s office: If the controlled environment of the army had been a splint or a cast, it ended up setting my bones into shapes they hadn’t been before I enlisted. So I gained some understanding of what it is to be deeply afraid and yet still do the thing you set out to do. For a period, living that pattern allowed me to say to myself that I wasn’t a coward, that I had a core I could depend on. So I started looking beyond where I was, and having thoughts about what could be next, and wanting it. I didn’t want to give up and disappear into the army.

Then I was out, and rather than going back to be near the people who had cared about me and supported me while I was in, I chose somewhere else. I couldn’t just go back to where I had been, among people who might have been tempted to say, “well, that’s all over now and you’re back.”

I was loved and cared for, but not a lot of people knew me. They just had the biography, and that question of cowardice was still very real, and was suddenly unresolved again because I figured out that physical courage isn’t moral courage. So, I wanted the new people in my life to know something more about me than where I’d been, but I was still struggling with what it was I’d want them to know, and if it was possible for there to be anything more to know. After all, there was what I thought I was, what I wanted to say to people I was, what I wanted people to think I was underneath, and what I wanted to be, but there was what I had been every single day for four years by just waking up where I was waking up and doing what I was doing:

I’d been the guy who got sent to the chaplain because he wouldn’t sing the baby-killing cadences, and then invited to declare himself a conscientious objector. Didn’t do it, though, because I wasn’t. I just didn’t like baby-killing cadences.

I’d been the guy whose boss told him he should seriously consider taking a subordinate into the woods to beat him up, and briefly wondered if it would need to come to that, then learned how to make anger and its energy palpable; maybe to help avoid taking that step and maybe to make it easier if I had to.

I’d been the guy who told a barracks bully that I’d take an eye or an ear, and needed to believe it.

I’d been everything that environment demanded of me, and I chose to stay in it.

I nearly started typing, “but in the end,” because that would allow this to be narrativized and resolved. But there’s no end because I’m still sitting here typing. There’s an ever-unfolding now that I needed to learn about.

There were all the moments where I looked back on some of the things I said and did and hated them. When I’d tell stories about things I’d seen or done and I’d realize people were repelled by the mere fact that I’d been there to see them. There was the year where I needed to get help because I’d see a picture of a maimed child in an Iraqi marketplace bombing, or read about a murder-suicide on an army post from some solider who’d come back from the wars changed, and I’d think about how I’d wanted to be some part of that, and that’d be it for the day, stopped by anger and grief. I’m so glad I worked at home: I don’t know what I would have done with people around when those moments came. Maybe I would have just swallowed it whole instead of composing some polite fiction of a status message and going to sit in my room.

Then there was just more life, and a slowly growing recognition that I couldn’t ever un-be those things. When he was little, Ben thought I’d once been a knight. It was heartbreaking to explain that I hadn’t been. But it was strengthening to realize that the more truthful I could make myself be with him, the better a parent I could be to him.

I figured out that I had to start being the person I wanted to be in that ever-unfolding now. I had to accept that some people would see the biography and think things they’d be justified to think, and that I had to set that aside: There’s no erasing it, and to erase it would be to erase me. Instead, I had to learn how to be open to the things that I can hear and feel are right, and accept that they might be incongruous with what I’ve been.

Because of all that, because I once set aside everything I said I was and became something else, and because I then spent years trying to make all of that make sense, I’ve got a deep aversion to saying I’m anything at all. To the extent it’s any of my business how people talk about themselves or what they are — and it almost never is — I wish there’d be less “speaking as a …” and more “because I live my life thus.”

At the same time, self-identification helps people, right? It helps us hold each other — and ourselves — accountable.

I read bell hooks’ Feminism is for Everybody where she writes “the soul of our politics is the commitment to ending domination,” and I thought to myself “yes, that’s right, I want to live that and teach my son that.” I put down the book and thought “I agree with her, and other people who call themselves feminists,” and then I felt okay saying “I’m a feminist.”

Despite my aversion to saying “I’m this” or “I’m that,” I think “I’m a feminist” is a thing worth saying.

Because I’m a man, steeped in this culture and taught habits of thought that are anti-feminist, I’ll sometimes do things that aren’t feminist things to do. I’ve been lucky to have people in my life who have been gentle and patient with me when I’ve done this. Some day I’ll meet someone who won’t be as kind, or who will want to prove that I’m not a feminist at all. Depending on who that comes from, that could be upsetting or embarrassing.

The alternative, my heart tells me, is to be less supportive than I could be; to be an “ally” who can still maybe slip back and forth, maybe never having to own being wrong or hypocritical ever again because I remember how hard it was to put a sense of self together again after being something besides what I wanted to be.

All we can do is be what we are in the ever-unfolding now. We can open ourselves to hearing what’s right, and we can try to choose what’s right, or at least choose what’s less wrong. We can accept that we’ll sometimes fail at that. We can allow ourselves to be held accountable. We can try again.

Docs Decomposer Week 7

This weekend I solved a problem I’ve been bothered by for weeks: Given a list of pages with a dynamic priority or risk button on each row, the sortable tables didn’t reflect changes in priority or risk until I reloaded the page. It felt good to see that one go down, and it felt good to drop a bunch of lines of code in the process.

So, I think we’re done here. Or at least, we’re in a place where I can do everything with this tool I wanted to do, plus a few things I hadn’t thought of at the time.

I didn’t keep very close track of my time. If I had to guess, I think I spent somewhere around 40-50 hours, or an average of an hour a day from my first commit on February 13. The vast majority of that time was here and there on weekends, but I put in a few lunches, too. It occupied a funny space: I didn’t want to devote work work to it in case I got stuck and couldn’t see it through. So it felt better to file it under “hobby that may prove useful.”

Other stuff I finished up this weekend:

  • Some rake automation, which makes setting everything up pretty quick.
  • A HTML reimport button, to refresh the contents of a page if the underlying content in the git repo hasn’t been recently refreshed.
  • User pages, so it’s possible to look up a user and all their commented or flagged pages.
  • Sortable tables that work.
  • Unicorn/nginx, which was ops’ recommended approach to get this thing onto an internal box.

Stuff I’d like to do next:

  • Connecting Devise, which I’m using for authentication, with LDAP.
  • An “export new metadata” gadget, to make it easier for writers to quickly generate new YAML frontmatter that includes their tags and risk assessment, for copy and paste into documentation.
  • Revising the importers to understand our pre-release content repository and internal preview servers.
  • Write some tests. I’ve never really done much with testing, and I’d like to learn how. That’d make my friend in ops, who’s been helping me get this ready to deploy somewhere, a little happier with the whole affair.

Docs Decomposer #4

Tags ended up going in pretty easily with acts-as-taggable-on. It allows for mutltiple kinds of tags, which means with judicious use of forms you can pretty much create any kind of taxonomy you want. I’m just using keywords for now, in a classic free-tagging setup. With judicious use of forms to control input, I’ll be able to add risk and priority tags.

Docs Decomposer

Which means, I guess, that the thing is pretty much “done” in terms of the basic features I’d like it to have:

  1. Individual user accounts.
  2. The ability to quickly reduce a given page to just the steps and CLI instructions.
  3. The ability to flag a page with a click.
  4. The ability to comment on a page.
  5. The ability to tag a page.

Over lunch, I broke my “don’t do this in the office” rule briefly to add some markup to our Jekyll templates to make it easier to grab the rendered content for import. The importer became a little simpler as a result, and the pages lose a little bit of extra nav cruft from our templates.

So, as it stands, I could pretty much run an inventory session for the team from this thing running on my laptop. The one thing that’s still vexing me about it is the notion of unique and trackable page elements (ordered lists and code blocks, mostly).

In my Padrino prototype, each page showed the rendered content, plus a tab that showed only ordered lists and pre-blocks. Each <ol> and <pre> was checksummed, and the “elements” tab showed which other pages in the docs corpus had the exact same content.

What I really, really want is something like this:

  • You look at the page and see a <pre> block that concerns you, either because it looks very perishable or could be harmful if the information has aged out.
  • You hover over the element to expose a flag or comment button, plus a little stats box that tells you where else that element appears.
  • Once you flag that element, it’s flagged everywhere it appears in the corpus, receiving a special visual treatment.

How’s that work?

Everything is parsed by Nokogiri, so I can just write a little checksumming service in my Elements controller that will be seeing the same HTML whether it’s getting it during the content import phase, or the presentation/review phase. So:

  1. User loads a page.
  2. JavaScript finds each element of interest on the page (each <pre> and <ol>) and sends it over to the checksumming service.
  3. The checksumming service returns a unique i.d. for the element.
  4. The element gets wrapped in a div with that i.d. (in case it already has an i.d.)
  5. The comment/flag widget for that element is just AJAX calls to the controller against the i.d. of the wrapper, which gets a class to reflect its flagged status.
  6. Each flagged element gets either a modal/lightbox or a page with the comment history.

Now that I write it all out, though, it seems pretty doable. I should probably write more stuff out.

Flaws in the plan? Mostly that it’s going to add some load time as each page is pulled in, dissected, and checksummed. Fortunately, there are advanced GIF technologies to make that seem almost pleasant:


It’s something I could do at import time, too, I guess. There’s nothing sacred about the underlying markup.

Oh, the other problem is “what about when that element changes, even if it’s for the better?” At that point, the checksum changes and the flag/comment history disappears because it becomes a “new” element. There goes the inventory. Which means the inventory might become something less about the literal content and more about what/where it is, e.g. “ordered list on page #{foo} under the heading #{bar}.” So when a flag gets thrown on an element, the existence and “coordinates” of the thing are logged somewhere, along with a snapshot of what it looked like at the time. That’s probably going to be enough to find it again.


So we could go either way here. I think writing the checksumming service and coding up the process sounds interesting and fun. I think deciding that flagging elements and leaving comments on them as a one-time process with no expectation of permanence might be okay. I think automating a log based on “there’s a fishy set of instructions on the page called #{foo} under the heading #{bar}” sounds like a useful middle ground. I’ve also already figured out the xpath needed to do just that: “Show me a thing plus the most recent heading that occurred before it,” which has proven great for explaining what some of the lists and pre blocks are trying to tell you at a glance.

Well … something to think about.

Docs Decomposer Weekend 3


I added a Comment model to the project this weekend, then wired it up to a modal form. Useful things I learned to use this weekend:

  • simple_form, which makes it a little easier to write forms in Rails, and which understands Bootstrap.
  • markdownizer, which can take a text column and render it to Markdown on creation, which will help make the comments a little more expressive.

And since I’m using twitter-bootstrap-rails, I got some handy freebies for styling the flash with Bootstrap text styles.

A few things I didn’t get to:

  • Making it possible for users to delete their own comments.
  • Making it possible for users to edit their own comments.
    In other words, the comments feature sort of sucks, but at least you can suffer in Markdown.

  • Connecting the act of flagging to invoking the comments form.

And to replace the spreadsheet we came up with to review the upstream platform docs last week, I probably need to add a tags field of some kind. I think that’s a solved problem.

One more weekend?

Docs Decomposer

Menubar and Docs Decomposer and CSS Bootstrap

Docs Decomposer Weekend 2

Last week I left off with a gnawing sense of unhappiness because the flagging buttons weren’t dynamic: When Rails dropped Prototype.js, it dropped pretty much all the stuff I knew about AJAX and Rails. I could have left well enough alone, but it bothered me; and we’re doing this in the name of (re)learning.

I had fun working on it. Last weekend’s time involved a lot of re-orientation on the basics. This week felt pretty fluid as stuff I’d forgotten came back, and as I got used to a few of the tweaks I added to Emacs last week (including flymake and Projectile).

In the process of rewriting the flagging code into something a little easier to work with, I learned about acts_as_votable, and that encouraged me to just toss the flagging code I’d written altogether. Yay, cargo culting. That didn’t make the dynamic flagging button situation any better, but it did make a few other features fall down much more quickly.

So this weekend I added:

  • Flag status indicators to the page lists. You can tell if you’re the one who flagged the page, and if someone else also flagged it along with
  • A toggle to hide all the unflagged pages
  • A little list of who flagged a page on the page itself, along with next/previous buttons for each page, to make it easier to run through review without going back and forth to the master page list:

Next up, I guess, is commenting. I was hoping to get to it this weekend, but once I had acts_as_votable in place, it was a lot easier to run through a bunch of flag-related things and implement them, and I was ready for some easy stuff once I’d finally managed to make the flagging buttons dynamic.

Once I’ve got commenting in place, it’ll be time to go back through and do a lot of housekeeping. I’ve got view code that probably ought to go into partials, controllers that could probably stand to have some logic moved out into the models, and a lot of code that … well … it’s optimistic is what it is!

After that?

The thing I keep thinking about and sketching out is how being able to flag a given element on a page might work. One of the reasons I decided to just import HTML straight into the app was that it gave me access to the markup. For instance, once you find and checksum an ordered list, it’s pretty easy to wrap it in a div and give it an i.d. At that point, it can be targeted for flagging widgets and such.

Dunno. Bedtime.

Weekend Science Project (Rails After Time Away)

We need to do a documentation inventory at work. We’ve added a lot to the docs over the past year, and we’ve got a few things lurking around in there that need to be flagged for QA or revalidation. The stock answer is “put all the pages in a spreadsheet and start clicking/scrolling and annotating the list,” but I’ve been pining to fiddle around with a tool for a little while, and spreadsheets suck, so I decided to see how quickly I could put something together to make the job a little easier for the team (and reviewers down the road).

I started out with a Padrino app, which was a great way to do a proof of concept on what I was after:

  • Find all the Markdown files in the docs repo.
  • Use the filenames to compose the live URLs of each document.
  • Pull the HTML in from the server and store it for fast retrieval/decomposition
  • Identify all the elements of interest on each page and store them with a checksum.

For that last, “elements of interest” became “ordered lists and things inside pre tags.” They represent the step-by-step instructions in the docs; either in “do this, then this, then this” form, or in “start typing this stuff on the command line” form. They’re the things people gravitate to and start following, and we know that the average technical user is prone to looking for just those things and not looking at the surrounding text.

Putting a checksum on each of them will provide a way to do reports that show when an element appears on multiple pages, and across multiple versions of the docs. You kind of expect things like ordered lists to change a little over time. If one has persisted over four or five releases without changing, you might want to look at it and make sure it hasn’t been overlooked.

So after four or so hours of work, my Padrino app could do all that stuff, wrapped up in Bootstrap. I could fire up the app, get a list of all the documentation pages by product version, and use some widgets to do a few useful things:

  • See all the elements of interest in their own tab.
  • Preview a docs page, then click a button to show only the headings, pre blocks, and ordered lists.

That seemed pretty useful just as a way to help someone rip through a collection of pages and look for just the things most likely to cause a user pain, then maybe enter them in a log.

Once I was home for the weekend, though, I realized that I wasn’t as familiar with Padrino as I once had been with Rails, so I decided to do a quick port. Since I’d used ActiveRecord for the original app, and since I was happy with my db schema, it was pretty simple to set up the models, re-run the migrations, and reuse the importer scripts to repopulate my development db with content.

I spent a few hours on Saturday and a big chunk of Sunday trying to see how far I could get before, well, having enough time to blog about it before ending the long weekend and going to bed. I didn’t want to put any more time in on it at work, because if I ran into a dead end and couldn’t make what I wanted, I didn’t want to feel obligated to try using it.

I had to relearn a few things about Rails that have changed since I last used it much (during v2 times), and I had to learn some new things about jQuery that I’ve never dealt with before. Still, I’m pretty happy with where it’s at now:

First, I implemented flags. For now, all you can do is flag a page if you see something you think might be a problem that needs further review. I’ve got a few ideas about how to flag individual elements. One way is super simple, but doesn’t allow you to flag them in the context of the text. The other is tougher and I’m still working on relearning Rails’ AJAXy stuff to figure out a way to flag an element in place, store it, then have it highlighted as flagged next time you visit that page.

Next, I added a working user authentication model with Devise, so flags can belong to individual people. For now, it just means that if two people look at the same page, they don’t have to agree on its flaggability. Down the road, it’ll mean there’s a way to share the tool with all our technical reviewers so that they can flag things and we can capture all the flag data from them. I’d like to add a way to enter a comment, too, but one thing at a time.

Finally, I got thiiiiis close to making the flag button truly dynamic. All the AJAX stuff I knew from when I built my last Rails app has changed, and I couldn’t figure it all out in time, so for now I just force a page refresh when the user clicks the flag button to get it to change its state from “flag this” to “unflag this.”

There are a few more things I’d like to get to:

  • Report pages, naturally: Every flagged page, how many flags it has. That won’t take much.
  • Flaggable elements (ol, pre): I can already do this the easy way, but I’d love to do it the hard way.
  • Comments to go with flags, with design to accommodate a running list of flag comments down the side of a page preview.
  • Dynamic page import. Right now, we get the map of all the files then suck in their HTML and store it. The advantage is that it’s pretty fast. The disadvantage is that the content will drift from reality over the course of a release cycle. Way better to either suck it in each time the page is viewed or offer a “re-import the HTML” button people can hit.
  • Links straight to the files in the Github repo, so people can quickly fix things from the app if they spot something.

It’s in a place where I knock most of that stuff off with another leisure-time sprint, then see about hosting it somewhere relatively secure where I can put it in front of a few people for real feedback.

It also reminds me of all the stuff I wish I knew more about, like testing. I guess if I can get it into a useable state for other people, I can use it as a sandbox for learning about that stuff.

Anyhow, here it is. Hope you had a good Presidents Day weekend. Good night.

On the Quicksilver (and the curious marketing conceit “RV resort”)

IMG 4917

We took our new Quicksilver trailer out on its inaugural camping trip this weekend. A few notes on the whole thing:

We’ve got a Livin’ Lite Quicksilver 8.0. It’s a tent trailer, not a pop-top, so when it folds out it’s sort of like having an old-school canvas tent with a bimini frame sitting up off the ground on a big aluminum box.

Not being a pop-top, and being made of aluminum, it only weighs about 850 pounds, which is well under our Toyota Matrix’s 1,500-pound towing capacity. Driving it out to Mt. Hood this weekend was pretty easy. It was very quiet, and the main thing I noticed about it was how it affected braking: I definitely needed to give myself more time to slow down.

Setting it up is very easy: It has a vinyl cover you unsnap and roll up, a set of four aluminum struts that hold up the bed ends when it’s unfolded, and a bunch of snaps, velcro and bungie loops to hold the tent top in place. Ideally, you’ll want to deploy it with two people, but I’ve managed to put it up and take it down on my own. With two people, it takes well under 10 minutes to get from “completely closed up” to “fully deployed.”

Setup on the inside, once the tent is up, is pretty easy, too. The galley top (with a sink and a cabinet) can be lifted into place by one person. It has a folding table and removable seat cushions that stand up in a minute or two. There are also little light/fan combination units that clip onto the bars next to each bed end and plug in to 12-volt power sockets.

IMG 4909

As RVs go, it’s a pretty simple affair.

Each end of the tent has a double mattress. It’s also possible to collapse the dining table and lay it between the two dinette seats, then put their cushions down to sleep two more people. The mattresses on the beds are a little thin, so next time I think we’ll bring along our Therm-a-Rest pads.

It has an electrical system with three standard household outlets and a 12v adapter. You can run it off its own 12v deep-cycle battery, or you can connect it to shore power. It also has a small sink with a faucet that can either work with city water connected from the outside, or pump water from a plastic, 7-gallon tank in the galley base. It was pretty nice being able to wake up and start the water for the French press with an electric kettle. There’s no built-in stove, but there’s enough counter space to use the two-burner camp stove our dealer threw in. Alternately, there’s a small aluminum table you can mount outside the trailer to use for cooking.

It’s got pretty decent storage. The galley offers three small cabinets with plenty of space to stow cables, hoses, the camp stove, and first aid kit. There’s another cabinet by the door that can hold a few things you might want to grab out even before the trailer is fully deployed. The dinette seats also offer storage compartments. For travel, you can slide a few things under the dining table when it’s folded and placed over the edges of the dinette seats. We were able to fit everything for our trip into the trailer itself (including cooler and folding chairs), and didn’t have anything in the car with us.

We had good weather for our trip. It got down to the low 40s overnight, and we used a small ceramic space heater running off the electrical system to keep the trailer warm. I slept in an unzipped sleeping bag and stayed pretty comfortable.

IMG 4913

I can’t name many downsides. Sleeping with a heater in such cool weather did cause some condensation. We toweled a lot of it off before we packed the tent back down, and since it was sunny and in the high 50s this afternoon when we got home, we just set it back up again to air out and dry out a little more.

The city water pressure from our hookup was a little high and caused a small leak around one of the pipes. We spotted that happening pretty quickly. One of the nice things about the all-aluminum body is that it wasn’t a huge deal to towel up the water without fear of rot setting in.

All in all, though, it’s mainly a big tent on wheels, with plenty of space to sit around if the weather turns (or if you just feel like hanging out in there). It definitely changes your outlook about the weather when you know you’re sleeping four feet off the ground under a waterproof vinyl top. Because it’s a little more weatherproof than a tent, and because it’s easy to heat if need be, it extends our camping season quite a bit. Because it’s a little more comfortable to sleep in than a tent, it also extends our range. We’ll probably do a few more trips to some of the regional parks like Oxbow and Stub Stewart, just to make sure we’ve got the hang of driving a trailer around the metro area, but we’ve already got a spot reserved at Crater Lake this summer, and I’d like to figure out a longer trip somewhere further out before next year.

Where We Stayed

When we bought the trailer, the dealer included a year’s membership in an RV park network. We can stay in any of the parks in the Pacific NW for free for up to 30 nights this year.

We stayed at Mt. Hood Village. Since our trailer is just 16′ when fully deployed, we opted to stay in what you might call the “rustic” section of the facilities: Dirt sites with water and electricity (but no sewer or cable t.v.)

That was probably for the best: We had the entire area to ourselves. The premium area was packed pretty tightly with really big RVs. Yeah, they had a shorter walk to the (indoor) swimming pool and hot tub, but they also had to deal with all the hooting and yammering of people out under their awnings, drunk on Coors Light and the novelty of just-a-hoodie weather in January.

The vibe was pretty friendly. Our family did get the side-eye from a dude with a pony tail and the most gigantic owl tattoo I have ever seen: It spanned his chest and its eyes encompassed his pecs. He seemed a little miffed we were in the hot tub (which was huge … it could have easily seated 10 people), maybe because he was hoping to maul his girlfriend in there. Al & Ben left to go swimming, and he did get a little nasty with the towel-off once he and his girlfriend decided to climb out. Another couple in the corner looked to be completely fucked up on something that made them squint into the far distance and occasionally slur giggling observations. Oh, and Ben & I shared a sauna with a guy who’d bark “shut-it-shut-it-shut-it-the-heat-the-heat-the-heat” when people came in or out. He was also super worked up about a missing flashlight, and he snarled recriminations at one of his children through the steamed glass.

Still, people did smile and say “hi,” so friendly enough; but I think we’d have been okay just sitting by the fire, too. I also think that perhaps “RV resort” is one of the more interesting bits of branding nomenclature I’ve encountered in a while if that place is an average specimen. Your average state park is doing what it can to make the sites feel a little isolated from each other, and what you lose in the way of a hot tub, gift shop and swimming pool you make up for in relative quiet, hiking trails, twilight ranger shows at a rustic amphitheater, and fewer opportunities to see some dude with a ginormous owl tattoo toweling his lady off all nasty.

The membership is free for a year, though, so really we can live in both worlds if we choose.

IMG 4915


This video is 14 minutes of camper setup competency that I find a little hypnotic. It helps that the Livin’ Lite company is located in Northern Indiana, and so I’m hearing the voice of my people (more or less: I’m about two years more “from Oregon” than I am “from Indiana,” at this point).

Which reminds me of another thing that I enjoyed this weekend:

I worked at an RV plant the summer after I graduated from high school. I was really, really bad at it, but I learned a lot: I installed air conditioners, manufactured step-well covers, routed and secured fiberglass sheets to partition walls, undercoated vans, and did a bit of finishing work here and there.

Sitting at the table enjoying my coffee this morning, knowing the trailer was made in the same town where I helped put together RVs, it was pretty easy to see bits and pieces that looked like things I’d made or assembled that summer. You might see some of that stuff and not think twice about it, assuming a machine did it, but I spotted a few things: A small nick on the crimp on an otherwise perfect aluminum cover; and the thumbnail impression of a screw that had gone in a little off, then got pulled back out a bit and tightened back down a tiny fraction of an inch the other direction. For a second, I could smell routed fiberglass and rolls of carpet in a hot warehouse.

A Few More Org Findings

So, I learned something this week. Rather, I did something that was useful to me, which is stop short of trying to get GNUS working again. Instead, I focused on seeing if I could get comfortable enough with MobileOrg to use it (mostly I did) and I kept on working on making some of the things I like about OmniFocus work in org-mode. I think that slightly more constructive behavior — pulling away from a fit of emacsimalism before going completely toxic on it — made it easier to keep on going with org-mode.

So, here’s some of the stuff I learned this week. It’s mostly about how to use MobileOrg a little better, and how to control how much of your org-mode data you have to see at a time.

Better MobileOrg

I’m learning to trust MobileOrg, but it takes a little effort to make it work smoothly, especially if you’re coming from something like OmniFocus or Things.

Save all your open org mode files from the agenda with C-x C-s

Things has the very best sync I’ve seen in a todo app: It seems to “just happen.” Others require some sort of action on the part of the user, so if you’re the type to spend some time at your desk squaring away your actions for the day, then head into back-to-back meetings, it’s a pain if you don’t remember to sync before heading out.

Org-mode can, depending on your setup, complicate matters even more. If you live in multiple files and org-mobile-push without saving them, you get an imperfect sync. The best answer (without rigging up some kind of auto-sync), is to use the standard Emacs save command (C-x C-s) from an agenda buffer: It saves all your open org-mode files. Then you know it’s safe to push to MobileOrg.

Cheat a little with emacsclient until you can remember to save-and-sync

Emacsclient is able to run elisp from the command line, so if you can ssh into a machine with your org files and emacs on it, you can do an org-mobile-push from the command line without opening Emacs:

~$ emacsclient –eval ‘(org-mobile-push)’~

Set up refiling to more easily move things out of your inbox

Refiling allows you to move a thing from one org file to another. With MobileOrg, where the default capture method dumps you into an inbox file, it’s helpful to set up your refiling targets. With this example:

   (("~/Dropbox/org/work.org" :level . 1)
    ("~/Dropbox/org/personal.org" :level . 1))))

C-c C-w would present you with the level 1 headings from your work.org and personal.org files as targets for refiling, meaning a given org headline will be moved to the last line under the heading you select.

Leveraging the Agenda and Sub-Tree Narrowing for Focus

I really like the way OmniFocus handles its Perspectives and Focus features. It’s easy to quickly narrow down your view to things you need to work on or think about right now. I’ve learned two ways to attain similar constrained views in org-mode.

Narrow focus with the agenda

In a tool like OmniFocus or Things, you might have a few different views into your task lists to better organize what you’re working on and when you’re working on it, but the mouse-driven interface of those tools generally means your experience is one of moving between areas of the UI, or doing one-click state changes.

Custom Agenda Commands to Narrow to Contexts

With org-mode, you can use custom agenda views to pull off something like perspectives or context views, you’ll just be doing it with the keyboard.

When I use OmniFocus, my tendency is to make a lot of people contexts and a few mode contexts, but not a lot of place contexts. With org-mode, I use tags as contexts, and tag items with people. Then I made a few custom agenda commands that make it easy to drill down to specific people (either at my desk, or when I’m using MobileOrg):

    (setq org-agenda-custom-commands
          '(("p" . "People")
            ("pn" "Nick F." tags-todo "nickf")
            ("pm" "Michelle F." tags-todo "michellef")
            ("pl" "Lauren" tags-todo "lauren")
            ("pL" "Larissa" tags-todo "larissa")
            ("pp" "Pete" tags-todo "pete")
            ("pj" "Jean" tags-todo "jean")
            ("pi" "Isaac" tags-todo "isaac")

            ("g" .  "Groups")
            ("gt" "Tech Pubs Team" tags-todo "team")
            ("gl" "Tech Pubs Leads" tags-todo "leads")
            ("gs" "Engineering Staff" tags-todo "staff")

            ("o" tags-todo "office")


These views make it possible to generate an agenda (C-c a) then tap the p, g or o keys to get pre-built tag searches by people on my team, teams I work on, or items I’ve tagged as “office” (which is my way of saying “things where I need to get up and walk over to someone’s desk to talk face-to-face.”) Those same custom commands appear as agenda views in MobileOrg, so I can walk into a 1:1 and easily see everything tagged with the person I’m speaking to.

Restrict the Agenda to a Single File

You can also restrict the files the agenda uses to generate itself by invoking the agenda (C-c a) then tapping the < key before tapping a to generate the agenda. That will limit the agenda to the file you invoked it from. If you keep your todos in “work” and “personal” files, that means you can effectively filter out one or the other with a single extra keystroke.

Restrict the Agenda to a Single Subtree

You can also restrict the agenda by tapping the < key a second time. That will restrict it to the current subtree. That’s helpful if you keep lists of single-action tasks, or want to focus on the todos for a single project.

Narrowing focus with org-narrow-to-subtree and widen

The agenda has a bunch of single-key commands to cycle todo status, etc. By enabling org speed commands, you can get the same commands in an org-file. s is useful for narrowing to the current headline (great for focusing on a single project or area of concern). Map w to widen to quickly expand the file again:

(setq  org-speed-commands-user (quote (("w" . widen))))

Use TODO label faces

You can customize the way todo faces look by keyword. STUCK, WAITING and DELEGATED each get a special face so that when I’m scanning a file, they stand out a little (white on red, orange and gray, respectively).

If you use the OmniFocus defer date, use a “scheduled” date in org-mode

Like OmniFocus, org-mode has both a start date and a deadline date. Keep the agenda clear by planning start dates for things with “scheduled.”

You can make custom links in org mode really easily

Here’s one for linking to JIRA tickets:

Then use a “jira:” URL scheme in a standard org-mode link, jira:doc-988

Opening that link with C-c C-o will open your default browser and execute the search (which will take you to the ticket).

© Michael Hall, licensed under a Creative Commons Attribution-ShareAlike 3.0 United States license.