Writing pretty & consistent multiline-conditions? - ruby

For a better readability i normally prefer writing multiline-conditions in if-statements as follows:
if
something == nice and
anotherthing == bad
then
do_something
and_so_on
end
I am currently facing while-loops (at a point where i can't avoid them) and have to use multiple conditions. I tried the following and got a corresponding syntax error, unexpected keyword_do_block (SyntaxError):
while
something == nice and
anotherthing == bad
do
do_something
and_so_on
end
It turned out that the optional do-keyword can't stand alone in the next line, in opposite to the then-keyword. This is how it works -- after putting the do at the end of the preceding line:
while
something == nice and
anotherthing == bad do
do_something
and_so_on
end
For my comprehension of readability, this is probably not as readable as the corresponding if-statement, which "enforces" a newline and accentuates the indentation (again).
Did i miss an alternative syntax and/or is it probably a flaw in Ruby's syntax-design (shouldn't it be possible to put the do into the next line)?

You can use begin end while syntax, Your conditions will be at the end of the statement but as the if statement:
begin
do_something
and_so_on
end while
something == nice and
anotherthing == bad
BUT in this case it will execute once before checking the condition
You can kind of reverse it and it will work:
while
something == nice and
anotherthing == bad
begin
do_something
and_so_on
end
end
Good luck!

Related

OR condition in `if` statement

