How are symbols used to identify arguments in ruby methods
Symbols and hashes are values like any other, and can be passed like any other value type.
Recall that ActiveRecord models accept a hash as an argument; it ends up being similar to this (it's not this simple, but it's the same idea in the end):
class User
attr_accessor :fname, :lname
def initialize(args)
@fname = args[:fname] if args[:fname]
@lname = args[:lname] if args[:lname]
end
end
u = User.new(:fname => 'Joe', :lname => 'Hacker')
This takes advantage of not having to put the hash in curly-brackets {}
unless you need to disambiguate parameters (and there's a block parsing issue as well when you skip the parens).
Similarly:
class TestItOut
attr_accessor :field_name, :validations
def initialize(field_name, validations)
@field_name = field_name
@validations = validations
end
def show_validations
puts "Validating field '#{field_name}' with:"
validations.each do |type, args|
puts " validator '#{type}' with args '#{args}'"
end
end
end
t = TestItOut.new(:name, presence: true, length: { min: 2, max: 10 })
t.show_validations
This outputs:
Validating field 'name' with:
validator 'presence' with args 'true'
validator 'length' with args '{min: 2, max: 10}'
From there you can start to see how things like this work.
Using Symbol as Parameter for the Function in Ruby
It's fairly common for people learning Ruby to not quite understand symbols. A symbol is a standard Ruby type, just like other built in Ruby types. Here is an example of how a symbol is just an object that is of a certain type:
'A'.class # => String
1.class # => Fixnum
:a.class # => Symbol
As with any other type, symbols have methods. If you open IRB and type :a.methods.sort
it will show you all the methods you can call on a symbol. E.g., :a.to_s # => 'a'
As you've noticed, symbols are often used as Hash
keys. However, other types can also be Hash
keys:
my_hash = { 'A' => 'an A', 1 => 'a 1', :a => 'the symbol a'}
my_hash['A'] # => 'an A'
my_hash[1] # => 'a 1'
my_hash[:a] # => 'the symbol a'
Just like you can pass a String
or other types to a method, you can pass a Symbol
. In fact, in the last example, we are passing a String
, a Fixnum
, and then a Symbol
to my_hash
's []
method.
The reason people really like symbols for hash keys is that they are very lightweight to reuse. Here is an example showing one of the main differences between a symbol and any other object:
"a".object_id # => 70098399407740
"a".object_id # => 70098399393460
"a".object_id # => 70098399388140
:a.object_id # => 359368
:a.object_id # => 359368
:a.object_id # => 359368
As you can see, I create three strings that have the value "a", and they each have a different object id. In other words, there are three String
objects in memory that contain the value "a". In contrast, every time I use :a
it has the same object id. There is only one :a
object in my entire program.
It's not uncommon to pass symbols to methods. You'll see this a lot in Rails.
Rails methods called as symbols when used as parameters?
If you look at the interface of Object#send
in ruby 2.1 you will find the symbol
variant first and the string
variant second, but up to ruby 2.0 there used to be only the symbol
variant! As all methods will be passed through #send
it made sense to use its parameter wherever you referred to a method in order to avoid constant calls to #to_sym
everywhere. I do not know why this change was introduced, but I guess the symbols
will stick around for some time, at least they are one character less to type :-)
That goes without saying that symbols are allocated only once throughout a program, they will not be garbage collected and are therefore slightly more efficient than strings. As processors still get faster more quickly than RAM I guess the distinction has become less important lately.
As you can see in the discussion of new features of ruby 2.1 the difference between symbols and frozen strings is becoming less. Frozen strings are now only allocated once, symbols are now frozen and there is even a hint about symbols possibly being garbage collected in the future. This would eventually eliminate an attack vector for a denial of service attack if external data is turned into symbols. On the other hand, def
and define_method
now return the names of the methods they define as symbols (instead of returning nil
as they used to).
In Ruby, what does the symbol = mean in a method's parameter list?
Yes, it has the same meaning. In both cases, it's definition of a hash. Ruby allows to omit curly braces of hash literal, if it is the last parameter in method's signature.
Example:
def my_method a, b, h
puts a
puts b
puts h
end
my_method(1, 2, :timeout => 30)
# my_method(1, 2, timeout: 30) # alternative syntax for ruby 1.9+
# >> 1
# >> 2
# >> {:timeout=>30}
Note that it only works for last parameter which is hash. If you have several hashes at the end, you have to use normal form (with curly braces) for all but the last.
Why do we use symbols as parameters?
It's common to use hashes passed as arguments to methods because then you don't have to worry about the ordering of arguments. Also, used quite frequently for optional arguments. Consider the examples below:
def method_with_args(name, age, dollar_amount, occupation) # gets sort of ugly and error prone
# do something with args
end
def method_with_hash(hash = {}) # cleans up ordering and forces you to 'name' variables
name = hash[:name]
age = hash[:age]
dollar_amount = hash[:dollar_amount]
occupation = hash[:occupation]
# do stuff with variables
end
The second part of your question:
@tweets = Tweet.recent.includes(location)
location here is expected to be defined as a variable or method on the object that calls the method. If you run the code, the error will prompt you with that information.
In terms of hash access and performance: symbol access to a hash is about 2x faster. String access allocates a string to memory every time it's instantiated which creates a lot of garbage objects. Take a gander at this blog post
Ruby passing symbol(s) as keyword argument(s)?
Yes, the correct syntax is navigate_to page: :inbox
.
While this is common Ruby, it is short and equivalent for several different things. First, the braces.
You are actually calling:
navigate_to(page: :inbox)
Second, the keyword argument pattern originates from hashes as arguments. Before there were keyword arguments, a common way would be to pass in a hash[1], like so:
def navigate_to(options)
page = options[:page]
end
navigate_to({ page: :inbox })
But when last argument in a method call is a hash, one can leave out the {}
.
And last, the actual keys in the hash. A while ago (1.8 -> 1.9 IIRC) a short version was introduced for the following:
{ :page => 'Some Value' }
, namely { page: 'Some Value' }
. When Some Value
is a symbol, that becomes { page: :inbox }
.
So, taking all that:
navigate_to page: :inbox
Orignates from:
navigate_to({ :page => :inbox })
It might make more sense reading it like this, or knowing it comes from that.
And I know Ruby, nor ruby-ist, like braces, ()
, as can be seen in the mindboggling DSL of for example rspec, but I can advise especially new developers, to add them. It often makes code better understandable.
navigate_to(page: :inbox)
is probably easier to understand than navigate_to page: :inbox
, especially when you start calling through other methods: navigate_to page page_from_session :user
.
[1] But, to stress, that is not really what is happening here. Keyword arguments and hash arguments do differ, now that we have keyword arguments. this just shows why the syntax is this way.
How method name is converted into a symbol in Ruby?
it's not clear for me, how Ruby converts name of method into :symbol?
That's the way Method#name
works, it returns the name of the method as a symbol:
m = "foo".method(:size) #=> #<Method: String#size>
m.name #=> :size
m.call #=> 3
All methods referencing other methods usually work this way. For example, Object#methods
returns a array of method names:
"foo".methods
#=> [:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, ...]
In method definition we give it name
meth
... but if we want check, does any method exist, we give intomethod_defined
symbol:meth
meth
would be a reference to a variable or another method, whereas :meth
is just a symbol:
meth = :foo
Mod.method_defined? meth #=> false, equivalent to Mod.method_defined? :foo
Mod.method_defined? :meth #=> true
Variable Arguments with a symbol
You can use a hash(a popular Ruby idiom) as a last argument, in which you can store values with lists, strings, anything:
def my_method(var1, options={})
options[:values] ||= []
options[:names] ||= []
#code
end
From there you can call:
my_method(whatever_arg, :values => ['1', '2', '3', '4'], :names => ['mike'])
Usage of symbols and table of symbols
Symbols by themselves are not connected with methods in any way. Certain methods can accept symbols to dynamically call other methods, using symbols as their names. Observe:
class Foo
def bar
"called bar"
end
def baz
"called bazzz"
end
def invoke m
send m
end
end
f = Foo.new
f.invoke :bar # => "called bar"
f.invoke :baz # => "called bazzz"
# since `invoke` just delegates execution to `send`, and `send` can accept strings as well
# we can pass a string here.
f.invoke 'baz' # => "called bazzz"
This is the same case as in
before_filter :init_session
validates_presence_of :first_name
And many others
Edit:
if I have a symbol :x and also defined a method x(), does it mean that these two are linked somehow?
No, they are not linked in any way. You can safely ignore that passage about symbol table. It's unnecessary implementation detail for you at the moment.
Related Topics
Differencebetween Methods and Attributes in Ruby
Passing Param Values to Redirect_To as Querystring in Rails
$Redis Global Variable with Ruby on Rails
How to Read the Body Text of an Email Using Ruby's Net/Imap Library
No Database Connection in Rails Console
Understanding Io.Select When Reading Socket in Ruby
Rails - Testing JSON API with Functional Tests
Difference Between Add_Dependency and Add_Runtime_Dependency
Getting a Dns Txt Record in Ruby
Building a Windows Executable from My Ruby App
How to Remove Validation Using Instance_Eval Clause in Rails
How to Uninstall Ruby on Rails and Do a Clean Install
Ruby on Rails 3 - Public Live Chat
What Is the Preferred Way (Better Style) to Name a Namespace in Ruby? Singular or Plural