While learning ruby I’ve run into a few gotchas that I think may be worth discussion/sharing. It’s worth noting that I’m coming from a Java,C#,C based background. These may not seem that odd to people coming from different backgrounds.

  1. strings aren’t auto converted into numbers or strings
     irb(main):001:0> "1" + 1
     TypeError: can't convert Fixnum into String
         from (irb):1:in `+'
         from (irb):1
    
  2. false and nil are the only valid false values, anything else is true
     irb(main):001:0> p "rats" if 1 == true
     => nil
     irb(main):002:0> p "rats" if 0 == false
     => nil
     irb(main):003:0> p "yay?" if nil
     => nil
     irb(main):004:0> p "oro?" if 6
     "oro?"
     => nil
     irb(main):005:0> p "oro?" if -6
     "oro?\n"
     => nil
     irb(main):005:0> p "will print" if 0
     "oro?\n"
     => nil
    
  3. && and || are the high priority AND/OR constructs, and and or are low priority versions

     irb(main):016:0> x = nil or 99
     => 99
     irb(main):017:0> x
     => nil
     irb(main):018:0> x = 42 and 99
     => 99
     irb(main):019:0> x
     => 42
     irb(main):020:0> x = nil || 99
     => 99
     irb(main):021:0> x
     => 99
     irb(main):022:0> x = 49 && 100
     => 100
    

    That last one came as a surprise to me, as I expected it to return true. Guess not.

  4. Exceptions and raise vs catch

    catch/throw are not the same as raise/rescue. catch/throw allows you to quickly exit blocks back to a point where a catch is defined for a specific symbol, raise rescue is the real exception handling stuff involving the Exception object. If your exception doesn’t inherit from StandardError it will NOT be caught by default!

      irb(main):001:0> class MyException < Exception
      irb(main):002:1>   end
      => nil
      irb(main):003:0> def exception_miss
      irb(main):004:1>   raise MyException.new
      irb(main):005:1>   rescue
      irb(main):006:1>   p "saved!"
      irb(main):007:1>   end
      => nil
      irb(main):008:0> begin
      irb(main):009:1*     exception_miss
      irb(main):010:1>   rescue Exception
      irb(main):011:1>   p "not so much"
      irb(main):012:1>   end
      "not so much"
      => nil
      irb(main):015:0> def exception_hit
      irb(main):016:1>   raise NewException
      irb(main):017:1>   rescue
      irb(main):018:1>   p "excellent!"
      irb(main):019:1>   end
      => nil
      irb(main):020:0> exception_hit
      "excellent!"
      => nil
      irb(main):021:0> def catcher
      irb(main):022:1>   throw :testing
      irb(main):023:1>   rescue
      irb(main):024:1>   p "gotcha?"
      irb(main):025:1>   end
      => nil
      irb(main):026:0> catcher
      "gotcha?"
      => nil
      irb(main):071:0> def throw_no_rescue
      irb(main):072:1>   throw :gotme
      irb(main):073:1>   end
      => nil
      irb(main):074:0> def fire
      irb(main):075:1>   catch(:gotme) do
      irb(main):076:2*       p "start"
      irb(main):077:2>     throw_no_rescue
      irb(main):078:2>     p "after throw"
      irb(main):079:2>     end
      irb(main):080:1>   p "after catch"
      irb(main):081:1>   end
      => nil
      irb(main):082:0> throw_no_rescue
      NameError: uncaught throw `gotme'
              from (irb):72:in `throw'
              from (irb):72:in `throw_no_rescue'
              from (irb):82
              from ♥:0
      irb(main):083:0> fire
      "start"
      "after catch"
      => nil
    
  5. no class/module unloading
  6. no native unicode (ruby < 2.0)
  7. private never truely private
     irb(main):001:0> class MonkeyNinja
     irb(main):002:1>   private
     irb(main):003:1>   def secret_weakness
     irb(main):004:2>     p "eep! dead monkey!"
     irb(main):005:2>     end
     irb(main):006:1>   end
     => nil
     irb(main):007:0> m = MonkeyNinja.new
     => #<MonkeyNinja:0x2c01d5c>
    
     irb(main):008:0> m.methods
     => [...no secret weakness! ]
    
     irb(main):009:0> m.private_methods
     => [stuff, "secret_weakness", "lambda"]
     irb(main):010:0> m.send :secret_weakness
     "eep! dead monkey!"
     => nil
    
     irb(main):011:0> m.secret_weakness
     NoMethodError: private method 'secret_weakness' called for #<MonkeyNinja:0x2c01d5c>
             from (irb):10
             from ♥:0
    
     irb(main):012:0> m.instance_eval do
     irb(main):013:1*     secret_weakness
     irb(main):014:1>   end
     "eep! dead monkey!"
     => nil
    
     irb(main):015:0> def m.kill_you
     irb(main):016:1>   secret_weakness
     irb(main):017:1>   end
     => nil
     irb(main):018:0> m.kill_you
     "eep! dead monkey!"
     => nil
    
  8. ½ != 0.5 unless require ‘mathn’ (this is the default in many languages anyway, sometimes integer math is your friend). With mathn ½ becomes a different type (Rational) than 0.5 (Float).
     irb(main):010:0> 1.0/2
     => 0.5
     irb(main):011:0> 1/2
     => 0
     irb(main):012:0> p "normalcy" if 1/2 == 0.5
     => nil
     irb(main):013:0> require 'mathn'
     => true
     irb(main):014:0> 1/2
     => 1/2
     irb(main):015:0> p "normalcy" if 1/2 == 0.5
     "normalcy"
     => nil
     irb(main):016:0> a = 0.5
     => 0.5
     irb(main):017:0> a.class
     => Float
     irb(main):018:0> b = 1/2
     => 1/2
     irb(main):019:0> b.class
     => Rational
    
  9. class variables and their inheritance semantics
     irb(main):001:0> class OmgFun
     irb(main):002:1>   @@taco = "testing"
     irb(main):003:1>   def taco
     irb(main):004:2>     @@taco = "me"
     irb(main):005:2>     end
     irb(main):006:1>   def self.to_s
     irb(main):007:2>     @@taco
     irb(main):008:2>     end
     irb(main):009:1>   end
     => nil
     irb(main):010:0> class B < OmgFun
     irb(main):011:1>   @@taco = "B's taco"
     irb(main):012:1>   end
     => "B's taco"
     irb(main):013:0> class C < B
     irb(main):014:1>   @@taco = "C's taco"
     irb(main):015:1>   end
     => "C's taco"
     irb(main):016:0> C.to_s
     => "C's taco"
     irb(main):017:0> B.to_s
     => "C's taco"
     irb(main):018:0> OmgFun.to_s
     => "C's taco"
    
  10. singleton class modification for the class objects isn’t good or really allowed
    irb(main):001:0> class Daffy
    irb(main):002:1>   @@mine = "all mine"
    irb(main):003:1>   def to_s
    irb(main):004:2>     @@mine
    irb(main):005:2>     end
    irb(main):006:1>   end
    => nil
    irb(main):007:0> class << Daffy
    irb(main):008:1>   def jigga
    irb(main):009:2>     @@mine = "bunny time"
    irb(main):010:2>     end
    irb(main):011:1>   end
    => nil
    irb(main):012:0> d = Daffy.new
    => all mine
    irb(main):013:0> Daffy.jigga
    (irb):9: warning: class variable access from toplevel singleton method
    => "bunny time"
    irb(main):014:0> d
    => all mine
    irb(main):015:0> d = Daffy.new
    => all mine
    irb(main):016:0> def Daffy.wha
    irb(main):017:1>   @@mine = "wabbit"
    irb(main):018:1>   end
    => nil
    irb(main):019:0> Daffy.wha
    => "wabbit"
    irb(main):020:0> d
    => all mine
    irb(main):021:0> d = Daffy.new
    => all mine
    

    or

    irb(main):001:0> class A
    irb(main):002:1>   @@foo = "bar"
    irb(main):003:1>   def self.print_foo
    irb(main):004:2>     p @@foo
    irb(main):005:2>     end
    irb(main):006:1>   end
    => nil
    irb(main):007:0> A.print_foo
    "bar"
    => nil
    irb(main):008:0> A.class_eval do
    irb(main):009:1*     @@foo = "no go"
    irb(main):010:1>   end
    => "no go"
    irb(main):011:0> A.print_foo
    "bar"
    => nil
    irb(main):012:0> A.class_eval do
    irb(main):013:1*     def self.new_hotness
    irb(main):014:2>     p @@foo
    irb(main):015:2>     end
    irb(main):016:1>   end
    => nil
    irb(main):017:0> A.new
    A.new          A.new_hotness
    irb(main):017:0> A.new
    A.new          A.new_hotness
    irb(main):017:0> A.new_hotness
    "no go"
    => nil
    irb(main):018:0> A.instance_eval do
    irb(main):019:1*     p @@foo
    irb(main):020:1>   end
    "no go"
    => nil
    irb(main):021:0> A.module_eval do
    irb(main):022:1*     p @@foo
    irb(main):023:1>   end
    "no go"
    => nil
    irb(main):024:0> A.print_foo
    "bar"
    => nil
    
  11. assignment methods always return the assigned value, not the return value of the assignment function
    irb(main):001:0> class Foo
    irb(main):002:1>   def self.bar=(val)
    irb(main):003:2>     return "apeiros!"
    irb(main):004:2>     end
    irb(main):005:1>   end
    => nil
    irb(main):006:0> Foo.bar = "meep"
    => "meep"
    irb(main):007:0> Foo.bar
    NoMethodError: undefined method 'bar' for Foo:Class
            from (irb):7
            from ♥:0
    
  12. local variable scoping is tricky and can hide other variables and methods (this is slated for a change in ruby 2.0)
    irb(main):008:0> class Pizza
    irb(main):009:1>   def my_hut
    irb(main):010:2>     p "tasty!"
    irb(main):011:2>     end
    irb(main):012:1>   def breadstick
    irb(main):013:2>     if nil
    irb(main):014:3>       my_hut = "never executed"
    irb(main):015:3>       end
    irb(main):016:2>     my_hut
    irb(main):017:2>     end
    irb(main):018:1>   end
    => nil
    irb(main):019:0> m = Pizza.new
    => #<Pizza:0x2c63cc8>
    irb(main):020:0> m.breadstick
    => nil
    
  13. Another class variable trouble spot. You might think that class_eval would provide access to the class’s context and variables, apparently not so much. This example/issue came from Gary at a New Haven Ruby Brigade meeting.
    irb(main):001:0> class A
    irb(main):002:1>   @@avar = "hello"
    irb(main):003:1>   end
    => "hello"
    irb(main):004:0> A.class_variables
    => ["@@avar"]
    irb(main):005:0> A.class_eval { puts @@avar }
    NameError: uninitialized class variable @@avar in Object
            from (irb):5
            from (irb):5:in 'class_eval'
            from (irb):5
            from ♥:0
    irb(main):006:0> A.instance_eval { puts @@avar }
    NameError: uninitialized class variable @@avar in Object
            from (irb):6
            from (irb):6:in 'instance_eval'
            from (irb):6
            from :0
    irb(main):007:0> A.module_eval { puts @@avar }
    NameError: uninitialized class variable @@avar in Object
            from (irb):7
            from (irb):7:in 'module_eval'
            from (irb):7
            from ♥:0
    irb(main):008:0> class A
    irb(main):009:1>   def my_avar
    irb(main):010:2>     @@avar
    irb(main):011:2>     end
    irb(main):012:1>   end
    => nil
    irb(main):013:0> a = A.new
    => #<A:0x2c27890>
    irb(main):014:0> a.my_avar
    => "hello"
    irb(main):015:0> a.instance_eval { puts @@avar }
    NameError: uninitialized class variable @@avar in Object
            from (irb):15
            from (irb):15:in 'instance_eval'
            from (irb):15
            from :0
    

posted on December 14, 2006