I've read my code up and down for about 30 mins now. I can't for the life of me see where user_response is undefined. I'm very new to coding so I don't know how much of the code would be appropriate to paste in here. I figure that launch and get_action are essential but the rest couldn't hurt?
error => rb:32:in `launch!': undefined local variable or method `user_response' for
<Guide:0x007fb019984718> (NameError)
class Guide
class Config
##actions = ['list', 'find', 'add', 'quit']
def self.actions
##actions
end
end
def initialize(path=nil)
# locate the restaurant text file at path
Restaurant.filepath = path
if Restaurant.file_usable?
puts "Found restaurant file."
# or IF a new text file can't be created, create a new file
elsif Restaurant.create_file
puts "Created restaurant file."
# exit if create fails
else
puts "Exiting"
exit!
end
end
def launch! #! means that it is a strong powerful method!
introduction
# action loop
result = nil
until result == :quit
action = get_action
result = do_action(user_response)
end
conclusion
end
def get_action
action = nil
# Keep asking for user input until we get a valid action
until Guide::Config.actions.include?(action)
puts "Actions: " + Guide::Config.actions.join(", ") if action
print "> "
user_response = gets.chomp
action = user_response.downcase.strip
end
return action
end
def do_action(action)
case action
when 'list'
puts "Listing..."
when 'find'
puts "Finding..."
when 'add'
puts "Adding..."
when 'quit'
return :quit
else puts " I don't understand that command."
end
end
def introduction
puts "<<< Welcome to the Food Finder >>>"
puts "This is an interactive guide to help you find the food you crave."
end
def conclusion
puts "<<< Goodbye and Bon Appetit! >>>>"
end
end
I think you want to do this :
def launch! #! means that it is a strong powerful method!
introduction
# action loop
result = nil
until result == :quit
result = do_action(get_action)
end
conclusion
end
The only time you define a variable called user_response is in your get_action method.
The way you define it there makes it a local variable and it will not be accessible from anywhere but inside the get_action method.
Related
Undefined method for nil:Nilclass
In a class, a method counts the number of words in a paragraph.An error occurs when a method is called(1). I can’t understand how to pass the argument methods using send.
If I remove the class and put the def calc_1(paragraph) method into the loop, then everything works, I start calling the select method. It turns out he does not see my books variable with text, when there is a class.
#books = "You can use this knowledge to create small tools that might help."
class Filecalculation
def select
loop do
puts "# Will we search : сounting words in text File(1)".cyan
print "\n>>>>>> "
input = gets.chomp
search_method = "calc_#{input}".to_sym
if (respond_to?(search_method))
contents = send(search_method, #books)
end
end
end
def calc_1 paragraph
word_count = paragraph.split.length
puts "#{word_count} words"
end
end
Filecalculation.new.select
If replaced by search_method = "calc_#{input}".to_sym also works.
Helped add def initialize #books end.
Instead of contents = send (search_method, #books) you can use send (search_method, #books).
require "colorize"
class Filecalculation
def initialize
#books = "You can use this knowledge to create small tools that might help you."
end
def calc_1 paragraph
word_count = paragraph.strip.squeeze(' ').count(' ') + 1
puts "#{word_count} words"
end
def select
loop do
puts "# Will we search : Calculation_lines paragraph(1)".cyan
print "\n>>>>>> ".yellow
input = gets.chomp
search_method = "calc_#{input}" #.to_sym
if (respond_to?(search_method))
contents = send(search_method, #books)
else
puts "exit "
exit
end
end
end
end
Filecalculation.new.select
I'm starting to learn metaprogramming in Ruby and (I think) I'm understanding how to add instance methods and variables, but only if passed in one at a time. For example, test.new_value = true. I'm wondering how to add an extra depth to my command with two dots test.like.this. For example:
class StackOverflow
def initialize(name)
#name = name
end
def method_missing(argument, *args)
argument = argument.to_s
if argument =~ /failing/
puts 'FOUND FAILING'
puts argument
puts args[0]
return
end
if argument =~ /this_test_is/
puts 'FOUND THIS TEST IS'
puts argument
puts args[0]
return
end
if argument =~ /this_works=/
instance_variable_set("##{argument.to_s.chop}", args[0])
return
else
return instance_variable_get("##{argument}").to_s
end
end
end
test = StackOverflow.new("post")
test.this_works = true
puts "IT WORKED: " + test.this_works
test.this_test_is.failing
gives me the following output:
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
IT WORKED: true
FOUND THIS TEST IS
this_test_is
undefined method `failing' for nil:NilClass
(repl):44:in `<main>'
What I'm looking to do is treat this as a variable and value pair. I'd like to know how to do both of these scenarios:
A: Recognise this_test_is and treat it as a variable to have it store the string (or symbol is fine) failing.
B: Recognise failing as the variable and if I see this_test_is then set failing to true, as opposed to false if I find this_test_is_not.
Thank you in advance!
You need to add some kind of recursion :
class StackOverflow
def initialize(name)
#name = name
end
def method_missing(argument, *args)
argument = argument.to_s
if argument =~ /failing/
puts 'FOUND FAILING'
puts argument
puts args[0]
return
end
if argument =~ /this_test_is/
puts 'FOUND THIS TEST IS'
puts argument
puts args[0]
return StackOverflow.new("this_test_is")
end
if argument =~ /this_works=/
instance_variable_set("##{argument.to_s.chop}", args[0])
return
else
return instance_variable_get("##{argument}").to_s
end
end
end
test = StackOverflow.new("post")
test.this_works = true
puts "IT WORKED: " + test.this_works
test.this_test_is.failing
prints this :
IT WORKED: true
FOUND THIS TEST IS
this_test_is
FOUND FAILING
failing
I am attempting to use the String from a variable to call a Method within the program.
How can one use the String from the Variable to call said Method without having to nest or make multiple checks?
module Player
##location = "location"
def Player.input(input)
if input == "look"
"Call Method from ##location"
end
end
def Player.set_location(input)
##location = input
end
end
def input
print "> "
Player.input(#stdin.gets.chomp)
end
def "name of Method can be same as ##location"
...
end
def "another name of Method can be same as ##location"
...
end
def "another name, etc"
...
end
What do you mean by without having to nest ? I wonder if your code is a contrived example. Defining methods in the special object main is OK for a quick test, if they are also called from main, otherwise they must be put in a class.
So the answer could be as simple as that :
module Player
#location = 'location'
def Player.input(input)
puts "in Player.input(#{input})"
if input == 'look'
puts "Calling method <#{#location}>"
Switch.send(#location)
else
puts 'wrong input, must be "look"'
end
end
def Player.set_location(input)
#location = input
end
end
def input
print "> "
Player.input(gets.chomp)
end
class Switch
def self.abc
puts 'in abc'
end
def self.def
puts 'in def'
end
def self.location
puts 'in location'
end
def self.method_missing(name, *args, &block)
puts "There is no method #{name} in #{self.name}"
end
end
input
input
Player.set_location('abc')
input
Player.set_location('xyz')
input
Execution :
$ ruby -w t.rb
> looc
in Player.input(looc)
wrong input, must be "look"
> look
in Player.input(look)
Calling method <location>
in location
> look
in Player.input(look)
Calling method <abc>
in abc
> look
in Player.input(look)
Calling method <xyz>
There is no method xyz in Switch
I'm having some trouble understanding scope in ruby.
Here is a link to a repo if you'd like to download/run what I'm talking about to see for yourself:
https://github.com/minervadreaming/killshit
I have several .rb files present - specifically, I'm having an issue calling a class method from an instance. Seems like I'm not properly following scope, but I'm not sure how or why.
Here is a snippet from room.rb:
module KillShit
class Room
attr_reader :player, :monster, :game
def initialize(player, monster, game)
#player = player
#monster = monster
#game = game
end
def action
outline
Player.describe(player)
vs
Monster.describe(monster)
outline
#rolling a d20 to see who takes a turn
turn = rand(1..100)
if turn <= 20
monster_attack
else
puts "What would you like to do?"
puts "1. Attack!"
puts "2. Defend!"
puts "3. Run away!"
#Give the player magic if they're at least level 2
if player.maglevel >= 1
puts "4. Cast spell"
else
end
prompt; action = gets.chomp
if action == "1"
attack(player, monster)
elsif action == "2"
defend
elsif action == "3"
flee
elsif action == "4" && player.maglevel >= 1
magic
else
action
end
end
end
def magic
puts "What magic would you like to cast?"
if player.maglevel == 1
puts "1. Heal"
puts "2. Fireball"
puts "3. Tremor"
prompt; magic = gets.chomp
if magic == "1"
Spells.heal(player)
elsif magic == "2"
Spells.fireball(player, monster)
elsif magic == "3"
Spells.tremor(player, monster)
else
magic
end
elsif player.maglevel == 2
puts "1. Greater Heal"
puts "2. Firestorm"
puts "3. Earthquake"
prompt; magic = gets.chomp
if magic == "1"
Spells.greaterheal(player)
elsif magic == "2"
Spells.firestorm(player, monster)
elsif magic == "3"
Spells.earthquake(player, monster)
else
magic
end
else
end
end
end
end
As you can see, when the player chooses to cast a spell it calls out to a Spells class, which I have in spells.rb. Here is a snippet from that code:
require_relative 'room'
require_relative 'player'
require_relative 'monster'
module KillShit
class Spells
attr_accessor :player, :monster
#Checking if user has enough MP to cast spell
def self.mp_check(req, player)
req_mp = req
if player.mp < req_mp
puts "#{player.name} doesn't have enough MP!"
action
else
end
end
def self.heal(player)
req_mp = 3
Spells.mp_check(req_mp, player)
player.mp -= req_mp
amt = rand(3..10)
player.hp += amt
puts "#{player.name} has been healed by #{amt} HP!"
Room.action
end
end
end
The problem is in the "Room.action" call at the end of the self.heal method (or just "action" as was my first attempt, as can be seen in the self.mp_check method). When it is hit, the following error is received:
spells.rb:27:in `heal': undefined method `action' for KillShit::Room:Class (NoMethodError)
I thought that it might've been because I needed to define "action" as a class method with "self." but that isn't doing it. I also thought that it's because I'm trying to call "action" from the Class itself as opposed to the instance of the class with which I am working, and so I should call .action but I can't figure out how to do so.
Any ideas? What should I be reading up on to better understand the concept behind what's happening here?
Thanks!
You Should call the method Room#action like:
KillShit::Room.action
Just after a brief look - you did not defined class but instance method action.
Class methods need to by referenced with keyword self or class name directly, so you would need fix the definition this way:
module KillShit
class Room
…
def self.action
…
end
…
end
end
Updated answer:
module KillShit
class Room
def self.action
puts 'action in Room !'
end
end
class Spells
def self.heal(player)
Room.action
end
end
end
KillShit::Spells.heal('Wallace')
# action in Room !
class DobbsyKretts
def initialize
#Receive idea
puts "Enter an idea, a secret or anything else you want to secretize; hit enter to stop typing and save the file"
(#idea = gets).reverse.upcase
#Filename and saving - to encrypt the file
puts "Enter the file name you'd like to have this saved as; Type PLAN at the beginning for plans and REM for reminders"
(#file_name = gets.chomp.upcase)
File::open("DobbsyKrett-"+ #file_name + ".txt", "w") do |f|
f << #idea
end
end
def unzip
puts "Do you want to withdraw PLAN or REM"
response = gets.chomp.upcase!
puts "Invalid" if !["PLAN","REM"].include?(response)
file_contents = nil
Dir['DobbsyKrett-'+response+"*.txt"].each do |file_nom|
file_contents = File.read(file_nom)
end
puts file_contents
end
end
somethingsomething1 = DobbsyKretts.new
somethingsomething1.unzip
def unzip
puts "Do you want to withdraw PLAN or REM"
#response = gets.strip
if #response.downcase != "plan" and #response.downcase != "rem"
puts "Invalid" end
Dir["DobbsyKrett-"+#response+".txt"].each do |file_nom|
#value = file.read(file_nom)
end
puts #value
end
end
The function gets will return a string with the line-ending character at the end which is not what you expected. To remove it, use the chomp function:
#response = gets.chomp
It is okay for a method (e.g. unzip) to create new instance variables (e.g. #valueholder). In general it's always better for your variables to have the smallest possible scope, so unless you need to read valueholder later, you should just use a local variable (remove the # from the name):
Dir["DobbsyKrett-"+#response+".txt"].each do |file_nom|
valueholder = File.read(file_nom)
end
puts valueholder
Also, valueholder is a terrible name for a variable but if you made it a local variable that could be excused.
Also, your block startings/endings are mismatched. Here's a fixed version of your function that shouldn't result in syntax errors:
def unzip
puts "Do you want to withdraw PLAN or REM"
response = gets.chomp.downcase
if !["plan","rem"].include? response
puts "Invalid"
else
Dir["DobbsyKrett-#{response}.txt"].each do |file_nom|
valueholder = file.read(file_nom)
end
puts valueholder
end
end
Edit: You should capitalize File to correctly call File.read.