What does &. (ampersand dot) mean in Ruby? - ruby

I came across this line of ruby code. What does &. mean in this?
#object&.method

It is called the Safe Navigation Operator. Introduced in Ruby 2.3.0, it lets you call methods on objects without worrying that the object may be nil(Avoiding an undefined method for nil:NilClass error), similar to the try method in Rails.
So you can write
#person&.spouse&.name
instead of
#person.spouse.name if #person && #person.spouse
From the Docs:
my_object.my_method
This sends the my_method message to my_object. Any
object can be a receiver but depending on the method's visibility
sending a message may raise a NoMethodError.
You may use &. to designate a receiver, then my_method is not invoked
and the result is nil when the receiver is nil. In that case, the
arguments of my_method are not evaluated.

Note: Even though #Santosh gave a clear and full answer, I would like add some more background and add an important note regarding its use with non instance variables.
It is called "Safe Navigation Operator" (aka "Optional chaining operator", "Null-conditional operator", etc.). Matz seems to call it "lonely operator". It was introduced in Ruby 2.3. It sends a method to an object only if it is not nil.
Example:
# Call method `.profile` on `user` only if `user` is not `nil`
#user&.profile
# Equivalent to
unless #user.nil?
#user.profile
end
"Edge case" with local variables:
Please note, above code uses instance variables. If you want to use safe navigation operator with local variables, you will have to check that your local variables are defined first.
# `user` local variable is not defined previous
user&.profile
# This code would throw the following error:
NameError: undefined local variable or method `user' for main:Object
To fix this issue, check if your local variable is defined first or set it to nil:
# Option 1: Check the variable is defined
if defined?(user)
user&.profile
end
# Option 2: Define your local variable. Example, set it to nil
user = nil
user&.profile # Works and does not throw any errors
Method background
Rails has try method that basically does the same. It uses send method internally to call a method. Matz suggested that it is slow and this should be a built-in language feature.
Many other programming languages have similar features: Objective C, Swift, Scala, CoffeeScript, etc. However, a common syntax is ?. (question dot). But, this syntax could not be adopted by Ruby. Because ? was allowed in method names and thus, ?. symbol sequence is already a valid Ruby code. For example:
2.even?.class # => TrueClass
That's why Ruby community had to come up with different syntax. It was an active discussion and different options were considered (.?, ?, &&, etc.). Here is a list of some considerations:
u.?profile.?thumbnails
u\profile\thumbnails
u!profile!thumbnails
u ? .profile ? .thumbnails
u && .profile && .thumbnails
# And finally
u&.profile&.thumbnails
While choosing the syntax, developers looked at different edge cases and the discussion is quite useful to go through. If you want to go through all variants and nuance of the operator, please see this feature introduction discussion on official Ruby issue tracker.

Be wary! Though the safe navigation operator is convenient it can also be easy to trick yourself into changing your logic with it. I recommend avoiding the use of it in flow control. Example:
str = nil
puts "Hello" if str.nil? || str.empty?
# The above line is different than the below line
puts "Hello" if str&.empty?
In the first example, str.nil? returns true and str.empty? is never called, causing the puts statement to be executed. In the second example however, str&.empty? returns nil which is falsey, and the puts statement is never executed.

safe navigation operator (&.): tells Ruby to only call the next method if the receiver isn’t nil. Otherwise, the expression returns nil.
Practical In Action
Let’s construct a Roster object for a Sports team. The Roster will contain multiple Player objects.
class Roster
attr_accessor :players
end
class Player
attr_accessor :name, :position
def initialize(name, position)
#name = name
#position = position
end
end
With these two objects, we can create a roster for a 2-on-2 women’s basketball tournament:
moore = Player.new("Maya Moore", "Forward")
taurasi = Player.new("Diana Taurasi", "Guard")
tourney_roster1 = Roster.new
tourney_roster1.players = [moore, taurasi]
If we want to know the forward for our 2-on-2 team, we might find the name this way:
if tourney_roster1.players.first.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
But what if our opposing roster isn’t set correctly?
tourney_roster2 = Roster.new
if tourney_roster2.players.first.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
tourney_roster2 hasn’t yet been set with any players. The preceding code will raise a NoMethodError because tourney_roster2.players returns nil. We can add conditional statements to avoid this, but it makes our if statement verbose and unclear:
if tourney_roster2.players &&
tourney_roster2.players.first &&
tourney_roster2.players.first.position == "Forward"
Instead, we can use the safe navigation operator to avoid the NoMethodError:
if tourney_roster2.players&.first&.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
Thus,
>> tourney_roster2.players&.first == nil
#=> true
>> tourney_roster2.players&.first&.position == nil
#=> true
Some legitimate use cases: The safe navigation operator comes in handy when working with multiple objects, as shown here, and when chaining methods together.

it used for nil check, such as in kotlin and swift
For example;
with Object -> Swift and Kotlin
model = car?.model
this model can be nil(Swift) or null(Kotlin) if we have not defined the model value in car class.
we use that ampersand instead of question mark in ruby
model = car&.model
if use car.model without ampersand and if model is nil the system cannot continue running.

Here's a short-read (3 mins) I found on this - it is pretty good.
To add to the above, it acts like the try! method in Rails, not the try method.
Because it will raise a NoMethodError exception if the receiver is not nil and does not implement the tried method.
Example taken from the above article:
account = Account.new(owner: Object.new)
account&.owner&.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
account.try(:owner).try(:address)
# => nil
account.try!(:owner).try!(:address)
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`