Why is
if name.eql? 'Name1' || name.eql? 'Name2'
# doSomething
end
not allowed in Ruby? What is the good practice to do this in Ruby?
What is the good practice to do this in ruby?
Parenthesizing method calls to avoid silly ambiguities.
if name.eql?('Name1') || name.eql?('Name2')
You can use a case-expression:
case name
when 'Name1', 'Name2'
# do something
when 'Name3'
# do something else
else
# if all else fails
end
ruby gives us the option to omit parenthesis and delegate the responsibility to the interpreter. what you've done is confuse the ruby interpreter because your lack of parentheses is ambiguous. Ie, ruby can't figure out what you're trying to do
this can be viewed a number of ways if we specify each parens
if(name.eql?('Name1') || name.eql?('Name2'))
if(name.eql?('Name1' || name.eql?('Name2'))
if(name.eql?('Name1')) || name.eql?('Name2')
a good practice is to specify parens when it starts to become ambiguous. Here that would mean putting them on the method arguements
if name.eql?('Name1') || name.eql?('Name2')

Detecting a missing sub!, sort!, map!, etc

After returning to Ruby from a long stint coding in another language, I regularly assume that foo.sort, foo.map {...}, foo.sub /bar/, 'zip' will change foo. Of course I meant foo.sort!, etc. But that usually takes 3 or 4 debugging potshots before I notice. Meanwhile, the sort is calculated, but then isn't assigned to anything. Can I make ruby warn about that missing lvalue, like a C compiler warns of a function's ignored return value?
You mean like Perl's somewhat infamous "using map in void context"? I don't know of Ruby having such a thing. Sounds like you need more unit testing to catch mistakes like this before they can worm into your code deeply enough to be considered bugs.
Keep in mind Ruby's a lot more flexible than languages like Perl. For example, the following code might be useful:
def rewrite(list)
list.map do |row|
row += '!'
end
end
Now technically that's a map in a void context, but because it's used as a return value it might be captured elsewhere. It's the responsibility of the caller to make use of it. Flagging the method itself for some sort of warning is a level removed from what most linting type tools can do.
Here's a very basic parser :
#forgetful_methods = %w(sort map sub)
Dir['*.rb'].each do |script|
File.readlines(script).each.with_index(1) do |line, i|
#forgetful_methods.each do |method|
if line =~ /\.#{method}(?!!)/ && $` !~ /(=|\b(puts|print|return)\b|^#)/
puts format('%-25s (%3d) : %s', script, i, line.strip)
end
end
end
end
# =>
# brace_globbing.rb ( 13) : subpatterns.map{|subpattern| explode_extglob(match.pre_match+subpattern+match.post_match)}.flatten
# delegate.rb ( 11) : #targets.map { |t| t.send(m, *args) }
It checks every ruby script in the current directory for sort, map or sub without ! that aren't preceded by =, puts, print or return.
It's just a start, but maybe it could help you find some of the low hanging fruits.
There are many false positives, though.
A more complex version could use abstract syntax trees, for example with Ripper.

Can't understand "unless" keyword in Ruby

I'm not a native English speaker. I know it sounds stupid nonetheless. I write in Python & C. But I can't quite understand how unless works.
Is it healthy to think of it from a logical standpoint? Eg, consider the if keyword: if condition. If condition is true, the code runs. What's the logical explanation for unless?
Is there any other way to think of it?
unless x is equivalent to if !x
Ruby unless modifier:
Syntax: code unless conditional
Executes code if conditional is false.
Example:
$var = 1
print "1 -- Value is set\n" if $var
print "2 -- Value is set\n" unless $var
$var = false
print "3 -- Value is set\n" unless $var
This will produce following result:
1 -- Value is set
3 -- Value is set
The easiest way is: unless is an opposite of if. It works in the same places (I think) as if:
foo = nil
puts "This will not show" if foo
puts "But this will" unless foo
if foo then
puts "This will not show"
end
unless foo
puts "But this will"
end
It looks nicer in the code if you write unless foo instead of if !foo - but that is only my opinion. In the longer form, you can even use else, but please, do not.
Here is a set of rules to follow to make the code more readable.
The way i think of it is to say:
"The block of code is going to be executed, unless the condition is true"
unless <condition>
<code>
end
hungry = true
unless hungry
puts "I'm writing Ruby programs!"
else
puts "Time to eat!"
end
This is the easy way to understand

What is the difference between "if" statements with "then" at the end?

What's the difference between these two Ruby if statements when we put a then at the end of the if statement?
if(val == "hi") then
something.meth("hello")
else
something.meth("right")
end
and
if(val == "hi")
something.meth("hello")
else
something.meth("right")
end
then is a delimiter to help Ruby identify the condition and the true-part of the expression.
if condition then true-part else false-part end
then is optional unless you want to write an if expression in one line. For an if-else-end spanning multiple lines the newline acts as a delimiter to split the conditional from the true-part
# can't use newline as delimiter, need keywords
puts if (val == 1) then '1' else 'Not 1' end
# can use newline as delimiter
puts if (val == 1)
'1'
else
'Not 1'
end
Here's a quick tip that is not directly related to your question: in Ruby, there is no such thing as an if statement. In fact, in Ruby, there are no statements at all. Everything is an expression. An if expression returns the value of the last expression that was evaluated in the branch that was taken.
So, there is no need to write
if condition
foo(something)
else
foo(something_else)
end
This would better be written as
foo(
if condition
something
else
something_else
end
)
Or as a one-liner
foo(if condition then something else something_else end)
In your example:
something.meth(if val == 'hi' then 'hello' else 'right' end)
Note: Ruby also has a ternary operator (condition ? then_branch : else_branch) but that is completely unnecessary and should be avoided. The only reason why the ternary operator is needed in languages like C is because in C if is a statement and thus cannot return a value. You need the ternary operator, because it is an expression and is the only way to return a value from a conditional. But in Ruby, if is already an expression, so there is really no need for a ternary operator.
The then is only required if you want to write the if expression on one line:
if val == "hi" then something.meth("hello")
else something.meth("right")
end
That brackets in your example are not significant, you can skip them in either case.
See the Pickaxe Book for details.
The only time that I like to use then on a multi-line if/else (yes, I know it's not required) is when there are multiple conditions for the if, like so:
if some_thing? ||
(some_other_thing? && this_thing_too?) ||
or_even_this_thing_right_here?
then
some_record.do_something_awesome!
end
I find it to be much more readable than either of these (completely valid) options:
if some_thing? || (some_other_thing? && this_thing_too?) || or_even_this_thing_right_here?
some_record.do_something_awesome!
end
# or
if some_thing? ||
(some_other_thing? && this_thing_too?) ||
or_even_this_thing_right_here?
some_record.do_something_awesome!
end
Because it provides a visual delineation between the condition(s) of the if and the block to execute if the condition(s) evaluates to true.
There's no difference at all.
And, just FYI, your code can be optimized to
something.meth( val == 'hi' ? 'hello' : 'right' )

RegEx in Ruby: Just one match?

I'm trying to figure out how to check if a string matches a regular expression, but I want to know if the entire string matches just once. Here's my code but it seems absurdly long
def single_match(test_me, regex)
ret_val = false
test = regex.match(test_me)
if (test.length==1 && test[0].length == test_me.length)
ret_val = true
end
return ret_val
end
is there an easier way to do this?
P.S. Here's the method I'm really trying to write, since people always seem to ask why I want the gun these days:
def is_int(test_me)
return single_match(test_me, /[0-9]*/)
end
Edit Thanks everybody. Here's where I'm really using it, but this regex stuff is always interesting to go through. Thanks for the great and educational answers.
You don't need to do this, your method can be replaced by using the regular expression of /^[0-9]*$/. The ^ tells it match start of a line and $ tells it match end of the line. So it will match: start of line, 0 to any in range of 0 to 9, and finally end of line.
def is_int(test_me)
test_me =~ /^[0-9]*$/
end
And you don't need the return statements, Ruby implicitly returns the last statement.
Edit:
It probably would be easier and look better to use the to_i instance method of String class.
def is_int(test_me)
test_me.to_i.to_s == test_me
end
Edit: (did some tests)
Comparing the performance between the two methods shows that .to_i.to_s == way is 5% faster. So it is up to personal preference to which ever looks better and if you want to handle leading zeroes.
To do what you really want should be even simpler
def is_int(test_me)
test_me.to_i.to_s == test_me
end
This?
def single_match(str, regex)
str.match(regex).to_s == str
end
To answer your original question, for the sake of people finding this page in a search, "scan" will return an array of matches, so if you want to find out how many times some regexp matches, e.g. how many runs of digits there are, you can do:
mystring.scan(/\d+/).size

Resources