Saturday, October 4, 2008

Regex Changes in RSpec 1.1.5 Stories

RSpec 1.1.5 changed how the Given, When, and Then match statements are parsed and can break some of your stories.

One of my Then statements was as follows:

Then("the event should have $count artists?") do |count|
   #...
end

The match statements are converted to regular expressions so the question mark would just make the 's' on artists optional so this would match both:

Then the event should have 4 artists

and...

Then the event should have 1 artist

After updating RSpec to 1.1.8 from 1.1.4 (the versions jumped so fast to align with Rspec-rails but 1.1.5 has most of the changes) I was seeing a bunch of my steps were now pending.

I ran a diff on the lib/spec/story/step.rb file between tags 1.1.4 and 1.1.5 in rspec and noticed the method building the matcher was updated.

-    def assign_expression(string_or_regexp)
-      if String === string_or_regexp
-        expression = string_or_regexp.dup
-        expression.gsub! '(', '\('
-        expression.gsub! ')', '\)'
-      elsif Regexp === string_or_regexp
-        expression = string_or_regexp.source
-      end
-      while expression =~ PARAM_PATTERN
-        expression.gsub!($2, "(.*?)")
-      end
-      @expression = Regexp.new("^#{expression}$")
+  def init_name(name)
+    @name = name
+  end
+
+  def init_expression(string_or_regexp)
+    if String === string_or_regexp
+      expression = string_or_regexp.dup
+      %w<? ( ) [ ] { } ^ !>.each {|c| expression.gsub! c, "\\#{c}"}
+    elsif Regexp === string_or_regexp
+      expression = string_or_regexp.source
+    end
+    while expression =~ PARAM_PATTERN
+      expression.sub!($2, "(.*?)")
     end
+    @expression = Regexp.new("\\A#{expression}\\Z", Regexp::MULTILINE)
+  end

I didnt realize the matcher was parsed differently based on if it was passed in as a String or a Regexp. Now if you give a string to match with any of the %w<? ( ) [ ] { } ^ !> characters they will be escaped to match those actual characters. Previously it was just parenthesis.

Anyway to fix your current matchers just send them in as regexs instead of strings.

Then(/the event should have $count artists?/) do |count|
   #...
end

Don't worry about escaping the dollar sign, the PARAM_PATTERN strips that out before it gets converted back into a regex.

1 comment:

delee said...

hi phil,
saw your message on my blog.
how did you get there>
by tje way, do you understand dutch?
frankly, i don't understand a thing of your blog.
anyhow, i was pleased to read your message. chiao neoleo