For all of those who came here from Typescript, it's the same as the ? operator

what does mean object&.an_attribute in ruby on rails?
I am new in Ruby on rails and I saw this kind of code but I don't understand it:
In Ruby, like in most mainstream programming languages, user code cannot modify the fundamental workings of the programming languages, nor can it change the programming language's syntax.
Since Ruby on Rails is just Ruby code, it should be immediately obvious that this cannot possibly have anything to do with Ruby on Rails.
Therefore, we need to look at Ruby for an explanation, not Ruby on Rails.
The safe navigation operator or safe navigator is specified in language/safe_navigator_spec.rb of the ruby/spec, in particular here:
context "when context is nil" do
it "always returns nil" do
eval("nil&.unknown").should == nil
eval("[][10]&.unknown").should == nil
end
it "can be chained" do
eval("nil&.one&.two&.three").should == nil
end
it "doesn't evaluate arguments" do
obj = Object.new
obj.should_not_receive(:m)
eval("nil&.unknown(obj.m) { obj.m }")
end
end
It is documented in the Calling Methods section of the Ruby Syntax documentation:
&., called “safe navigation operator”, allows to skip method call when receiver is nil. It returns nil and doesn't evaluate method's arguments if the call is skipped.

Related

Unidentified method `add` for Ruby class

