ruby-on-railsrails-routinghigh-voltage

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


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.


Solution

  • 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'