Variable does not equal variable when it should in ruby - ruby

I am making my first web server, but When i use the if statement to compare user input from html file with a string, they just skip it even if it is true.
require 'socket'
require 'pry'
class Parser
def parse(req, clients)
save = File.new("requests.txt", "w+")
save.write(req.chomp)
save.close
words = IO.readlines("requests.txt").last(1)
if words == "Username=test&Password=1234"
success(clients)
end
#binding.pry
puts words
end
end
def success(succ_client)
success_file = File.read("templates/success.html")
stuff = "HTTP/1.1 200\r\n" + success_file.force_encoding('UTF-8')
succ_client.puts(stuff)
end
def response(cli)
file = File.read("templates/passpage.html")
content = "HTTP/1.1 200\r\n" + file.force_encoding('UTF-8')
cli.puts(content)
end
serv_sock = TCPServer.new('10.0.2.15', 8080)
loop {
client = serv_sock.accept()
requests = Parser.new
requests.parse(client.readpartial(2043), client)
response(client)
client.close
puts "Connected"
}
I tried using #compact!, nil?, and using pry to decode to find whats the issue, but I just cant find the problem, when i puts the words variable it puts the correct value but its just not the right one I guess. I tried decoding the words but that still didn't work unless i did it wrong.
It has been 5 days on this problem and this is my first ruby program, and web-server, So ill appreciate any help I can get with this to move forward in life.

There are several issues with your code.
Primarily, your call to IO.readlines("requests.txt").last(1) returns an Array of zero or one strings. However, you are comparing it to a single string. Since the string is not equal to the array, the comparison fails.
Likely, you want to use IO.readlines("requests.txt").last instead. This returns the last element or the array or nil if the array returned by the readlines method is empty.
Please have a look at the documentation of the Array#last method to learn more about the returned types of this method.
In addition to that, even if your if statement eventually matches, your intended call to the success method fill fail, because you have defined it in the global main object, rather than your Parser class.
Also, when you eventually call success from your parse method, you will be returning two responses as you also call the response method later. You may want to rethink the way you select the correct responses...

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.

How to set a variable equivalent to single array object while iterating through array?

Noob question. I need to pass 3,000+ URLs from a CSV sheet to Selenium. I need Selenium to navigate to each one of these links, scrape information and then put that information into a CSV.
The issue I am running into is when I push my CSV URLS into an array, I cannot pass one single object (url) into Selenium at a time.
I know I likely need some sort of loop. I have tried setting up loops and selecting from the array using .map, .select. and just a do loop.
urls.map do |url|
#driver.navigate.to #{url}
name = #driver.find_element(:css, '.sites-embed-
footer>a').attribute('href')
puts name
kb_link = name
kb_array.push(kb_link)
puts 'urls is #{n}'
end
In the above example, Selenium returns an "invalid URL" error message. De-bugging with Pry tells me that my 'url' object is not a single url, but rather still the entire array.
How can I set Selenium to visit each URL from the array one by one?
EDIT: ----------------
So, after extensive de-bugging with Pry, I found a couple issues. First being that my CSV was feeding a nested array to my loop which was causing the URL error. I had to flatten my array and un-nest it to get around that issue.
After that, I had to build a rescue into my loop so that my script didn't die when it encountered a page without the CSS element I was looking for.
Here's the finalized loop.
begin
#urls1.each do |url|
#driver.navigate.to(url)
#driver.manage.timeouts.implicit_wait = 10
name = #driver.find_element(:css, '.sites-embed-
footer>a').attribute('href')
puts name
kb_link = name
kb_array.push(kb_link)
puts 'done'
rescue Selenium::WebDriver::Error::NoSuchElementError
puts 'no google doc'
x = 'no google doc'
kb_array.push(x)
next
end
What about using .each?
Example:
array = [1, 2, 3, 4, 5, 6]
array.each { |x| puts x }
In your code:
urls.each do |url|
#driver.navigate.to #{url}
name = #driver.find_element(:css, '.sites-embed-footer>a').attribute('href')
puts name
kb_link = name
kb_array.push(kb_link)
puts 'urls is #{n}'
end
First of all, it doesn't make much sense to use map if you don't use the result of the block somewhere. map, applied to an Enumerable, returns a new Array, and you don't do anything with the returned array (which in your case would contain just the return values of puts, which is usually nil, so you would get back just an array of nils with the side effect that something is written to stdout.
If you are only interested in the side effects, each or each_with_indexshould be used to traverse an Enumerable. Given the problems you have with map and with each, I wonder what is the actual content of your object urls. Did you ever inspect it? You could do a
p urls
before entering the loop. With 3000 URLs, the output will be huge, but maybe you can run it on a simpler example with less URLs.

Difference between if and case - ruby

