Corporate Leave Policies?

Quick question: if your company has a defined leave policy (i.e. you’re not a freelancer), what is it? How much paid time off/vacation do you get a year? And if you happen to know, how much paid paternity/maternity leave are you eligible for?

Thanks!

Class.new and .inherited()

In Ruby, the typical way to define a class is using the class keyword:

class Foo
  # ...
end

The class keyword, however, is effectively just syntax sugar for the Class constructor:

Foo = Class.new
  # ...
end

Using Class.new is occasionally preferable, e.g. when you want an anonymous class which isn’t assigned to a constant:

@myclass = Class.new
  # ...
end

I like to use this technique for certain metaprogramming tasks, and when testing/spec-ing modules:

describe MyModule
  before :each do
    @test_class = Class.new do
      include MyModule
    end
  end

  # ...

end

There are a few subtle semantic differences between the class keyword and Class.new. Because the Class constructor uses a block to define the contents of the class, it can reference the surrounding lexical scope:

>> method_name = "foo"
=> "foo"
>> class KeywordClass
>>   define_method(method_name) do
?>       puts "hello"
>>     end
>>   end
NameError: undefined local variable or method `method_name' for KeywordClass:Class
        from (irb):3
>> DynamicClass = Class.new do
?>     define_method(method_name) do
?>       puts "hello"
>>     end
>>   end
=> DynamicClass
>> DynamicClass.new.foo
hello
=> nil

I’d known about this difference for a long time. The other day I came across another difference between these two methods of class definition which was new to me, and can lead to potentially surprising behavior. It concerns the order of execution and the .inherited() callback.

When a parent class defines a class method called inherited:

class A
  def self.inherited(other)
    puts "in A.inherited"
  end
end

And then that class is subclassed:

class B < A
  puts "in class B body"
end

The order of execution is 1) run the A.inherited callback; then 2) execute the class definition body. You can see this if you run the above two blocks; the output will be:

in A.inherited
in class B body

If, however, we try to inherit from A dynamically, using the Class constructor:

C = Class.new(A) do
  puts "in class C body"
end

The output is reversed:

in class C body
in A.inherited

I’m not sure if either of these behaviors is right or wrong, per se; but it can catch you by surprise if you are expecting dynamic class definition to have the same order of operations as the keyword form. In particular, it can lead to some confusing side effects when using certain libraries that extend the behavior of Ruby’s classes. For instance, I discovered this difference while working on some code that used the class-inheritable attributes feature of ActiveSupport:

myclass = Class.new do
  class_inheritable_accessor :bar
  self.bar = 42
end
>> myclass.bar
=> nil
>> # hey!  where'd the value of bar go?! 

As it turns out, class_inheritable_accessor and its friends work by defining Object.inherited to initialize some variables that ActiveSupport uses for bookkeeping. In the dynamic form of the class definition, the class body would assign values to the inheritable attributes – and then Object.inherited would be called back, re-initializing the variables and wiping out my assigned values. The solution was to separate the class creation and definition into two steps:

myclass = Class.new
myclass.instance_eval do
  class_inheritable_accessor :bar
  self.bar = 42
end

So there you go, another, lesser-known semantic distinction between “static” and “dynamic” class definition in Ruby.

Switching to Disqus

I’ve been pretty unsatisfied with the default WordPress commenting system for a number of reasons. And I’ve received several complaints about the way OpenID was being handled in comments. I’ve switched the comments over to being managed by “Disqus”:http://disqus.com. Hopefully this will address the issues and be more convenient for all concerned.

Always something new

After 5-6+ years of working with Ruby, I am still periodically reminded of features I’d forgotten about. Today it was the fact that you can override the backtick operator:

>> def `(cmd)
>>   puts "Do you really want to #{cmd}?"
>>   end
=> nil
>> `rm -rf *`
Do you really want to rm -rf *?
=> nil

The Trifecta of FAIL; or, how to patch Rails 2.0 for Ruby 1.8.7

It’s an oft-stated fact that most disasters result not from a single point of failure but from a combination of failures reinforcing each other. I wouldn’t term the problem I ran into last Friday a disaster, but it certainly cost me several hours of time trying to find a workaround.

Culprit #1: Rails

Rails’ ActiveSupport added a handy little method called #chars to the String class. In and of itself this doesn’t seem like such a bad thing, and a lot of other handy methods in ActiveSupport are built on top of #chars. However, as we’ll see, taking advantage of Ruby’s open classes to extend core types has a way of drawing the unwanted attention from the Law of Unintended Consequences.

Culprit #2: Ruby

