Andrew
Andrew

Reputation: 238777

Ruby: Regex in case statements

I'm new to Ruby. I'm trying to figure out how to write a nested case statement. Here is an example of what I am trying to do:

# cucumber, webrat paths.rb

  def path_to(page_name)

    case page_name

    when /the "?(.*)"? event registration homepage/ then
      case $1
      when '2011 CIO GL Global'  then '/event/index/id/236'
      when '2011 CIO IS Chicago' then '/event/index/id/275'
      when '2011 CIO ES Denver'  then '/event/index/id/217'
      when '2011 CIO ES Vancouver, BC' then '/event/index/id/234'
      else
        raise "Can't find mapping for \"#{$1}\" to a path.\n" +
          "Now, go and add a mapping in #{__FILE__}"
      end

    when /the contact match verification page/
      '/attendee/user-verification'
    end
  end

In my feature file it says:

When I go to the "2011 CIO IS Chicago" event registration homepage

It's failing on this step because its raising the exception mentioned above, even though I have it defined in my case statement above. What am I doing wrong?

Upvotes: 4

Views: 4660

Answers (5)

bowsersenior
bowsersenior

Reputation: 12574

I would approach this a little differently. In your cucumber feature, I suggest changing the wording like so:

When I go to the registration homepage for the event named "2011 CIO IS Chicago"

And in your paths.rb file I would handle all events with a single regexp, like so:

when /the registration homepage for the event named "?(.*)"?/ then
  event = Event.find_by_name($1)
  raise "could not find an event with name: #{$1}" if event.blank?
  event_path(event) 
  # or create the route manually like so:  
  # "event/index/id/#{event.id}"

This relies on your Event model having a method that can find an event given its name (in this case I assumed find_by_name), and a resourceful route set up for :events.

Upvotes: 0

Andy
Andy

Reputation: 778

The problem is in your regex. Try removing the last ? as it's causing the " to be optional and the greedy search up front (.*) is putting it in the match $1.

Observe:

> s = 'the "2011 CIO IS Chicago" event registration homepage'
> /the "?(.*)"? event registration homepage/.match s #=> <MatchData "the \"2011 CIO IS Chicago\" event registration homepage" 1:"2011 CIO IS Chicago\"">
> $1 #=> "2011 CIO IS Chicago\""
> /the "?(.*)" event registration homepage/.match s #=> #<MatchData "the \"2011 CIO IS Chicago\" event registration homepage" 1:"2011 CIO IS Chicago">
> $1 #=> "2011 CIO IS Chicago"

Edit: if you want the " to be optional you'll probably need to do something like: (?:"(.*)"|(.*)). And then you'll need to use a nil guard to find out which reference is returned. $1 || $2.

Upvotes: 0

Thomas Andrews
Thomas Andrews

Reputation: 1597

Yeah, the second question mark is confusing the regular expression.

The expression:

.*b?

can match the string "axb" in two ways, either with .* matching the entire thing, or with .* matching the 'ax' and 'b?' matching the 'b'. The regular expression algorithm is "greedy" - it prefers to match as much as it can as early as it can.

I'd rewrite the regular expression as:

    when /the "?([^"]*)"? event registration homepage/ then

to make sure that that $1 doesn't end up with any double quotes in it...

Upvotes: 4

DigitalRoss
DigitalRoss

Reputation: 146093

It works for me. How are you testing it?

Update: Aha, it's because your regular expression is matching the trailing quote, so $1 ends in a quotation mark which is not present in your inner case.

There are a few ways to fix this.

  1. You could match "?[^"]*"?
  2. The selectors for the inner case could just end in ..."?'
  3. You could use a non-greedy match: "?(.*?)"?.

Upvotes: 1

lrm29
lrm29

Reputation: 498

Change

 when /the "?(.*)"? event registration homepage/ then

to

 when /the "?(.*)" event registration homepage/ then

Upvotes: 1

Related Questions