DWB
DWB

Reputation: 1554

Why do callbacks use symbols in Ruby on Rails

I am struggling to understand when and when not to use symbols in Rails. I understand that symbols are not too dissimilar from a string without many of the methods. I also understand the symbols make good keys as symbols of the same name occupy one address in memory.

What I struggle to understand is why Rails decides to use symbols in some cases. If I had the callback

before_action :ask_stack_overflow_question

def ask_stack_overflow_question
  puts "why did I just use a symbol?"
end

I don't quite understand why the treat the method as a symbol? Why would I need to keep a method constant?

I can't find any answer to this in the documentation.

Thank you

Upvotes: 6

Views: 1123

Answers (4)

vgoff
vgoff

Reputation: 11313

Ruby symbols are immediate values. The important thing about a symbol is the symbol itself. So in places where the identity of the something is important, you can use a symbol.

Memory wise, it is something that is permanent (during the run of the program), is immutable, is always the same object.

Strings do not have this luxury in Ruby. "this string" and "this string" would be two separate objects, and subject to garbage collection.

:this_symbol would not be subject to garbage collection, is always the same object, and as such is memory efficient. The symbol may reference a local variable of the name this_symbol or a method of the name this_symbol or any other named object, but can be referenced by the exact same object, namely :this_symbol.

When to not use a symbol? When you need to temporarily have many of them, one time or few time use, in a long running program will eventually fill memory, with no hope of restoring that memory for other use.

So, they aren't really using the symbol as a method, but Ruby does reference method names by their symbol. I think that method names that change would not be as useful as method names that stay the same.

Upvotes: 1

Surya
Surya

Reputation: 15992

If you read this code which defines before_action on the second line:

[:before, :after, :around].each do |callback|
    define_method "#{callback}_action" do |*names, &blk|
      _insert_callbacks(names, blk) do |name, options|
        set_callback(:process_action, callback, name, options)
      end
    end

Which calls set_callback method here, which calls get_callback method when the method is being invoked. Which creates a callback chain. This chain of callbacks gets formed here, and later used by this method for execution. Now, you can either pass a string or as a symbol. But, the preferred way is to pass symbol as they are immutable and can not be altered during the process of execution.

Upvotes: 1

tadman
tadman

Reputation: 211560

This isn't as tricky as it seems.

When calling before_action you do not want the method to be executed immediately, but you have to describe that action you want to have performed somehow. A symbol is used here to represent the name of the method to call.

So if this is captured as name, then later, somewhere in the code, this is converted into a send(name) at the appropriate time.

There's a big difference between these two. The immediate version is:

before_action method_name

Which has the effect of executing method_name immediately, as Ruby will be compelled to run method_name to find out what it returns, then pass that through to before_action as an argument.

The deferred convention is:

before_action :method_name

Which is simply a polite request to before_action, passing in a symbol as an argument. You're leaving it up to before_action to deal with this however it sees fit, so it may or may not execute that method in the future, it depends on the situation.

This is how it works within Ruby with very few exceptions. The only one I can think of that doesn't immediately execute a method when it's specified immediately instead of as a representative symbol is the quirky alias construct.

Upvotes: 10

sites
sites

Reputation: 21785

It's the cheapest way to reference something, then a symbol makes sense.

There is usually the possibility to not to use symbol:

before_action do
  # do something
end

But with this way, you will not be able to reuse the method, just in case. Neither, you may lose some expressiveness.

Both options are correct, in my opinion, whichever option you choose, what is important is that your code is readable and not redundant.

Upvotes: 0

Related Questions