This is a fake banking app:
class Bank
private def initialize
$login = Hash.new
puts "Welcome to HEIDI BANK!"
end
def add(keyVal)
keyVal.each do |key, value|
$login[key] = value
end
end
def NewUser
puts "What is the name of the user you would like to add?"
user = gets.chomp
puts "What is the password you would add?"
pass = gets.chomp
passsym = pass.to_sym
$login.add(user => passsym)
end
end
you_bank = Bank.new
you_bank.NewUser
When I attempt to run it, I get:
Welcome to HEIDI BANK!
What is the name of the user you would like to add?
12
What is the password you would add?
13
then an error is raised:
in `NewUser': undefined method `add' for {}:Hash(NoMethodError)
How would I fix this error? It seems I might have to figure out how to call Bank.add or something. Do hashes have an add function built in, like with samp_array.push for arrays?
You defined add as an instance method of Bank, but you are calling it on $login, which is an instance of Hash. Call it on the instance of Bank itself, which can be omitted:
def NewUser
...
add(user => passsym)
end
I already provided the answer in the comments but I wanted to make a longer form answer to help you out. It appears that you're a young programmer and new to Ruby so I would like to help you adopt healthy habits. Accordingly, I have refactored your code and included comments explaining the changes with links to resources that you can read to help understand why the changes were made.
These are mostly minor issues but they're important when writing any kind of production code or when writing code that others may have to read or use in the future.
# Allows the use of STDIN.noecho which will not echo text input back to the console:
# https://stackoverflow.com/a/29334534/3784008
require 'io/console'
# Use two spaces of indentation for Ruby, not tabs and not 4 spaces:
# https://github.com/rubocop-hq/ruby-style-guide#source-code-layout
class Bank
# The initialize method is not a private method:
# https://stackoverflow.com/q/1567400/3784008
def initialize
# Use single quotes for non-interpolated strings
# https://github.com/rubocop-hq/ruby-style-guide#strings
puts 'Welcome to HEIDI BANK!'
# Don't instantiate the `login` variable here; it should be lazily instantiated:
# http://blog.jayfields.com/2007/07/ruby-lazily-initialized-attributes.html
end
# Use snake_case for method names
# https://github.com/rubocop-hq/ruby-style-guide#naming
def new_user
puts 'What is the name of the user you would like to add?'
user = gets.chomp
puts 'What is the password you would add?'
# Suppress local echo of the password as it is typed
# https://stackoverflow.com/a/29334534/3784008
pass = STDIN.noecho(&:gets).chomp
# Do not call .to_sym on pass; if pass == an Integer then it will raise an exception,
# e.g., 1.to_sym => NoMethodError: undefined method `to_sym' for 1:Integer
{ user => pass }
end
end
Then you run it like you were doing before:
you_bank = Bank.new
Welcome to HEIDI BANK!
=> #<Bank:0x00007f8cc9086710>
you_bank.new_user
What is the name of the user you would like to add?
foo
What is the password you would add?
=> {"foo"=>"bar"}
Two other notes on your original code:
Don't use global variables (variable name preceded with a $). There is more explanation at this answer. If you end up needing a variable that is accessible within the instance of the class you should use an instance variable.
Write DRY code. There's no need for the add(keyVal) method because it's implemented already in Hash by merge. Try translating the issue you want to resolve into something you can search for, in this case you want to "add to hash in ruby" and the first Google result for that query is this answer that details how to do that.
I hope this helps you out.
Update
Below you asked "what is lazy instantiation?" The short answer is: don't assign variables until they need to be used. For example:
# Bad; don't do this
class Foo
def initialize
# This variable is instantiated when calling `Foo.new`,
# before it needs to be used, and so takes up memory right
# away vs. only once it's needed
#variable_used_by_example_method = 'foobar'
end
def example_method
puts #variable_used_by_example_method
end
end
# Better; okay to do this
class Foo
def initialize
end
def example_method
# This variable is lazily instantiated, that is, it is not
# instantiated until `Foo.new.example_method` is called
#variable_used_by_example_method = 'foobar'
puts #variable_used_by_example_method
end
end
For your other question about comparing usernames and passwords against what users type in, I recommend you think through the problem and if you still are unsure then post a new question. The question you've asked isn't clear enough for me to give you a good answer. Right now you're experimenting with the code to figure out how everything works, and when you're experimenting and learning it's okay to do things that don't squarely fit into object-oriented programming design patterns. But any answer I give you based on what you've told me so far would either go against those patterns, or would be too advanced. (e.g., use a database and associated models in a framework like Rails)
I wouldn't do the first because that would be bad advice, and I wouldn't do the second because you should have a firmer grasp of Ruby and programming first. So my recommendation is for you to think these through:
What is the plain-English step-by-step explanation of your overall goal?
How can that explanation be described in terms of object-oriented logic?
What code can you write to encapsulate that logic?
What are the gaps between your ability to describe the logic and your ability to write code for that logic?
Then you can begin to ask specific questions to fill in those gaps.

How does the syntax MODULE::METHODNAME('string') work

