Topic: Syntax is confusing. Where do I use :, @, so on

OK so I'm reading the Agile Rails Development book.

In Ruby I understand @ is an instance variable.  The thing that gets really confusing is where the book goes into how Rails does everything magically for you and allows you to throw around variable names.

So in the controller I have a method calling a view and set an instance variable like @cart.

Then in the view, I always get confused.  Some places they reference the cart variable like :cart.  Other places it's @cart I think...  As I understand this isn't part of Ruby.  This is Rails "magically" throwing around variable names so that in one place it needs to be a variable with a : preceeding it which means something along the lines of "Thing named whatever".  Then things preceeded by the @ refer directly to the instance variable created in the function.  I'm a little confused where each correct format of variable goes.

Also how does the scope of the variable work?  If I create the variable in a method of a controller I know it should be visible in the corresponding view template.  Is it also visible to other methods in the controller?  It's clearer in Java for example when you explicitly declare a variable in each method and see that it's newly created, and also I know the scope rules in Java, but here I don't know whether or not a variable is already in existence or what?  I've gone through some ruby tutorials and I can't really find a great description about scope.  It would really help me understand what's going on.  Also if I call the controller method again is the variable still visible or is it a new instance of that variable and the old one will get cleaned by the garbage collector?

Like if I do this:

def method_1

  if(@var == nil)
    @var = 34
  else
    method_2(@var)
  end

end


I call method_1 and it creates @var with value 34.  Then the second time will @var have a value of 34 or will it be nil again?

And will @var be visible to method_2 in this situation if method_1 is called first?

def method_1
 
  @var = 34

end

def method_2

  method_3(@var)

end

Last edited by ill (2009-06-26 17:03:24)

Re: Syntax is confusing. Where do I use :, @, so on

You need to understand the difference between a variable and a symbol.  This is a Ruby thing, not a Rails thing in particular.

@whatever is an instance variable.  :whatever is a symbol called "whatever".  Symbols are sort of weird to get used to if you're new to Ruby.  They live in a nebulous land half way between strings and defined constants.  The best way to think of them, though, are as literal tokens that work like defined constants in other languages.

In C, for example, you would say:

#define FOO 0x01
#define BAR 0x02
#define BAZ 0x03

And you could then use FOO, BAR, and BAZ in your code a symbolic constants rather than the literal values they stand for.  In Ruby, instead of defining constants like that, you just invent symbols wherever you need them.

That's the short explanation, anyway.  For more details, get a good book on Ruby and read up on it.

In Rails, symbols get used all over the place, for many different purposes.  You'll see them in two main places: as hash keys (e.g. link_to "Click here", :controller => "foo", :action => "bar"), and in methods that extend your models with validations, associations, and filters.  Stuff like:

class Foo < ActiveRecord:Base
  has_one :bar
  validates_presence_of :name
  before_save :capitalize_name

Rails uses symbols for the purpose of naming other parts of your application in a reliable and meaningful way.  When you say "has_one :bar", you're telling Rails that your application has a model called Bar and that there's a foreign-key relationship between the 'foos' and 'bars' tables in the database.  When you say "validates_presence_of :name" you're telling Rails that the model shouldn't allow new objects to be created with no value for the model's "name" field, which in turn tells Rails to expect a column called "name" in the model's database table.  When you say "before_save :capitalize_name" you're telling Rails that the model has a method called "capitalize_name" which should be called before instances of the model are saved to the database.

For the most part, that's how it works.  What can get tricky is that there are many instances where Rails _can_ accept either a string or a symbol in order to specify something, so as you look in tutorials for this and that, in books, or generally in other people's code you may see inconsistencies.  But in most cases, the "idiomatic Rails" way to do these things is with symbols.

In views, you will mostly only be working with instance variables, of the @whatever form.  If you've got a view that has both @whatever and :whatever, and they seem to mean the same thing, well, I would raise my eyebrow at that and wonder what's going on too.  Post some code if you want more help on a specific instance of this in your application.

As far as variable scoping: instance variables are not visible (not in the way you mean) between controller actions.  You can, however, use class-level variables (@@whatever) to share values between actions if you need to.  I don't think I have ever needed to do that, however.  Generally speaking, there are cleaner ways to handle state information that needs to persist across multiple queries to your application (e.g. session variables).

This issue of scope can be confusing, I grant you, because of the standards for naming certain variables within controllers.  For example, there is always a "params[]" hash available to a controller.  And most controllers use @name_of_model (e.g. @user) to store a single record in all of the controller's actions.  You just need to be aware that typically the _value_ stored in "@user" (or any other such variable) has to be re-established anew on each call to an action by one means or another.  This is why a lot of RESTful controller actions start with some form of "@foo = Foo.find(...)"  It looks like the same variable in all those different actions, but it's not.  It's separate variables, with separate values, that happen to share the same name.  If you think about it, this is necessarily how it must work, because what if multiple web browsers accessed the same controller at the same time, but in different actions (or in the same action, even)?  Things would get mighty confused if the actions were all sharing the same value for @foo.

