Showing posts with label don't try this at home kids. Show all posts
Showing posts with label don't try this at home kids. Show all posts

Thursday, 14 November 2013

Buy Shares in the Garden Path, friends; it's a wonder!

Not giving actual securities advice, of course; merely commenting on the metaphorical "garden path" one is "led down" by seemingly trustworthy sources. The next thing you know, it's three minutes since you started falling; you've no idea where "bottom" is and even less idea how you got there. (For the record, at three minutes, were it not for atmospheric drag slowing you down, you'd have been falling for ~158,760 meters and have an instantaneous speed of ~1764 m/s. Happy landings!)

All right, what am I blathering on about? It's really simple. (Until you fall off that cliff.) See here…

The problem is actually documented rather clearly in the Position section of the W3C "DOM Level 2 Traversal Range" spec, and had I read that instead of relying on the documentation of the jQuery++ class that wraps it, I'd have saved several hours and $DEITY-knows-how-many litres of stomach acid. According to the spec,

The offset within the node is called the offset of the boundary-point and its position. If the container is [any of 5 different types of] node, the offset is between its child nodes. If the container is a CharacterData, Comment or ProcessingInstruction node, the offset is between the 16-bit units of the UTF-16 encoded string contained by it.

(Emphasis mine.)

The jQuery++ documentation gives an example that uses a single element containing a single text node. No mention is made of the (sensible once you figure it out) distinction between offsets in a text node vs. offsets in an element. And attempting to adapt their example to your markup, which is unlikely to be so trivially simple, is bound to lead to confusion unless you know the answer to The Riddle of the Magical Redefining Offset™.

Now you do, and you can be about your work a great deal more quickly than I.

So how do I do what I set out to do, which is to build up an HTML fragment matching selected text on a page? With great and grandiose ceremony, alas.

  1. Get the active Range for the block-level element containing my content. That gives me the starting and ending offsets (child nodes of that outer block) corresponding to the child nodes that the selection overlaps.
  2. If that outermost range spans multiple child nodes
    1. walk down the first selected node and its descendants, until we find the actual text node containing the start of the selection;
    2. Add that text and the trailing child nodes, if any, of the element node containing that text node to a buffer;
    3. Iterate for that element node's parent element node's trailing child nodes, and on again until we've walked back up to the outermost content area;
    4. Add the entire markup of each succeeding top-level child node up to but not including the block identified by that initial ending offset;
    5. Descend into the top-level ending node and its descendants, adding markup of each node until we reach the actual node containing the endpoint of the selection;
    6. Add the selected text fragment from the end point text node to the buffer. Done.
  3. If that outermost range has a single child node (the starting and ending offsets are the same)
    1. walk down the selected node and its descendants, until we find the actual text node containing the start of the selection;
    2. Add that text and the trailing child nodes, if any, of the element node containing that text node to a buffer;
    3. Iterate for that element node's parent element node's trailing child nodes, and on again until we find a node containing the endpoint node (or that node itself);
    4. Descend into the top-level ending node and its descendants, adding markup of each node until we reach the actual node containing the endpoint of the selection;
    5. Add the selected text fragment from the end point text node to the buffer. Done.

Oh, my aching head.

If anybody has any better ideas, I'd love to hear them. Oh, did I mention that this is in CoffeeScript/JavaScript in the browser, so we don't have any fancy tools like Nokogiri, which I've previously described as "the Swiss Army Ginsu Chainsaw for parsing markup". With tooling like that, this little exercise would be over and done with in an hour. Building a no doubt buggy improper subset of its functionality, in Script, has taken days. Plural, and never mind how plural. If I were a drinking man, there'd be a crate of top-shelf Scotch in my immediate future.

Sunday, 31 March 2013

Playing Mind Games With git, And Seemingly Winning

I had some bad experiences with git a few years back, and until a year or so ago, you couldn't get me to touch it with a ten-parsec pole. Let's just say that things got lost… changes, files, jobs, companies, little things like that. So, building out a Rails shop over the last year, with git being The Standard™(thank you, Github), you'd think I'd be super-extra-hyper-careful about it, right?

Well, the training wheels have to come off sometime, and if they take rather important bits of your skull along for the ride, consider it a Learning Experience. You were warned, weren't you? You did read that .2-point type on the side of the tin with the big label LIFE! that said "Death will be an inevitable finale to Life. Don't bother the lawyers", didn't you? Oh, well. Details.

Rule #16,472 in Dickey's Secrets of Software Survival states: "Never write important code while seriously ill with flu or whatnot. The results would be worse than if you had written the code while drunk. In fact, you may want to give serious consideration to getting drunk before attempting to code while sick." Because what violating 16,472 can get you is shenanigans like committing great laundry lists of files, several times, only to find yourself staring at a red bar and wondering how you got there. (Hot the Red Bar in Cable Street in London, but the red bar that is your test tool saying "your code and/or your specs/tests are busted, bub". That red bar.) This has actually happened to me not once, but twice in the last couple of months.)

At which point, you mutter dark threats under your breath at the imbecile who put you in this position (yourself) and start retracing your steps. Whereupon you find that your last genuine, not-because-things-were-wildly-cached proven-working version was five commits earlier. Five commits that have been pushed to the Master Repo on Github earlier. You then note that each of these touched a dozen files or so (because you don't like seeing "19,281 commits" in the project window when you know you're just getting started), and a couple hours of spelunking (as opposed to caving) leaves you none the wiser. What to do?

The first thing to remember is that there is nothing structurally or necessarily semantically significant about the master branch in git. As far as I can tell, it's merely the name assigned to the first branch created in a new repo, which is traditionally used as the gold-standard branch for the repo. (Create new branch, do your thing, merge back into master. Lather; rinse; repeat. The usual workflow.) On most levels, there's nothing that makes deleting the master branch any different than any other.

Of course, the devil is in the details. You've got to have at least one other surviving branch in the repo, obviously, or everything would go poof! when you killed master. And remote repos, like Github, have the additional detail that specifies HEAD as the default branch on that remote. (Thanks to Jefromi for this explanation on StackOverflow, to another guy's question.) There always has to be a default branch; it's set to master when the repo is created and very rarely touched. But it's nice to know that you can touch, or maul, it when the need arises.