I recently had cause to use the nokogiri gem to parse html but while i going through their documentation, i came across this ruby syntax that i hadn't seen before
html_doc = Nokogiri::HTML('<html><body><h1>Mr. Belvedere Fan Club</h1></body></html>')
xml_doc = Nokogiri::XML('<root><aliens><alien><name>Alf</name></alien></aliens></root>')
The part of interest for me is Nokogiri::HTML('...'). This looks very much like a method invocation but i know ruby method names cannot be in capital letters. So i looked through code files nokogiri gem and i came across the following definition
module Nokogiri
class << self
###
# Parse HTML. Convenience method for Nokogiri::HTML::Document.parse
def HTML thing, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML, &block
Nokogiri::HTML::Document.parse(thing, url, encoding, options, &block)
end
end
# more code
end
I tried reproducing the same code
module How
class << self
def DOESTHISWORK
puts "In How Method"
end
end
end
How::DOESTHISWORK
But it keeps coming back with the error "uninitialized constant How::DOESTHISWORK (NameError)". I know it has to do with the method name starting in capitals but i just haven't been able to figure out how it works in nokogiri.
The difference is in the Nokogiri example the method is being called with parentheses and a parameter value which identifies it as a method call. Your DOESTHISWORK method takes no parameters but can be called with empty parentheses e.g.
irb(main):028:0> How::DOESTHISWORK()
In How Method
=> nil
If you add a parameter to your method that can also serve to identify it as a method like so:
irb(main):036:0> How::DOESTHISWORK 'some param'
Starting method names with a lowercase letter is good practice but isn't enforced. Something that begins with a capital letter is assumed to be a constant and will be looked up as such, this is why the parentheses or parameter is needed to indicate a method is being referred to. Another example:
irb(main):051:0> def Example
irb(main):052:1> puts "An example!"
irb(main):053:1> end
=> nil
irb(main):054:0> Example
NameError: uninitialized constant Example
from (irb):54
from /Users/mike/.rbenv/versions/1.9.3-p194/bin/irb:12:in `<main>'
irb(main):055:0> Example()
An example!
=> nil
I also found this post to be very helpful
What are the restrictions for method names in Ruby?
It's good practice, while not mandatory, to start the method name with
a lower-case character, because names that start with capital letters
are constants in Ruby. It's still possible to use a constant name for
a method, but you won't be able to invoke it without parentheses,
because the interpeter will look-up for the name as a constant

Calling setters and getters

I have a setter and a getter method for attribute :isbn on class Book:
class Book
attr_accessor :isbn
end
book01 is an instance of Book:
book01 = Book.new
Which of these is the preferred way when setting an instance attribute?
book01.isbn=("9876")
book01.isbn= "9876"
book01.isbn = "9876"
Why does this not work as an option?
book01.isbn("9876")
# => ArgumentError: wrong number of arguments (1 for 0)
In your example:
book01.isbn=("9876")
book01.isbn= "9876"
book01.isbn = "9876"
The last 2 examples are 'syntactic sugar', which are things that technically aren't proper syntactically but are kept in the language because they keep the code cleaner. The first example is the only way that would work if Ruby didn't support syntactic sugar. Why?
Because attr_acccessor :isbn behind the hood creates the following code for you:
def isbn
#isbn
end
def isbn=(new_isbn)
#isbn = new_isbn
end
These are 2 totally different methods, this might be confusing because the only difference in name is the = sign. But that doesn't mean anything, and doesn't change the fact they are totally different methods.
So with:
book01.isbn=("9876")
you're actually calling def isbn=(new_isbn) which is a method, nothing more, nothing else. And with:
book01.isbn= "9876"
book01.isbn = "9876"
you're just calling the SAME method, just using 'syntactic sugar'. Behind the hood, Ruby sees all of these 2 as:
book01.isbn=("9876")
Can you guess why this code will not work?
book01.isbn("9876")
Because, as we saw earlier, behind the hood Ruby creates 2 methods. The first method doesn't accept ANY arguments, therefore, you get the error you're getting (Ruby is just telling you, I expected 0 arguments, and you provided 1, therefore I raised ArgumentError).

More natural way of Proc calling in Ruby 1.9

As we know, there are several way of Proc calling in Ruby 1.9
f =->n {[:hello, n]}
p f[:ruby] # => [:hello, :ruby]
p f.call(:ruby) # => [:hello, :ruby]
p f.(:ruby) # => [:hello, :ruby]
p f === :ruby # => [:hello, :ruby]
I am curious, what is more 'natural' way of calling Proc? 'Natural', probably, means more Computer Science - like way.
The second option is by far the most used.
p f.call(:ruby) # => [:hello, :ruby]
It makes it more similar to a standard method. Also, some libraries actually rely on duck typing when validating arguments checking the availability of the #call method. In this case, using #call ensures you can provide a lambda or any other object (including a Class) that responds to #call.
Rack middlewares are a great example of this convention. The basic middleware can be a lambda, or you can supply more complex logic by using classes.
I always use option 3. Considering the syntactic ambiguities of being able to call methods without parentheses, this is the closest you can get to actual method call syntax.
I saw the first way used in Rack source code. It confused me in a long time. It's picked from lib/rack/builder.rb (version: 1.6.0.alpha)
module Rack
class Builder
...
def to_app
app = #map ? generate_map(#run, #map) : #run
fail "missing run or map statement" unless app
# This is the first option calling a proc
# #use is a array of procs (rack middleware)
#use.reverse.inject(app) { |a,e| e[a] }
end
...
end
end

rails rspec - how to check for a model constant?

How can I do something like:
it { should have_constant(:FIXED_LIST) }
In my model (active record) I have FIXED_LIST = 'A String'
It's not a db attribute or a method and I haven't been able to use responds_to or has_attribute to test for it (they fail). What can I use the to check for it. - btw I have the shoulda-matchers installed.
Based on David Chelimsky's answer I've got this to work by slightly modifying his code.
In a file spec/support/utilities.rb (or some other in spec/support) you can put:
RSpec::Matchers.define :have_constant do |const|
match do |owner|
owner.const_defined?(const)
end
end
Note the use of "RSpec::Matchers.define" in stead of "matchers"
This allows to test for constants in your specs, like:
it "should have a fixed list constant" do
YourModel.should have_constant(:FIXED_LIST)
end
Note the use of "have_constant" in stead of "have_const"
It reads a little silly, but:
describe MyClass do
it { should be_const_defined(:VERSION) }
end
The reason is that Rspec has "magic" matchers for methods starting with be_ and have_. For example, it { should have_green_pants } would assert that the has_green_pants? method on the subject returns true.
In the same fashion, an example such as it { should be_happy } would assert that the happy? method on the subject returns true.
So, the example it { should be_const_defined(:VERSION) } asserts that const_defined?(:VERSION) returns true.
If you want to say have_constant you can define a custom matcher for it:
matcher :have_constant do |const|
match do |owner|
owner.const_defined?(const)
end
end
MyClass.should have_const(:CONST)
If you're trying to use the one-liner syntax, you'll need to make sure the subject is a class (not an instance) or check for it in the matcher:
matcher :have_constant do |const|
match do |owner|
(owner.is_a?(Class) ? owner : owner.class).const_defined?(const)
end
end
See http://rubydoc.info/gems/rspec-expectations/RSpec/Matchers for more info on custom matchers.
HTH,
David
Another option to simply make sure the constant is defined – not worrying about what it's defined with:
it 'has a WHATEVER constant' do
expect(SomeClass::WHATEVER).not_to be_nil
end
A warning to anyone trying to test that constants are defined: If your code references an undefined constant while defining a class, then your specs will crash before they get to your test.
This can lead you to believe that
expect { FOO }.to_not raise_error
is failing to catch the NameError, because you'll get a big stack trace, instead of a nice "expected not to raise error, but raised NameError."
Amidst the huge stack trace, it can be difficult to notice that your test is actually crashing on line 1: requre "spec/spec_helper" because your entire application is failing to load before it gets to your actual test.
This can happen if you have dynamically defined constants, such as is done by ActiveHash::Enum, and you then use them in the definition of another constant. Don't bother testing that they exist, every spec in your app will crash if one of them fails to be defined.
You could use
defined? YOUR_MODEL::FIXED_LIST
In RSpec 2, I was able to get this to work in one line as follows:
it { subject.class.should be_const_defined(:MY_CONST) }
That is, check against the class, instead of the instance.
In My model
class Role < ActiveRecord::Base
ROLE_ADMIN = "Administrador"
end
In My rspec
RSpec.describe Role, type: :model do
let(:fake_class) { Class.new }
describe "set constants" do
before { stub_const("#{described_class}", fake_class) }
it { expect(described_class::ROLE_ADMIN).to eq("Administrador") }
end
end
For ruby 2.1.5 and rspec 3.5.0 I am able to test that constant SEARCH_CHARS_TO_IGNORE is defined in the class DiffAlertsDatatable as follows:
expect(DiffAlertsDatatable.const_defined?(:SEARCH_CHARS_TO_IGNORE)).to eq(true)

Resources