Single quotes vs Colon --- What's the difference? (creating buttons)

I'm programming my way through the Agile Web dev book (3rd) and
there's a part in there that's causing a bit of confusion (p. 146 to
be exact).

You're creating a button in a partial (named _cart.html.erb) using the
following code:

<%= button_to "Checkout" , :action => 'checkout' %>

However, earlier in the book, we created a button to empty the cart
by:

<%= button_to "Empty cart" , :action => :empty_cart %>

both are pointing to actions defined in the store_controller.rb.
Everything's the same except that checkout is in single quotes and
empty_cart is preceded by a colon. Why is this?

Is it that 'checkout' is passed a variable where empty_cart is not?
Or, am I having bigger brain fart than that?

Thank you in advance.

both will do the same in this case; you could swap the colons and quotes and everything would still be merry. The quotes define a string, the colon define the variable as a symbol. Symbols are a way to store a string only one time; that is, you can save memory by using symbols for strings which are used many times in your app. One restriction is, you cannot modify a symbol like you would modify a string. But for things like method names (like the checkout and empty_cart ones in your example), they are great.

hope it helps,

Maximiliano

:empty_cart is a symbol, whereas 'checkout' is a string. Your params hash will still wind up with :action => 'empty_cart' in either case, as the params hash is simply derived from parsing the query string. In this context, a string and a symbol are functionally equivalent because the symbol is converted to a string preparatory to sending the HTTP POST request.

Using symbols as a kind of named constant is idiomatic when that object is immutable. So, empty_cart won't change names during your application's duration -- you can us a symbol instead of a string, thus keeping one and only one copy of it in memory.

Use this as a background: http://en.wikipedia.org/wiki/String_interning

I'm not sure whether this explanation helps or confuses :slight_smile:

AlwaysCharging wrote:

Is it that 'checkout' is passed a variable where empty_cart is not?
Or, am I having bigger brain fart than that?

Here is a simple example that will hopefully illustrate the difference
between a symbol and a string:

$ irb
a = :foo
=> :foo
b = :foo
=> :foo
a.equal?(b)
=> true
c = 'foo'
=> "foo"
d = 'foo'
=> "foo"
c.equal?(d)
=> false

In this example notice that a is the same "thing" as b. But, c is NOT
the same "thing" as d. In other words in all case :foo refers to the
same instance (same thing) no matter how it's used. The variables c and
d reference two separate instances of strings containing the same three
characters ("foo").

So it's good practice to use symbols to reference "things" in your
program. This is why symbols make good keys in hashes since the keys in
hashes are generally used to identify things in the hash and the actual
characters are not as important.

Example:
attributes = { :name => "William", :occupation => "Programmer" }
puts attributes[:name]
=> "William"

Read attributes[:name] as, "Get me the thing referenced by :name from
attributes." The actual characters in the symbol :name don't really
matter it could have been called :xyz and it would still mean the same
thing { :xyz => "William" }; attributes(:xyz) => "William".

Now imagine what would happen if strings were used as keys:

a = { 'name' => "William", 'occupation' => "Programmer" }
b = { 'name' => "Steve", 'occupation' => "Project Manager" }

In this case there would be four separate String instances to represent
the same keys (same string of characters 'name' and 'occupation' but
when symbols are used for keys only two symbols are created :name and
:occupation. Each occurrence of each symbol, no matter how many hashes
exist, are only stored in memory once. So even if a thousand hashes use
the symbol :name the symbol would only take up memory once on its first
usage.

So it's good practice to use symbols to reference "things" in your
program. This is why symbols make good keys in hashes since the keys in
hashes are generally used to identify things in the hash and the actual
characters are not as important.

But, pay attention to the API for the library you are using. They don't always accept a symbol -- some expect a string. They'll do something like this:

case passed_in_argument
when "foo" then ...
when "bar" then ...
end

If you pass in :foo for the argument it won't match.

Now that said, most of Rails runs stringify_keys on the arguments so it's a non issue. Not all the plugins do. Just something to watch out for if your symbol isn't doing what you expected it to do.

Example:
attributes = { :name => "William", :occupation => "Programmer" }
puts attributes[:name]
=> "William"

Read attributes[:name] as, "Get me the thing referenced by :name from
attributes." The actual characters in the symbol :name don't really
matter it could have been called :xyz and it would still mean the same
thing { :xyz => "William" }; attributes(:xyz) => "William".

Now imagine what would happen if strings were used as keys:

a = { 'name' => "William", 'occupation' => "Programmer" }
b = { 'name' => "Steve", 'occupation' => "Project Manager" }

In this case there would be four separate String instances to represent
the same keys (same string of characters 'name' and 'occupation' but
when symbols are used for keys only two symbols are created :name and
:occupation. Each occurrence of each symbol, no matter how many hashes
exist, are only stored in memory once. So even if a thousand hashes use
the symbol :name the symbol would only take up memory once on its first
usage.

I thought I'd read somewhere though that a symbol once created is never destroyed so it takes up space in RAM. A string will be destroyed once it goes out of scope. The take away being that if you create a thousand symbols but only use them once you're going to be eating up a lot of RAM. An edge case maybe as typically you'll use the symbol many times, but something to keep an eye on.

Someone please correct me if I'm wrong about the symbol/ram/usage issue.

-philip

Philip Hallstrom wrote:

As it goes with many things in life there are trade-offs, and finding
the right balance is often complex. There is overhead in constantly
creating and destroying objects, but keeping them around consumes
memory. Also keep in mind that symbols are stored internally as
integers, hence making comparisons significantly more efficient than
comparing strings.

Another reason why they make good hash keys. There is a good post on
symbols at http://blog.hasmanythrough.com/2008/4/19/symbols-are-not-pretty-strings

Fred

On the other hand.

The HashWithIndifferentAccess which ActiveSupport adds to Ruby and is
used extensively by Rails doesn't actually use symbols as the hash
keys.

Although it allows symbols and strings to be used interchangeably as
key arguments to the Hash methods. It converts symbiols to strings
before actually using them as keys, rather than the other way around,
which simply allows keystrokes to be saved in source code :str instead
of 'str'.

The reason it does it this way is that the keys often come in the form
of strings, and those strings come from outside the app, for example
when url parameters get converted to keys in the params hash. Since
converting strings to symbols makes a copy of the string the first
time that particular string gets interned as a symbol, and those can
never be garbage collected, strings rather than symbols are used as
the keys.