Here's what I did:

  1. I went back to that last proven good commit, five commits back, and created a new repair branch from that commit;

  2. I scavenged the more mundane parts of what had been done in the commits on master after the branch point, and made (bloody well) sure specs fully covered the code and passed;

  3. I methodically added in the code pieces that weren't so mundane, found the bug that had led me astray earlier, and fixed it. This left me with a repair branch that was everything master would have been had it been working.

Now for the seatbelts-and-helmets-decidedly-off part.

  1. I verified that I was on the repair branch locally;

  2. I deleted the master branch in my local repo

  3. I ran git branch master to create a new master branch. (Note that we haven't touched the remote repo yet);

  4. I checked out the (new) master branch (which at this point is an exact duplicate of repair, remember);

  5. I (temporarily) pushed the repair branch to origin;

  6. I used the command git remote set-head origin repair to remove the only link that really mattered to the existing master branch;

  7. I deleted the master branch on the remote ("origin") repo as I would any other remote branch;

  8. I force-pushed the new master to the remote, using the command git push --force origin master. I needed the --force option to override git's complaint that the remote branch was ahead of the one I was pushing;

  9. I ran git remote set-head origin master to restore git's default remote branch to what it had recently been; and

  10. I deleted the repair branch from origin

Transmogrification complete, and apparently successful. Even my GUI git client, SourceTree, only paused a second or so before displaying the newly-revised order of things in the repo. It's useful to know that you can trade the safety scissors for a machete when you really do feel the need.

However… so can anyone else with access to the repo. And I can easily see scenarios in a corporate setting where that might be a Very Bad Thing indeed. In a repo used by a sizeable team, with thousands of commits, it wouldn't be all that difficult for a disgruntled or knowingly-soon-to-be-disgruntled team member to write a program that would take the repo back far enough into the mish-mashed past to deter detection, create a clandestine patch to a feature branch that (in the original history) was merged into master some time later, and then walk the commits back onto the repo, including branches, before pushing the (tainted) repo back up to origin. I'd think that your auditing and SCM controls would have to be pretty tight to catch something like that. I'd also think that other DVCSes such as Mercurial or Bazaar would have a much harder time successfully doing what I did, and therefore, would be less likely to such exploitation. That this hasn't been done on a scale wide enough to be well-publicised, I think, speaks quite loudly about the ethics, job satisfaction, and/or laziness of most development-team members.

What do you think?