开发者

Rails 3 Routes - allowing for slugs

What is the correct way to set up the routes to allow an extra path element (a slug) to be passed to a URL in Rails 3. I'd like to not break some of the magic you get with show and new when you list a object as a resource.

Here is an example:

http://somewebserver.com/topics/1/lear开发者_如何学JAVAning-rails

but I would still like this to work

http://somewebserver.com/topics/1

and these

http://somewebserver.com/topics/new

http://somewebserver.com/topics/1/edit


While it's probably better to make your slugs look like "1-learning-rails", since Rails 3 now allows the use of Rack applications to handle routes, so you could do something like this:

# lib/topic_slugger.rb
class TopicSlugger
  AD_KEY = "action_dispatch.request.path_parameters"

  def self.call(env)
    controller = (env["PATH_INFO"].split("/")[1].camelize+"Controller").constantize
    glob = env[AD_KEY][:glob]
    slug, action_name = nil
    if glob
      path_params = glob.split("/")
      if path_params.length == 1
        if ["new","edit"].include?(path_params.first)
          # no slug present
          action_name = path_params.first
        else
          slug = path_params.first
        end
      else
        action_name = path_params.first
        slug = path_params.last
      end
    end
    env[AD_KEY][:slug] = slug if slug
    action = if action_name # "new" or "edit"
               action_name.to_sym
             else
               case env["REQUEST_METHOD"]
               when "PUT" then :update
               when "DELETE" then :destroy
               else :show
               end
             end
    controller.action(action).call(env)
  end
end

# config/routes.rb
require 'topic_slugger'

Ztest::Application.routes.draw do
  # handle show, new, edit, update, destroy
  match 'topics/:id/*glob' => TopicSlugger
  # handle index, create
  resources :topics
end

This takes requests of the form "/topics/1/foo/bar" and passes them to the TopicSlugger Rack app, which decides whether the glob contains a combination action/slug (like "new/learning-rails"), or just a slug ("learning-rails"), adds the slug to the environment's request parameters, then passes the environment to the controller action, which itself is a Rack application. "index" and "create" are handled normally by the resources statement.

So for example "GET /topics/1/new/learning-rails" would be dispatched to TopicsController#new with a params hash of { :id => "1", :slug => "learning-rails, :glob => "new/learning-rails" }


Because the / is considered a path separator, Rails encourage you to separate the ID from the slug using a - which is more "neutral".

See this screencast.

Otherwise, you need to pass a custom :id validation and let Rails know that :id can include /, but it's not that simple.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