How to create new functions for String class in Ruby? - ruby

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"

Related

what is the difference between passing method name directly or by user input to respond_to method in ruby?

I wrote the following code:
obj = Object.new
def obj.talk
puts "Talk called"
end
if obj.respond_to?(talk)
obj.send(talk)
else
puts "No talk"
end
which outputs following:
undefined local variable or method `talk' for main:Object (NameError)
But when I pass it through user input it works as:
obj = Object.new
def obj.talk
puts "Talk called"
end
request = gets.chomp
if obj.respond_to?(request)
obj.send(request)
else
puts "No talk"
end
which outputs Talk called when entering the string talk from terminal.
Thanks #Amadan, it works by using the symbol name of the method. Following works.
obj = Object.new;
def obj.talk
puts "Talk called"
end
if obj.respond_to?(:talk)
obj.send(:talk)
else
puts "No talk"
end
Talk called

Ruby Metaprogramming "Depths"

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

Calling a Method using the Input of the User (using their String)

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

Why doesn't my hash work in Ruby?

I have this phonebook program that looks up all the contacts, deletes a contact, and adds a contact. I created a hash global variable called contactList. However, the program fails to recognize this. What did I do wrong?
class PhoneBook
contactList = hash.new
def Add(newContact = {})
flag = false
if newContact.length < 1
return flag
else
flag = true
newContact.collect do |name, number|
contactList[name] = number
end
return flag
end
end
def delete (targetName)
if !contactList.has_key?(targetName)
return false
else
contactList.delete(targetName)
return true
end
end
def displayContact (targetName)
number = -1
if contactList.has_key?(targetName)
number = contactList(targetName)
puts "Contact name : #{targetName}, Contact Number, #{number}"
else
puts "#{targetName} doesn't exist in the phonebook"
end
end
def displayAllContacts
if !contactList.empty?
contactList.each {|name, number| puts "Contact name: #{name}, contact number #{number}" }
else
puts "You don't have any contact details your phonebook"
end
end
end
Because you have defined class local variable contactList, whereas you wanted to have an instance variable.
Remove this line
contactList = hash.new
and add the following method:
def contactList
#contactList ||= {}
end
P.S. there is no such thing as hash.new, you most likely meant Hash.new.
P.P.S. By Ruby naming conventions your variables/methods names should be snake-cased, not camel cased.
So it should be contact_list.

Undefined local variable or method 'user_response'

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.

Resources