It’s not set in stone anywhere, but there’s a fairly well accepted convention in open source projects that versions are divided into a major version, a minor version, and a tiny or patch version. New major versions indicate API-breaking changes. A new minor version may introduce new features, but existing code should continue to work as-is. And a new tiny version indicates that the API remains fixed; the only difference is that bugs have been fixed and security holes patched.

Ruby 1.8.7 is a minor release masquerading as a tiny release. Among the features backported into 1.8.7 from Ruby 1.9 is a new #chars attribute. Unfortunately, it is incompatible with the Rails 2.0 implementation of #chars. This, incidentally, is a prime example of one of the subtler ways that patching the core classes can bite you. Even if you are adding new methods rather than re-writing existing ones, the chances are good that someone else will have the same idea only with a slightly different implementation and semantics. Bang, incompatibility.

Culprit #3: MacPorts

We have an app which has not yet been ported to Rails 2.1. This, in itself, would not have been a problem; we can keep running it under Ruby 1.8.6 with Rails 2.0, no problem. However, I have a nasty habit of trying to keep my software up to date. So I run sudo port upgrade outdated periodically, and watch all the errors from unmaintained ports go scrolling across my terminal for 24 hours or so.

The last time I did this, one of the ports that did manage to build was Ruby. Version 1.8.7. The next time I ran our app, it of course promptly crashed.

This is the point at which I discovered something I hadn’t realized about MacPorts: it has no downgrade path. Coming from the world of Debian, Ubuntu, and apt-get, I just expected any package management system to handle the case where the user specifies an older version to be installed.

In fact, there’s a way to do it in MacPorts, but it’s painful.

Fail.

fail owned pwned pictures
So there I was with a broken app, no time in the iteration to upgrade it to Rails 2.1, and no easy way to get back to Ruby 1.8.6. Lame.

Rescue

After bitching and moaning on Twitter for awhile, I decided Bob helps those who help themselves, so I took a look at the crash backtrace I was getting. I traced it back to a line in vendor/rails/activesupport/lib/activesupport/core_ext/string/access.rb:

        def first(limit = 1)
          chars[0..(limit - 1)].to_s
        end

In the Rails 2.0 version of String#chars, #chars returns an Array or Array-like object which can be subscripted with #[]. The Ruby 1.8.7 version, by contrast, returns an Enumerable::Enumerator.

“That’s easy enough” thought I, and, fully expecting that patching this one issue would just reveal another incompatibility, and another, and another…, I changed the code to:

        def first(limit = 1)
          chars.to_a[0..(limit - 1)].to_s
        end

Lo and behold, the app worked perfectly.

Of course, YMMV. But as a quick kludge this one was surprisingly painless.

Lessons Learned

Here’s what I took away from this experience:

  1. Be wary of adding methods to core classes. What could possibly go wrong? More than you think.
  2. Patch releases should be true patch releases. It’s tempting to include a neat new feature as a bonus –
    again, what could possibly go wrong? Resist this urge.
  3. Macs are shiny, but for industrial-strength development support, nothing beats a Debian-based system with APT.
  4. Every now and then taking a clawhammer to vendor code is the shortest (short-term) way from point A to point B. Personally I prefer to either keep this kind of change local or, if necessary, version it with something like Piston, rather than maintaining it as a monkey-patch.
