What are the Ruby Gotchas a newbie should be warned about?
Wikipedia Ruby gotchas
From the article:
- Names which begin with a capital letter are treated as constants, so local variables should begin with a lowercase letter.
- The characters
$
and@
do not indicate variable data type as in Perl, but rather function as scope resolution operators. - To denote floating point numbers, one must follow with a zero digit (
99.0
) or an explicit conversion (99.to_f
). It is insufficient to append a dot (99.
), because numbers are susceptible to method syntax. - Boolean evaluation of non-boolean data is strict:
0
,""
and[]
are all evaluated totrue
. In C, the expression0 ? 1 : 0
evaluates to0
(i.e. false). In Ruby, however, it yields1
, as all numbers evaluate totrue
; onlynil
andfalse
evaluate tofalse
. A corollary to this rule is that Ruby methods by convention — for example, regular-expression searches — return numbers, strings, lists, or other non-false values on success, butnil
on failure (e.g., mismatch). This convention is also used in Smalltalk, where only the special objectstrue
andfalse
can be used in a boolean expression. - Versions prior to 1.9 lack a character data type (compare to C, which provides type
char
for characters). This may cause surprises when slicing strings:"abc"[0]
yields97
(an integer, representing the ASCII code of the first character in the string); to obtain"a"
use"abc"[0,1]
(a substring of length 1) or"abc"[0].chr
. The notation
statement until expression
, unlike other languages' equivalent statements (e.g.do { statement } while (not(expression));
in C/C++/...), actually never runs the statement if the expression is alreadytrue
. This is becausestatement until expression
is actually syntactic sugar overuntil expression
statement
end, the equivalent of which in C/C++ is
while (not(expression)) statement;
just likestatement if expression
is an equivalent toif expression
statement
endHowever, the notation
begin
statement
end until expressionin Ruby will in fact run the statement once even if the expression is already true.
- Because constants are references to objects, changing what a constant refers to generates a warning, but modifying the object itself does not. For example,
Greeting << " world!" if Greeting == "Hello"
does not generate an error or warning. This is similar tofinal
variables in Java, but Ruby does also have the functionality to "freeze" an object, unlike Java.
Some features which differ notably from other languages:
The usual operators for conditional expressions,
and
andor
, do not follow the normal rules of precedence:and
does not bind tighter thanor
. Ruby also has expression operators||
and&&
which work as expected.def
insidedef
doesn't do what a Python programmer might expect:def a_method
x = 7
def print_x; puts x end
print_x
endThis gives an error about
x
not being defined. You need to use aProc
.
Language features
- Omission of parentheses around method arguments may lead to unexpected results if the methods take multiple parameters. The Ruby developers have stated that omission of parentheses on multi-parameter methods may be disallowed in future Ruby versions; the current (November 2007) Ruby interpreter throws a warning which encourages the writer not to omit
()
, to avoid ambiguous meaning of code. Not using()
is still common practice, and can be especially nice to use Ruby as a human readable domain-specific programming language itself, along with the method calledmethod_missing()
.
ruby edge cases
In your first example tmp2 is not assigned until it reaches the if statement.
Your second example is not unexpected. Even though x is never assigned it informs the interpreter that you are talking about variable x not function x in the next line. Ruby tries to be pretty loose when determining the context of a name but it will take clues where available. It helps to be specific, for instance:
def y
x = 1 if false
x() + 2
end
Constant Assignment Bug in Ruby?
Catch that? The constant was appended to at the same time the local variable was.
No, it wasn't appended to, and neither was the local variable.
The single object that both the constant and the local variable are referring to was appended to, but neither the constant nor the local variable was changed. You cannot modify or change a variable or constant in Ruby (at least not in the way that your question implies), the only thing you can change is objects.
The only two things you can do with variables or constants is dereferencing them and assigning to them.
The constant is a red herring here, it is completely irrelevant to the example given. The only thing that is relevant is that there is only one single object in the entire example. That single object is accessible under two different names. If the object changes, then the object changes. Period. It does not mysteriously split itself in two. Which name you use to look at the changed object doesn't matter. There is only one object anyway.
This works exactly the same as in any other programming language: if you have multiple references to a mutable object in, say, Python, Java, C#, C++, C, Lisp, Smalltalk, JavaScript, PHP, Perl or whatever, then any change to that object will be visible no matter what reference is used, even if some of those references are final
or const
or whatever that particular language calls it.
This is simply how shared mutable state works and is just one of the many reasons why shared mutable state is bad.
In Ruby, you can generally call the freeze
method on any object to make it immutable. However, again, you are modifying the object here, so anybody else who has a reference to that object will all the sudden find that the object has become immutable. Therefore, just to be safe, you need to copy the object first, by calling dup
. But of course, that's not enough either, if you think of an array, for example: if you dup
the array, you get a different array, but the objects inside the array are the still the same ones in the original array. And if you freeze
the array, then you can no longer modify the array, but the objects in the array may very well still be mutable:
ORIG = ['Hello']
CLONE = ORIG.dup.freeze
CLONE[0] << ', World!'
CLONE # => ['Hello, World!']
That's shared mutable state for you. The only way to escape this madness is either to give up shared state (e.g. Actor Programming: if nobody else can see it, then it doesn't matter how often or when it changes) or mutable state (i.e. Functional Programming: if it never changes, then it doesn't matter how many others see it).
The fact that one of the two variables in the original example is actually a constant is completely irrelevant to the problem. There two main differences between a variable and a constant in Ruby: they have different lookup rules, and constants generate a warning if they are assigned to more than once. But in this example, the lookup rules are irrelevant and the constant is assigned to only once, so there really is no difference between a variable and a constant in this case.
Inserting into Set changing the order of Elements in an Array in Ruby
The order of elements in a Hash is not guaranteed. You'll have to sort the keys if you want a guaranteed order.
This is supposedly fixed in Ruby 1.9 I believe.
Edit: I'm assuming your results in an Array, if its a Hash then order isn't guaranteed and you'll have to sort the keys, here's what my test looks like:
#!/usr/bin/ruby -W
require 'pp'
require 'set'
results = Array.new
results << {:url => 'http://lifehacker.com'}
results << {:url => 'http://stackoverflow.com'}
results << {:url => 'http://43folders.com'}
results << {:url => 'http://lolindrath.com'}
results << {:url => 'http://stackoverflow.com'}
results << {:url => 'http://lifehacker.com'}
@search_results = Array.new
duplicates = Set.new
results.each { |result| @search_results.push(result) unless duplicates.add?(result[:url])}
puts "## @search_results"
pp @search_results
If I run that, here's the result:
## @search_results
[{:url=>"http://stackoverflow.com"}, {:url=>"http://lifehacker.com"}]
I found that odd, so just to be sure, I put a .nil?
add the end of .add?
and here was my result:
## @search_results
[{:url=>"http://lifehacker.com"},
{:url=>"http://stackoverflow.com"},
{:url=>"http://43folders.com"},
{:url=>"http://lolindrath.com"}]
Now that was what I was expecting: is this what you mean by "garbled"?
Edit 2: Upon further investigation, I think this is because of Ruby's super strict rules when converting non-Boolean data to Booleans (see Ruby Gotchas on Wikipedia and Stack Overflow, of course) so that basically anything that only false is really false and everything else is true. so the .nil?
is converting it explicitly to true/false.
irb(main):007:0> puts "zero is true" if 0
zero is true
=> nil
irb(main):008:0> puts "zero is false" unless 0
=> nil
What are the most important things to know about Ruby?
Everything (except false
and nil
) evaluates to true
in a boolean context.
This is different from other languages where empty constructs or 0
frequently evaluate as false
.
if 0
puts "0 evaluates to true"
end
Why does this evaluate to false? S == /[S]/ = 0
"S" == /[S]/
is false because ==
in Ruby doesn't evaluate whether a regexp matches, it just determines whether two objects are equal. The string "S"
and the regexp /[S]/
are of course completely different objects and not equal.
=~
(which is a correct way to match a regexp against a string in Ruby) returns the match position. In your first example the match position is the beginning of the string, 0. In the second example there is no match, so =~
returns nil.
i = true and false in Ruby is true?
The operators &&
and and
have different precedence, and =
happens to be in between.
irb(main):006:0> i = true and false
=> false
irb(main):007:0> i
=> true
irb(main):008:0> i = true && false
=> false
irb(main):009:0> i
=> false
irb(main):010:0>
The first is read as (i = true) and false
, the second as i = (true && false)
.
How to enable Ruby warnings in Rails?
Put this somewhere in your initialisation code (such as config/application.rb
):
$VERBOSE = true
You'll probably also get some warnings from Rails itself though.
Related Topics
Ssl_Connect Returned=1 Errno=0 State=Sslv3 Read Server Certificate B: Certificate Verify Failed
Why Is "Slurping" a File Not a Good Practice
Why Are Exclamation Marks Used in Ruby Methods
How to Remove Rvm (Ruby Version Manager) from My System
Understanding the Rails Authenticity Token
How to Avoid Nomethoderror For Missing Elements in Nested Hashes, Without Repeated Nil Checks
How to Find Where a Method Is Defined At Runtime
Ruby Block and Unparenthesized Arguments
How to Generate a Random String in Ruby
How to Reload the Current Page in Ruby on Rails
How to Remove Rvm (Ruby Version Manager) from My System
What's the Difference Between Equal, Eql, ===, and ==