Calling multiple methods on a variable - ruby

So I have the following code:
print "input please: "
user_input = gets.chomp.downcase!
if user_input.include? "s"
user_input.gsub!(/s/, "th")
else
puts "There is no S in your input"
end
which throws me an error regarding the include method
when I run this it works:
print "input please "
user_input = gets.chomp
user_input.downcase!
if user_input.include? "s"
user_input.gsub!(/s/, "th")
else
puts "There is no S in your input"
end
is it not possible to call multiple methods on a variable?

It is permissible (and recommended!) to chain methods in Ruby as you have done with gets.chomp.downcase!. However, using downcase! as opposed to the form downcase is causing an unexpected behavior in your code. According to the docs, the downcase! form
Downcases the contents of str, returning nil if no changes were made.
So if your input does not contain any upper case letters, downcase! returns nil and that causes an error down the line when you call .include? on it. Try it out with input like this string:
input please: No letter in here!
# prints
There is no S in your input
But the same called without the upper N errors:
input please: no letter in here!
Traceback (most recent call last):
test.rb:4:in `<main>': undefined method `include?' for nil:NilClass (NoMethodError)
If you supply input that does contain upper case characters, you'll get no error with your original code. The fix for this is to use the non-! form:
# Don't use the ! form of downcase
user_input = gets.chomp.downcase
Because the downcase! form is intended to modify a variable rather than modify a transient string such as the string returned by .chomp.
I suspect you intended to puts user_input there also, following your .gsub! call. Your string replacement s to th does work correctly if you add that in.

Related

