Rubywarrior Level 4 (cleaning up my code help) - ruby

I'm learning programming through Ruby and I found the awesome Rubywarrior by Ryan Bates from Railscasts. Unfortunately I'm stuck in that my code is throwing up syntax error messages (unexpected $end).
I'm not asking for the answer, I would like to sort that out myself, but if someone could point out where my code is getting the error from that would be super. Thanks!
class Player
def initialize
#maxhealth = 20
#dying = 7
#previoushealth = #maxhealth
#health = warrior.health
#warrior = warrior
end
def play_turn(warrior)
# If there are no enemies, rest until health is 100%
turn_start_check(warrior)
actions(warrior)
turn_end_check(warrior)
end
def actions(warrior)
if #damaged_since_last_turn
warrior.shoot!
elsif
#health < #maxhealth
warrior.rest!
else
warrior.walk!
end
end
def hurt?(warrior)
warrior.health < 20
end
def healthy?(warrior)
warrior.health = 20
end
def alone?(warrior)
warrior.feel.empty?
end
def should_i_move?(warrior)
if healthy? and alone?
warrior.rest!
else
warrior.walk!
end
# Put code here for if health from previous turn is less than last term
# If true don't rest and keep moving forward
def turn_start_check(warrior)
#damaged_since_last_turn = #previoushealth > warrior.health
end
def turn_end_check(warrior)
#previoushealth = warrior.health
end
end

My guess:
def should_i_move?(warrior)
if healthy? and alone?
warrior.rest!
else
warrior.walk!
end # <<MISSING THIS ONE
end

That error message means that you are missing an end keyword somewhere. Check your code to see if all your statements are properly written.

With Ruby 1.9.3, if you turn on warnings, you get the following warnings:
-:46: warning: mismatched indentations at 'end' with 'if' at 42
-:57: warning: mismatched indentations at 'end' with 'def' at 41
and then the error
-:57: syntax error, unexpected $end, expecting keyword_end
line 46 corresponds to the first end after def should_i_move?(warrior)
This should work with earlier versions of Ruby 1.9 as well.

Related

Ruby Minitest simple test raise error - wrong number of arguments error

