Difference between if and case - ruby - 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

Related

Variable does not equal variable when it should in 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...

Is the order of the equality operator important in Ruby?

I have used the bcrypt library in my Ruby program. I noticed that the order of the equality operator seems to be important. Depending on which variable is left or right of the '==' I get a different result.
Here is an example program:
require 'bcrypt'
my_pw = "pw1"
puts "This is my unhashed password: #{my_pw}"
hashed_pw = BCrypt::Password.create(my_pw)
puts "This is my hashed password: #{hashed_pw}"
20.times{print"-"}
puts
puts "my_pw == hashed_pw equals:"
if (my_pw == hashed_pw)
puts "TRUE"
else
puts "FALSE"
end
puts "hashed_pw == my_pw equals:"
if (hashed_pw == my_pw)
puts "TRUE"
else
puts "FALSE"
end
Regards
schande
Yes, there is a difference.
my_pw == hashed_pw calls the == method on the my_pw string and passes hashed_pw as an argument. That means you are using the String#== method. From the docs of String#==:
string == object → true or false
Returns true if object has the same length and content; as self; false otherwise
Whereas hashed_pw == my_pw calls the == method on an instance of BCrypt::Password and passes my_pw as an argument. From the docs of BCrypt::Password#==:
#==(secret) ⇒ Object
Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
This doesn't really have anything to do with equality. This is just fundamentals of Object-Oriented Programming.
In OOP, all computation is done by objects sending messages to other objects. And one of the fundamental properties of OOP is that the receiver object and only the receiver object decides how to respond to this message. This is how encapsulation, data hiding, and abstraction are achieved in OOP.
So, if you send the message m to object a passing b as the argument, then a gets to decide how to interpret this message. If you send the message m to object b passing a as the argument, then it is b which gets to decide how to interpret this message. There is no built-in mechanism that would guarantee that a and b interpret this message the same. Only if the two objects decide to coordinate with each other, will the response actually be the same.
If you think about, it would be very weird if 2 - 3 and 3 - 2 had the same result.
That is exactly what is happening here: In the first example, you are sending the message == to my_pw, passing hashed_pw as the argument. my_pw is an instance of the String class, thus the == message will be dispatched to the String#== method. This method knows how to compare the receiver object to another String. It does, however, not know how to compare the receiver to a BCrypt::Password, which is what the class of hashed_pw is.
And if you think about it, that makes sense: BCrypt::Password is a third-party class from outside of Ruby, how could a built-in Ruby class know about something that didn't even exist at the time the String class was implemented?
In your second example, on the other hand, you are sending the message == to hashed_pw, passing my_pw as the argument. This message gets dispatched to the BCrypt::Password#== method, which does know how to compare the receiver with a String:
Method: BCrypt::Password#==
Defined in: lib/bcrypt/password.rb
#==(secret) ⇒ Object
Also known as: is_password?
Compares a potential secret against the hash. Returns true if the secret is the original secret, false otherwise.
Actually, the problem in this particular case is even more subtle than it may at first appear.
I wrote above that String#== doesn't know what to do with a BCrypt::Password as an argument, because it only knows how to compare Strings. Well, actually BCrypt::Password inherits from String, meaning that a BCrypt::Password IS-A String, so String#== should know how to handle it!
But think about what String#== does:
string == object → true or false
Returns true if object has the same length and content; as self; false otherwise […]
Think about this: "returns true if object has the same length and content". For a hash, this will practically never be true. self will be something like 'P#$$w0rd!' and object will be something like '$2y$12$bxWYpd83lWyIr.dF62eO7.gp4ldf2hMxDofXPwdDZsnc2bCE7hN9q', so clearly, they are neither the same length nor the same content. And object cannot possibly be the same content because the whole point of a cryptographically secure password hash is that you cannot reverse it. So, even if object somehow wanted to present itself as the original password, it couldn't do it.
The only way this would work, is if String and BCrypt::Password could somehow "work together" to figure out equality.
Now, if we look close at the documentation of String#==, there is actually a way to make this work:
If object is not an instance of String but responds to to_str, then the two strings are compared using object.==.
So, if the author of BCrypt::Password had made a different design decision, then it would work:
Do not let BCrypt::Password inherit from String.
Implement BCrypt::Password#to_str. This will actually allow BCrypt::Password to be used practically interchangeably with String because any method that accepts Strings should also accept objects that respond to to_str.
Now, as per the documentation of String#==, if you write my_pw == hashed_pw, the following happens:
String#== notices that hashed_pw is not a String.
String#== notices that hashed_pw does respond to to_str.
Therefore, it will send the message object == self (which in our case is equivalent to hashed_pw == my_pw), which means we are now in the second scenario from your question, which works just fine.
Here's an example of how that would work:
class Pwd
def initialize(s)
#s = s.downcase.freeze
end
def to_str
p __callee__
#s.dup.freeze
end
def ==(other)
p __callee__, other
#s == other.downcase
end
end
p = Pwd.new('HELLO')
s = 'hello'
p == s
# :==
# "hello"
#=> true
s == p
# :==
# "hello"
#=> true
As you can see, we are getting the results we are expecting, and Pwd#== gets called both times. Also, to_str never gets called, it only gets inspected by String#==.
So, it turns out that ironically, the problem is not so much that String#== doesn't know how to deal with BCrypt::Password objects, but actually the problem is that it does know how to deal with them as generic Strings. If they weren't Strings but merely responded to to_str, then String#== would actually know to ask them for help.
Numeric objects in Ruby have a whole coercion protocol to make sure arithmetic operations between different "number-like" operand types are supported even for third-party numerics libraries.
The expressions would be equivalent if both operands were, for instance, of type String. In your case, one operand is a String and the other one is a BCrypt::Password. Therefore my_pw == hashed_pw invokes the equality method defined in the String class, while hashed_pw == my_pw invokes the one defined in BCrypt::Password.
I have never worked with BCrypt::Password, but would expect that you get false for the former and true for the latter.
In Ruby you can override the equality method for a given class or instance:
class Test
define_method(:==) do |_other|
true
end
end
Test.new == 'foo' # => true
Test.new == nil # => true
Test.new == 42 # => true
'foo' == Test.new # => false
nil == Test.new # => false
42 == Test.new # => true
Generally speaking, it's considered bad practice to override equality without making it symetric, but you sometimes see it in the wild.

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.