How can I solve undefined method `[]' on Ruby?

I'm trying to get an if statement for users who put incorrect data.
Here's my code:
class Breweries::CLI
def start
puts "Hello!"
puts "---------------------------"
puts "Please enter your location:"
input = gets.strip.downcase
#data = Breweries::API.get_breweries(input)
#objects = Breweries::HoppyCode.all
if input.length < 1
puts "Sorry!!"
puts "```````"
start
else
display_info
end
end
def display_info
puts "You'll love the following spots!"
puts "********************************"
#objects.each.with_index(1) {|brewery, index| puts "#{index}. #{brewery.name}"}
puts "Please make a selection by index number for more information:"
input = gets.strip.downcase
if(input.to_i > 0)
#brewery = #objects[input.to_i - 1]
puts "name: #{#brewery.name}"
puts "street: #{#brewery.street}"
puts "city: #{#brewery.city}"
puts "phone: #{#brewery.phone}"
puts "website_url: #{#brewery.website_url}"
display_info
elsif (input == "quit")
quit
elsif (input == "menu")
start
end
end
def quit
puts "Goodbye. Drink responsibly and enjoy."
end
end
When I put something that would generate an error, it returns the following:
Please enter your location: nvifpejvf80ejvip
Traceback (most recent call last):
2: from bin/breweriesCLI:6:in `<main>'
1: from /home/munificent-format-5297/Development/breweries/lib/breweries/cli.rb:8:in `start' /home/munificent-format-5297/Development/breweries/lib/breweries/api.rb:6:in `get_breweries': undefined method `[]' for nil:NilClass (NoMethodError)
How can I solve the undefined method '[]' error? Here's the API code in case that's necessary.
class Breweries::API
def self.get_breweries(input)
#breweries_hash = HTTParty.get("https://api.openbrewerydb.org/breweries?by_city=#{input}")
breweries_obj = {
name: #breweries_hash[1]["name"],
street: #breweries_hash[3]["street"],
city: #breweries_hash[4]["city"],
phone: #breweries_hash[10]["phone"],
website_url: #breweries_hash[11]["website_url"]
}
Breweries::HoppyCode.new(breweries_obj)
end
end
When the input is invalid, the call to
#breweries_hash = HTTParty.get("...")
returns not the object you expect (I’d suggest it returns an empty hash.) That makes it impossible to get to details in the following lines. Depending on how are you to handle it, you might decide to e. g. early return from this function, or raise, or do something else.
To approach this, start with debugging the issue, like this:
#breweries_hash = HTTParty.get("...")
puts #breweries_hash.inspect
...
That way you’ll see what gets returned and get the ideas of how to handle it.
If I am right, and what is returned is an empty hash, you might want to early return from this function.
#breweries_hash = HTTParty.get("...")
return if #breweries_hash.empty?
...
Identifying the Problem
There are lots of ways to solve for the nil problem, but at a quick glance it seems like part of the problem here is that you're somehow expecting input to return a valid Hash object from your API call, but an empty String or an instance of FalseClass may not do that. Consider the following:
input = gets.strip.downcase # <RETURN> here gets an empty string
input #=> ""
input.to_i > 0 #=> false
Then consider that some downstream of Breweries::API.get_breweries is expecting to operate on a Hash object instead if an instance of NilClass. In this case, that looks like #breweries_hash[1]["name"] and other operations on #breweries_hash.
Some Options
Without knowing more about your code, I don't want to be prescriptive here. But in general, you can do one or more of the following:
Coerce arguments into the expected class in the method call, the method signature, or the method body. For example, for Array objects:
# coerce a String to an Array, raising an exception if it can't
input = ""
Array(input)
#=> [""]
# coerce some Array to a Hash
array = [:name, "foo", :street, "bar"]
Array(array.each_slice 2).to_h
#=> {:name=>"foo", :street=>"bar"}
Explicitly check that you have an Hash object:
fail "#breweries is not a Hash" unless #breweries.is_a? Hash
Raise an exception rather than return 0 if input isn't actually a valid Integer representation in the first place:
input = Integer(gets.strip.downcase)
Check if your Hash or Array object responds to the relevant method calls, and raise a more helpful exception message:
raise sprintf("#brewery: %s", #brewery.class) unless #brewery.respond_to? :[]
There are other things you might do as well. Broadly speaking, you need to adjust your code to check the return value of your call to ensure it's not nil, then branch/raise/rescue appropriately depending on whether or not you ever expect nils as a valid return value from Breweries::API.get_breweries.
A Note on Using Exceptions for Non-Exceptional Circumstances
As a rule of thumb, you should only raise exceptions for truly unexpected circumstances, or when the program should halt because some condition can't (or shouldn't) be handled within the program during runtime. Which is best in your particular use case is really a design decision, and outside the scope of the original question. However, you might want to read Avdi Grimm's Exceptional Ruby for a deeper explanation of when exceptions might better than branching or handlers (or vice versa), but the choice in your code is a little left of center of the problem you're actually dealing with right now.

ArgumentError: wrong number of arguments (given 0, expected 1) Ruby

ArgumentError: wrong number of arguments (given 0, expected 1).
The code opens the file and looks at the paragraph and counts, the error
is in the center of the code. An error occurs when a method is called(1).
I can’t understand how to pass the argument methods.
#books = "You can use this knowledge to create small tools that might help."
require "colorize"
class Filecalculation
def select
loop do
puts "# Will we search : calculation_lines paragraph(1)".cyan
print "\n>>>>>> ".yellow
input = gets.chomp
search_method = "calc_#{input}"
if (respond_to?(search_method))
I can’t understand how to pass the argument to this place.
contents = send(search_method, #books)
else
puts "Unknown input: #{input.inspect}, method #{search_method} not defined."
end
end
end
# =================== calc_1 сounting words in Text File
def calc_1 paragraph
word_count = paragraph.split.length
puts "#{word_count} words"
end
end
Filecalculation.new.select
If you call send(search_method) you call a method without arguments. To pass arguments to the method being called, you need to pass them as next send args:
send(search_method, arg1, arg2)
in your case
send(search_method, paragraph)
Docs

include works with if but not else

I doing a Ruby botcamp. I'm supposed to write code that replaces all user input of 's' with 'th' so it reads like Daffy Duck is speaking. If I enter an s it will be replaced with th. That works! But If I don't enter an 's' it's supposed to print that none were included in my elsif statemnt. Instead I'm getting the error 'undefined method `include?' for nil:NilClass'. Other than that error, the interpretor is telling me the code is good.
print "Input a string: "
user_input=gets.chomp.downcase!
if user_input.include?"s"
user_input.gsub!(/s/, "th")
puts "Your string is #{user_input}!"
elsif
puts "There are no s's in your string!"
end
Any ideas on what I need to change?
You need to be careful with built-in ruby methods that end with an exclamation point (!). A lot of them will return nil if no changes were made:
'test'.downcase! # => nil
'Test'.downcase! # => "test"
Since you are assigning the result to a variable, there's no need to use the exclamation point method, since those modify in-place, you can just use the normal downcase method.
'test'.downcase # => "test"
You also later on have an elsif with no condition, that should probably just be an else. It's actually executing the first line of the "body" of the elsif as the conditional:
if false
puts "a"
elsif
puts "b" # recall that `puts` returns `nil`
puts "c"
else
puts "d"
end
This results in
b
d
being output

