3

I am trying to create a method that asks for the user to create a pin number, like a bank account pin. It can only be 4 numbers in length, and every character entered must be numbers. I have found when I use this code:

puts "Please give us a 4 digit pin number for your account:"
@response = gets.chomp 

unless @response.to_i.is_a?(Integer) && @response.to_str.length == 4
    puts "Your response must be 4 numbers in length."
    create_pin
else
    @pin = @response.to_i
    puts "Your pin has been set."
end

It does not work as I would prefer, because the .to_i method will gladly convert '2134a' to 2134. As well, if there is no Integer in the string, it records the value as 0, which is not false as I need it to be.

Test if a string is basically an integer in quotes using Ruby?

I found a really helpful post, but it seems to get quite complicated in the thread do to regexp. This post was also shut down because the creator was not specific enough:

Check if string contains only positive numbers in Ruby

So I'll try be clear, I am trying to limit the user input so they can only type numbers 0 through 9. Math equations like 10^3 need to be fail, as well as negative numbers like -1234, and I'm trying to limit them to four digits the way any pin would be limited. So far, I'm completely stumped and haven't seen a way to do it that doesn't contain a whole bunch of regexp.

Perhaps I could check if the string contains a list of unacceptable characters.

Thanks ahead.

Edited for better phrasing and to correct an error I wrote in the second paragraph.

Community
  • 1
  • 1
Mark H Jr
  • 347
  • 1
  • 5
  • 10
  • 4
    It's a trivial regex. Personally, I'd recommend spending some more time trying to figure out, or show us what you've tried. It doesn't get much easier than this, and it's easily searchable. – Dave Newton May 01 '14 at 20:36
  • In all honesty, I haven't a clue how to use regexp. I was wondering if there was a particular method like numeric? (which hasn't worked) that I could run, but I guess I'll have to learn regexp sooner or later. Also, I never know if I'm posting too much or too little code. I'm supposed to stay on topic, but I never know what's on topic since many of my mistakes have simply been syntax related. – Mark H Jr May 01 '14 at 20:45
  • "it records the value as nil, which is not false as I need it to be." just to clarify `nil` and `false` are actually the only things that are `false` in ruby. Your issue is not `nil` but that a `String` starting with a non-numeric character will return `0` when `to_i` is called on it. You are saying convert this string to an `Integer` and then asking is this an `Integer`. The answer is obviously of course it is because you just made it one. – engineersmnky May 01 '14 at 20:51
  • 2
    Regex skills are mandatory for any dev. – Dave Newton May 01 '14 at 20:55
  • So I've noticed. wvandaal and hbejgel both linked me to Rubular. It seems like a great resource for beginners. Also, engineersmnky you're right, I meant to type 0 rather than nil. I discovered that problem when all the pins using only letters actually passed and saved as 0. I'll edit to fix it. – Mark H Jr May 01 '14 at 21:06
  • I've found http://www.regular-expressions.info/ to be pretty helpful. Also [the docs](http://www.ruby-doc.org/core-1.9.3/Regexp.html), though dense, are good to read through. Also, I think the regex section in [Why's Poignant Guide](http://www.rubyinside.com/media/poignant-guide.pdf) is good, but not everyone enjoys that book. – Reinstate Monica -- notmaynard May 02 '14 at 15:00

4 Answers4

8

Try this:

def valid_pin?(pin)
  /^\d{4}$/ === pin
end

What this is saying basically is:

  1. /^d{4}$/ is a regular expression, you can tell because it is enclosed in / / with a pattern in the middle
  2. the ^ and $ characters denote the beginning and end of your string, respectively. Basically what is ensures is that strings with 4 consecutive numbers but which have other characters at the beginning or end (i.e. "a1234" and "5678b") will not be accepted.
  3. \d is the regular expression character denoting digits. When followed by {4}, this means to look for exactly 4 digits. Ruby also allows you to specify a minimum value using this notation {3,} or a range {3,6}.
  4. The === method (as correctly mentioned by @iamnotmaynard), sometimes called the threequals operator, returns a boolean (true or false) value depending on whether or not the regular expression matches the given string.

In the context of your code:

puts "Please give us a 4 digit pin number for your account:"
@response = gets.chomp 

unless valid_pin?(@response)
  puts "Your response must be 4 numbers in length."
  create_pin
else
  @pin = @response.to_i
  puts "Your pin has been set."
end

If you want to try and learn about Regular Expressions (commonly referred to as RegExes), I would encourage you to play around on the awesome site Rubular, there is a general keyword list and RegEx sandbox to help you text your creations.

wvandaal
  • 4,265
  • 2
  • 16
  • 27
  • 3
    This is the correct regex. If you use [Regexp#===](http://www.ruby-doc.org/core-1.9.3/Regexp.html#method-i-3D-3D-3D) you can simplify the command and avoid the slightly hackish `!!` (since it returns boolean): `/^\d{4}$/ === "1234" => true` – Reinstate Monica -- notmaynard May 01 '14 at 21:03
0

The following will work:

puts "Please give us a 4 digit pin number for your account:"
@response = gets.chomp 

unless @response.match(/\d{4}/) && @response.length == 4
  puts "Your response must be 4 numbers in length."
  create_pin
else
  @pin = @response.to_i
  puts "Your pin has been set."
end
Kalman
  • 8,001
  • 1
  • 27
  • 45
0

This can be done in a couple of lines using a regex. Something like the following should accomplish it.

if @response =~ /^\d{4}$/
   @pin = @response
else
   puts "Your pin must be 4 numbers in length."
   create_pin
end

In the regex, '^' and '$' denote the start and end of the string, respectively. Adding these guarantees that you won't have junk before or after the 4 digits you're looking for. And so you avoid answers such as '1234a'

The '\d{4}' will match 4 consecutive digits 0-9.

So you've narrowed the user down to only responses like '3421', '9843', etc.

0

You should try out using regular expressions:

unless /[0-9]{4}/.match(@response).to_s.length == 4 && @response.length == 4 
    puts "Your response must be 4 numbers in length."
    create_pin
else
    @pin = /[0-9]{4}/.match(@response).to_s.to_i
    puts "Your pin has been set."
end

You can toy with regular expressions in this website: http://rubular.com/

hbejgel
  • 4,674
  • 2
  • 17
  • 27