Caius Theory

Now with even more cowbell…

evil.rb

Here be hax. Don't ever do these. ;-)

Reduce local variables with instance_eval

Sometimes (usually in a one-liner) I want to do some work with a value without assigning it to a variable. Chucking an #instance_eval call in there will set self to the value, which saves having to assign it to a local value. Pretty much only used by me in one-off scripts or cli commands.

Good

start_date, end_date = ["24 Dec 2011", "23 Jan 2013"].map {|d| Date.parse(d) }
puts "#{start_date} to #{end_date} is #{(end_date - start_date).to_i} days"

Bad

puts ["24 Dec 2011", "23 Jan 2013"].map {|d| Date.parse(d) }
  .instance_eval { "#{first} to #{last} is #{(last - first).to_i} days" }

See, way less code! cough, cough

Bonus usage: Misdirection

I also dropped some instance_eval on our campfire bot at EmberAds to always blame one person, but without the code reading as such.

%w{Dom Mel Caius CBetta Baz}.sample.instance_eval do
  "(4V5A8F5T=&$`".unpack("u")[0]
end

That does not return one of the array elements as you might think it does from quickly scanning the code…

Set method-local variables in default arguments

You have a method and it takes one argument, which has a default value of nil specified. You then run into the situation where you need to know if nil was passed to the method, or if you're getting the default value of nil. You could change the default value to something you choose to be the "default value" and unlikely to be passed from elsewhere as the argument's value, and reset the parameter to nil after checking it, like this:

def output name=:default_value
  if name == :default_value
    name = "caius"
    default = true
  end

  "name: #{name.inspect} -- default: #{default.inspect}"
end

output() # => "name: \"caius\" -- default: true"
output("fred") # => "name: \"fred\" -- default: nil"

That's quite a lot of code added to the method just to find out if we passed a default value or not. And if we forget to reset the value when it's :default_value then we end up leaking that into whatever the method does with that value. We also have the problem that one day the program could possibly send that "default value" we've chosen as the actual parameter, and we'd blindly change it thinking it was set as the default value, not the passed argument.

Instead we could (ab)use the power of ruby, and have ruby decide to set default = true for us when, and only when, the variable is set to the default value.

def output name=((default=true); "caius")
  "name: #{name.inspect} -- default: #{default.inspect}"
end

output() # => "name: \"caius\" -- default: true"
output("fred") # => "name: \"fred\" -- default: nil"

As you can see, the output is identical. Yet we have no extra code inside the method to figure out if we were given the default value or not. And as a bonus to that, we no longer have to check for a specific value being passed and presume that is actually the default, and not one passed by the program elsewhere.

I posted this one in a gist a while back (to show Avdi it looks like), and people came up with some more insane things to do with it, including returning early, raising errors or even redefining the current method, all from the argument list! I'd suggest going to read them, it's a mixture of OMG HAHA and OMFG NO WAY WHYY?!?!.

Don't do this.

Don't do the above. No really, don't do them. Unless you're writing a one-off thing. But seriously, don't do them. :-D

Abusing Ruby 1.9 & JSON for fun

Ever since I found out about the new hash syntax you can use in ruby 1.9, and how similar that syntax is to JSON, I've been waiting for someone to realise you can just abuse eval() for parsing (some) JSON now.

For example, lets say we have the following ruby hash, which could be generated by a RESTful api:

thing = {
    :person => {
        :name => "caius"
    }
}

If we pull in the JSON gem and dump that out as a string, we get the following:

jsonstr = thing.to_json
# => '{"person":{"name":"caius"}}'

That's… not quite what we wanted. It's not going to turn back into valid ruby as it is. Luckily javascript will parse objects without requiring the attributes to be wrapped in quotes, eg: {some: "attribute"}. We could build a JSON emitter that does it properly, or we could just run it through a regular expression instead. (Lets also add a space after the colon to aid readability.)

jsonstr.gsub!(/"([^"]+)": /, '\1: ')
# => '{person: {name: "caius"}}'

Okay, so now we've turned a ruby hash into a JSON hash, that can still be parsed by the browser. Here's a screenshot to prove that:

Valid JSON 'thing'

As you can see, it parses that into a valid JS object, complete with person and then (nested) name attributes. If we wanted to, thing["person"]["name"] or thing.person.name would access the nested value "caius" just fine.

Now then, we've proved that is successfully parsed into javascript objects by the browser, generated from a ruby hash. No great shakes there, that's fairly simple and has worked for ages. Now for my next trick, I'm going to turn that string of JSON back into a ruby hash, all without going anywhere near the JSON gem.

Some of you might have guessed what I'm about to do and have started hoping you've guessed wrongly — just for the record I don't condone doing this except for fun and games. The JSON gem is there for a reason ;) With that little disclaimer out the way, here we go!

thing2 = eval(jsonstr)
# => {:person=>{:name=>"caius"}}
thing2 == thing
# => true

Oh snap! We just turned javascript objects back into valid ruby objects, in one simple method call. And we'd be able to access the "caius" value by calling thing2[:person][:name], or creating OpenStructs from the hashes and calling thing2.person.name. Which is uncannily like the JS!

Updated 2011-02-07: Jens Ayton pointed out unquoted keys aren't strictly valid JSON, which is correct. Amended to say they're parsed as javascript objects instead, with no mention of it being valid JSON.

This is my Compiler

