1

I'm trying to implement semi-static pages as per this railscast

At first I named my class 'About', but that threw the following error:

Invalid route name, already in use: 'page' (ArgumentError) You may have defined two routes with the same name using the :as option, or you may be overriding a route already defined by a resource with the same naming.

After some googleing, it seemed like it was conflicted with active_admin for some reason, so I rename the table to 'Page' and I've carefully renamed all the appropriate files, classes and methods etc. from 'About' to 'Page'

This is my Page model:

    class Page < ActiveRecord::Base

      validates_uniqueness_of :url

      def to_param
        url
      end   

    end

And these are my routes:

  get 'signup', to: 'users#new', as: 'signup'
  get 'login', to: 'sessions#new', as: 'login'
  get 'logout', to: 'sessions#destroy', as: 'logout'

  devise_for :admin_users, ActiveAdmin::Devise.config
  ActiveAdmin.routes(self)
  resources :users 
  resources :sessions
  resources :password_resets
  resources :posts do 
    resources :comments
    resources :votes, only: [:new, :create]
    resources :flags, only: [:new, :create]
  end
  resources :comments do 
    resources :comments
    resources :votes, only: [:new, :create]
    resources :flags, only: [:new, :create]
  end
  resources :newsletters
  resources :pages, except: :show
  resources :subscribers, only: [:index, :new, :create] 
  # resources :prelaunch
  # get 'about', to: 'prelaunch#about'
  root to: 'posts#index' 
  get ':id', to: 'pages#show', as: :page  

I'm still getting the same error as described above.

The only way I can get it to half-work is by dropping the 'as: :page' bit, which stops the conflict, and hardcoding the url I want to point to into the code e.g.

<%= link_to page.name, "localhost:3000/#{page.url}" %>

which is far from ideal.

I can't find any help in Routing from the Outside In.

Could anyone help?

jfdimark
  • 2,239
  • 1
  • 14
  • 27
  • 1
    Why do you have `resources :pages, except: :show` and also `get ':id', to: 'pages#show', as: :page`. The second one mostly would 'reintroduce' the show action you omitted in the first definition. (also `rake routes` may help you to see what exactly is defined) – thorsten müller Feb 28 '14 at 11:13
  • I'm following the railscast, and this how it's set up. I assumed is was to avoid the exact conflict I'm having. – jfdimark Feb 28 '14 at 11:41

3 Answers3

1

Here's the fix:

#config/routes.rb
resources :pages, except: :show

(remove get ':id', to: 'pages#show', as: :page)

This will create the standard RESTful routes, which will create a routing structure except the show action


Slugs

How to create app-wide slug routing for Rails app?

If you want to have /about etc, you'll have to generate them specifically:

#config/routes.rb
if Page.all.any?
    Page.all.each do |page|
       get page, to: "pages#show", id: page.id
    end
end

This can also be handled with friendly_id

Community
  • 1
  • 1
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • Thanks. I don't want the standard routing structure for the show action though. I don't want app.com/pages/[:id], I just want app.com/[:id] - which should call the pages#show action. Your solution seems to only allow for /pages/[:id]. – jfdimark Feb 28 '14 at 12:30
  • Nope - my solution creates a set of new routes for each `Page` you have. If you don't want the page show route, just use `except: :show` as you have already – Richard Peck Feb 28 '14 at 12:37
  • I was having issues getting your solution to work, but it's helped me on the way to a fully working answer. Thanks. – jfdimark Feb 28 '14 at 12:56
  • No problem! What did you do to get it working properly? – Richard Peck Feb 28 '14 at 12:56
  • I've created a separate answer to show what the finished code looks like. – jfdimark Feb 28 '14 at 14:49
  • Nice work! Might be worth giving my answer an upvote because it lead you to the end result? :p – Richard Peck Feb 28 '14 at 14:51
1

Ok, after a lot of hacking around and a helpful pointer from Rich Peck, I've got a working solution.

Routes:

  resources :pages, except: :show
  if Page.all.any?
    Page.all.each do |page|
       get "#{page.url}", to: "pages#show", as: "#{page.url}", id: page.id
      end
  end

Controller:

  def show
    @page = Page.find(params[:id])
  end

Note, I've used the friendly_id gem as suggested.

To dynamically generate links:

Application controller:

  def about_us
    @pages = Page.all
  end
  helper_method :about_us

Pages helper:

def about_link(page)
    link_to page.name, "/#{page.url}"
end

NB: - you need to include the / otherwise it will try to prepend the name of the controller for the page you're on (I'm not sure why).

My footer:

<% about_us.each do | page | %>
<%= about_link(page) %>
<% end %>

UPDATE:

I've had a lot of trouble deploying my app to Heroku, and I believe it's because of the pages routes.

I've now changed to a much simpler solution:

  resources :pages, path: ""

and the problem's have gone away.

jfdimark
  • 2,239
  • 1
  • 14
  • 27
1

Have you considered using a gem that "does the work for you"? I've been using the https://github.com/thoughtbot/high_voltage gem to take care of static pages for me, without any hassle. It takes care of both routing and controller, only leaving the creation of the pages in a dedicated view/pages folder. Linking to a static page is as simple as creating a link to page_path(:name_of_the_page)

Danny
  • 5,945
  • 4
  • 32
  • 52
  • I considered it, but I'm trying to keep away from gems and any external dependencies where possible, unless i've used it before to good effect. Plus it's such a simple exercise I didn't anticipate the conflict that arose. It's solved now though, if you can see my answer? – jfdimark Feb 28 '14 at 14:49
  • I take a different approach, looking at the number of downloads for a particular gem on the ruby toolbox website before considering using it. High voltage has 250000 downloads, which for me kind of 'proves' its stability. I never create non-value added code myself... – Danny Feb 28 '14 at 14:57