Ruby if statement multiline condition syntax? - ruby

Hi all new to ruby is this if statement valid syntax?
if (verify_login_id = Login.where(params[:email_address], "active" =>1).select('id')# => [#<Login id: 767>]
verify_admin_id = Admin.where("login_id" => verify_login_id.first.id).exists? #=> true)
puts "trueee"
else
raise(ActiveRecord::RecordNotFound.new("Authorization is required to access this endpoint!"))
end

Although setting variable values within an if statement is syntactically valid, it is very problematic.
It can be mistakenly read as a comparison rather than assignment, and in many cases it is a result of someone trying to make a comparison and confusing the equals predicate == with the assignment operator =. That's why a lot of IDEs today mark such code as a warning and a probable error.
In your code it also seems quite unneeded... Break it into two more readable lines:
verified_login = Login.where(params[:email_address], "active" =>1).select('id').first # => [#<Login id: 767>]
if verified_login && Admin.where("login_id" => verified_login.id).exists? #=> true
puts "trueee"
else
raise(ActiveRecord::RecordNotFound.new("Authorization is required to access this endpoint!"))
end
Some more observations - your assignment to verify_login_id is not an id, but a Login object, and your assignment to verify_admin_id is not an id either - but a boolean (and you are not using it anyway). This might seem besides the point - but it adds up to an unreadable and an unmaintainable code.

No it is not a valid code. In the second line, it contains an invalid #-comment that has removed a single ) from the code. (exact snip is #=>true) and should be ) #=>true)
I've removed the comments from the code, added the missing parenthesis and the parser seems to accept it. I couldn't run it of course. So, try this one, it might work:
if (verify_login_id = Login.where(params[:email_address], "active" =>1).select('id')
verify_admin_id = Admin.where("login_id" => verify_login_id.first.id).exists?)
puts "trueee"
else
raise(ActiveRecord::RecordNotFound.new("Authorization is required to access this endpoint!"))
end
Regarding multiline condition in the if - yes, it is possible. Sometimes directly, sometimes with a small trick. Try this:
if (1+2 >=
3)
puts "as"
end
It works (at least on Ruby 1.9.3). However, this will not:
if (1+2
>=
3)
puts "as"
end
This is because of some internal specific of how Ruby interpreter/compiler is designed. In the last example, to make it work, you need to inform Ruby that the line has not ended. The following example works:
if (1+2 \
>=
3)
puts "as"
end
Note the \ added at the end of problematic line

Related

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.

Need clarifying of exit and abort script Ruby

I am writing a little practice program with if and else. The code is as follows:
puts "What is your name?"
user_name = $stdin.gets.chomp
print "Hello #{user_name}! Welcome to Puzzles and Riddles v.1!"
puts "There are two doors \n 1. Riddles \n 2. Puzzles. \n Which door do you go through?"
answer_1 = $stdin.gets.chomp
if
answer_1 == "1"
puts "You have taken the Riddle room!"
print "Here is your riddle: \n You use a knife to slice my head and weep beside me when I am dead. \n What am I?"
end
riddle_1_answer = $stdin.gets.chomp
if
riddle_1_answer == ( riddle_1_answer == "An onion" ) || ( riddle_1_answer == "an onion" ) || ( riddle_1_answer == "Onion" ) || ( riddle_1_answer == "onion" )
puts "The correct answer is: An onion! \n You have advanced to round two."
else
puts "Sorry, your answer is incorrect. Think about it."
end
puts "Riddle 2. \n What has 4 fingers and a thumb, but is not living?"
riddle_2_answer = $stdin.gets.chomp
Now if the user got riddle_1_answer wrong how would I make it so that the program exits/aborts?
I tried adding exit(0) to the else section and it would terminate the program but would also come up with an error. So I'm not sure if the error is causing the program to end or its the exit(0) command
It's obvious from your question, the sample code, and your answers to #AndrewMarshall, that you need some guidance.
Here you go.
First, ruby is one of many programming languages, and there is a reason why many experienced programmers end up gravitating to it: ruby is object-oriented, expressive, powerful, and reasonably concise without being unnaturally terse. So, if you are going to learn ruby, learn by reading lots of good ruby code, from which you'll learn good practices.
Second, appearance matters because it impacts or enhances readability and comprehension. A good appearance lends to improved readability and more rapid comprehension. A bad appearance does the opposite.
The lack of alignment of the if, else, and end tokens in your code is bad; it made it hard to see the structure of the code's logic.
There are many rules of thumb in programming. Here are a few such rules that apply to most languages:
use alignment and indention properly
always think about "edge cases" (or errors)
limit and isolate complexity (use functions, modules, classes, & methods)
Don't Repeat Yourself (DRY)
So let's apply those two principles to your code and transform it a bit.
The first two lines:
puts "What is your name?"
user_name = $stdin.gets.chomp
What if the user enters CTRL-D (EOF)?
What if the user enters an empty line?
Is an error acceptable? An EOF on STDIN returns a nil, which causes an error on the chomp.
Is an empty string (zero length) name acceptable? If not, what should we do about it?
When there are complexities on doing something relatively simple, like getting a user name, encapsulate the complexities in a function or method, so the code needing the user name is not made confusing by the complexities of getting it.
Here's a replacement. First, let's manage the details (and complexities) of getting a user name within a small function.
def get_user_name
name = ''
while name.size == 0 do
print "What is your name? "
name = gets
exit(1) if name.nil? # exit program on EOF
name.strip!
end
name
end
Notice that we don't use chomp on name until after we've made sure that it isn't nil. Many programs react to EOF on input by exiting, aborting, or continuing on without any more questions. In this example, we'll just assume the user wants to quit.
Notice also that we used strip! instead of chomp!? That's because strip! will remove both leading and trailing whitespace, including the trailing newline.
Notice also that we didn't use $stdin.gets, but instead just gets? This is because the default object for gets is $stdin.
Probably a better approach to managing exceptional situations within small functions (methods), is to raise an exception, and let the higher level application logic decide how to manage it. With that in mind, here is a revised definition:
def get_user_name
name = ''
while name.size < 1 do
print "What is your name? "
name = gets
raise "End of input" if name.nil? # raise exception on EOF
name.strip!
end
name
end
Now, that get_user_name is defined, we can use it wherever we need a user name. We know that EOFs are managed, and we know that we won't get an empty string.
user_name = get_user_name
Now, let's do the rest of your original code, but aligned and indented properly.
print "Hello #{user_name}! Welcome to Puzzles and Riddles v.1!"
puts "There are two doors \n 1. Riddles \n 2. Puzzles. \n Which door do you go through?"
answer_1 = $stdin.gets.chomp
if answer_1 == "1"
puts "You have taken the Riddle room!"
print "Here is your riddle: \n You use a knife to slice my head and weep beside me when I am dead. \n What am I?"
end
riddle_1_answer = $stdin.gets.chomp
if riddle_1_answer == ( riddle_1_answer == "An onion" ) || ( riddle_1_answer == "an onion" ) || ( riddle_1_answer == "Onion" ) || ( riddle_1_answer == "onion" )
puts "The correct answer is: An onion! \n You have advanced to round two."
else
puts "Sorry, your answer is incorrect. Think about it."
end
puts "Riddle 2. \n What has 4 fingers and a thumb, but is not living?"
riddle_2_answer = $stdin.gets.chomp
Now that the alignment and indentation is correct, it's easier to see the logic, and its flaws. It's also easier to see a pattern of logic, and whenever you see a pattern, then DRY it up, and make methods (functions) to encapsulate the repetition.
But first, let's fix the obvious bugs.
The if expression is broken. Look at it again, and you'll see this:
if riddle_1_answer == TEST1 || TEST2 || TEST3 || TEST4
where I've used TESTn to replace the various case-sensitive tests you had.
This if expression will always fail because the value of riddle_1_answer will never be true or false and the result of the various TESTn expressions will always be true or false. I'm pretty sure you wanted this:
if TEST1 || TEST2 || TEST3 || TEST4
Second, when testing for a string value, it's not necessary to test all case variations. Just downcase the answer and test on lowercase test values (unless case-sensitivity is important). Alternatively, if simple character string tests aren't sufficient, then use a regular expression and use the i option for case-insensitive matching. Example:
if riddle_1_answer =~ /(?:an )?onion/i
Will test for "an onion" or "onion" in upper, lower, and mixed case.
Perhaps more important than these little errors, one should look to avoid repetition. The general pattern appears to be:
Ask a question
Accept an answer
Check the answer
Change the program state based on the answer
Repeat
When you see things like this, you should start thinking of arrays and hashes. Arrays are used when the values can be numerically indexed, and hashes are used when you want to get values associated with varying keys. Then, a simple loop can be used to iterate across the values of the array or hash.
So, seeing the pattern above, it becomes more clear that we will need a method to prompt for the question, get the answer, deal with a possible EOF and empty strings, validate non-empty answers, possibly repeating the question & answer when needed.
So let's define a little method to get an answer
# prompt_and_get_answer PROMPT, ANSWERS_DATA
#
# issue PROMPT, and get an answer, which must be one of the
# values in ANSWERS_DATA array, or one of the keys of the
# ANSWERS_DATA hash.
def prompt_and_get_answer prompt, answers_data
ans = ''
while ans.size < 1
print prompt
ans = $stdin.gets
if ans.nil?
raise "End of input"
end
ans.strip!
if answers_data.class == Hash # hash?
answers = answers_data.keys.sort
else
answers = answers_data.sort
end
matches = answers.grep(/#{ans}/i) # match possible valid answers
case matches.size # how many items found?
when 0
puts "#{ans} is not a valid answer. Use one of:"
puts answers.join(', ')
ans = ''
when 1 # return the match or the value of the matching key
ans = answers_data.class == Hash ? answers_data[matches[0]] : matches[0]
else
puts "#{ans} is ambiguous; be more specific to match one of:"
puts answers.join(', ')
ans = ''
end
end
ans
end
Now, we can use this method with a prompt as an argument, and get an answer, knowing that it's not going to be an empty answer and it's not going to be an EOF.
First, let's name the questions, and let's show the prompts as they would appear on output, without using the explicitly escaped newlines (\n).
$prompt1 = <<EOQ
There are two doors
1. Riddles
2. Puzzles
Which door do you go through?
EOQ
$answer1 = [ '1', '2' ]
$prompt2 = <<EOQ
Here is your riddle:
You use a knife to slice my head and weep beside me when I am dead.
What am I?
EOQ
$answer2 = [ 'onion' ]
ans1 = prompt_and_get_answer $prompt1, $answer1
if ans1 == '1'
do_riddles
elsif ans1 == '2'
do_puzzles
else
raise "Bad answer to prompt1"
end
def do_riddles
while true
ans = prompt_and_get_answer $prompt2, $answer2
if ans == 'onion'
puts "yay! you're right!"
break
else
puts "nope. Try again"
end
end
end
You can see from this that we've used methods (functions) to break the logic into smaller pieces, and to separate the details of getting answers from the details of testing them.
Later, we can see how to use tables and hashes to build up a list of questions.
Good luck.

Break in While Loop when Specific Input is Entered

I am trying to halt input from a user when their input is 42.
The correct answer on the website I'm working on is:
while line = gets
break if (/42/ =~ line)
x << line
end
The code I tried that does not work is:
while line = gets.chomp
break if (line == 42)
x << line
end
Why is this the case? Am I missing some limitations to what I can use in my if statement?
The problem is that 42 is an integer, but line is a string:
1.9.3p392 :001 > "42" == 42
=> false
So it's never the case that your if statement is getting triggered, because it's comparing two different kinds of things. Matching with a Regex fixes it, though it's looking for "42" to appear anywhere in the input (e.g. "3427"). I think what you meant to say was
while line = gets.chomp
break if (line == "42")
x << line 
end
In other words, break when the input is a string with the characters 4 and 2 in it.
I suspect it's because you're comparing a number to a string. The example uses a regular expression it appears. "42" == 42 will give you false in ruby.
<< is a method(Append) on Array or String class objects. But your x not holding any referencing to such objects. Thus you are getting undefined local variable or method 'x' for main:Object (NameError).
Try this instead(by fixing local variable x to hold a practical object and converting line value to Fixnum object:
x = "hi"
while line = gets.chomp
break if (line.to_i == 42)
x << line
end
This program will help you to halt input from a user when their input is 42.
until (line = gets.chomp).to_i == 42
x << line
end
This of course bypasses the if statement you were asking about.
Your limitation for the if is based solely on the fact that you are comparing a string that will always be a string to a number, and this will never be equal. (as others have mentioned)
So we must reconsider the conditional statement. In this case, I considered it "out of place" and moved the comparison to the 'while' loop, and then inverted it to an 'until' statement, to be able to positively express the condition to end the loop on. Whenever I see a 'break' in a loop, I try to get rid of that smell, as the condition to leave a loop should really be expressed in the loop condition if possible.
I hope this helps.

Ruby case statement with multiple variables using an Array

I'd like to compare multiple variables for a case statement, and am currently thinking overriding the case equals operator (===) for Array is the best way to do it. Is this the best way?
Here is an example use case:
def deposit_apr deposit,apr
# deposit: can be nil or 2 length Array of [nil or Float, String]
# apr: can be nil or Float
case [deposit,apr]
when [[Float,String],Float]
puts "#{deposit[0]} #{deposit[1]}, #{apr*100.0}% APR"
when [[nil,String],Float]
puts "#{apr*100.0}% APR on deposits greater than 100 #{deposit[1]}"
when [[Float,String],nil]
puts "#{deposit[0]} #{deposit[1]}"
else
puts 'N/A'
end
end
The only problem is the Array case equals operator doesn't apply the case equal to the elements of the Array.
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
N/A
It will if I override, but am not sure what I'd be breaking if I did:
class Array
def ===(other)
result = true
self.zip(other) {|bp,ap| result &&= bp === ap}
result
end
end
Now, it all works:
ruby-1.9.2-p0 > deposit_apr([656.00,'rupees'],0.065)
656.0 rupees, 6.5% APR
Am I missing something?
I found this question because I was looking to run a case statement on multiple variables, but, going through the following, came to the conclusion that needing to compare multiple variables might suggest that a different approach is needed. (I went back to my own code with this conclusion, and found that even a Hash is helping me write code that is easier to understand.)
Gems today use "no monkey patching" as a selling point. Overriding an operator is probably not the right approach. Monkey patching is great for experimentation, but it's too easy for things to go awry.
Also, there's a lot of type-checking. In a language that is designed for Duck Typing, this clearly indicates the need for a different approach. For example, what happens if I pass in integer values instead of floats? We'd get an 'N/A', even though that's not likely what we're looking for.
You'll notice that the example given in the question is difficult to read. We should be able to find a way to represent this logic more clearly to the reader (and to the writer, when they revisit the code again in a few months and have to puzzle out what's going on).
And finally, since there are multiple numbers with associated logic, it seems like there's at least one value object-type class (Deposit) that wants to be written.
For cleanliness, I'm going to assume that a nil APR can be considered a 0.0% APR.
class Deposit
def initialize(amount, unit='USD', options={})
#amount = amount.to_f # `nil` => 0.0
#unit = unit.to_s # Example assumes unit is always present
#apr = options.fetch(:apr, 0.0).to_f # `apr: nil` => 0.0
end
end
Once we have our Deposit object, we can implement the print logic without needing case statements at all.
class Deposit
# ... lines omitted
def to_s
string = "#{#amount} #{#unit}"
string << ", #{#apr * 100.0}% APR" if #apr > 0.0
string
end
end
d = Deposit.new(656.00, 'rupees', apr: 0.065)
d.to_s
# => "656.0 rupees, 6.5% APR"
e = Deposit.new(100, 'USD', apr: nil)
e.to_s
# => "100.0 USD"
f = Deposit.new(100, 'USD')
f.to_s
# => "100.0 USD"
Conclusion: If you're comparing multiple variables in a case statement, use that as a smell to suggest a deeper design issue. Multiple-variable cases might indicate that there's an object that wants to be created.
If you are worried about breaking something by changing Array behavior, and certainly that's a reasonable worry, then just put your revised operator in a subclass of Array.
it's definitely not the best way. even more - you should not redefine methods of standart classes as core functionality may depend on it - have fun debugging then.
defensive style is nice(with lot of type checks and whatnot) but it usually hurts performance and readability.
if you know that you will not pass anything else than bunch of floats and strings to that method - why do you need all those checks for?
IMO use exception catching and fix the source of problem, don't try to fix the problem somewhere in the middle

Ruby while syntax

Does anybody why I can write this:
ruby-1.8.7-p302 > a = %w( a b c)
=> ["a", "b", "c"]
ruby-1.8.7-p302 > while (i = a.shift) do; puts i ; end
a
b
c
=> nil
Which looks like passing a block to while.
And not:
while(i = a.shift) { puts i; }
Is it because the "do" of the while syntax is just syntaxic sugar and as nothing to do with the "do" of a block?
Is it because the do of the while syntax is just syntaxic sugar and as nothing to do with the do of a block?
More or less, yes. It's not syntactic sugar, it's simply a built-in language construct, like def or class, as #meagar already wrote.
It has nothing to do with the do of a block, except that keywords are expensive and so reusing keywords makes sense. (By "expensive" I mean that they limit the programmer in his expressiveness.)
In a while loop, there are two ways to separate the block from the condition:
the do keyword and
an expression separator.
There are, in turn, two different expression separators in Ruby:
the semicolon ; and
a newline
So, all three of the following are valid:
while i = a.shift do puts i end # do
while i = a.shift; puts i end # semicolon
while i = a.shift
puts i end # newline
[Obviously, that last one wouldn't be written that way, you would put the end on a new line, dedented to match the while. I just wanted to demonstrate what is the minimum needed to separate the parts of the while loop.]
By the way: it is highly un-idiomatic to put the condition in parentheses. There's also a lot of superfluous semicolons in your code. And the variable name i is usually reserved for an index, not an element. (I normally use el for generic elements, but I much prefer more semantic names.)
It is also highly un-idiomatic to iterate a collection manually. Your code would be much better written as
a.each(&method(:puts)).clear
Not only is it much easier to understand what this does (print all elements of the array and delete all items from it), it is also much easier to write (there is no way to get the termination condition wrong, or screw up any assignments). It also happens to be more efficient: your version is Θ(n2), this one is Θ(n).
And actually, that's not really how you would write it, either, because Kernel#puts already implements that behavior, anyway. So, what you would really write is this
puts a
a.clear
or maybe this
a.tap(&method(:puts)).clear
[Note: this very last one is not 100% equivalent. It prints a newline for an empty array, all the other ones print nothing.]
Simple. Clear. Concise. Expressive. Fast.
Compare that to:
while (i = a.shift) do; puts i ; end
I actually had to run that multiple times to be 100% clear what it does.
while doesn't take a block, it's a language construct. The do is optional:
while (i = a.shift)
puts i
end

Resources