Re: Syntax is confusing. Where do I use :, @, so on

OK, thanks.

So symbols do kinda make since except for the way you say they're like constants.  What is the =>?  The way I understand it is you're setting the value of a simbol with that operator and it's used a lot like when setting attributes of an HTML tag with a helper method.

I kinda feel about symbols the way I used to feel about pointers in C when I first started heh...

Another one of the biggest sources of confusion is differentiating between methods and variables.

Ruby doesn't require parentheses around parameters which in my opinion isn't too great but whatever.  Like in C or Java or all other similar syntaxes I always know

appleSauce is a variable
appleSauce() is a function call unless you're throwing around function pointers but I'm not gonna get into that...

In all the sample code I've seen for Rails I always have to wonder, is that a variable, a function, built into rails or created by the user?

How do you guys make sure you understand what goes on in your code?  Personally I will always put parentheses around method parameters to avoid confusion for myself and I like the way it looks heh.

Also it's insane how many built in things there are into Ruby on Rails.  Your example for the Foo class uses before_save and has_one.  I'm not even sure where to look this stuff up.  I know about the RoR API docs and have looked up many things.  Is there like a central organized place to look up all the model methods?  I'm just constantly finding all these random functions that I had no clue were even available.

Re: Syntax is confusing. Where do I use :, @, so on

Again, get a good book on Ruby (the Pragmatic Programmers "Programming Ruby" is generally well regarded). 

The => operator is the hash assignment operator.  In full, hashes (aka associative arrays) look like this:

hash_var = { :keyname => somevalue, :keyname2 => someother value, et cetera }

That creates a hash, and assigns it to hash_var.  You can then ask for hash_var[:keyname] and you'll get back somevalue, just like you expect.

Fine, but Ruby loves its syntactic shortcuts, and it turns out that simply writing:

:somekey => somevalue

is shorthand for "create a hash with just this one key, value pair, right here in place."

This is handy when, say, a function or method requires a hash as one of its parameters.  You see this all the time in Rails, where these little in-place hashes are used to pass what amounts to configuration information into helpers.  For example, in link_to, you can say this:

<%= link_to "Click here", "/catalog/product/17" %>

And that does what you would expect, it creates a link to the relative url "/catalog/product/17", which presumably maps to the "catalog" controller, the "product" action, and :id value 17.

That works, only, it's ugly because the code is  not at all self-documenting.  Someone reading the code has to know enough about the application's controllers and routing tables to really understand what that relative URL maps to.  The Rails Way to do this is, instead, to use hashes with pre-defined keys that tell link_to what you want in a more explicit way:

<% link_to "Click here", :controller => "catalog", :action => "product", :id => 17 %>

Now the code says exactly what it means, and anyone who comes along later will have no trouble understanding it.  Yay for inline hashes!  They provide a convenient syntax for creating self-documenting code.

> In all the sample code I've seen for Rails I always have to wonder, is that a variable, a function, built into rails or created by the user?

Yeah, I've been there.  Learn to love "grep" is about all I can tell you.  If you have that question in your own code, grep for the thing in question and see where it turns up.  If it doesn't show up anywhere in your code, then it's probably something you can search for in the Rails docs.

> Also it's insane how many built in things there are into Ruby on Rails.  Your example for the Foo class uses before_save and has_one.  I'm not even sure where to look this stuff up.

Yes, yes it is.  It's totally insane how much stuff is built into Rails.  I 100% empathize with you there.  I felt exactly the same sense of bewilderment when I was first learning it.  I wouldn't say, even now, that I know half of what Rails really has to offer.  All I can tell you is hang in there.  It really is an awesome framework, but it does take some time to learn it.

My suggestion would be to get the "Agile Web Development with Rails" (AWDWR) book, set aside a weekend sometime, and diligently go through the extended tutorial in section 1 of the book.  That's probably the fastest, most effective, hands-on way to get your head around how Rails works.

But yes, the book's one huge failing (IMHO) is that it doesn't provide a comprehensive, well-organized reference section to what's actually available in the core Rails classes.  For that, you have to learn by reading the forums, by looking at other people's code, by playing with plugins, and by looking stuff up in the Rails documentation set (which, itself, is no paragon of documentation excellence).

Poor quality docs are, IMHO, the single greatest weakness of Rails.  Shame, that...