Beginner Ruby - about Objects

Beginner on Ruby here...below are two similar codes, I understand the procedures of this very basic code but I'd like to understand the theory behind it...
First
def stats(ppg)
if ppg > 20
puts "The PG is considered elite"
else
puts "The PG is not considered elite"
end
end
stats(28)
Second
def stats(ppg)
if ppg > 20
"The PG is considered elite"
else
"The PG is not considered elite"
end
end
puts stats(28)
So the first piece automatically writes out the strings whereas the second piece does not - is stats(ppg) from the first piece considered an object or a method with the variable as the argument?
def stats(ppg) in both cases is the same thing, i.e. the beginning of a method definition for a method named stats that takes one argument or parameter that will be named ppg inside the method body.
stats(28) in both cases is the same thing, i.e. a call to a method named stats with the literal Fixnum 28 passed as its argument, whereupon it will be assigned to the variable ppg inside the method body from the definition.
In Ruby, every expression involves two distinct phenomena: side effects, and return values. The expression a = 1 has a side effect of assigning the value of 1 to the variable a, and a return value of 1. Not every method in Ruby has a side effect, but every method does have a return value — either an explicit return value (by using return), or else the return value of the last expression evaluated in the method body.
puts is an unfortunate method in Ruby, because it is used in so many beginner examples, yet its behavior is confusing. It has a side effect of printing its argument to stdout, but its return value is nil (which often confuses beginners, who expect it to return the value of its argument).
The difference between your first method and second method is that the first method, because it uses puts internally, has the side effect of printing a string to stdout and a return value of nil, while the second method has no side effect, but a return value of the string itself.
Therefore, when you call your first method without puts in front, the side effect of printing the string occurs and you see the output. When you call your second method, there is no printing side effect, so in order to have the string printed to stdout, you have to call puts. The argument to puts is the return value of your stats method, i.e. the string you wanted to print.

Ruby defined?( 42[0][:foo] ) && defined?( 93[0]["bar"] ) == true. Why?

Short story:
"Why does defined?(59[0][:whatever]) evaluate to true?"
Long story:
I came across some strange behaviour lately which threw me off.
I was developing a method that did some washing of the data:
#Me washing input data:
def foo(data)
unless data && defined?(data[0]) && defined?(data[0][:some_param])
method2(data[0][:some_param])
else
freak_out()
end
end
I usually write tests where I spew in all kinds of junk data to make sure nothing strange happens:
describe "nice description" do
it "does not call method2 on junk data" do
expect(some_subject).to_not receive(:method2)
random_junk_data_array.each do |junk|
some_subject.foo(junk)
end
end
end
Well, method2 was called here. It happened when junk was a fixnum.
I am using ruby 2.1.0, and I can see that Fixnum has a #[] method which fetches the bit at that position, nice.
But why is fixnum[0][:some_param] considered to be defined?
defined? expression tests whether or not expression refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.). The return value is nil if the expression cannot be resolved. Otherwise, the return value provides information about the expression.
Let me explain with an example :-
defined?("a") # => "expression"
# this returns "method", as there is a method defined on the class String. So, the
# method invocation is possible, and this is a method call, thus returning `"method"`.
defined?("a".ord) # => "method"
# it return nil as there is no method `foo` defined on `"a"`,
# so the call is not possible.
defined?("a".foo) # => nil
Now coming to your point :-
As you said data[0] gives a Fixnum instance, and of-course Fixnum#[] exist. Thus fixnum_instance[:some_param] is also a valid method call. It just testing if the method is defined or not. If defined, it will tell yes this is a "method" expression. Otherwise nil. Not actually will check if the method call succeeded or not.
In Ruby all objects has truthy values except nil and false, thus "method" being a string object also has the truthy value, thus your condition got succeeded.

Resources