0

[[:punct:]] doesn't match any punctuation when it's called by a rails model test. Using the following code

test "punctuation matched by [[:punct:]]" do
  punct_match = /\A[[:punct:]]+\Z/.match('[\]\[!"#$%&\'()*+,./:;<=>?@\^_`{|}~-]')

  puts punct_match
  puts punct_match.class
end

this outputs a non-printable character and NilClass.

However, if I execute the same statement

punct_match = /\A[[:punct:]]+\Z/.match('[\]\[!"#$%&\'()*+,./:;<=>?@\^_`{|}~-]')

in irb matches correctly and outputs

[\]\[!"#$%&'()*+,./:;<=>?@\^_`{|}~-]
=> nil

What am I missing?

Ruby version => 2.2.4, Rails version => 4.2.6

  • What *exactly* are you entering in irb? – Tom Lord May 22 '17 at 15:03
  • input: punct_match = /\A[[:punct:]]+\Z/.match('[\]\[!"#$%&\'()*+,./:;<=>?@\^_`{|}~-]') output: #?@\\^_`{|}~-]"> – Mohamed AbuIssa May 22 '17 at 15:12
  • Using MRI v2.2.4? I tried this, and got no match. Cannot reproduce. – Tom Lord May 22 '17 at 15:22
  • Ahhhh hang on a minute. You're using Ruby `2.4` not `2.2`, aren't you? The behaviour of this regex did change. Your rails vs pry environments are in different ruby versions. – Tom Lord May 22 '17 at 15:28
  • woow! Isn't rails a gem? it should be use the same ruby as you know ruby. – Mohamed AbuIssa May 22 '17 at 15:36
  • Yes, `rails` is a gem (which contains a bundle of smaller gems such as `active_record`, `active_support`, `active_model`, ...). But it's perfectly possible to have multiple versions of ruby installed (e.g. using `rvm` or `rbenv`). You must have configured `pry` to run as a `2.4.x` gem. – Tom Lord May 22 '17 at 15:38

1 Answers1

2

The behaviour of /[[:punct:]]/ changed slightly in ruby version 2.4.0.

This bug was raised in the ruby issues, which links back to this (much older) issue in Onigmo - the regexp engine used since Ruby 2.0+.

The short answer is, these characters were not matched by /[[:punct:]]/ in ruby versions <2.4.0, and are now matched:

$+<=>^`|~

You must be running irb in a newer ruby version than this rails application, which is why it matches there.


On a separate note, you should alter your code slightly to:

/\A[[:punct:]]+\z/.match('[]!"#$%&\'()*+,./:;<=>?@^_`{|}~-]')
  • Use \z, not \Z. There is a slight difference: \Z will also match a new line at the end of the string.
  • You have unnecessary back-slashes in the string, such as '\^'
  • You have repeated a [ character: '[\]\['
Tom Lord
  • 27,404
  • 4
  • 50
  • 77