Regular expression in Ruby

Hi all, I’m new to RoR, and I’ve only used regular expressions in bash, Java, and Perl. I am trying to validate a date input on my model, and I have the following line in my Item class.

validates_format_of( :purchase_date, :with => /\d{2}/\d{2}/\d{4}/, :message => " must be in the format dd/mm/yyyy")

Now, when I use the eclipse plugin QuickRex to get my regular expression, the string value “02/02/1997” matches my pattern. Do ruby regular expression not accept “\d” as a valid identifier for a number?

Thanks, Todd

irb(main):006:0> '02/15/2008' =~ /\d{2}\/\d{2}\/\d{4}/ => 0 irb(main):007:0> '02/15/08' =~ /\d{2}\/\d{2}\/\d{4}/ => nil

So the first expression matched at position zero, the second one
didn't match at all. I don't see a problem with \d.

As s.ross pointed out this should work.

OP, Did you try it? You didn't actually say you did.

Also, note that this will also validate things like:

"My cat's name is fluffy he was born on 12/12/2003, isn't he cute?"

You should anchor the regexp i.e.

/^\d{2}\/\d{2}\/\d{4}$/

You can also get rid of a few of the "leaning toothpicks" by using %r instead of / to delimit the regexp:

%r(^\d{2}/\d{2}/\d{4})

Oops, left off the end anchor on that last:

%r(^\d{2}/\d{2}/\d{4}$)

Thanks for the help guys. Every time I try this it fails. I'm giving it the input of "02/02/1978" I've tried both of the following regular expressions.

%r(^\d{2}\/\d{2}\/\d{4}$) and /^\d{2}\/\d{2}\/\d{4}$/

Neither seems to work. Any ideas what's going on? I'm using Ruby 1.8.6, and Rails 1.2.3. Below is my entire model class, am I missing something?

class Item < ActiveRecord::Base

  belongs_to :channel;

  validates_presence_of :name, :purchase_price, :purchase_date;   validates_numericality_of(:purchase_price, :message => " must me a number");   validates_format_of( :purchase_date, :with => /^\d{2}\/\d{2}\/ \d{4}$/, :message => " must be in the format dd/mm/yyyy") end

Thanks, Todd

couple of SWAGs, since you don't say where input comes from: trailing newline from somewhere in the chain, or you split on tabs and left the tabs in the captured fields (tho i don't remember how to do this in regexes)? encoding mismatch?

BTW generally preferred not to top-post.

Hmmm. I'm not sure. The input just comes from a plain form post from a form that's generated by rhtml, I don't format in any special way. It won't pass validation in either Firefox or Safari. I thought that perhaps I'd misspelled the field, but when I leave it blank it fails the validates_presence_of check.

Fire up script/console and test your regex against a string (I
believe I clipped some IRB lines earlier for this regex). If it
succeeds, then instantiate your model:

a = Item.new(:name = 'a', :purchase_price => 3)

now

a.valid?

should be false because you didn't set the purchase date.

a.purchase_date = '02/02/78' a.valid?

should be false because the year is in the wrong format.

a.purchase_date = '02/02/1978' a.valid?

should be true because you've satisfied all your validations.

You could even write this in a test (which makes more sense in the
long run):

def items_should_validate_fields    a = Item.new(:name = 'a', :purchase_price => 3)    assert !a.valid?    # some assertion about what errors are reported    # and so on end

Hope this helps.

Is purchase_date a date / datetime field in your database? if so then purchase_date will be an instance of Date or Time and those won't match a regular expression. If you are validating an input in this way, the validation should be happening at the controller level: as far as the model concerned a date is just a date. Sure validate stuff like 'this date is not in the past' or 'this date can't be before this other date' but the sort of input validation you are doing doesn't belong there. If purchase_date is just a string , then that sounds wrong.

Fred

At the model level, this works:

   private    def validate      unless attributes_before_type_cast['date_of_loss'] =~ /^(\d+(-|\/)){2}\d{4}/        errors.add('date_of_loss', 'use the form: mm/dd/yyyy')      end    end

My field was 'date_of_loss'. Fill in your own.

At the model level, this works:

   private    def validate      unless attributes_before_type_cast['date_of_loss'] =~ /^(\d+(-| \/)){2}\d{4}/        errors.add('date_of_loss', 'use the form: mm/dd/yyyy')      end    end

This is a little wonky, as I believe the following irb session shows

>> r.comment_read_at = '10/31/2007' => "10/31/2007" >> r.comment_read_at => Wed Oct 31 00:00:00 UTC 2007 >> r.comment_read_at_before_type_cast => "10/31/2007" >> r.valid? => true >> r.save => true

So far so good. I've set my datetime column, and if i were to validate then the attribute before typecast is the string I supplied, and the format is ok.

>> r.reload [snip] >> r.comment_read_at => Wed Oct 31 00:00:00 UTC 2007 >> r.comment_read_at_before_type_cast => "2007-10-31 00:00:00" >> r.valid? => false

So if you save and reload a valid record it becomes invalid: attributes_before_type_cast represents the string values that come out of the database, which will depend only on how your database formats dates, not what the user typed in.

Fred

Actually, that's the behavior I expect. The type *before type cast* is a string exactly as input. You are validating its format. The type after the cast is datetime and may not conform to your validation. But that's not where the data will be corrupted, right?

Actually, that's the behavior I expect. The type *before type cast*
is a string exactly as input. You are validating its format. The type
after the cast is datetime and may not conform to your validation.
But that's not where the data will be corrupted, right?

Well yes that's of course true, but it's deeply unhelpful if the behaviour of your model

foo.valid?

=> true

foo.save foo.reload foo.valid?

=> false

Every time you or the user updates the record you'd have to coerce the field to the pattern you wanted.

Fred

What you do in your validate method is up to you. It is extremely helpful that Rails automatically stores date/time values in a database native form where it can be sorted, used in criteria, etc. It may not be as helpful to you that the default implementation of Date#to_s produces an ISO date string. So, your validator could do something along the lines of:

private def validate    # don't bother to validate if data was pulled from the database    if attributes_before_type_cast['date_of_loss'] == String      unless attributes_before_type_cast['date_of_loss'] =~ /^(\d+(-|\/)){2}\d{4}/        errors.add('date_of_loss', 'use the form: mm/dd/yyyy')      end    end end