August 10, 2007

Why use parens in Ruby?

Here is another ruby gotcha. Ruby allows you to skip parenthesizing method calls. These two lines are equivelent:

set_table_name "users"
set_table_name("users")

In some cases this will make your code mildly more readable. In practice though this is dangerous. Consider this code:

if arr1.include? "foo" && !arr2.include? "bar" then
  # do something
end

Ruby order of operations says that && will be evaluated prior to calling my_array.include?. So first you evaluate:

"foo" && !arr2.include? "bar"

Which always returns true or false. Then you evaluate;

if arr1.include? true
  # do something
end

Which is clearly not what you intended. This code is correct:

if arr1.include?("foo") && !arr2.include?("bar") then
  # do something
end

As a rule, you should never call a function without parenthesizing the parameters. Even if you are writing code which is not in an IF statement or is in an IF statement, but is not a part of boolean logic, you should still parenthesize it because it is possible someone else will come along and change your code not realizing this gotcha exists. Code defensively, use parens.

July 20, 2007

Nice Ruby Reference

This site provides a references for many rdocs in one place

http://www.noobkit.com/

Covers:

ActionMailer 1.3.3
ActionPack 1.13.3
ActionWebService 1.2.3
ActiveRecord 1.15.3
ActiveSupport 1.4.2
Daemons 1.0.6
Ferret 0.11.4
Hpricot 0.6
net-ssh 1.1.2
pdf-writer 1.1.3
Rails 1.2.3
Rake 0.7.3
RedCloth 3.0.4
RFacebook 0.6.2
Rio 0.4.0
RSpec 1.0.5
Ruby 1.8.6
Syntax 1.0.0

May 15, 2007

Rails & MySQL gotcha

Just so you know, don't ever do this in MySQL:

SELECT * FROM foo WHERE id IS NULL;

Why you ask? Seems like a pretty normal thing to do.

MySQL has a known bug that will cause this to return the last row inserted into the DB, ie. something random, instead of what you are looking for. In reality, this was actually a design choice for MySQL. This was how they implemented the ability to figure out the ID for a row inserted with auto-increment. This is fixed in MySQL version 5.0.25 which will probably be in our next upgrade. If you have to use an older version of MySQL, do this:

def find_user_by_id(user_id)
return nil if user_id == nil

@user ||= User.find_by_id(user_id )
@user
end

not this:

def find_user_by_id(user_id)
@user ||= User.find_by_id(user_id )
@user
end

For more information, see...

http://bugs.mysql.com/bug.php?id=14553

April 25, 2007

ActionScript Delegate

In ActionScript there is an object you can use for passing references to members functions of a class called Delegate. This is useful for associating event listeners to a method on an instance of a class rather than a static:


function hookEvent(element)
{
element.onSelectionChange = Delegate.create(this, this.on_change);
}

function on_change() // <-- note: not a static
{
// do stuff
}

Using this helps a lot with your code organization. This pattern is also available in javascript though it's slightly more arcane:

setTimeout((function() {this.sendPoll()}).bind(this), this.options.timeout);

January 29, 2007

Log rotation in Ruby

I just checked in a modification to the standard log object that changes a few things to allow for nice log rotation. First, log objects you create through Log.create now use a wrapper class for the webrick log object. You can still use all the methods on the logger object like .info, .debug etc. However, the log engine now keeps track of all created logfiles & when it recieves a SIGUSR1 on linux it will reopen all the log files:


jay@foo:/var/log/call_svc$ ls
call_svc-std.log.1 call_svc-std.log.2
jay@foo:/var/log/call_svc$ ps -ef | grep ruby
jay 4435 4003 0 11:59 pts/0 00:00:02 ruby bin/ragi_server.rb
jay@foo:/var/log/call_svc$ kill -SIGUSR1 4435
jay@foo:/var/log/call_svc$ ls
call_svc-std.log call_svc-std.log.1 call_svc-std.log.2


So this means we can now create logrotate scripts that don't require restarting any services:


/var/log/call_svc/call_svc-std.log {
    rotate 30
    daily
    postrotate
        /sbin/kill -SIGUSR1 `cat /snapvine/pids/call_svc`
    endscript

}


I'll try to find a moment to clean it up and post it...

-jay