Im a newbie to rails and I need your help on this one.
On the code below, if the first input is wrong and the second one is correct, the expression evaluates the first input.
I would like that the user input is always evaluated.
require 'date'
puts "x is not a valid date"
until (x = gets.chomp) == ((x.match(/\d{4}-\d{1,2}-\d{1,2}$/).to_s &&Date.strptime(x, '%Y-%m-%d')rescue false) ==true)
In general, you should try to separate your data inout logic from data validation and later processing. That way, your methods concern themselves with only one thing each, which makes them much easier to reason about and thus more robust.
Here, we are introducing a valid_date_string? method which checks if the passed input object is a valid date string.
require 'date'
def valid_date_string?(input)
# First, we check that the given input is of the expected type.
# We do this here explicitly since we are validating a String. Most
# of the time, you would rely on duck typing instead.
return false unless input.is_a?(String)
# Then, we ensure roughly valid syntax (i.e. it looks like a date)
return false unless input =~ /\A\d+-\d+-\d+\z/
# Then, we "parse" it and split it into an array of year, month, day...
parts = input.split('-').map(&:to_i)
# ...and validate that this represents a valid date
Date.valid_date?(*parts)
end
In this solution, I took care not to rely on Exceptions for program flow since this is usually frowned upon as it can result in undetected bugs and has poor performance.
We can then use this method in some input loop to ask the user to input some value
def get_valid_date
loop do
input = gets.chomp
return input if valid_date_string?(input)
# Here, the user entered an invalid date. We can now do our
# error handling and try again.
puts "Invalid date given. Please try again..."
end
end
my_valid_date_string = get_valid_date
I would approach this problem as follows:
# date_test.rb
def date_input
puts "Please enter a date (format YYYY-MM-DD):"
d = gets.chomp
if d =~ /\d{4}-\d{1,2}-\d{1,2}$/
puts "You entered: #{d}"
else
puts "#{d} is not a valid date."
date_input
end
end
date_input
Explanation:
prompt to enter a date (specifying the expected format)
gets and chomp the user input
validate the format
if valid, puts the date
if not valid, puts the error and start again
When you run it:
$ ruby date_test.rb
Please enter a date (format YYYY-MM-DD):
invalid date
invalid date is not a valid date.
Please enter a date (format YYYY-MM-DD):
2017-10-13
You entered: 2017-10-13
$
Related
I'm just trying to make this code work and I keep getting:
<=': comparison of String with 21 failed (ArgumentError)
Please tell me what I'm doing wrong.
I'm learning and I've gone through every iteration of the code I can think of to try and make it work, I'm just not sure what I've done wrong.
puts "How old are you?"
old = gets.chomp
if old <= 21
return "You are not legally allowed to buy alcohol in the US"
else
return "You are legally allowed to buy alcohol in the US"
end
I believe you have to use to_i to convert the string into an integer.
The previous answer is correct but more verbosely, here is the line you need to change in your code:
old = gets.chomp.to_i
But you might also want to make sure user only enters an integer because calling .to_i on non-numeric characters will return 0.
You might want to look at Accept only numeric input
Try/improve as needed:
input = gets.chomp
if(val = Integer(input) rescue false)
val < 21 ? 'Not old enough' : 'The usual martini?'
else
'You did not provide an age (number)'
end
It checks if the input is an Integer, so it accounts for an input like foo.
Hth...
After creating the loop to check that the phone number is 10 characters I believe the phone issue is now resolved. Now I'm working with checking the email address, name, and making sure it outputs correctly, and making sure 2 names are entered by the user. Having issues getting the email address to validate and output in the correct format.
NAME_PATTERN = /([\w\-\']{2,})([\s]+)([\w\-\']{2,})/
EMAIL_PATTERN = /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
PHONE_PATTERN = /^(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})$/
def valid_name?(name)
!!name.match(NAME_PATTERN)
end
puts "Enter your first and last name (John Doe): "
while (name=gets)
names = name.split(" ", 2)
if valid_name?(name)
puts "Great, that looks like it could work."
break
else
puts "Please enter first and last name."
end
end
def valid_email?(email)
!!email.match(EMAIL_PATTERN)
end
puts "Enter your email address (joe#info.com): "
while (email = gets)
if valid_email?(email)
puts "Great, that looks like it could work."
break
else
puts "Invalid email address entered. Please try again. "
end
end
def valid_phone?(number)
!!number.match(PHONE_PATTERN)
end
puts "Enter your phone number including area code (numbers only): "
while (number=gets)
if valid_phone?(number)
puts "Great, that looks like it could work."
break
else
puts "Invalid phone number entered. Please try again."
end
end
puts names
puts email
puts number
I suspect you didn't intend to use the assignment operator here:
if (email = /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i)
Try this:
if email =~ /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
You have a similar error in the phone_number method:
if number = 10
I'm not sure what you intended here. Perhaps this?
if number.size == 10
You have more problems, however. Take a look at this loop:
loop do
if number.size == 10
break
else
puts "Invalid phone number entered. Please try again."
end
end
How will the user ever exit this loop if the number is invalid? The value of number never changes.
Here's a Ruby flavoured approach to what you're trying to do:
# Define constants for things that are special and get re-used.
EMAIL_PATTERN = /\A\S+#\S+\z/
# Methods that test something and return true or false often end with
# a question mark (?) to indicate this.
def valid_email?(email)
!!email.match(EMAIL_PATTERN)
end
# Try and keep your support methods separate from the main body of
# your program.
puts "Enter your email address (joe#info.com): "
# This sets up a loop that waits until you get a valid response.
while (email = gets)
email = gets
if valid_email?(email)
puts "Great, that looks like it could work"
break
else
# Note that the message is rendered here, not in the validation
# method, so there's no assumptions about how this method is used.
puts "Invalid email address entered. Please try again. "
end
end
If you try and structure your code this way you'll find it's a lot easier to keep things organized. This is one of the big challenges when learning programming so as not to get overwhelmed.
Regular expressions are great for validating things that conform to a very specific pattern, but try not to get overly confident in the pattern of everyday things. Even the humble IPv4 address comes in a multitude of forms.
I've tried to make Ruby sleep for an amount that the user has input by doing this:
puts "Time?"
time = gets.chomp
time.to_i
sleep(time)
Does anyone know what I'm trying to do and what I'm doing wrong?
The following works for me:
puts "Time?"
time = gets.chomp
sleep(time.to_i)
.to_i doesn't convert the value and overrides the variable, it simply returns the converted result. So it needs to either be used directly in the sleep argument or set its own (or another variable):
time = time.to_i
sleep(time)
Calling time.to_i returns an integer, but doesn't change time itself. Therefore time is still a string.
Change it to:
puts 'Time?'
string = gets.chomp
time = string.to_i
sleep(time)
Because to_i doesn't care if there is a "\n" at the end of the string, you can skip the chomp call. Therefore you can just write:
puts 'Time?'
time = gets.to_i
sleep(time)
How do I write code to fore a user to enter a certain value type such as an int, and then force or loop a prompt until a user enters an int, rather than a string or numbers with string characters? I am thinking some type of Boolean with for or while loop.
Let's start with some basics. Put this into a file userinput.rb:
print "Please enter a number: "
input = gets
puts input
Then run with ruby userinput.rb. You get a prompt and the program outputs whatever you type in.
You want your input to be an integer, so let's use Integer() to convert the input:
print "Please enter a number: "
input = gets
puts Integer(input)
Type in an integer and you'll get an integer output. Type in anything else and you'll get something like this:
userinput.rb:3:in `Integer': invalid value for Integer(): "asdf\n" (ArgumentError)
from userinput.rb:3:in `<main>'
Now you can build a loop that prompts the user until an integer is typed in:
input = nil # initialize the variable so you can invoke methods on it
until input.is_a?(Fixnum) do
print "Please enter a number: "
input = Integer(gets) rescue nil
end
The interesting part is input = Integer(gets) rescue nil which converts the integer and, in case of an ArgumentError like above, the error gets rescued and the input var is nil again.
A more verbose way of writing this (except that this catches only ArgumentError exceptions) would be:
input = nil # initialize the variable so you can invoke methods on it
until input.is_a?(Fixnum) do
print "Please enter a number: "
begin
input = Integer(gets)
rescue ArgumentError # calling Integer with a string argument raises this
input = nil # explicitly reset input so the loop is re-entered
end
end
Some notes:
Please don't get confused by Integer and Fixnum. Integer is the parent class that also encapsulates big numbers, but it's fairly standard to test for Fixnum (as in the loop head). You could also just use .is_a?(Integer) without changing the behavior.
Most Ruby tutorials probably use puts over print, the latter's output doesn't end with a newline, which makes the prompt appear in one line.
My current code is this:
print "Feed me input."
def get_input
input_value=gets.chomp
if !input_value
print "you didn't type anything"
else
input_value.downcase!
if input_value.include? "s"
input_value.gsub!(/s/,"th")
else
print "You entered a string but it had no 's' letters."
end
end
return input_value
end
get_input()
if !get_input
get_input
else
puts "#{get_input}"
end
I'm not sure why it isn't working. When I run it I get prompted for input then when I press enter after entering none I get the "You entered a string but it had no 's' letters", not the "you didn't type anything" that I wanted.
Every object except false and nil is treated as false if they are used as predicates. Even empty string is treated as true:
s = ""
puts true if s # => true
Use String#empty? to check if it is empty string.
As you said When I run it I get prompted for input then when I press enter after entering none - It means what happened acctually is
input_value="\n".chomp #( you gets methods take only `\n` as input)
"\n".chomp # => ""
so your input_value variable holds and empty string object. Now in Ruby every object has true value, except nil and false. Said that "" is also true,but you did !input_value,which means you are making it false explicitly. That's the reason in the below if-else block, else part has been executed and you didn't see the expected output "you didn't type anything".
if !input_value
print "you didn't type anything"
else
input_value.downcase!
if input_value.include? "s"
#.. rest code.
So I would suggest you in such a context replace the line if !input_value to if input_value.empty?, Which will make your code to behave as you are expecting. I didn't take your logic as a whole,but tries to show you how to code to meet your needs:
print "Feed me input."
def get_input
input_value=gets.chomp
if input_value.empty?
puts "you didn't type anything"
false
else
puts "found value"
input_value.downcase!
end
end
until input = get_input
# code
end
puts input
output
kirti#kirti-Aspire-5733Z:~/Ruby$ ruby test.rb
Feed me input.
you didn't type anything
you didn't type anything
you didn't type anything
HH
found value
hh
kirti#kirti-Aspire-5733Z:~/Ruby$