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
Related
I am learning ruby and getting this error
My code:
class New_class
hash{}
File.readlines('file.txt').each do |line|
if (line =~ /^(\w+)=>(.*)/)
hash[$1] =$2
end
end
def check
a='2345'
value = hash.fetch{a,''}
if (value == '')
puts 'Error no value found'
else
puts value
end
end
end
var=New_class.new
var.check
Error :undefined method 'fetch'
Here I want hash to run one time and store all the key/value so that I can use the hash in multiple methods and check for values. Anyone know how to fix this error or any better way to do?
The hash variable is out of scope. You can make it global by changing it to $hash.
Also fetch uses round brackets not curly brackets.
class New_class
$hash = {}
File.readlines('file.txt').each do |line|
if (line =~ /^(\w+)=>(.*)/)
$hash[$1] =$2
end
end
def check
a='2345'
value = $hash.fetch(a,'')
if (value == '')
puts 'Error no value found'
else
puts value
end
end
end
I can't figure out how to exit the game loop. I'm having a hard tried making a lose? function, I tried doing like lose?(x) that would return true when x==1 but that didn't get it to exit the run method. Here's my code for the Game class.
class Minesweeper
attr_accessor :board
def initialize
#board = Board.new
end
def run
puts "Welcome to minesweeper!"
x = nil
play_turn until win? || lose?(x)
end
def play_turn
board.render
pos, command = get_input
debugger
if !explode?(pos, command)
board.set_input(pos,command)
else
puts "You lose!"
lose?(1)
end
end
def explode?(pos, cmd)
board.grid[pos[0]][pos[1]].bomb && cmd == "reveal"
end
def get_input
pos = nil
command = nil
until pos && check_pos(pos)
puts "What position?"
pos = parse_pos(gets.chomp)
end
until command && check_command(command)
puts
puts "What would you like to do (e.g. reveal, flag... ~ else?)"
command = gets.chomp
puts
end
[pos, command]
end
#Some code here (check_pos, etc)
def lose?(x)
return true if x == 1
false
end
def win?
# board.all? {}
end
end
I had explode? in the Board class before, but for the sake of being able to end the game, moved it here. Any help is greatly appreciated!
If we expand your run method, a little, it becomes more obvious what the issue is:
def run
puts "Welcome to minesweeper!"
x = nil
until(win? || lose(x)) do
play_turn
end
end
In this method, you're creating a new variable x which is scoped to this method and has a value of nil. This variable is never set in the scope of that method, so is always nil, meaning that the check win? || lose(x) could also be written win? || lose(nil), and lose will never return true.
If you return a value from your play_turn method, you can then use that. Note that the result of the last thing executed in the method is what is returned:
def play_turn
board.render
pos, command = get_input
debugger
if !explode?(pos, command)
board.set_input(pos,command)
true
else
puts "You lose!"
false
end
end
which means your run method can then check the result:
def run
puts "Welcome to minesweeper!"
# we now know that play_turn returns true if the turn was not a loser
# (i.e. it should continue to the next loop) and false if the player
# lost, so we can use that returned value in our loop.
while(play_turn) do
if win?
puts "looks like you won!"
# if the player wins, we want to exit the loop as well. This is done
# using break
break
end
end
end
Note that this also means that you don't need a separate lose method
A course on ruby asks to write a Title class, which is initialized with a string. It has one method fix, which should return a title-cased version of the string. Here is what I have for my code.
class Title
attr_reader :string
def initialize(string)
#string = string
end
def fix
not_capitalized = %w{a and the of}
word_array = string.downcase.split(" ")
title_array = []
word_array.each_with_index do |word, index|
if index == 0 || !not_captialized.include?(word)
title_array << word.capitalize
else
title_array << word
end
end
title_array.join(" ")
end
end
I keep getting a NameError undefined local variable or method `not_captialized'. What am I doing wrong?
You have a typo in your variables.
The first one is not_capitalized
The second one is not_captialized
Just rename the second one and it should work
i.e. if index == 0 || !not_capitalized.include?(word)
Just as the error message says. You never defined a local variable or method not_captialized, but are trying to use it.
I want to check the type of strings that I encounter:
class String
def is_i?
/\A[-+]?\d+\z/ === self
end
def is_path?
pn = Pathname.new(self)
pn.directory?
end
end
def check(key)
puts case key
when is_i?
puts "Could be a number"
when is_path?
puts "This is a path"
else
puts "Ok"
end
end
When I run check("1345425") I get the following error:
undefined method `is_i?' for main:Object (NoMethodError)
What should I do to correct it?
You have defined functions on String instance, hence:
def check(key)
puts case
when key.is_i?
"Could be a number"
when key.is_path?
"This is a path"
else
"Ok"
end
end
or
def check(key)
puts case key
when ->(s) { s.is_i? }
"Could be a number"
when ->(s) { s.is_path? }
"This is a path"
else
"Ok"
end
end
UPD Please also note that I removed superfluous subsequent calls to puts.
Your check method isn't defined on the String class, so when you write when is_i? and when is_path? it is trying to call those methods on your main, hence the error.
You should call these methods on your String object (key) like this:
...
when key.is_i?
...
when key.is_path?
...
You may change it like this
class String
def is_i?
/\A[-+]?\d+\z/ === self
end
def is_path?
pn = Pathname.new(self)
pn.directory?
end
end
def check(hash_key)
puts case hash_key
when Proc.new { hash_key.is_i? }
return "Could be a number"
when Proc.new { hash_key.is_path? }
return "This is a path"
else
return "Ok"
end
end
Running check('1312312') in irb returns result as "Could be a number"
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.