What is wrong in this if structures? - ruby

I have the method, which shows me the error :
"syntax error, unexpected end-of-input, expecting keyword_end". Where i made a mistake ?
def clicked(p,u)
if (Like.where(post_id: p.id).empty?)
return false
else
posts=Like.where(post_id: p.id)
if ((posts.length<1) && (posts[0].user_id==u.id) && (posts[0].action!=nil))==true then return true
else
if ((posts.length<1) && (posts[0].user_id!=u.id)) then return false
else posts.each do |i|
if (i.user_id==u.id) then return true
end
end
end
end
end

posts.each should be closed by a end keyword
def clicked(p,u)
if (Like.where(post_id: p.id).empty?)
return false
else
posts=Like.where(post_id: p.id)
if ((posts.length<1) && (posts[0].user_id==u.id) && (posts[0].action!=nil))==true then return true
else
if ((posts.length<1) && (posts[0].user_id!=u.id)) then return false
else
posts.each do |i|
if (i.user_id==u.id) then return true
end
# The missing end
end
end
end
end
end

To make it easier for you and others to spot problems of this sort, avoid using the "if....then" form of Ruby's if statement, especially in conjunction with an "else" clause on a separate line. Also, avoid putting code on the same line as the "else" keyword. Instead of this:
if some_condition then do_something
else
do_something_else
end
or this:
if some_condition
do_something
else do_something_else
end
prefer this:
if some_condition
do_something
else
do_something_else
end
Using these fairly standard formatting practices will make this class of syntax errors much easier to avoid, and easier to debug when they do occur.

Related

Return from a loop from a different method

My understanding is you can exit from a program with a return. How do you return from a loop? When I run return_method as in the following, I want to exit the loop with "RETURNING" returned.
def return_method
return "RETURNING"
end
loop do
puts "Enter:"
answer = gets.chomp
if answer == 'run'
return_method
end
break if answer == 'y'
end
break doesn't work within my method.
A typical way to escape from nested loops of from nested method calls is to use catch ... throw.
RETURNING = "RETURNING"
def return_method
throw RETURNING
end
catch(RETURNING) do
loop do
puts "Enter:"
answer = gets.chomp
if answer == 'run'
return_method
end
break if answer == 'y'
end
end
It's generally not the case that a method call forces the calling method to do something as abrupt as return. That's what exceptions are for, they will bubble up if not caught, but that's seriously heavy-handed for this sort of thing. Instead make your method return a truthy value if you want to break the loop:
def return_method
puts "RETURNING"
true
end
loop do
puts "Enter:"
answer = gets.chomp
case (answer)
when 'run'
break if return_method
when 'y'
break
end
end

Ruby one line if return statement

is there a way to shorten this line on Ruby?
if (res = bla_permission_invalid).is_a? String then return res end
on
def something # many things that like this
if (res = bla_permission_invalid).is_a? String then return res end
# do something else
return true
end
when the content of bla_permission_invalid are something like
def bla_permission_invalid
return invalid_address_report_func if invalid_address?
return permission_error_report_func if #user.not_one_of? [ :group1, :group2 ]
return nil
end
invalid_adress_report_func and permission_error_report_func returns string
If possible values are String and NilClass, then the code can be simplified to this:
def something
res = bla_permission_invalid()
return res if res # strings are truthy, so they'll be returned but nil will proceed
# do something else
true
end
def something
bla_permission_invalid || (
# do something else
true)
end
For fun, one could rewrite your something method like this:
def something
true.tap do
bla_permission_invalid.tap { |res| return res if res.is_a? String }
# do something else (thx Sergio)
end
end
But more importantly, Mark Thomas deserves credit for his observation, that the problem at hand should be solved by using custom exceptions.
Error code approach is good in languages that don't have exceptions. Ruby has them.
Mark Thomas already noted in his comment that it looks like you're trying to handle errors on your own using some kind of string identifier. You could use exceptions instead:
class AddressError < StandardError; end
class PermissionError < StandardError; end
def something
bla_permission_invalid
# do something
true
end
def bla_permission_invalid
raise AddressError if invalid_address?
raise PermissionError if #user.not_one_of? [ :group1, :group2 ]
end
In the above code something calls bla_permission_invalid, performs its work and returns true. If an exception is raised in bla_permission_invalid, it automatically propagates up the call stack, you don't have to explicitly return it from something.
To handle the exception:
begin
something
rescue AddressError
# handle address error
rescue PermissionError
# handle permission error
end

Ruby case statement on a hash?

