Just use the ordinary (non-sugar) form.
request.path.match(/\A\/(?<slug>(?!admin|assets)\w+)/)&.[](:slug)
Answer from sawa on Stack OverflowJust use the ordinary (non-sugar) form.
request.path.match(/\A\/(?<slug>(?!admin|assets)\w+)/)&.[](:slug)
For better readability I would pick #dig instead of the #[].
Like,
request.path.match(/\A\/(?<slug>(?!admin|assets)\w+)/)&.dig(:slug)
instead of
request.path.match(/\A\/(?<slug>(?!admin|assets)\w+)/)&.[](:slug)
To quote from the syntax documentation for the safe navigation operator:
&., called “safe navigation operator”, allows to skip method call when receiver isnil. It returnsniland doesn't evaluate method's arguments if the call is skipped.
As such, the arguments of your log method are not evaluated if the logger is nil when you call it as
logger&.log("something happened at #{Time.now}")
With that being said, note that the Ruby core logger offers a different solution to your exact issue, namely to avoid having to evaluate potentially expensive arguments if the log level is to high.
The Ruby core logger implements its add method something like this (simplified):
class Logger
attr_accessor :level
def initialize(level)
@level = level.to_i
end
def add(severity, message = nil)
return unless severity >= level
message ||= yield
log_device.write(message)
end
def info(message = nil, &block)
add(1, message, &block)
end
end
You can then use this as
logger = Logger.new(1)
logger.info { "something happened at #{Time.now}" }
Here, the block is only evaluated if the log level is high enough that the message is actually used.
Expression Parsed But Not Executed
The argument to logger&.log isn't evaluated when logger.is_a?(NilClass) == true. Every Ruby expression that's evaluated should have an impact, so consider:
test = 1
nil&.log(test+=1); test
#=> 1
If the argument were evaluated by the interpreter, test would equal two. So, while the parser certainly parses the expression in your argument, it doesn't execute the inner expression.
You can verify what the parser sees with Ripper#sexp:
require 'ripper'
test = 1
pp Ripper.sexp "nil&.log(test+=1)"; test
[:program, [[:method_add_arg, [:call, [:var_ref, [:@kw, "nil", [1, 0]]], [:@op, "&.", [1, 3]], [:@ident, "log", [1, 5]]], [:arg_paren, [:args_add_block, [[:opassign, [:var_field, [:@ident, "test", [1, 9]]], [:@op, "+=", [1, 13]], [:@int, "1", [1, 15]]]], false]]]]] #=> 1
This clearly shows that the parser sees the incremented assignment in the symbolic expression tree. However, the assignment is never actually executed.
There are 2 seperate operators here:
Safe navigation operator
&.- It is safe navigation operator which was introduced in Ruby 2.3.0. It basically returnsnilif the callee isnilinstead of raising excecptionundefined method called for Nil class. eg:a = 1 a.next # => 2 a&.next # => 2 a = nil a.next # => NoMethodError (undefined method `next' for nil:NilClass) a&.next # => nil ## No exception, returns nilYou can read about it more here and documentation
Unary
&: This operator is a little more complex. It is almost equivalent to calling#to_procbut not quite that. But for this discussion let us think like that. So, if you have a Proc, calling with&in front of it will call#to_procon the Proc and convert it into a blockmultiply_by_2 = Proc.new { |x| x * 2 } # => #<Proc:0x00007fb4771cf560> # &multiply_by_2 almost equivalent to { |x| x * 2 } but is not correct syntax [1, 2].map(&multiply_by_2) # => [2, 4] # equivalent to [1, 2].map { |x| x * 2 }But what happens if we give a symbol like :abc to
&operator instead of a proc. It will try to call#to_procon the symbol and ruby has definedSymbol#to_procwhich roughly translates to something like this:def to_proc # this will return some block like { |x| x.send(:abc) } lambda { |x| x.send(self) } endSo
&:abcroughly translates to this block{ |x| x.abc }using the below transformation&:abc =====> :abc.to_proc =====> { |x| x.send(:abc) } ====> { |x| x.abc }So, instead of doing
[1, 2, 3].map { |x| x.next }, you could do[1, 2, 3].map(&:next)as&:nextis roughly equivalent to the block{ |x| x.next }.See unary & (which is the main source of what I have written here) for more reading.
It's ruby syntax, & calls to_proc on the object and passes the result as a block to the method.
An explanation from the pickaxe book, programming Ruby 1.9 and 2.0
Blocks Can Be Objects
Blocks are like anonymous methods, but there’s more to them than that. You can also convert a block into an object, store it in variables, pass it around, and then invoke its code later. Remember we said that you can think of blocks as being like an implicit parameter that’s passed to a method? Well, you can also make that parameter explicit. If the last parameter in a method definition is prefixed with an ampersand (such as &action ), Ruby looks for a code block whenever that method is called. That code block is converted to an object of class Proc and assigned to the parameter. You can then treat the parameter as any other variable. Here’s an example where we create a Proc object in one instance method and store it in an instance variable. We then invoke the proc from a second instance method.
class ProcExample
def pass_in_block(&action)
@stored_proc = action
end
def use_proc(parameter)
@stored_proc.call(parameter)
end
end
Use it like so
eg = ProcExample.new
eg.pass_in_block { |param| puts "The parameter is #{param}" }
eg.use_proc(99)
produces:
The parameter is 99
&. works like #try!, not #try.
And here is description of #try! (from documentation):
Same as #try, but will raise a NoMethodError exception if the receiving is not nil and does not implemented the tried method.
So, &. saves you from calling a method on nil, but if the object is present it will try to call the method as usual, including raising NoMethodError if the method is not implemented.
#try, on the other hand, saves you from calling a method on nil and calling a method that is not implemented. It will return nil in either case, and never raise NoMethodError.
The quote is from Rails Documentation, and so it's important to emphasize that Ruby does not provide #try; it's provided by Rails, or more accurately ActiveSupport. The safe navigation operator (&.) however, is a language feature presented in Ruby 2.3.0.
I am arriving to the party a bit late here, the other answers have covered how it works, but I wanted to add something that the other answers have not covered.
Your question asks What is the difference between try and &. in Ruby. Ruby being the key word here.
The biggest difference is that try doesn't exist in Ruby, it is a method provided by Rails. you can see this or yourself if you do something like this in the rails console:
Copy[1, 2, 3].try(:join, '-')
#=> "1-2-3"
However if you do the same thing in the irb console, you will get:
Copy[1, 2, 3].try(:join, '-')
NoMethodError: undefined method `try' for [1, 2, 3]:Array
The &. is part of the Ruby standard library, and is therefore available in any Ruby project, not just Rails.
foo&.bar is shorthand for foo && foo.bar, so what would you expect the result of the expression nil && nil.nil? to be?
This is because nil&.nil? is shorthand for nil && nil.nil?. This would evaluate to nil && true, which is then nil.
(nil && x).nil? always evaluates to true, for any value of x.
While the syntax has power, this specific case has some potential to become a 'gotcha' for a developer:
(stuff&.things).nil? => This produces true if stuff doesn't exist, or stuff.things returns nil.
vs. the below case:
stuff&.things&.nil? => This produces nil in every case except the case where stuff.things returns something other than nil, in which case it would return false.
Because of the difficulty in normal boolean logic of differentiating between false and nil, it is unlikely that this would make sense in normal logic.