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