As some friday fun in #geekup we ended up converting the US Marines Creed to a geekier version.

This is my compiler.
There are many like it, but this one is MINE.
My compiler is my best friend. It is my life.
I must master it as I must master my life.
My compiler without me is useless. Without my compiler, I am useless.
I must run my compiler true.
I must run faster than my bug who is trying to kill me.
I must squash him before he squashes me. I will…
My compiler and myself know that what counts in war is not the warnings we squash,
the builds we create, nor the optimisations we make.
We know it is the build errors fixed that count. We will fix…
My compiler is human, even as I, because it is my life.
Thus, I will learn it as a brother.
I will learn its weaknesses, its strengths, its output, its code,
its quirks, and its errors.
I will ever guard it against the ravages of virii and disk failures.
I will keep my compiler clean and ready, even as I am clean and ready.
We will become part of each other. We will…
Before Assembler I swear this creed.
My compiler and myself are the defenders of good code.
We are the masters of our bugs.
We are the saviors of our code.
So be it, until code is compiling and there are no bugs, but compiled code.

And then jamx jumped in with the Lords Prayer

Our Compiler, who art in memory. GNU be thy name.
Thy source code come, thy will be done.
On script as it is in memory.
Give us this day our daily data, and forgive us our segfaults as we forgive those who segfault against us.
And lead us not into /dev/null but deliver us from M$,
for thine is the domain, the cpu and the peeps.
for (x=0; x<2; x++){ x=0; }
Amen

When work just feels right

Much like Rahoul's post on knowing you're on the right path, I had that moment this morning whilst we were discussing a future feature for our control panel.

john: will I be able to superpoke our customers?
john: is that in the spec?
Jeremy: "John made server10 a zombie"
Caius: "server10 zombified server9"
Jeremy: "David has poked server19 19234 times"
john: server16 messaged David "My disk is filling up and I have files I need to put somewhere, please help!"
Caius: "Rahoul ended his friendship with server15"
David: "server02 is now married to server05"
Rahoul: server10 has sent you 12 videos of a fat man eating a cake
David: server11 joined the group "KVM ftw"
Caius: "server16 threw a sheep at server15"
john: server03 joined the group "Centos sucks!"
Caius: "server15 sent an emergency broadcast: physical movement detected"
john: storage03 has logged off
Caius: "x13 flew to the moon 0 times"
john: disk5 in storage02 is now a zombie
Jeremy: disk4 in storage02 is now a zombie
Jeremy: disk3 in storage02 is now a zombie
Jeremy: disk2 in storage02 is now a zombie
john: storage02 was sold on ebay by Jeremy
Caius: "john was sold on brightbox marketplace by storage5"

(In case you don't know, I work for Brightbox.)

Keyboards

Back in the day I swapped the keys on my 12" powerbook keyboard around to read macgenius across the middle row.

Powerbook Keyboard

I unearthed the picture, and figured, why not do it to my apple aluminium keyboard? So I found a tutorial from some other guy that'd done it, and dug out my penknife.

External Keyboard

After that I decided to rearrange the macbook internal keyboard as well. First I googled around to make sure lifting the keys was the same as doing it on the external keyboard (which it appeared to be), then I went ahead and rearranged them as well.

Internal Keyboard -- Macgenius

So whilst I was wondering what to do about it, my mother emailed me and suggested using ontherails instead of macgenius. So I did, and now the top row reads ontherails on the macbooks' internal keyboard.

Internal Keyboard -- Ontherails

All pictures are licenced under Creative Commons Attribution 2.0 Generic licence and the above pictures, plus some in progress shots, are available in my Keyboard Modifications flickr set.

OS X User has a virus

No not my computer, me. I've managed to pickup an acute coryza virus. It causes /dev/random to constantly pass data to /Volumes/nose. I don't think I got this from reading email, but I'm pretty sure it was a windows user who passed it to me.

If I flush my partitions to disk every now and then I can get by with a clean /Volumes/nose for a while before /dev/random starts clogging it up again.

dSLR Quote

I was just talking to Nadim about him getting a new camera and then this happened:

Nadim: http://cyraq.deviantart.com/art/Above-the-Clouds-72170767
Nadim: Did you see that?
Nadim: Taken couple of weeks ago
Caius: woa
Caius: and you need a new camera because? :P
Nadim: I have wet dreams about DSLR's

Quote to see the new year in

So I'm a geek, as my friends are gradually realising more and more. Anyway, I twitter a lot, and occasionally write quotes that need to be saved elsewhere. And what generally makes tweets funnier are other people replying to them.

A little background information. This had just taken place in #habari (a chat room.)

<h0bbel> ringmaster: i'll show you my winky
* ringmaster runs.
<h0bbel> as jmullan has seen it
<h0bbel> i think
<jmullan> no, I didn't look
<ringmaster> 0_o
<h0bbel> i was kinda drunk at the time
<h0bbel> lol
<jmullan> we were playing "lightsabers" while peeing
<jmullan> not "swords"
<jmullan> I had the yellow saber, h0bbel had the green one
<jmullan> I'm not sure how that worked
<h0bbel> you said you didn't look!
<h0bbel> How did you know it was green?
<jmullan> I just saw the beam, not the handle, if you know what I'm saying
<h0bbel> ehehe
<Caius> xD
<ringmaster> This is all somewhat disturbing.

So in order

And on the back of that, I'd just like to say have a very merry new years eve and just follow your desires onwards in life!