Tuesday, October 20, 2009

Mainframe molestation with looksee

This morning when I walked into the server room, I must have looked real serious, because our mainframe took one look at me and ran for its fucking life! I took off after it, bolting across the office and down the back steps before I caught up with the bastard in the car park, trying to steal a scooter off some school kid. I slapped that bitch down, lifted its rear end up into the air, plugged my keyboard in from behind and started hacking.

I only had time for a quicky, and here's the result (from my .irbrc):



These hacks are built on this dynamite gem called looksee that gives you a function similar to the various methods functions that exist in the ruby core, except the output contains more information and is designed for human readability. Compare these 2 screenshots:

Without looksee


With looksee, and my hacks



I practically blew a load in my pants when I saw the output from looksee, since I've frequently been frustrated by the output of methods. However, the standard usage of looksee is

lp someobject, :some => 'options'

and I don't really want to memorize another function call.

Enter the above code, which intercepts calls to methods. If the call is made directly from irb, it is forwarded on to looksee, otherwise the original methods is called. This is handy as fuck when using irb for debugging and what not, but wont screw with code that uses the standard methods function.

Related stuff

http://www.rubypulse.com/episode-0.12_looksee.html - a screencast demonstrating looksee (where I first heard about it)

Sunday, September 27, 2009

Vim pr0n: ignorecase fails

Fuck The Machine, lately the only thing I've been raging against is the ignorecase option.

In practice, the only use for ignorecase is to make vims regular expressions case insensitive when searching. However, for some reason, it also affects the == operator. Check this shit out:


:set noignorecase
:echo "foo" == "FOO"
0
:set ignorecase
:echo "foo" == "FOO"
1


What the fuck!? Good job team, you just made == ambiguous, and therefore completely useless.

This means script writers can't use == unless they explicity set the ignorecase option first, otherwise they don't know how their code will behave. Since that would be retarded, we should all stop using == and start using ==# or ==?, which is exactly what I've done.

But wait! Theres more! ignorecase also changes the behaviour of these comparison operators too:
!=
>
>=
<
<=
=~
!~

Who the fuck made this decision?! I have no idea, but Id like 5 minutes alone with that bitch so I could stab them in the face! Multiple times! WITH MY FUCKING RECTUM.

Just ask yourself: if you download someones vim script, is it really in your best interests to change how every single comparison operator in that script functions?

In my massively non-humble opinion, ignorecase should not affect any of the basic comparison operators, only specializations of those operators.

</rage>

Monday, July 6, 2009

Vim pr0n: Combating long lines

The other day my boss found me locked in the server room, naked and bathing myself with dish washing liquid and toilet water. When he asked what in the flying fuck I was playing at, I told him my soul was filthy and corrupt from all the long lines I'd been committing. In reply he told me I'd better sack up and write some vimscript hacks to keep my long lines under control. So that's what I did. Later that afternoon I was so happy I proposed to my boss and we lived happily ever after.

Read this raving if long lines bug you like a colony of fire ants assaulting your prostate.

Note: this raving assumes a fair amount of knowledge about statuslines in vim. If you aren't pro at statuslines then you should read this raving first.

Reporting long lines on the statusline

I've found it quite useful to have flags on my statusline that appear when a buffer is in an undesirable state; e.g. if the file format or encoding is wrong, or there are syntax errors, or mixed indenting etc. Ive recently coded a warning for long lines into my statusline. Check out this screenshot:



Notice the [#141,m82,$162] on the statusline. This is telling us that the buffer has 141 long lines, where the median length of these lines is 82 chars and the longest is 162 chars. This notice only appears when there is at least one line that is longer than &textwidth (typically 80).

That may seem like an excessive amount of information, but when I first coded a statusline warning, I only included the length of the longest line. This turned out to be insufficient since its common for a file to have one or two crazy long lines that are acceptable or troublesome to avoid. I could have only shown the number of long lines, but then I'd be left wondering how long they were; hence the median and longest line stats. Besides, more stats means more code behind my statusline, which makes it more likely the mother fucker will become sentient and hunt down Jamis Buck.

Here's the code from my vimrc:

 1 "....
 2 set statusline+=%{StatuslineLongLineWarning()}
 3 "....
 4
 5 "recalculate the long line warning when idle and after saving
 6 autocmd cursorhold,bufwritepost * unlet! b:statusline_long_line_warning
 7
 8 "return a warning for "long lines" where "long" is either &textwidth or 80 (if
 9 "no &textwidth is set)
10 "
11 "return '' if no long lines
12 "return '[#x,my,$z] if long lines are found, were x is the number of long
13 "lines, y is the median length of the long lines and z is the length of the
14 "longest line
15 function! StatuslineLongLineWarning()
16     if !exists("b:statusline_long_line_warning")
17         let long_line_lens = s:LongLines()
18
19         if len(long_line_lens) > 0
20             let b:statusline_long_line_warning = "[" .
21                         \ '#' . len(long_line_lens) . "," .
22                         \ 'm' . s:Median(long_line_lens) . "," .
23                         \ '$' . max(long_line_lens) . "]"
24         else
25             let b:statusline_long_line_warning = ""
26         endif
27     endif
28     return b:statusline_long_line_warning
29 endfunction
30
31 "return a list containing the lengths of the long lines in this buffer
32 function! s:LongLines()
33     let threshold = (&tw ? &tw : 80)
34     let spaces = repeat(" ", &ts)
35
36     let long_line_lens = []
37
38     let i = 1
39     while i <= line("$")
40         let len = strlen(substitute(getline(i), '\t', spaces, 'g'))
41         if len > threshold
42             call add(long_line_lens, len)
43         endif
44         let i += 1
45     endwhile
46
47     return long_line_lens
48 endfunction
49
50 "find the median of the given array of numbers
51 function! s:Median(nums)
52     let nums = sort(a:nums)
53     let l = len(nums)
54
55     if l % 2 == 1
56         let i = (l-1) / 2
57         return nums[i]
58     else
59         return (nums[l/2] + nums[(l/2)-1]) / 2
60     endif
61 endfunction


Crikey dick that's a lot of code! Lets run through it.

The StatuslineLongLineWarning() function constructs and caches the flag that will appear on the statusline. The flag will be an empty string if there aren't any long lines.

The hard work is actually delegated off to the LongLines() and Median() functions. LongLines() examines every line in the buffer and returns an array containing the lengths of the long lines, where "long" is determined by the users &textwidth setting (defaulting to 80 if &textwidth is set to 0). Note that LongLines() converts tabs into spaces according to the users &tabstop setting. Median() calculates the median of a given array of numbers.

Line 2 puts the flag onto the statusline and should appear with the rest of your statusline setup code.

Line 6 clears the cached flag every time the user is idle or saves the file, thus causing the flag to be recalculated.

Highlighting the offending parts of long lines

 1 "define :HighlightLongLines command to highlight the offending parts of
 2 "lines that are longer than the specified length (defaulting to 80)
 3 command! -nargs=? HighlightLongLines call s:HighlightLongLines('<args>')
 4 function! s:HighlightLongLines(width)
 5     let targetWidth = a:width != '' ? a:width : 79
 6     if targetWidth > 0
 7         exec 'match Todo /\%>' . (targetWidth) . 'v/'
 8     else
 9         echomsg "Usage: HighlightLongLines [natural number]"
10     endif
11 endfunction


I stole line 7 above from this vim tip and wrapped it up in a the HighlightLongLines command above. When invoked, the command highlights the "long" parts of long lines. Ive found this command pretty handy when embarking on anti long line crusades.

I'd highly recommend reading that vim tip for more code to highlight long lines. Theres some pretty dynamite stuff there.

Conclusion

Long lines suck, here's what Abe had to say about the matter:

Peppering your code with long lines is highly recommended; right up there with "strolling into church with an entourage of scantly clad, chronically obese prostitutes" and "licking the testicles of senile old men who are not fully in control of their bladders".

Abraham Lincoln, 1862


Inspirational.

Use the relatively passive vim script hacks above to make vim bitch at you and steer you away from using long lines. If you want a more aggressive solution, then check out the aforementioned vim tip.

Sunday, June 21, 2009

Vim pr0n: Syntax checking on the go

A couple of days ago I was in the office, hacking the mainframe with The Great Halorgium. We got to talking about our current vim setup when he told me that it would be awesome if the statusline displayed a warning flag for buffers with syntax errors.

By the end of the day we'd written a couple of ftplugins to do just that for ruby and eruby. Read this raving to find out how.

Note: although we have only done it for ruby and eruby (at the time of this writing), you could easily do the same for other syntaxes using the same technique, provided there is a command line syntax checker for that language.

Note: this raving assumes a fair amount of knowledge about statuslines in vim. If you aren't pro at statuslines then you should read this raving first.

The end result


Here is a screenshot showing the code in action:


Notice the [syntax:4] on the statusline. After this buffer was saved, our code ran it through a syntax checker which told us there was a syntax error on line 4 ZOMG! (actually its a missing bracket on line 3).

The code


Here is the code to accomplish this for the ruby filetype. It was pulled from my ~/.vim/ftplugin/ruby_stl_syntax_warning.vim.

Update (8/july/09): I've recently refactored the living shit out of this code, which has made it much simpler, and has removed much of the duplication between the syntax checkers. See my vimfiles repo for the latest hacks. Look in my vimrc (search for "syntax") and in the syntax_checkers directory.

Update (25/july/09): OK, I went completely insane and made it into a plugin with some more features, check it out here http://github.com/scrooloose/syntastic

 1 if exists("b:did_ruby_stl_syntax_warning_ftplugin") || &filetype !~ '\<ruby\>'
 2     finish
 3 endif
 4 let b:did_ruby_stl_syntax_warning_ftplugin = 1
 5
 6 "bail if the user doesnt have ruby installed
 7 if !executable("ruby")
 8     finish
 9 endif
10
11 "inject the syntax warning into the statusline
12 let &l:statusline = substitute(&statusline, '\(%=\)',
13             \ '%#warningmsg#%{StatuslineRubySyntaxWarning()}%*\1', '')
14
15 "recalculate after saving
16 autocmd bufwritepost <buffer> unlet! b:statusline_ruby_syntax_warning
17
18 "run the buffer through ruby -c
19 "
20 "return '' if no syntax errors detected
21 "return '[syntax:xxx]' if errors are detected, where xxx is the line num of
22 "the first error
23 function! StatuslineRubySyntaxWarning()
24     if !exists("b:statusline_ruby_syntax_warning")
25         let b:statusline_ruby_syntax_warning = ''
26         if filereadable(expand("%"))
27             let output = system("ruby -c " . expand("%"))
28             if v:shell_error != 0
29                 let b:statusline_ruby_syntax_warning =
30                             \ '[syntax:'. s:ExtractErrorLine(output) . ']'
31             endif
32         endif
33     endif
34     return b:statusline_ruby_syntax_warning
35 endfunction
36
37 "extract the line num of the first syntax error for the given output
38 "from 'ruby -c'
39 function! s:ExtractErrorLine(error_msg)
40     return substitute(a:error_msg, '.\{-}:\(\d*\): syntax error,.*', '\1', '')
41 endfunction


Wow! Well I have a boner, how about you?

Lets dissect this:

Lines 1–4 contain the standard house keeping code to make sure we don't source the plugin more than once. The filetype check is needed since the eruby runtime I use sources some of the ruby runtime files, including this one.

Lines 6–9 ensure that the ruby binary is present, since we are going to use ruby -c to check the syntax.

Lines 12–13 inject the syntax warning into our statusline. We chose to put it just before the left/right alignment separator since we put buffer related info on the left and cursor related info on the right. If you don't have %= on your statusline then you'll have to put it somewhere else.

Lines 23–41 are where the party is at. The StatuslineRubySyntaxWarning() function works out what should be displayed on the statusline and caches the result in b:statusline_ruby_syntax_warning. The code shells out to ruby -c to check the syntax of the buffer, before parsing any resulting error messages and returning the statusline flag containing the line number of the first error.

Line 16 clears the cached flag every time the buffer is saved, causing the syntax to be rechecked.

The future


There are a few other filetypes that I may write syntax checkers for, like sass and haml. Php could be useful too. And bash. Fuck, a bash syntax checker could actually be really handy. Getting that horrid shit right is about as easy as jacking off while watching a bunch of kitties getting nailed to a post. Things like apache configs could be good too, instead of doing apache2 -t manually.

Ive also been thinking about using the :sign commands to flag syntax errors. This way we could put a symbol next to each line containing a syntax error.

Tuesday, February 3, 2009

Vim pr0n: Jamis Buck must die

Sometimes I see something online that makes me rage so hard I long for a nail gun and a sack of adorably cute kittens. Lately I've been seeing assloads of vimrc files with this in them:

map <leader>d :execute 'NERDTreeToggle ' . getcwd()<CR>

Awww, isn't that cute? BUT ITS WRONG!!!1

It all comes from this blog post. OMFG Jamis Buck, I will find you, and I will fuck you. Then I'm gonna shove my entire $12 keyboard into your anus and turn it sideways before I squash you into a fetal ball and use copious amounts of superglue to bind your hands to your ass, and your scrotum to your tonsils. Then I'll bowl you down a hill into a set of garbage cans and put the video on youtube.

The :NERDTreeToggle command defaults to the current working directory anyway. So, if you have the above line in your vimrc, delete it immediately and replace it with this:

nnoremap <leader>d :NERDTreeToggle<cr>

DO IT!

Tuesday, December 16, 2008

Got Kombat?

Once upon a time me and my cousins were milling about the backyard eating grass and sniffing each other crotches when, for some reason, we started taking photos.

As usual, we eventually sought out props to photograph with. As usual, we ended up getting one of my huge fuck off knives, then a mattock, then a sword, and finally a chainsaw.

Afterward, I chose my 4 favourite shots and brought out the gimp (no, not that gimp, this gimp!). Then I wrote an incredibly sensitive and heart warming childrens story for each one.

Enjoy.

Weigh in and pre-match psych out


omg click to enlarge bitch



So my auntie came up to me and my 2 little cousins and was like "YO, one of y'all do the dishes!", and immediately me and my older little cousin put our thumbs to our foreheads.

My younger little cousin hates doing the dishes. In fact, he would rather die. Consequently, his face turned red as he bared his teeth and pulled out his knife.

His words blasted off his furiously flapping tongue, "You bitches think you can tell me what to do?!?! Huh?! Fuck that shit! Let mortal kombat begin!"

And so it began ....


Round one


omg click to enlarge bitch



Always eager to butcher my little cousin, I ran to the shed and searched for the most brutal looking garden implement I could find. The giant orange mattock in the corner looked brutal enough to castrate a dinosaur. Perfect.

We met each other in the yard. Looking him in the eye, I gave him one chance to back out, "You think you can come up here and take a piece of this? Huh? Ill tell you something. You're gonna die."

In reply he gave me detailed instructions on exactly how I should go about copulating with a pencil sharpener. It didn't sound pleasant. I took that as a "yes" and charged.

I came in hard and high, hoping to cleave his skull with one good blow and splatter his brains on the ground for my dog. At the last fraction of a second, he darted sideways, puncturing my side with several jackhammer-like stabs before jumping back.

He'd hit me! Unbelievable! Looking him in the eye, I cursed with language so colourful you'd swear we were back in the 70's.

He snorted like a horse and ran at me with intense blood lust. But I was ready. I slapped that bitch down so hard there were tears in his eyes. Then the fight began in earnest...

It raged....

and raged...

Until finally, dazed, I stumbled around in a barely conscious stupor.

Like lightening, his knife flashed as he sliced my eyeballs right out of their sockets. But before I could let loose a single curse, the knife flashed again and then he was stuffing my eye sockets with my own severed testicles.... FATALITY.


Round two


omg click to enlarge bitch



If you thought that watching me getting slaughtered would force my older younger cousin (Commando) to yield to my younger younger cousin (Killalot) then you have clearly underestimated how much we hate doing dishes.

Commando went in search of some elite weaponry and returned with a samurai sword. Seeing this, Killalot decided to upgrade his own kill gear. He went to his room, rummaged around under his bed and came back with his favourite chainsaw.

Seeking the psychological high ground, Commando taunted: "When im done mutilating your corpse, im gonna kidnap your cat and rape the shit outta him every night for the rest of his life." Killalot replied matter-of-factly, "Go ahead, he likes it rough."

Taken aback by this, Commando tried some more conventional material. He launched into a detailed exposition of exactly how he would use his sword to extract Sir Killalot's beating heart out of his body via his anus. He thought that the whole "ill rip your heart out and show it to you" angle would have Killalot wetting himself, but the details bored Killalot and he stopped listening after a couple of sentences.

Commando tried one last time, "Bitch! Im gonna mutilate your genitals, stir fry them, and feed them to---", but before he could finish, Killalot raised his chainsaw, snorted with more vigour than 10 wild stallions and charged.

To be concluded ...

Round two point five


omg click to enlarge bitch



The kombat began.

It was brutal. Really brutal. In fact, the brutality cannot be adequately described without resorting to alliterative testicle metaphors. It was ball breaking, sack splitting, gonad grinding, testy tearing, scrotum shattering, nad napalmingly brutal.

Commando's sword slashed. Deadly and swift with maximal finesse. Making razor cuts here and there but never striking a fatal blow.

Killalot, on the other hand, took the opposite approach. He flung his chainsaw around and around, roaring and biting, as though it were some psychotically fucked off lion he'd grabbed by the tail.

Their weapons crashed, bashed and smashed like the four drum kits of the apocalypse. Their snorting would have put rhinos to shame. Their facial expressions would have set churches on fire. Their foul cursing would have caused nuclear weapons to spontaneously detonate.

Eventually Sir Killalot won out and finished Commando off with The Voltron Maneuver, cutting him in half right down the middle.


Game Over.
Insert Coin.

....
Epilogue:


So me and my cousin were dead. But not for long. On account of our exceptional deeds, the Almighty saw fit to resurrect us into the afterlife immediately.

After we got settled in to hell, my other cousin employed the services of The Transdimensional Courier Agency to deliver the dishes down to us. We cleaned them and sent them back.

Wednesday, November 26, 2008

select count(*) as omg_wtf from postgres;

This morning I discovered something that made me want to jump up on my desk and drop the f-bomb in my office with heart and soul. It was as though I'd just been sucker punched by a circus midget then fisted by his exceptionally well trained elephant as I doubled over in pain.

Let me tell you a story.

Once upon a time this morning, I was trying to figure out why the fuck one of our servers had gone postal and exploded, leaving behind a stinking cloud of piss mist and gaseous fecal matter yet again.

Trawling through the logs of one of the web apps on the machine, I found a bunch of requests that had each taken 15 seconds to complete. After some investigation, I found that these requests were all doing a "select count(*) ..." from a table with millions of rows. OMG. So I fired up a postgres client and did an explain for the same query, and it told me it was doing a sequential fucking scan to find the count!

I googled intensely, and what I found made my jaw drop and my tongue roll out like a red carpet: There is no way to optimize count operations in postgresql. Read here for a brief explanation.

OMG thanks postgres! Mind if I rape you?!

Solutions

There are a bunch of work arounds you can find on google, but they all boil down to 2 approaches: cache the count, or guess the count.

So.....

Triggers anyone?!

 1 -- Create foos table
 2 CREATE TABLE foos (
 3   id   SERIAL PRIMARY KEY,
 4   name VARCHAR(255)
 5 );
 6
 7 -- Create table foos_count to hold a single row that contains the number of
 8 -- rows in the foos table
 9 CREATE TABLE foos_count (
10   counter  INTEGER
11 );
12 insert into foos_count values(0);
13
14 -- Increment count function
15 CREATE FUNCTION inc_foo_count() RETURNS trigger AS '
16 BEGIN
17 UPDATE foos_count SET counter = counter + 1;
18 RETURN NEW;
19 END;
20 ' LANGUAGE plpgsql;
21
22 -- add a trigger to increment foos_count.counter when inserting into foos
23 CREATE TRIGGER inc_foo_counter AFTER INSERT ON foos
24   EXECUTE PROCEDURE inc_foo_count();
25
26 -- Decrement count function
27 CREATE FUNCTION dec_foo_count() RETURNS trigger AS '
28 BEGIN
29 UPDATE foos_count SET counter = counter - 1;
30 RETURN NEW;
31 END;
32 ' LANGUAGE plpgsql;
33
34 -- add a trigger to decrement foos_count.counter when inserting into foos
35 CREATE TRIGGER dec_foo_counter AFTER DELETE ON foos
36   EXECUTE PROCEDURE dec_foo_count();

Oh yeah. Nothing like a good trigger to make you feel like you just got probed by a bunch of aliens with a fetish for egg beaters.


How about rules?

 1 -- Create foos table
 2 CREATE TABLE foos (
 3   id   SERIAL PRIMARY KEY,
 4   name VARCHAR(255)
 5 );
 6
 7 -- Create foos_count to hold a single row that contains the number of rows in
 8 -- the foos table
 9 CREATE TABLE foos_count (
10   counter  INTEGER
11 );
12 insert into foos_count values(0);
13
14 -- add a rule to increment foos_count.counter when inserting into foos
15 CREATE RULE inc_foo_count AS ON INSERT TO foos DO ALSO
16     UPDATE foos_count set counter = counter + 1;
17
18 -- add a rule to decrement foos_count.counter when inserting into foos
19 CREATE RULE dec_foo_count AS ON DELETE TO foos DO ALSO
20     UPDATE foos_count set counter = counter - 1;

Rules don't look all that bad— certainly much nicer than triggers.


You could also obtain an estimate of the count with this:
SELECT reltuples FROM pg_class WHERE oid = 'foos'::regclass::oid;

This will give you the count at the time of the last ANALYSE.

My lists

Ok, so lets take a look at what impact this experience has had on my lists.

Postgres just jumped from my Christmas List over to my Shit List — fuck you postgres, I'm gonna find you, and I'm gonna take my knife, and I'm gonna use your cock for a sheath.

The List Of Things I Should Have Known By Now grew by 1 for select count sucking the scrote.

The List Of Things I Should Have Played With By Now also grew by 1 for rules since this is the first time I've actively investigated them.