trying to understand specific bit of ruby syntax - ruby-on-rails

I'm new to both Ruby and Rails, and as I go over various tutorials, I occasionally hit on a bit of Ruby syntax that I just can't grok.
For instance, what does this actually do?
root to: "welcome#index"
I can gather that this is probably a method named "root", but I'm lost after that. "To" isn't a symbol, is it? The colon would be before, as in ":to" if it were. Is this some form of keyword argument utilizing hashes? I can't make this syntax work when trying it in irb with ruby1.9.3.
I know this might be a RTFM question, but I can't even think of what to google for this.
Thanks!
I'm still playing around with this syntax,
def func(h)
puts h[:to]
end
x = { :to => "welcome#index" }
y = :to => "welcome#index"
z = to: "welcome#index"
func to: "welcome#index"
I see that this example only works with the lines defining "y" and "z" commented out. So the braceless and the "colon-after" syntax are only valid in the context of calling a method?

First, that's right - root is a method call.
Now
to: 'welcome#index'
is equivalent to
:to => 'welcome#index'
and it's a Hash where the key is :to symbol and value is 'welcome#index' string. You can use this syntax in defining hashes since Ruby 1.9.

It's equivalent to
root(:to => "welcome#index")
I'm having trouble finding the official documentation on the new hash syntax, but when you see foo: bar, it means that foo is a symbol used as a key in the hash and has a value bar.