This is going to sound weird, but I would love to do something like this:
case cool_hash
when cool_hash[:target] == "bullseye" then do_something_awesome
when cool_hash[:target] == "2 pointer" then do_something_less_awesome
when cool_hash[:crazy_option] == true then unleash_the_crazy_stuff
else raise "Hell"
end
Ideally, I wouldn't even need to reference the has again since it's what the case statement is about. If I only wanted to use one option then I would "case cool_hash[:that_option]", but I'd like to use any number of options. Also, I know case statements in Ruby only evaluate the first true conditional block, is there a way to override this to evaluate every block that's true unless there is a break?
You could also use a lambda:
case cool_hash
when -> (h) { h[:key] == 'something' }
puts 'something'
else
puts 'something else'
end
Your code is very close to being valid ruby code. Just remove the variable name on the first line, changing it to be:
case
However, there is no way to override the case statement to evaluate multiple blocks. I think what you want is to use if statements. Instead of a break, you use return to jump out of the method.
def do_stuff(cool_hash)
did_stuff = false
if cool_hash[:target] == "bullseye"
do_something_awesome
did_stuff = true
end
if cool_hash[:target] == "2 pointer"
do_something_less_awesome
return # for example
end
if cool_hash[:crazy_option] == true
unleash_the_crazy_stuff
did_stuff = true
end
raise "hell" unless did_stuff
end
I think, following is the better way to do the stuff you want.
def do_awesome_stuff(cool_hash)
case cool_hash[:target]
when "bullseye"
do_something_awesome
when "2 pointer"
do_something_less_awesome
else
if cool_hash[:crazy_option]
unleash_the_crazy_stuff
else
raise "Hell"
end
end
end
Even in case's else part you can use 'case cool_hash[:crazy_option]' instead of 'if' if there are more conditions. I prefer you to use 'if' in this case because there is only one condition.
in ruby 3.0 you can do the following with pattern matching
# assuming you have these methods, ruby 3 syntax
def do_something_awesome = "something awesome 😎"
def do_something_less_awesome = "something LESS awesome"
def unleash_the_crazy_stuff = "UNLEASH the crazy stuff 🤪"
you can do
def do_the_thing(cool_hash)
case cool_hash
in target: "bullseye" then do_something_awesome
in target: "2 pointer" then do_something_less_awesome
in crazy_option: true then unleash_the_crazy_stuff
else raise "Hell"
end
end
will return
do_the_thing(target: "bullseye")
=> "something awesome 😎"
do_the_thing(target: "2 pointer")
=> "something LESS awesome"
do_the_thing(crazy_option: true)
=> "UNLEASH the crazy stuff 🤪"
in ruby 2.7 it still works
# need to define the methods differently
def do_something_awesome; "something awesome 😎"; end
def do_something_less_awesome; "something LESS awesome"; end
def unleash_the_crazy_stuff; "UNLEASH the crazy stuff 🤪"; end
# and when calling the code above to do the switch statement
# you will get the following warning
warning: Pattern matching is experimental, and the behavior may change
in future versions of Ruby!

Ruby StringScanner used for lexing : how to get the line number?

I am using StringScanner for lexical analysis like this :
def next
#scanner.skip(/\s+/)
value,kind=nil,nil
TOKEN_DEF.each{|tok,regex| (kind=tok;break) if #scanner.scan(regex)}
return Token.new(kind,value,#line,#scanner.pos)
end
At first approximation, this works well, except that I can't figure out how to now get the #line number.
I have read the doc, where begin_of_line? method seems appropriate, but I cannot figure how to use it.
Keep the text that you are scanning in a variable and use 'count'
I use the following in my code:
def current_line_number; #text[0..#scanner.pos].count("\n") + 1; end
This code doesn't seem ready to go and for sure somewhere else more elegant solution, it just should give you something to think about.
class Retry < StandardError
end
class TextScanner
def initialize(filename)
#lines = IO.readlines(filename)
#fiber = Fiber.new do
#lines.each_with_index do |line, index|
#scanner = StringScanner.new(line)
#scanner.skip(/\s+/)
value, kind = nil, nil
begin
got_token = false
TOKEN_DEF.each do |tok, regex|
if #scanner.scan(regex)
Fiber.yield Token.new(tok, value, index, #scanner.pos)
got_token = true
end
end
raise Retry if got_token
rescue Retry
retry
end
end
"fiber is finished"
end
end
def next
#fiber.resume
end
end
text_scanner = TextScanner('sometextfile')
puts text_scanner.next #=> first token
puts text_scanner.next #=> second token
puts text_scanner.next #=> third token
...
puts text_scanner.next #=> "fiber is finished"
I think I have a simple solution. Here it is :
def next
#line+=1 while #scanner.skip(/\n/)
#line+=1 if #scanner.bol?
#scanner.skip(/\s+/)
#line+=1 if #scanner.bol?
#scanner.skip(/\s+/)
return :eof if #scanner.eos?
TOKEN_DEF.each { |tok,syntax| (kind=tok;break) if #scanner.scan(syntax)}
return Token.new(kind,nil,#line,#scanner.pos)
end

problem with if and else code... in ruby

Do not treat to the variables and conditions ...
def index
end
def search
count = 1
while count < 3
if count == 1
#movie = "not found" if #code1 == nil || #code1 == ""
if #movie == ""
end
end
if count == 2
#movie = "not found" if #code1 == nil || #code1 == ""
if #movie == ""
if #code1.include? "movshare"
end
if #code1.include? "novamove"
end
end
end
count++
end
end
end
what is the problem in this code? i get an error:
syntax error, unexpected keyword_end
you have one more unnecessary 'end'. There are 9 opening clauses including def, while and if and 10 closing end
You are confusing the interpreter with your count++. ++ does not exist in Ruby. You need to use count += 1. The interpreter is probably assuming that is an expression involving addition, and expecting another operand but instead finding end

Resources