Route Controller#show method like how Controller#index would in Rails

2.6k views Asked by At

Hi guys I am new to rails. Sorry if I can't define this question properly.

What I wanted is for:

domain.com/posts/1-sample-post

to be routed like this:

domain.com/1-sample-post

How do I achieve this in rails routes? I've tried searching for this for almost 3 hours. This is very easy in PHP frameworks. I thought this is easy in Rails too.

I forgot to mention I have High_voltage gem installed in my app for my static pages.

Did this:

#routes.rb
resources :posts
get '/:id' => 'posts#show'

Now my High_voltage pages could not be rendered.

Update Solution:

So here is what we did in the routes:

Rails.application.routes.draw do
  resources :authors
  constraints(lambda { |req| Author.exists?(slug: req.params["id"]) }) do
     get '/:id' => 'authors#show'
  end

  devise_for :users

  resources :posts
  constraints(lambda { |req| Post.exists?(slug: req.params["id"]) }) do
    get '/:id' => 'posts#show'
  end
end

Note that it is important to only use an exists? query here as it is very fast than other methods, so it won't eat that much loading time to render a record.

Special thanks to the guys below who helped a lot. Nathanvda, rwold, and Tai.

2

There are 2 answers

6
nathanvda On BEST ANSWER

So the other answer correctly suggested something like

get '/:id', to: 'posts#show'

But this is a catch-all route and if there are no other routes defined this will catch all routes, also your HighVoltage, if it is configured to serve pages on root. You now have two catch-alls: one to find a static page and one to find a post.

Best solution in this case, imho is to make the static pages explicit (since I am assuming there will not be that many?)

get '/about' => 'high_voltage/pages#show', id: 'about'
get '/:id' => 'posts#show'

If you have a lot of pages, it seems easiest to just present the high-voltage on a different route? E.g. something like

get '/pages/:id' => 'high_voltage/pages#show' 
get '/:id' => 'posts#show' 

In both of these cases, since we use explicit routing, you would have to disable the default routing in the high-voltage initializer:

# config/initializers/high_voltage.rb
HighVoltage.configure do |config|
  config.routes = false
end

[UPDATE: add special controller to consider both posts and pages]

Add a HomeController like this:

class HomeController < ApplicationController

  # include the HighVoltage behaviour --which we will partly overwrite 
  include HighVoltage::StaticPage    

  def show
    # try to find a post first 
    @post = Post.where(id: params[:id).first 
    if @post.present? 
      render 'posts/show'
    else 
      # just do the high-voltage thing
      render(
        template: current_page,
        locals: { current_page: current_page },
      )
    end 
  end 
end 

Of course I did not test this code, but I think this should get you started. Instead of doing the rendering of the post, you could also redirect to the posts-controller which is maybe easier (and you will use the PostsController fully) but adds a redirect and will change the url.

In your routing you will then have to write

get '/:id', 'home#show'   
6
rwold On

In your routes.rb file:

get '/:id-sample-post', to: 'posts#show', as: :sample_post

assuming that posts is your controller and show is the action that calls the view for your article with the given id.

EDIT AFTER OP COMMENT: The as: :sample_post clause should create a helper sample_post_path that can be invoked as <%= link_to "Show", sample_post %>.