I'm trying to design a simple Ruby calculator and I'm getting an error

So I've been messing around with Ruby for the first time after finishing the codecademy course up to "Object Oriented Programming, Part I" and I decided to start making a calculator. For some reason though, I get this error:
calc.rb:13:in `addition': undefined local variable or method `user_input' for main:Object (NameError)
from calc.rb:21:in `<main>'
I'm confused why it doesn't see my "user_input" array. Is it out of the scope of the method? Did I initialize it wrong?
Here's the code so you can see for yourself, it's obviously nothing sophisticated and it's not finished. I'm just trying to test for addition right now.
#!/usr/bin/env ruby
user_input = Array.new
puts "Would you like to [a]dd, [s]ubtract, [m]ultiply, or [d]ivide? "
type_of_math = gets.chomp
def addition
operator = :+
puts "Please enter the numbers you want to add (enter \"=\" to stop adding numbers): "
until gets.chomp == "="
user_input << gets.chomp.to_i
end
sum = user_input.inject(operator)
return sum
end
case type_of_math
when "a"
addition
when "s"
puts "Test for subtraction"
when "m"
puts "Test for multiplication"
when "d"
puts "Test for division"
else
puts "Wrong"
end
Consider this untested variation on your code. It's more idiomatic:
def addition
user_input = []
puts 'Please enter the numbers you want to add (enter "=" to stop adding numbers): '
loop do
input = gets.chomp
break if input == '='
user_input << input
end
user_input.map(&:to_i).inject(:+)
end
Notice that it puts user_input into the method. It also uses the normal [] direct assignment of an empty array to initialize it. Rather than chomp.to_i each value as it's entered it waits to do that until after the loop exits.
Instead of while loops, consider using loop do. They tend to be more easily seen when scanning code.
Also notice there's no return at the end of the method. Ruby automatically returns the last value seen.

Error when chaining with chomp

I wrote a small program as the following
print "Your string = "
input = gets.chomp.downcase!
if input.include? "s"
puts "Go yourself!"
end
But I got the error
undefined method `include?' for nil:NilClass (NoMethodError)
if I delete the exclamation mark (!) after downcase, the program runs properly.
I don't understand the reason.
String#downcase! will give you nil, if the string is already in down-cased. So use String#downcase, it is safe. I am sure, you passed from the command line to the method gets, a string which is already down-cased. Replace the line input = gets.chomp.downcase! with input = gets.chomp.downcase. Now you are safe.
String#downcase
Returns a copy of str with all uppercase letters replaced with their lowercase counterparts. If the receiver string object is already, downcased, then the receiver will be returned.
String#downcase!
Downcases the contents of str, returning nil if no changes were made.
One example to demonstrate this -
>> s = "abc"
>> p s.downcase
"abc"
>> p s.downcase!
nil
Now nil is an instance of the class NilClass, which has no instance method called #include?. So you got no method error. This is obvious.
>> nil.respond_to?(:downcase)
false
>> nil.respond_to?(:downcase!)
false
>> s.respond_to?(:downcase!)
true
>> s.respond_to?(:downcase)
true
Do not use downcase! as it can return nil if no changes have been made to the string.
Therefore, the correct code will be:
print "Your string = "
input = gets.chomp.downcase
if input.include? "s"
puts "Go yourself!"
end

Resources