I'm trying to learn the TDD approach, to do so I'm using pure Ruby app which is responsible for formatting UK phone number. In my class I want to test if the whitespaces and are removed from given phone number. Basically I'm trying to test the Ruby .delete(' ') method.
Tested module
# lib/formatter/phone_number/uk.rb
module Formatter
module PhoneNumber
module Uk
(...)
def self.format(number)
number.delete(' ')
end
end
end
end
The test
# test/lib/formater/phone_number/uk_test.rb
require 'minitest/autorun'
class UkTest < Minitest::Test
test 'remove whitespaces' do
invalid_number = '+44 12 12 12 12 '
Formatter::PhoneNumber::Uk.format(invalid_number)
assert_equal '+4412121212'
end
end
Which give me an error:
test/lib/formater/phone_number/uk_test.rb:6:in `test': wrong number of arguments (given 1, expected 2) (ArgumentError)
Aside from the fact that testing a method built in Ruby is not a good idea, what am I doing wrong?
You must define a test-method (the method name must start with test_).
Inside the test method you define your assertions. In your case, you compare the expected value with the result of your method.
class UkTest < Minitest::Test
def test_remove_whitespaces
invalid_number = '+44 12 12 12 12 '
assert_equal '+4412121212', Formatter::PhoneNumber::Uk.format(invalid_number)
end
end
Full test in one file:
module Formatter
module PhoneNumber
module Uk
def self.format(number)
number.delete(' ')
end
end
end
end
require 'minitest/autorun'
class UkTest < Minitest::Test
def test_remove_whitespaces
invalid_number = '+44 12 12 12 12 '
assert_equal '+4412121212', Formatter::PhoneNumber::Uk.format(invalid_number)
end
end
Same test with minitest/spec
require 'minitest/spec'
describe Formatter::PhoneNumber::Uk do
it 'removes spaces from numbers' do
invalid_number = '+44 12 12 12 12 '
_(Formatter::PhoneNumber::Uk.format(invalid_number)).must_equal('+4412121212')
end
end

NilCheck fix on safe navigation operator (&.)

This simple method on a class just run the status method using the safe navigation operator.
def current_status
account&.status
end
But reek report this warning:
MyClass#current_status performs a nil-check [https://github.com/troessner/reek/blob/master/docs/Nil-Check.md]
How can I properly write methods like this to avoid Nil Check?
I've also verified this post from thoughtbot but it seem like "too much" for just a safe navigation operator.
Ruby 2.3.1
The advice from "Example 4" in the linked post is verbose but pretty good :
class MyClass
def initialize(with_account = nil)
#account = Account.new if with_account
end
def current_status
account.status
end
def account
#account || NilAccount.new
end
end
class Account
def status
"Up!"
end
end
class NilAccount
def status
"Down!"
end
end
puts MyClass.new(:with_account).current_status
#=> "Up!"
puts MyClass.new.current_status
#=> "Down!"
If it's "too much" for you, account&.status might be just fine.
Whatever you do : you'll need to test your code as much as possible!
well, tell-dont-ask looks pretty good, but Example 4 looks like an overkill to resolve this specific case.
#andredurao I think, we can use this workaround to pass checks, for some reason reek is fine with it:
def current_status
return unless account
account.status
end

I am unable to execute this program

error:C:\Users\RR\Desktop\ruby_sandbox>ruby classes.rb classes.rb:44:
syntax error, unexpected end-of-input, expecting keyword_end
my code is:
class Animal
attr_accessor :name,:age,:sex,:location
def initialize(age=18,sex="not available",location="not specified")
puts "details of animal"
#age=age
#sex=sex
#location=location
end
def condition(age,name)
if animal.age>animal1.age
puts "#{animal.name } is older than #{animal1.name}"
else
puts "animals age are in increasing order"
end
end
Please take care about your indentation while writing ruby you will see where you missed the end keyword.
But in your case the problem is not only about the indentation before start to fix it. You can check some documentation about Class and Instance Methods in Ruby or this tutorial can help you in your case.
Good luck.

Building proper class and passing a class method

class Health
def initialize()
#hydration=hydration
end
def hydration
puts"Amount of Water drunk(in oz):"
x=gets.chomp
if #{x}>=20
puts"Good job! keep on it"
elsif #{x} >=(10...20)
puts"Could do better"
else
puts"Not healthy. Go get hydration"
end
end
drinks=Health.new()
puts drinks.hydration
end
I am new to ruby and what i'm trying to achieve is to be prompted. i'm getting the "syntax error, unexpected end-of-input, expecting keyword_end" on the puts drinks.hydration
I dont think you should get any syntax error with your current code. In order to make it working you need to remove the comments in if statements. Also the initialize statement is redundant as you are making explicit call to the method hydration in puts drinks.hydration.
class Health
def hydration
puts "Amount of Water drunk(in oz):"
x=gets.chomp.to_i
if x>=20
puts "Good job! keep on it"
elsif (10...20).include? x
puts "Could do better"
else
puts "Not healthy. Go get hydration"
end
end
end
drinks=Health.new()
puts drinks.hydration

DRY way of re-raising same set of exceptions in multiple places

short:
Is there a way in Ruby to DRY-ify this:
def entry_point_one
begin
do_something
rescue MySyntaxErrorOne, MySyntaxErrorTwo, MySyntaxErrorEtc => syn_err
raise syn_err.exception(syn_err.message)
end
end
def entry_point_two
begin
do_something_else
rescue MySyntaxErrorOne, MySyntaxErrorTwo, MySyntaxErrorEtc => syn_err
raise syn_err.exception(syn_err.message)
end
end
longer:
I'm building an interpreter. This interpreter can be called using different entry points. If I feed this interpreter a 'dirty' string, I expect it to raise an error. However, it would be nice if I don't get spammed by the by the entire back trace of every method called directly or indirectly by do_something, especially since the interpreter makes use of recursion.
As you can see in the above snippet, I already know a way to re raise an error and thereby removing the back trace. What I would like do is remove the duplication in the above example. The closest I have come thus far is this:
def entry_point_one
re_raise_known_exceptions {do_something}
end
def entry_point_two
re_raise_known_exceptions {do_something_else}
end
def re_raise_known_exceptions
yield
rescue MySyntaxErrorOne, MySyntaxErrorTwo, MySyntaxErrorEtc => syn_err
raise syn_err.exception(syn_err.message)
end
But that makes the method re-raise-known-exceptions show up in the back trace.
edit: I guess what I want would be something like a C pre-processing macro
You can just use the splat on an array.
Straight from IRB:
COMMON_ERRORS = [ArgumentError, RuntimeError] # add your own
def f
yield
rescue *COMMON_ERRORS => err
puts "Got an error of type #{err.class}"
end
f{ raise ArgumentError.new }
Got an error of type ArgumentError
f{ raise 'abc' }
Got an error of type RuntimeError
while thinking about it a bit more, I came up with this:
interpreter_block {do_something}
def interpreter_block
yield
rescue ExceptionOne, ExceptionTwo, ExceptionEtc => exc
raise exc.exception(exc.message)
end
Although it's still not quiet what I would like to have, at least now the extra entry in the back trace has become somewhat better looking.
It might be slightly evil, but I think you can simply remove the line from the backtrace ;-)
COMMON_ERRORS = [ArgumentError, RuntimeError]
def interpreter_block
yield
rescue *COMMON_ERRORS => err
err.backtrace.delete_if{ |line| line=~/interpreter_block/ }
raise err
end
I'm not sure it's such a good idea though. You'll have a hell of a lot of fun debugging your interpreter afterward ;-)
Side note: Treetop may be of interest to you.
This is a touch hackish, but as far as cleaning up the backtrace goes, something like this works nicely:
class Interpreter
def method1
error_catcher{ puts 1 / 0 }
end
def error_catcher
yield
rescue => err
err.set_backtrace(err.backtrace - err.backtrace[1..2])
raise err
end
end
The main trick is this line err.set_backtrace(err.backtrace - err.backtrace[1..2]). Without it, we get the following (from IRB):
ZeroDivisionError: divided by 0
from (irb):43:in `/'
from (irb):43:in `block in method1'
from (irb):47:in `error_catcher'
from (irb):43:in `method1'
from (irb):54
from /Users/peterwagenet/.ruby_versions/ruby-1.9.1-p129/bin/irb:12:in `<main>'
What we don't want in there are the second and third lines. So we remove them, ending up with:
ZeroDivisionError: divided by 0
from (irb):73:in `/'
from (irb):73:in `method1'
from (irb):84
from /Users/peterwagenet/.ruby_versions/ruby-1.9.1-p129/bin/irb:12:in `<main>'
If you have all of the information you need in the exceptions, and you do not need the backtrace at all, you can just define your own error and raise that, instead of reraising the existing exception. This will give it a fresh backtrace. (Of course, presumably your sample code is incomplete and there is other processing happening in the rescue block -- otherwise your best bet is to just let the error bubble up naturally.)
class MyError < StandardError; end
def interpreter_block
yield
rescue ExceptionOne, ExceptionTwo, ExceptionEtc => exc
raise MyError
end

Resources