mrt
mrt

Reputation: 1749

Optional parameter with constraint in the middle of Rails route

Is it possible to have a constraint on the optional route parameter that is 'in the middle' of the route?

I would like to have a following route:

get ':city(/:suburb)/:venue_type', venue_type: /bar|restaurant|cafe/

That would show a list of venues of specific type located in the city, or optionally have it narrowed down to suburb. The only :venue_types that I support are bar, restaurant and cafe.

Now I would like to achieve following mappings:

/nyc/manhattan/bar -> :city = nyc, :suburb = manhattan, :venue_type = bar
/nyc/bar           -> :city = nyc, :suburb = (nil),     :venue_type = bar
/nyc/whatever/cafe -> :city = nyc, :suburb = whatever,  :venue_type = cafe
/nyc/whatever      -> :city = nyc, :suburb = whatever,  :venue_type = (nil) - routing error

So far I've tried with the following which doesn't do the job:

class ValidSuburb
  INVALID = %w[bar restaurant cafe]
  def self.matches?(request)
    request.params.has_key?(:suburb) && !INVALID.include?(request.params[:suburb])
  end
end
get ':city(/:suburb)/:venue_type', venue_type: /bar|restaurant|cafe/, suburb: ValidSuburb.new

Is this possible to be achieved at all through constraints or do I have to resort in having multiple routes?

Upvotes: 0

Views: 1339

Answers (1)

deefour
deefour

Reputation: 35370

Maybe I'm missing something, but wouldn't it be simpler to just have 2 routes?

get ':city/:venue_type', constraints: { venue_type: /bar|restaurant|cafe/ }
get ':city/:suburb/:venue_type', constraints: { venue_type: /bar|restaurant|cafe/ }

Here if anything but "bar", "restaurant", or "cafe" are passed as that fragment after /nyc/.../bar, the first route will be skipped, allowing it to match to the second route.

If /nyc/whatever is passed, it won't meet the constraints/format of either route, causing the RouteError you're after.

Upvotes: 1

Related Questions