Ruby Quirks

by on April 11, 2007

Ruby Quirks – peculiarity of behavior? I know this topic is debatable and remember ‘one man’s meat is another man’s poison!

I plan to write down here (in no particular order), some of the little Ruby quirks that I’ve picked up and which, I now use comfortably.

1. Peter Cooper, the author of the book ‘Beginning Ruby’ introduced me to Real-Time chat using an IRC client. On the #ruby channel at irc://irc.freenode.net/ I heard of this quirk:

class MotorCycle
  def initialize(make, color)
    @make, @color = make, color
  end
end

m = MotorCycle.new('Honda', 'blue')
m.instance_variable_set(:@make, 'Kawasaki')
m.instance_variable_set(:@gears, 4)
puts m.inspect

Check the output of the above program. In the code above:

m.instance_variable_set(:@gears, 4)

sets the instance variable names by symbol to object, thereby frustrating the efforts of the class’s author to attempt to provide proper encapsulation. The variable did not have to exist prior to this call.
Update: Hal Fulton in his excellent book ‘The Ruby Way’ has this to say about instance_variable_set:

It’s true these methods are powerful and potentially dangerous. They should be used cautiously, not casually. But it’s impossible to say whether encapsulation is violated without looking at how these tools are used. If they are used intentionally as part of a good design, then all is well. If they are used to violate the design, or to circumvent a bad design, then all is not well.

2. This one is not really a quirk but appears to be one, especially for people coming from a Java background. Last year, Shashank Date gave the PuneRuby members a presentation on ‘Why Ruby Shines’ and three points stood out – ‘Expressions everywhere’, ‘Active Class Definitions’ and ‘Everything is an Object’.

Expressions everywhere – In Ruby, everything returns some value. Therefore a class definition is an expression and one can say something like:

c = class C
end

The value of c is nil.

Active Class Definitions – Look at the following program:

class C
  puts 'In class C'
end

When this class is read the first time, it executes puts and the output is – ‘In class C’.

Everything is an Object – After being with Java since 1995, the concept that classes in Ruby are first-class objects, is hard to digest at first – each is an instance of class Class. When a new class is defined (typically using class Name … end), an object of type Class is created and assigned to a constant (Name. in this case). Hal Fulton’s suggests a mantra to be recited everyday – “Class is an object, and Object is a class.”

3. If I want to swap two variables, I would normally use an additional temporary variable. In Ruby, this is not necessary:

x, y = y, x

will interchange the values of x and y.

4. Jaaron, a reader of this Learning Ruby Blog has this quirk for us. This one is well known and is the cause of much frustration.

x = 7
[1,2,3].each do |x|
end

If the name of a block parameter conflicts with the name of a local variable, the behavior is to assign the local variable to the argument. In this case, the local variable x gets assigned the value 1, then the value 2, then the value 3. The value 7 is lost.
Update (13th April): Matt Chen, Matthew King and Ara Vartanian have commented on this behavior. If you refer to Programming Ruby Second Edition eBook (page 100) it says:

The whole issue with variable scope and blocks is one that generates considerable discussion in the Ruby community. The current scheme has definite problems (particularly when variables are unexpectedly aliased inside blocks), but at the same time no one has managed to come up with something that’s both better and acceptable to the wider community. Matz is promising changes in Ruby 2.0.

5. Are instance variables inherited by a sub-class? David Black the author of Ruby for Rails has this to say: Instance variables are per-object, not per-class, and they’re not inherited. But if a method uses one, and that method is available to subclasses, then it will still use the variable — but “the variable” in the sense of one per object. See the following program:

class C
  def initialize
    @n = 100
  end

  def increase_n
    @n *= 20
  end
end

class D < C
  def show_n
    puts "n is #{@n}"
  end
end

d = D.new
d.increase_n
d.show_n

The output is:

>ruby p049instvarinherit.rb
n is 2000
>Exit code: 0

6. Morgan Schweers, a reader of this blog has this quirk for us. Imagine for a moment, that you want to be able to set a variable, but if it’s not set, you default to a known value. You’d rather do it on a single line.

One of my co-workers tried this:

expand = defined?( expand ) ? expand : true

but ‘expand’ is *defined* by being on the left hand side, BEFORE the RHS is evaluated, so defined? returns true, but because expand hasn’t got a value yet, it returns nil.

I tried:

expand = true unless defined?(expand)

and it doesn’t help either, which really shocked me. I always believed that the postfix-conditional was evaluated before even beginning to evaluate the operation, but I was distinctly disabused of this notion by testing.

Note that ‘expand?’ operator returns nil if its argument (which can be an arbitrary expression) is not defined; otherwise it returns a description of that argument.

I don’t understand the reason for the behavior, and I think it’s a bug, but I’d love to know a good language reason for it.

I am sure that you would have noticed many other Ruby Quirks. I’d definitely like to hear and add them here.


Information

Ruby is an open source thorough language of utmost balance with combination of Perl, Smalltalk, Lisp, Ada and Eiffel. With the recent developments in this language, most developers use this language in the middle tier. Today is the era of developing online business applications, mobile software and its applications. Most developers deploy their online applications after getting business web hosting and then they market for these projects through affiliate marketing campaigns or if they do not want to sell their project they can use ppc advertising for earning some revenue or can offer ppc management programs. When they are in the process of development usually they register domain and use domain parking facility, so that once their project is ready they can instantly put it on the internet.


Technorati Tags:

Posted by Satish Talim

