views:

415

answers:

5

Hello,

I'm new to Ruby and recently ran into an issue comparing to values when creating a Ruby on Rails application. In a controller I had the following statement that always returned false:

if (user.id != params[:id])

The problem was the user.id (which is an Active Record) is an integer and params[:id] is a string. It took me a while to figure this out and I finally changed it to:

if (user.id != params[:id].to_i)

Now the statement works as expected.

To avoid this error in the future is there a way to "compile" or get Ruby to warn you if you try to compare 2 different types? Some other issues I've ran into that I would like to "compile check" are:

  • Warn me if I create a variable but don't use it. To help check for typos in variable names.
  • Make sure a method exists in a Class so I can avoid method name typos and also to help refactoring, for example if I rename a method.

I'm currently using Ruby 1.8.6-27 RC2 with Rails 2.3.2 and RadRails IDE on Windows.

+5  A: 

Test first, then code. If you write tests that cover all branches of your application, you get the assurance that your code both runs and produces correct results.

EDIT: I should point out that the ability to compare two types, not depend on method names until the last second, etc. are core features of Ruby.

You don't call a method so much as you send a message to an object. The object is then responsible for figuring out how to handle the method. In Rails this is used to access DB columns in ActiveRecord. There are no methods for the columns until a message with the column name is sent to the object.

Static typing in Ruby goes against the duck typing system. One can often get polymorphism for free without worrying about complex inheritance/interface schemes.

I suggest embracing these features and compensate for the uncertainty through testing

Ben Hughes
+1  A: 

Ruby isn't supposed to be safe. It lets you compare any two objects, and that's where much of its power comes from. Rails wouldn't be possible without such dynamic design.

Even a compiled language such as Java or C won't stop you from doing == on two objects. As Ben said, it's best to test first. Inspect the structures you're working with. One way to get information about a Ruby object is to use:

puts object.class
mcandre
p object, will dump out a more detailed version of an object. mixing p and puts is generally a winner for getting debug info.
Ben Hughes
Yes. Print is my debugging tool of choice.You can also use ./script/console to access the objects interactively.
mcandre
A: 

In general, the best way (I know of) to avoid this type of issue for dynamic/scripting languages is to move "logic" to methods/commands and write unit tests for them. Basically, anything that can fail should be tested. The code on the page should be dumb logic... rather than display only those items that meet a certain criteria, it should display all items, and get that list of items from a method that only returns the ones that should be displayed.

RHSeeger
+1  A: 

Ruby doesn't allow you to redefine the == operator for Object. In ruby 1.8 you can't, Ruby 1.9 was supposed to do but I haven't been able to get my script working for core classes. It works well for custom defined objects.

class Object

  alias :equal_without_warning :==

  def ==(object)
    unless self.class == object.class
      warn("Comparing `#{self.class}' with `#{object.class}'")
    end
    equal_without_warning(object)
  end

end

Assuming I didn't do some stupid coding error, the answer is NO: you can't check whether you are comparing different type of objects.

Also, I would say you don't. Actually Ruby isn't designed to work in this way, this is more a java approach rather than Ruby style.

Simone Carletti
You'll have to do the same redefinition for core classes since they normally redefine == without reference to Object#==
rampion
A: 

Two things I'd suggest:

One: Read up on IRB (or script/console for rails). A common development practice in dynamic languages is to try out snippets of code inside a "live" interpreter (like IRB or the rails console). This practice goes back to the earliest dynamic languages like Smalltalk and Lisp. Ruby-debug is also really useful for troubleshooting problems and would have been a really easy way to figure out the error in your example.

Two: Read up on "Duck Typing". "Types" and variables work a little bit differently in Ruby than many folks expect them to. As I understand it, a variable like user.id doesn't have a "type". The value pointed to by user.id does have a type, but the variable itself doesn't. I believe that's part of why there's no tool that would have told you what your error was in advance of running the program. Comparing those two variables isn't an error because the variables don't have a type. user.id was pointing to an integer at that point in your program, but it would be perfectly legal to assign user.id to point to a string, at which point that comparison would have made a lot more sense. :-)

Bob McCormick
There's also no reason you can't compare two different types with `==`.
Chuck