This code
if response != Net::HTTPNoContent
raise Exception
end
puts "OK"
prints an exception, whereas this code
case response
when Net::HTTPNoContent
puts "OK"
else
raise Exception
end
prints "OK" to the console. I'd like to know what's going on.
If you need more details, let me know.
Your response variable is, I assume, a response object returned from using net/http. That object's type will be Net::HTTPNoContent.
In your first if variant, you are checking to see if your response object is equal to the Net::HTTPNoContent class. It's not going to be. The net/http library isn't going to return a class when your request is sent, it's going to return an object containing all of the information about your request's response (and will be of type Net::HTTPNoContent, or some other class depending on the result of the request).
In your case variant, however, things work a little differently. Ruby tries to be intelligent about what it does with the predicates you give each when branch. It will do an triple-equals (===) (like #is_a?, in this example, though it does other things) comparison against each branch, which evaluates to true if the object in question's class is (or is descended from) the class specified in the branch (or, of course, if the object is indeed equal).
(In Ruby, classes are objects too, which is why you can compare response to the class itself, like in your if version, and it still make sense to the interpreter.)
So this is best explained by rewriting your if version:
if !response.is_a?(Net::HTTPNoContent)
raise Exception
end
puts "OK"
Simple example:
a = 'a'
if a != String
p '-'
else
p '+'
end
case a
when String
p '+'
else
p '-'
end
Return:
#=> -
#=> +
It means that in first example you try to check if value of some response is equal to class. When you use case when String it would check if a.is_a? String

Ruby 2.1.3 options hash cannot be assigned with fetch

Please explain to me why this is happening:
def resizeImage(event_file, resize_width, resize_height, options = {})
puts options
aspect_ratio = options.fetch(:maintain_aspect_ratio, true)
puts aspect_ratio
"return value"
end
resizeImage('event_file', 'resize_width', 'resize_height', {maintain_aspect_ratio: false} )
{:maintain_aspect_ratio=>false}
false
=> "return value"
I want to set a variable to = the fetch from the hash to make my code more readable. But for some reason it is nil when I call it. It almost looks like the fetch is asynchronous, but this ain't ajax.
This is a pretty elementary problem I know, but so far as I can tell I am doing things just as they are described in more than one guide to using options in ruby methods.
-- Update --
so now I have it returning something else to try to separate the problem. I see that the puts statement for aspect_ratio doesn't actually return anything at all. What is happening?
-- Update2 --
Okay, I got confused. It is returning false. So there is something else wrong in my real method. Thank you for your time, I will accept the most detailed answer but I appreciate both.
Remove this line from your method:
puts aspect_ratio
Your Ruby method is returning to the value of the final statement, i.e. the value of puts.
When you remove that line, then your method will return aspect_ratio.
You actually have the correct structure down, however the final puts in your method is causing the return value to be nil. Ruby utilizes an implicit return, so unless specified, it will return the last value of a method. remove the puts aspect_ratio from the method, or ensure the last line is aspect_ratio and the method will properly return your value
def resizeImage(event_file, resize_width, resize_height, options = {})
puts options
aspect_ratio = options.fetch(:maintain_aspect_ratio, true)
puts aspect_ratio #remove this line
aspect_ratio
end
this can end up being shortened to
def resizeImage(event_file, resize_width, resize_height, options = {})
options.fetch(:maintain_aspect_ratio, true)
end
#joelparkerhendersons answer is correct
Im going to explain whats happening.
Everything in ruby returns something(apart from a tiny minority). It can be forced by using return but by default the last line of the method is returned.
Your method resizeImage returns what puts returns. puts returns nil
Therefore as #joelparkerhendersons suggested remove the puts

How to pass a block

For the sake of simplicity, I've tried to abstract the problem down to its core elements. I've included a small piece of functionality wherein I use Socket to show that I want to pass the block further down into a method which is a black box for all intents and purposes. I'm also passing a constant True for the sake of showing I want to pass arguments as well as a yield block.
With all that being said, if I small have a hierarchy of calls as such:
def foo(use_local_source)
if use_local_source
Socket.unix("/var/run/my.sock") &yield
else
Socket.tcp("my.remote.com",1234) &yield
end
end
foo(True) { |socket|
name = socket.read
puts "Hi #{name}, I'm from foo."
}
How can I pass the implicitly declared block right down through foo and into Socket as if I were calling Socket.tcp(...) { ... } directly.
I know I could set it as an argument, but it doesn't feel idiomatic to Ruby. Is this also untrue and I should pass it as an argument? I've tried combinations of & and *, and I get a range of exception.
def foo(use_local_source)
if use_local_source
yield Socket.unix("/var/run/my.sock")
else
yield Socket.tcp("my.remote.com",1234)
end
end
From the docs for yield:
Yields control back to the context that resumed the fiber, passing along any arguments that were passed to it.

Resources