Follow me on Twitter to communicate and stay connected

{ 12 comments… read them below or add one }

jaaron April 11, 2007 at 10:42 pm

This one is well known and is the cause of much frustration

x = 7
[1,2,3].each do |x|
end

If the name of a block parameter conflicts with the name of a local variable, the behavior is to assign the local variable to the argument. In this case, the local variable x gets assigned the value 1, then the value 2, then the value 3. The value 7 is lost.

Reply

Matt Chen April 13, 2007 at 8:00 pm

For Quirk 4,

The while, until, and for loops are built into the language and do not introduce new scope; previously existing locals can be used in the loop, and any new locals created will be available afterward.

The blocks used by iterators (such as loop and each) are a little different. Normally, the local variables created in these blocks are not accessible outside the block.

However, if at the time the block executes a local variable already exists with the same name as that of a variable in the block, the existing local variable will be used in the block. Its value will therefore be available after the block finishes.

== If you find more detail info about variable scope in chapter 7 of Programming Ruby 2nd.

Reply

Matthew King April 13, 2007 at 8:43 pm

“If the name of a block parameter conflicts with the name of a local variable, the behavior is to assign the local variable to the argument. In this case, the local variable x gets assigned the value 1, then the value 2, then the value 3. The value 7 is lost.”

This is because blocks are closures, and closures can refer to variables visible at the time they were defined (http://www.martinfowler.com/bliki/Closure.html)

Generally, closures are considered useful.

Reply

Ara Vartanian April 14, 2007 at 2:25 pm

I found this weird quirk on scoping a few days ago.

Reply

satish April 14, 2007 at 3:05 pm

The Programming Ruby Book second edition has this to say – “The whole issue with variable scope and blocks is one that generates considerable discussion
in the Ruby community. The current scheme has definite problems (particularly
when variables are unexpectedly aliased inside blocks), but at the same time no one has managed to come up with something that’s both better and acceptable to the wider community. Matz is promising changes in Ruby 2.0.”

Reply

Morgan Schweers April 15, 2007 at 6:12 am

Greetings,
Imagine for a moment, that you want to be able to set a variable, but if it’s not set, you default to a known value. You’d rather do it on a single line.

One of my coworkers tried this…
expand = defined?( expand ) ? expand : true

but ‘expand’ is *defined* by being on the left hand side, BEFORE the RHS is evaluated, so defined? returns true, but because expand hasn’t gotten a value yet, it returns nil. :(

I tried:
expand = true unless defined?(expand)

and it doesn’t help either, which really shocked me. I always believed that the postfix-conditional was evaluated before even beginning to evaluate the operation, but I was distinctly disabused of this notion by testing.

In both cases the above, in case it’s not clear by my description, ‘expand’ will end up as ‘nil’ if it’s not defined before the statement, instead of being set to the appropriate default. (If it is set, it will retain its prior value.)

I don’t understand the reason for the behavior, and think it’s a bug, but I’d love to know a good language reason for it.

– Morgan

Reply

chuck April 15, 2007 at 6:35 pm

I think what you’re looking for is:

expand ||= true

since the || evaluates falsely when expand null/undefined, the assignment goes through. It’s supposedly a fairly common Ruby idiom.

Reply

Morgan Schweers April 16, 2007 at 8:41 am

Greetings,
Sadly that doesn’t work if someone has explicitly passed in ‘false’ as the value.

That’s where the frustration comes in…

– Morgan

Reply

Anthony Eden April 20, 2007 at 10:23 am

Morgan,

How about this:

expand = (expand.nil? ? true : expand)

Reply

rhubarb May 12, 2007 at 2:33 pm

Your example in quirk 5 seems to contradict the statement of the quirk.

By calling
d.increase_n

you prove the statement that “…But if a method uses one, and that method is available to subclasses, then it will still use the variable” because increase_n is defined in the superclass C and does actually increment n. Fari enough.

But when you call
d.show_n

it shows that the method show_n in D can directly access n in its superlcass, when it calls
puts “n is #{@n}”

I mean if n had an attr_accessor and you referred to it directly then show_n could access it (with just n not @n) and it would make sense – since it would be a method call.

But here show_n is directly accessing the instance field n in C with no use of an inherited method.

It looks to me like that @n _is_ effectively inherited. Or unless I’m misunderstanding inherited – another way of saying it is that @n is behaving like a protected field in Java.

I say protected, not public, because while any method in D can get and set @n, you can’t do
d.n = 100 #NoMethodError: undefined method `n=’ for…
on the object.

Note that this direct setting restriction doesn’t affect the inherited/not-inherited argument either way though, because I can’t do this either

c = C.new
c.n = 100 #NoMethodError: undefined method `n=’ for

What I really want to say is: Where’s the quirk here? I’m sure its there but I’m missing it.

Reply

Ronald Fischer June 26, 2007 at 8:06 pm

My comment is on item 6 :

This is not a bug, but desired behaviour. In your example, the variable named ‘expand’ gets defined because the compiler, when parsing the statement, sees it on the lhs of an assignment operator. Being defined has nothing to do with the dynamic behaviour of the program. Even in the case

if false
expand=1
end

the variable ‘expand’ would be defined.

Reply

Dale Thatcher November 10, 2007 at 2:08 pm

Slight reformat of the solution in comment 9.

expand = true if expand.nil?

As long as you’re happy to use nil as not set then it should be fine.

Reply

Leave a Comment

Previous post:

Next post: