Ruby is a cool language with intuitive grammar. However there are a number of things which don’t seem to be expected. It might take long hours to debug some weird issues for unenlightened newbies.
Implicitly variable declaration
Variable mentioned in conditional block of code become declared and initialized with nil even if the declaration was not executed.
1 2 3 4 | |
Expected: addressing to var raises NameError: undefined local variable or method 'var'
Calling #utc and #gmt on Time object removes time zone information
1 2 3 4 5 6 7 | |
IMHO, this methods should be called utc! and gmtime! instead.
I have an experience when it caused a really voodoo thing: a test failed only from 20:00 to 00:00
in USA on CI server, and could never be reproduced in my time zone.
Instead it’s better to use getutc and getgm methods which return UTC and GMT time accordingly,
but don’t change Time object:
1 2 3 4 5 6 7 | |
Methods do not return value from ensure statement
Usually ruby methods return the value of the last method line unless return is called explicitly.
But how about this?
1 2 3 4 5 6 7 8 9 | |
So if you want to return a value from ensure statement use return word:
1 2 3 4 5 6 7 | |
Anchors ^ and $ do not mean a start and an end of a string
Most of script languages uses anchors ^ and $ of regular expressions as a start and an end of string accordingly.
But not in Ruby! In Ruby they are a start and an end of a line.
See the difference:
1 2 3 4 5 6 | |
Instead use \A and \z anchors. They are a start and an end of a string.
1 2 3 4 5 6 | |
Pay attention when you write validations. Read Egor Homakov’s article to get more information about it.
\m regexp option
When other languages use \s option to make . match newline, Ruby uses \m:
1 2 | |
Calling super and super() are not the same
There is no matter for ruby methods do you use parentheses or not. But be careful with super
since it’s not a method, but a key word.
Let me show an example when parentheses matter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
Output:
Child m1: arg = "foo"
Parent m1: arg = "foo"
Child m2: arg = "bar"
super.rb:6:in `m2': wrong number of arguments (0 for 1) (ArgumentError)
from super.rb:19:in `m2'
from super.rb:25:in `<main>'
When you use super it calls same method of parent class passing same arguments to it.
But when you use super(...) you have to pass arguments manually. In my example ArgumentError
was raised because Parent#m2 expects to receive exactly one argument, but nothing was passed to super()
However super() still delegates a passed block. If you don’t wanna pass
a block you have to do it explicitly using super(&nil):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Output:
Hi, m1
/tmp/super.rb:7:in `m2': no block given (yield) (LocalJumpError)
lambda and Proc.new act differently
It’s a well known thing but I want to remind.
There 2 differences between proc objects created with lambda and Proc.new:
lambdaraisesArgumentErrorif parameter is missing whenProc.newusesnilinstead.
1 2 3 4 5 | |
- For
lambdawordreturnmeans returning from proc object, when forProc.newit means returning from scope where proc is defined.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Note there is also method proc. In Ruby 1.8 it’s a synonym for lambda
but in Ruby 1.9 it’s a synonym for Proc.new. So avoid using proc to keep you code compatible
for both ruby versions.
DelegateClass instance does not eql itself
Ruby standard library provides DelegateClass which can be pretty useful. But some things are not so obvious about it:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
It happens because eql? is delegated to base object(animal):
1
| |
On other hand equal? is not:
1 2 | |
dup method do not work for all objects
As you might know every object in Ruby has dup
method inherited from Object class. It’s convenient, I like it. But it does not mean that every object
can be duplicated.
1 2 3 4 | |
It means you can’t make a deep copy of an array (with one level depth) like this:
1
| |
Or like this:
1
| |
Since all your objects respond to #dup.
ActiveSupport provides duplicable? method to inspect an ability
to duplicate an object, so finally your solution would look the next way:
1
| |
Internally ruby symbols and booleans point to constant memory addresses, and
there is no reason to copy them since they are unchangable.
But I wish the implementation of their #dup methods looked like this:
1 2 3 4 5 | |
So I hope the article was useful for you. If you know some fancy Ruby things I haven’t mentioned please let me know.
Thanks.