Reputation: 1554
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
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
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
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
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