Proper usage of Ruby statement modifiers
I find that I usually have no trouble reading those trailing conditionals (as they are also sometimes called), provided that other code readability guidelines are still followed. Putting a 60 character expression and a 40 character condition on the same line, you are going to end up with a 100 character gob of text, which is surely going to be unreadable, completely independent of the issue of trailing conditionals.
In the specific code sample you are showing, it is pretty much obvious that there must be a conditional following. Who would want to raise
an ArgumentError
without even taking a look at the arguments first?
Also, trailing conditionals are similar to guard clauses in math and functional languages, which also tend to be written after the expression they are guarding.
Last but not least, putting a couple of raise Bar if foo
and return nil if quux
expressions at the beginning of methods, as kind of guards, is actually considered good style, to simplify the control flow of the method. Again: since these come at the beginning of the method, it is kind of obvious that there has to be a condition, otherwise return
ing from the beginning of the method wouldn't make sense.
PS: I would actually use
unless
there, to get rid of the negation. With more complicated conditions, I find that unless
can sometimes be hard to parse, but in this case, it's more obvious, at least IMHO. Why should we avoid using rescue in its modifier form?
Firstly, because it hides all errors, including the ones you expect and the ones you don't, and a blanketWhy should we avoid using rescue in its modifier form in rails?
rescue
doesn't make it clear to future readers of your code which errors were expected or unexpected. This might not be a problem now, with a simple foo[:a][:b][:c]
, but at any given point in time somebody might modify that statement to read foo[:a][:b][some_method]
and suddenly any errors that should bubble out of some_method
are also swallowed. Secondly, there is usually a better less all-encompassing solution that is more explicitly designed to handle only the error you intend to ignore: A missing index or a nil
return value.
In your case, the alternative is not the massive if && && &&
you're suggesting. For a hash, you can use either dig
, which has all the benefits of rescue
without swallowing every type of exception that could be raised:
foo = bar.dig(:a, :b, :c)
Similarly, for chained method invocations, you can use try
(in Rails) or the safe navigation operator (in Ruby 2.3):foo = bar.try(:a).try(:b).try(:c)
# or
foo = bar&.a&.b&.c
Ruby 2.2.4 lexing an assignment in a conditional
Both lines won't work. And both lines will work. It is schrödinger expression :).
You can run it twice in a new repl:
a = b if b = "test"
#=> NameError: undefined local variable or method `b' for main:Object
a = b if b = "test"
#=> "test"
Let's look deeper, open a new repl:defined(b)
#=> nil
a = b if b = "test"
#=> NameError: undefined local variable or method `b' for main:Object
defined(b)
#=> local-variable
b
#=> "test"
a = b if b = "test"
#=> "test"
So actually Ruby has evaluated b = "test"
part and defined this variable in current scope. Both expressions a = b
and if b = "test"
were executed. More than it, if statement is executed before assignment statement:c = p("assignment") && b if b = p("if") && "test"
#=> "if"
#=> "assignment"
#=> NameError: undefined local variable or method `b' for main:Object
But b
variable was not defined in scope of assignment statement when it was evaluated first time. And on the second approach it was already defined so you received correct result.So, Never do assignments in this way
Proper usage of 'end_with?'?
As @razvans points out, the error message here is great.
The Error
You're initializing your object withinput_file_path: nil
, and then inside of call
you're checking if @input_file_path
(set to input_file_path
in the initializer, AKA nil
) ends with a string.There is no .end_with?
method on nil
. That's causing the NoMethodError
error.
The Solution
You could guard againstnil
in your end_with?
call, or give it a default value in the initializer. Can I put an if/unless clause on the next line in Ruby?
You can't. From page 107 (PDF page 127) of the final draft of ISO Ruby which usually isn't relevant, but basic things like this are and it also spares us from having to readCan I put an if/unless clause on the next line in Ruby?
parse.y
:unless-modifier-statement ::
statement [no line-terminator here] unless expression
This is pretty clear. It just doesn't get more similar to your Perl example than:raise ArgumentError, "incompatible object given: #{object.inspect}" unless
object.is_a?(ExampleObject) and object.respond_to?(:foo)`
or:raise ArgumentError, "incompatible object given: #{object.inspect}" \
unless object.is_a?(ExampleObject) and object.respond_to?(:foo)
Related Topics
Automatically Adding Proxy to All Http Connections in Ruby
How to Scrape a Website with The Socksify Gem (Proxy)
How to Check If an Object Is Nil in a View in Ruby
Camelcase Instead of Snake_Case in Rails Db
Will_Paginate Find Out If I'M on The Last Page
How to Run Capybara-Webkit (I.E. Forked Webkit_Server) on Heroku Cedar
Sha1 Hashes Not Matching Between My Rails and Cocoa Apps
Use a String to Access a Local Variable by Name
Use Pry in Gems Without Modifying The Gemfile or Using 'Require'
Pod Install in Xcode Bots Trigger
Where Is Ruby's Erb Format "Officially" Defined
Is 'Respond_To_Missing''s Second Argument Useful for Anything
How to Wait for System Command to End
How to Automatically Escape HTML Content Using Jekyll and Markdown