[ad#PostInline]

Phenomenal Cosmic Power

Jeff Atwood thinks monkeypatching should frighten you:

But if wielding that power doesn’t scare and humble you a little, too, then maybe you should leave the monkeypatching to the really smart monkeys.

Reg Braithwaite thinks it should exhilarate you:

And ultimately, that is what this line of code says to me about Ruby. It says that this is a language where the fringe is inventing new things. And to embrace ruby is to embrace the idea of a language being propelled by its user base.

I’m okay with either reaction. Metaprogramming may be pure and godlike, but then again, thou art God. It’s the people who have no reaction whatsoever that scare me. Use of power should be conscious and intentional. Otherwise you’re like a woodworker who addresses a mismatched joint by just hammering harder.

Revision-Stamp Your Rails Apps

One of my biggest pet peeves in doing QA is when I spend half a day trying to track down why a bug I thought I’d fixed has cropped up again, only to discover that the reporter was looking at an old version of the application. Good revision tracking is vital to doing efficient QA. Here’s a little trick for removing any uncertainty about what version of the app you’re looking at.

Capistrano, after it exports an application from an SCM repository (such as a Subversion repository), writes an extra file to the application’s root directory. The file is named “REVISION”, and, as you might imagine, it contains the revision number that was exported. Displaying this information in your application’s UI is straightforward. Here’s the addition I made to a view partial that is displayed on the application’s home page:

  <p>Application revision < %= app_revision %></p>

And here’s the app_revision helper method:

module ApplicationHelper
  # ...
  def app_revision
    revision_file = File.join(RAILS_ROOT, "REVISION")
    if File.readable?(revision_file)
      IO.read(revision_file)
    else
      "unknown"
    end
  end
end

Of course, this isn’t very efficient – it will re-load the file every time a page is rendered. Optimization is left as an exercise to the reader. But it accomplishes its purpose: now whenever someone reports a bug in the app, I can ask them to go to the application home page and tell me what the revision number is. No more trying to remember whether I pushed out the latest changes to the beta server.

Epic Fail, or, why the users hate us.

[I’m reposting this from my “personal journal”:http://avdi.livejournal.com as it has development applicability – in so much as it is a tale about how NOT to design software.]

So I’ve been trying to wire money to Kenya. No, I haven’t been contacted by the wife of a former dictator who needs my urgent assistance in moving large sums of money. Just doing someone a favor.

I go to the website. I’ve used it before, so I figure I’ll log in to my account. Try to log in with my usual credentials. Uh oh.

W0500 Sorry you're having trouble. Please try again later.

This does not bode well. I’m having trouble? News to me. And if the trouble is on my end, what good will trying again later do? Are they suggesting that I am having brain issues which will resolve themselves in a few hours?

OK, assume this is just their eccentric way of saying “login failed”. I have a dim recollection of not being able to use one of my regular passwords because they don’t allow special characters in their passwords (why is it always institutions which handle large sums of money that have poor password policies?) Email customer service, and meanwhile, click the “retrieve password” link. Notice while emailing customer service the lengthy backlog of previous Western Union customer service mail from the last time I tried to wire money through them. Oh dear, I must have blocked those memories.

OK, well, chin up and keep trying. While waiting for customer service, hit the “forgot password” link. Go through the first step, email address, date of birth… that’s odd. This is probably the first site I’ve ever seen where the “Date of Birth” field is a password-style field. Presumably so no potential identity thieves peering over my shoulder witness that top-secret information, and I have to go through all the hassle of changing my birth date to something new.

Anyway, moving on, ah, the Security Question.  OK, what’s it going to be – good ole’ mother’s maiden name?  First pet?  Model of car in which I first got to third base? Nope, not good enough for Western Union security.  They’ve gone the extra mile and required me to guess the question before supplying the answer. Now that’s secure!

Email customer service again. Looks like there’s a response to my first query already.  They are prompt, I’ll give them that.  Let’s see…

We apologize for the inconvenience, due to security reasons the system blocks the access after 2 or more unsuccesfull attempts.

Reply to customer service informing them that that’s nice, but this happened the very first time I’d logged in in months.

Oh look, a reply to my second problem.  They sure are on the ball over there.

We apologize for any problems you experienced with our site. However, if you are not being able to sign in with your current e-mail address and password, we may offer to deactivate your account so you may register again. Please keep in mind that no one at Western Union knows or can retrieve your password for you. If you wish to have your account deactivated, please send your request in an email to messaging@westernunion.com from the email address you used when you registered, and include your registered name, address and telephone number. Please note that if you have another e-mail address, you may use it to create a new account.

Ah, form letters detailing how to work around the flaws in the system.  Always a promising sign.

OK, let’s try setting up a new account under a different email address.  Let’s see, click “Sign UP”, enter name, rank, and serial number.  Like nearly every user interaction page in this system, the page has a CAPTCHA at the bottom.  Except this CAPTCHA has a difference: there’s no CAPTCHA, just the alt text.

Click little “reload” icon, no good.  Click the “audible captcha” icon – leads to a blank page.  I think they’ve hit upon an unbeatable anti-spam strategy here – a CAPTCHA you have to interpret via mental telepathy.

Deep breath.  Let’s take a time out and check our email, shall we?

Unfortunately, your e-mail has arrived blank.  Please resubmit your question or comment and we will be happy to assist you. Also, please keep in mind that we are unable to view attachments, enclosures, or hyperlinks.

Looks like they left something out of that list of things they are unable to view – replies to their own useless boilerplate messages.  It appears that Western Union has adopted the time-tested “hear no evil, see no evil”  approach to technical support.  If the techs can’t view the problem, it doesn’t exist!

While we’re checking email, let’s email them about their amazing vanishing CAPTCHA.  Prompt reply, as usual.

Usually when this happens, it is because of problems with the server. You may wish to double-check your Internet settings, or try again later. Please note that our site works best with Internet Explorer version 6.0. We are currently not compatible with Mozilla, Firefox nor Safari.

Western Union, after doing a costly internal audit to discover where their inefficiencies lay, discovered that 99% of their problems with online transactions stemmed from those annoying customers.  Excluding 20-30% of the web browsing population by limiting the web site to IE6 compatibility neatly alleviated the problem, and the assistant vice-president who thought of the idea was immediately promoted.  He is now working on a grand enterprise-wide plan to enhance the customer experience by requiring all requests to be submitted on Sumerian cuneiform tablets.

So let’s review.  I can’t log in.  I can’t reset my password. And I can’t register a new account.  Well, we gave it our best shot. Time to fall back on ye olde reliable wireless telephone.

Call Western Union… you can probably guess by now where this is going.  I’ll spare you the play-by-play and sum up.  I called, I punched in my information. I was put on hold for 15 minutes while screechy distorted musaak drilled slowly into my ear canal.  Then they disconnected me.  I called back and discovered that while I was on hold they had helpfully cancelled my transaction.  I spoke to a manager, re-started the transaction.  Gave them all my information over again, then sat on hold for another ten minutes or so.  Finally, after a total of 50 minutes on the phone, they informed me that my transaction had been rejected by the ineffable machines which sometimes grant, and sometimes deny, and no one can say why… for security reasons.

EPIC FAIL.

Does anyone know of a method for transferring funds overseas that doesn’t leave one with an urge to bash one’s forhead repeatedly into a concrete post?

You should be on ruby-talk

Working as I do in the Rails world these days, I’m periodically reminded of the difference between me and most Rails programmers. That is, the fact that I came to Rails via Ruby, rather than vice-versa. Usually this happens when someone at work or in the blog world expresses delight (or perplexity) about some Ruby feature that I thought everyone knew about. This then prompts me to put on my best “old coot” voice and ramble on about “young whippersnappers” with their Rails and their fancy conferences and their big pants and… get off my lawn, ya darned kids!

One of the biggest disconnects for me is the fact that almost no one I know in the Railsverse reads “ruby-talk”:http://blade.nagaokaut.ac.jp/ruby/ruby-talk/index.shtml regularly. Having gotten into Ruby somewhere between five and seven years ago, it’s difficult for me to imagine being a part of the Ruby community and not being privy to the discussions in ruby-talk. Ruby-talk was one of the first programming communities I ever regularly participated in (the other one was the Pragmatic Programmer mailing list). It was a warm and welcoming place compared to, say, the Perl communities of the time; full of the spirit of joyous discovery and show-and-tell that this amazing new language from Japan tended to inspire.

The S/N ratio has declined somewhat since some joker hooked up a conduit between “ruby-forum”:http://www.ruby-forum.com/forum/4 and the mailing list, leading in a steady stream of Rails newbies who have it confused with the RoR forum. But it’s still one of the friendliest and most helpful programming communities out there. And nowhere else – not on blogs, not on Reddit, nor on Twitter – will you find better and more thoughtful discussions on Ruby style, idioms, alternate ways of accomplishing tasks, gotchas, and just plain fun Ruby tricks. And, because new Ruby libraries and frameworks are usually announced there, it’s also a great place to find out about what’s going on in the wider Ruby universe – beyond Rails, beyond web development entirely.

I’m going to make a bold statement here:

If you are a developer working in Ruby, you should be reading ruby-talk.

No, let me amend that:

If you are a developer working in Ruby, you should be -reading- contributing to ruby-talk.

Because apart from being a great place to pick up new techniques, it’s also one of the easiest opportunities to give back to the Ruby community. Check in from time to time, and if a newbie asks a simple question you know the answer to, help them out! It takes five minutes, and it helps perpetuate the culture of supportiveness that is a big part of what makes Ruby so special.

It’s easy to get involved with ruby-talk. There are multiple interfaces. There is the “mailing list itself”:http://blade.nagaokaut.ac.jp/ruby/ruby-talk/index.shtml, which is how I interact with it. Then there is the mail-to-news gateway, which enables you to read it as the Usenet newsgroup comp.lang.ruby. You can get to it through “Gmane”:nntp://news.gmane.org if your ISP doesn’t provide NNTP service. Or you can interact via the “Google Groups frontend”:http://groups.google.com/group/ruby-talk-google. And finally, there’s the aforementioned “ruby-forum gateway”:http://www.ruby-forum.com/forum/4.

Whichever route you choose, I hope to see you on ruby-talk soon!