Here is an example of defining a function foo which takes a hash, and prints to screen.
def foo(hash)
puts hash.inspect
puts hash[:to]
end
foo to: "wecome#index" #method call without paratheses
Output of method call above:
{:to=>"welcome#index"}
welcome#index
Equivalent declarations:
h = {:to => "welcome#index"}
h = {to: "wecolme#index"}
Also, you can use Ripper (part of Ruby standard library) to understand how Ruby parses code. In the example below, I have already defined foo as above. Now, I call foo without using Ripper. Then I use Ripper to see how Ruby parses the method call.
[2] pry(main)> foo to: "welcome#index"
{:to=>"welcome#index"}
welcome#index
=> nil
[3] pry(main)> require 'ripper'
=> true
[4] pry(main)> Ripper.sexp 'foo to: "welcome#index"'
=> [:program,
[[:command,
[:#ident, "foo", [1, 0]],
[:args_add_block,
[[:bare_assoc_hash,
[[:assoc_new,
[:#label, "to:", [1, 4]],
[:string_literal,
[:string_content, [:#tstring_content, "welcome#index", [1, 9]]]]]]]],
false]]]]

In ruby braces in method calls are optional, so it can be rewritten as:
root(to: "welcome#index")
and it can be rewritten again as
root(:to => "welcome#index")
Hashes as keyword arguments(ruby 1.9) explained here as well: hash-constructor-parameter-in-1-9
P.S. and by the way general rule of the thumb for the rails-newcomers is "learn ruby first, then learn rails" ;)

As you correctly gathered, root is a method call. Or rather, it's a message send. Ruby, like Smalltalk, builds upon a messaging metaphor, where objects send messages to other objects, and those objects (called the receiver) respond to those messages.
In this case, you pass an argument to root, that's how you know it's a message send. Message sends are the only thing that can take arguments, if you see an argument, then it must be a message send. There are no functions, no static methods, no constructors, no procedures, only methods and message sends.
So, what is the argument? Well, in Ruby, a lot of things that are syntactically required in other languages are optional. For example, parenthesis around the argument list:
foo.bar(baz)
# can also be written as
foo.bar baz
If the very last argument to the message send is a Hash literal, you can leave off the curly braces:
foo.bar({ :baz => 23, :quux => 42 })
# can also be written as
foo.bar(:baz => 23, :quux => 42)
Put the two together, and you get:
foo.bar({ :baz => 23, :quux => 42 })
# can also be written as
foo.bar :baz => 23, :quux => 42
In Ruby 1.9, a new alternative Hash literal syntax was introduced. This literal syntax is very limited compared to the original one, because it can only express Hashes whose keys are Symbols which are also valid Ruby identifiers, whereas with the original syntax, you can write down a Hash with any arbitrary object as key. But, for that limited use case, it is very readable:
{ :baz => 23, :quux => 42 }
# can also be written as
{ baz: 23, quux: 42 }
If we put that feature together with the other two, we get the message send syntax you are asking about:
foo.bar baz: 23, quux: 42
If we have method declared with a single argument like this:
def foo.bar(opts) p opts end
opts will be bound to a single Hash with two key-value pairs.
These features were often used to emulate keyword arguments as found in other languages. And it has long been a desire of the Ruby community to get support for real keyword arguments. This support was implemented in two steps: first, the new Hash literal syntax was introduced in Ruby 1.9, which allows you to make message sends which look like they are using keyword arguments, even though they are really just a Hash. And then in a second step, in Ruby 2.0 real keyword arguments were introduced. The modified method signature would look like this:
def foo.bar(baz: nil, quux: nil) p baz, quux end
Note that at the moment, it is not possible to have required keyword arguments, they always need to have a default value and are thus always optional. You can, however, use the fact that default values can be arbitrary expressions and do something like this:
def foo.bar(baz: raise ArgumentError '`baz` must be supplied!',
quux: raise ArgumentError '`quux` must be supplied!') p baz, quux end
In a future version of Ruby (it was actually already implemented in February and will likely be in 2.1), required keyword arguments can be specified by omitting the default value:
def foo.bar(baz:, quux:) p baz, quux end
Note that there is a syntactic ambiguity now:
foo.bar baz: 23, quux: 42
# is this sending the message `bar` to `foo` with *one* `Hash` or *two* keywords?
This ambiguity is actually intentional, because it allows old client code written against APIs which use a Hash argument to work unchanged with new APIs that use keyword arguments. There are some semi-complex rules which determine whether that syntax will be interpreted as a Hash or as keywords, but mostly those rules work out the way you would expect them to.

Related

Ruby documentation over Symbol objects is misleading

I'm a newbie to Ruby and like others I had trouble wrapping my head around Ruby symbols. I know this subject has been brought up many times but I believe this post might slightly differ from the others. If not, my apologies. Take for instance this piece of code from the documentation.
module One
class Fred
end
$f1 = :Fred
end
module Two
Fred = 1
$f2 = :Fred
end
def Fred()
end
$f3 = :Fred
$f1.object_id #=> 2514190
$f2.object_id #=> 2514190
$f3.object_id #=> 2514190
My gripe is that it makes us think there is a link between the class, the module or the function and the :Fred symbol. No wonder people ask things like "can I assign a value to a symbol" or is the symbol a reference to another thing.
This code adds to the confusion :
class TestController < ApplicationController
layout :which_layout
def index
...
end
private
def which_layout
if condition
"layout1"
else
"layout2"
end
end
end
At first, I thought there was a reference to the function but in fact it's just that the behavior of the layout method will vary base on whether we'll pass a String (the template name) or a Symbol (call the method specified by the symbol) as stated by the documentation. (Does it look for a method.to_sym that is equivalent to the symbol we passed as argument?)
What I believe I've read however is that when creating a class, his symbol counterpart will be automatically created, that is :Fred will already exist in subsequent calls. But that's just it?
My question is : why did they have to include a class, a variable and a function to illustrate this? The context? Then why having the same name? Why not just do :
$f1 = :Fred
$f2 = :Fred
$f3 = :Fred
$f1.object_id #=> 2514190
$f2.object_id #=> 2514190
$f3.object_id #=> 2514190
When you use a symbol Ruby looks at list of existing symbols so when you reuse a symbol you are not creating a separate object in memory.
irb(main):006:0> :foo.object_id == :foo.object_id
=> true
You can contrast this with a string:
irb(main):007:0> "foo".object_id == "foo".object_id
=> false
This combined with the fact that they are so cheap to compare is what makes symbols so effective as hash keys.
What that pretty confusing example demonstrates is that symbols are not private to a scope - the table of symbols is global. It would be a bit less confusing if an instance variable was used instead of a global variable. I think it also attempts to demonstrate that module and class names are constants.
irb(main):016:0> Fred = Module.new do; end # this is the same as using the module keyword
irb(main):017:0> Fred.object_id != :Fred.object_id
=> true
Which means that Fred is a reference to the module while :Fred is a value (a symbol).
Symbols like strings are a value and thus cannot be a used as a reference. This is very much like true, false and nil which are singleton objects.
irb(main):008:0> true.class
=> TrueClass
irb(main):09:0> true.object_id == true.object_id
=> true
# you can even use the singletons as hash keys
irb(main):010:0> { true => 1, false => 2, nil => 3 }[true]
=> 1
The Rails example is not really that complicated. :which_layout is just an argument passed to the layout method. The layout method has a conditional which uses Object#send to dynamically call the :which_layout method if it exists. A string argument is instead used straight away to construct a glob.
My understanding is it's meant to illustrate the fact that all of the other "Fred's" do nothing to alter the fact that the symbol :Fred is unchanged in every context. Maybe changing the object_id list at the bottom of the example to something like this would make it more clear:
p One::Fred.object_id #=> 70222371662500
p Two::Fred.object_id #=> 3
p Fred().object_id #=> 8
p $f1.object_id #=> 2514190
p $f2.object_id #=> 2514190
p $f3.object_id #=> 2514190

=> operator vs = operator

I just started learning ruby on rails, and I'm wondering when I should use "=>" and when I should use "=" for assignment. I am seeing that you use "=>" for hash, for assigning values to symbols in migrations, but i'm not sure where to draw the line.
Thanks!
The => symbol is used solely for hashes. Ruby has a feature in which hashes can be passed as the last argument to a method call without including the surrounding braces. This provides something that resembles keyword arguments (though until Ruby 2.0, Ruby didn't have keyword arguments).
So when you see this:
t.integer :foo, :default => 5
What it really means is this:
t.integer(:foo, { :default => 5 })
The rest is just syntactic sugar designed to make it look nicer.
The = symbol, on the other hand, is the assignment operator you know and love from nearly any programming language.
I struggled with this for a while, but now prefer to use the new style for hashes wherever possible
t.integer :foo, default: 5
t.string :bar, default: 'Dave'
=> is not the same as assignment, but I can see why it is confusing. In a hash you create a key and a value as a pair. The key and value can be anything
{'key1' => 'some value', :symbol_key => 'other value'}
This is different to the assignment, which you can see clearly because if you want the above hash to remain available to your program, you either have to pass it to a method or assign it to a variable
myhash = {'key1' => 'some value', :symbol_key => 'other value'}
And only now can you retrieve stuff from your hash
puts myhash['key1']
So the => operator is actually used to construct hashes (or dictionary objects), assignment allows you to store values in the program.
What is happening quite commonly Rails (and therefore in migrations), is that the hash is being created and passed to the method call without you realising it. But the plumbing is still the same, it's still only a hash that is created.
In Ruby 1.9 you can now define hashes using a javascript-like syntax, so you might start seeing this as well.
myhash = {key1: 'some value', key2: 'other value'}

Create a dictionary as an attribute in FactoryGirl

Factory Girl allows to do something like:
FactoryGirl define
factory :post do
content "some content"
styles "styles here"
team 1
end
end
However, if I try something inside the factory block like:
factory :post do
content "some content"
styles "styles here"
team 1
my_dictionary {'a' => 1, 'b' => 2}
end
The my_dictionary does not get interpreted as a dictionary type. I don't know how to make a dictionary as an attribute inside FactoryGirl. Can anyone help me ?
The issue you observe comes from a syntax ambiguity in Ruby. The language uses curly braces both for defining hashes (which you call dictionaries) as well as for blocks (e.g. when using each loops). As you now use the hash as the only parameter to the my_dictionary method, it is unclear to the parser whether that opening curly brace is to be interpreted as the start of a block or a hash. In this case, Ruby defaults to the block assumption.
To enforce the interpretation as a method parameter, you can use parenthesis like so:
my_dictionary({'a' => 1, 'b' => 2})
Then, the statement can be parsed without any ambiguity. What you have here is just one of the rare cases where you can't easily omit the parenthesis for method calls.

Equivalent of .try() for a hash to avoid "undefined method" errors on nil? [duplicate]

This question already has answers here:
How to avoid NoMethodError for nil elements when accessing nested hashes? [duplicate]
(4 answers)
Closed 7 years ago.
In Rails we can do the following in case a value doesn't exist to avoid an error:
#myvar = #comment.try(:body)
What is the equivalent when I'm digging deep into a hash and don't want to get an error?
#myvar = session[:comments][#comment.id]["temp_value"]
# [:comments] may or may not exist here
In the above case, session[:comments]try[#comment.id] doesn't work. What would?
You forgot to put a . before the try:
#myvar = session[:comments].try(:[], #comment.id)
since [] is the name of the method when you do [#comment.id].
The announcement of Ruby 2.3.0-preview1 includes an introduction of Safe navigation operator.
A safe navigation operator, which already exists in C#, Groovy, and
Swift, is introduced to ease nil handling as obj&.foo. Array#dig and
Hash#dig are also added.
This means as of 2.3 below code
account.try(:owner).try(:address)
can be rewritten to
account&.owner&.address
However, one should be careful that & is not a drop in replacement of #try. Take a look at this example:
> params = nil
nil
> params&.country
nil
> params = OpenStruct.new(country: "Australia")
#<OpenStruct country="Australia">
> params&.country
"Australia"
> params&.country&.name
NoMethodError: undefined method `name' for "Australia":String
from (pry):38:in `<main>'
> params.try(:country).try(:name)
nil
It is also including a similar sort of way: Array#dig and Hash#dig. So now this
city = params.fetch(:[], :country).try(:[], :state).try(:[], :city)
can be rewritten to
city = params.dig(:country, :state, :city)
Again, #dig is not replicating #try's behaviour. So be careful with returning values. If params[:country] returns, for example, an Integer, TypeError: Integer does not have #dig method will be raised.
The most beautiful solution is an old answer by Mladen Jablanović, as it lets you to dig in the hash deeper than you could with using direct .try() calls, if you want the code still look nice:
class Hash
def get_deep(*fields)
fields.inject(self) {|acc,e| acc[e] if acc}
end
end
You should be careful with various objects (especially params), because Strings and Arrays also respond to :[], but the returned value may not be what you want, and Array raises exception for Strings or Symbols used as indexes.
That is the reason why in the suggested form of this method (below) the (usually ugly) test for .is_a?(Hash) is used instead of (usually better) .respond_to?(:[]):
class Hash
def get_deep(*fields)
fields.inject(self) {|acc,e| acc[e] if acc.is_a?(Hash)}
end
end
a_hash = {:one => {:two => {:three => "asd"}, :arr => [1,2,3]}}
puts a_hash.get_deep(:one, :two ).inspect # => {:three=>"asd"}
puts a_hash.get_deep(:one, :two, :three ).inspect # => "asd"
puts a_hash.get_deep(:one, :two, :three, :four).inspect # => nil
puts a_hash.get_deep(:one, :arr ).inspect # => [1,2,3]
puts a_hash.get_deep(:one, :arr, :too_deep ).inspect # => nil
The last example would raise an exception: "Symbol as array index (TypeError)" if it was not guarded by this ugly "is_a?(Hash)".
The proper use of try with a hash is #sesion.try(:[], :comments).
#session.try(:[], :comments).try(:[], commend.id).try(:[], 'temp_value')
Update: As of Ruby 2.3 use #dig
Most objects that respond to [] expect an Integer argument, with Hash being an exception that will accept any object (such as strings or symbols).
The following is a slightly more robust version of Arsen7's answer that supports nested Array, Hash, as well as any other objects that expect an Integer passed to [].
It's not fool proof, as someone may have created an object that implements [] and does not accept an Integer argument. However, this solution works great in the common case e.g. pulling nested values from JSON (which has both Hash and Array):
class Hash
def get_deep(*fields)
fields.inject(self) { |acc, e| acc[e] if acc.is_a?(Hash) || (e.is_a?(Integer) && acc.respond_to?(:[])) }
end
end
It can be used the same as Arsen7's solution but also supports arrays e.g.
json = { 'users' => [ { 'name' => { 'first_name' => 'Frank'} }, { 'name' => { 'first_name' => 'Bob' } } ] }
json.get_deep 'users', 1, 'name', 'first_name' # Pulls out 'Bob'
say you want to find params[:user][:email] but it's not sure whether user is there in params or not. Then-
you can try:
params[:user].try(:[], :email)
It will return either nil(if user is not there or email is not there in user) or otherwise the value of email in user.
As of Ruby 2.3 this gets a little easier. Instead of having to nest try statements or define your own method you can now use Hash#dig (documentation).
h = { foo: {bar: {baz: 1}}}
h.dig(:foo, :bar, :baz) #=> 1
h.dig(:foo, :zot) #=> nil
Or in the example above:
session.dig(:comments, #comment.id, "temp_value")
This has the added benefit of being more like try than some of the examples above. If any of the arguments lead to the hash returning nil then it will respond nil.
#myvar = session.fetch(:comments, {}).fetch(#comment.id, {})["temp_value"]
From Ruby 2.0, you can do:
#myvar = session[:comments].to_h[#comment.id].to_h["temp_value"]
From Ruby 2.3, you can do:
#myvar = session.dig(:comments, #comment.id, "temp_value")
Another approach:
#myvar = session[:comments][#comment.id]["temp_value"] rescue nil
This might also be consider a bit dangerous because it can hide too much, personally I like it.
If you want more control, you may consider something like:
def handle # just an example name, use what speaks to you
raise $! unless $!.kind_of? NoMethodError # Do whatever checks or
# reporting you want
end
# then you may use
#myvar = session[:comments][#comment.id]["temp_value"] rescue handle
When you do this:
myhash[:one][:two][:three]
You're just chaining a bunch of calls to a "[]" method, an the error occurs if myhash[:one] returns nil, because nil doesn't have a [] method. So, one simple and rather hacky way is to add a [] method to Niclass, which returns nil: i would set this up in a rails app as follows:
Add the method:
#in lib/ruby_extensions.rb
class NilClass
def [](*args)
nil
end
end
Require the file:
#in config/initializers/app_environment.rb
require 'ruby_extensions'
Now you can call nested hashes without fear: i'm demonstrating in the console here:
>> hash = {:foo => "bar"}
=> {:foo=>"bar"}
>> hash[:foo]
=> "bar"
>> hash[:doo]
=> nil
>> hash[:doo][:too]
=> nil
Andrew's answer didn't work for me when I tried this again recently. Maybe something has changed?
#myvar = session[:comments].try('[]', #comment.id)
The '[]' is in quotes instead of a symbol :[]
Try to use
#myvar = session[:comments][#comment.id]["temp_value"] if session[:comments]

What's the difference between "=" & "=>" and "#variable", "##variable" and ":variable" in ruby?

I know these are the basics of rails but i still don't know the full difference between = sign and => and the difference between #some_variable, ##some_variable and :some_variable in rails.
Thanks.
OK.
The difference between the = and the => operators is that the first is assignment, the second represents an association in a hash (associative array). So { :key => 'val' } is saying "create an associative array, with :key being the key, and 'val' being the value". If you want to sound like a Rubyist, we call this the "hashrocket". (Believe it or not, this isn't the most strange operator in Ruby; we also have the <=>, or "spaceship operator".)
You may be confused because there is a bit of a shortcut you can use in methods, if the last parameter is a hash, you can omit the squiggly brackets ({}). so calling render :partial => 'foo' is basically calling the render method, passing in a hash with a single key/value pair. Because of this, you often see a hash as the last parameter to sort of have a poor man's optional parameters (you see something similar done in JavaScript too).
In Ruby, any normal word is a local variable. So foo inside a method is a variable scoped to the method level. Prefixing a variable with # means scope the variable to the instance. So #foo in a method is an instance level.
## means a class variable, meaning that ## variables are in scope of the class, and all instances.
: means symbol. A symbol in Ruby is a special kind of string that implies that it will be used as a key. If you are coming from C#/Java, they are similar in use to the key part of an enum. There are some other differences too, but basically any time you are going to treat a string as any sort of key, you use a symbol instead.
Wow, a that's a lot of different concepts together.
1) = is plain old assignment.
a = 4;
puts a
2) => is used to declare hashes
hash = {'a' => 1, 'b' => 2, 'c' => 3}
puts hash['b'] # prints 2
3) #var lets you access object instance variable.
class MyObject
def set_x(x)
#x = x
end
def get_x
#x
end
end
o = MyObject.new
o.set_x 3
puts o.get_x # prints 3
4) ##var lets you access class ('static') variables.
class MyObject
def set_x(x)
##x = x # now you can access '##x' from other MyObject instance
end
def get_x
##x
end
end
o1 = MyObject.new
o1.set_x 3
o2 = MyObject.new
puts o2.get_x # prints 3, even though 'set_x' was invoked on different object
5) I usually think of :var as special 'label' class. Example 2 can be rephrased like this
hash = {:a => 1, :b => 2, :c => 3}
puts hash[:b] # prints 2

Resources