What's the best way to handle nested static pages in rails? - ruby-on-rails

EDIT: Sorry, the title is a little unclear, I wanted to use 'semi-static' pages, using the render helper and ruby vars. The ERB templating system etc. Sorry guys! My fault!
I've been looking into creating nested semi-static pages for a rails 3.1.3 app I'm building, and I've yet to find an answer that would suit all my needs. Any ideas?
All of the solutions I've come across are about creating just top level pages, like so:
- Site root
--- About (http://wwww.example.com/about)
--- Contact (http://wwww.example.com/contact)
--- Products (http://wwww.example.com/products)
--- Pricing (http://wwww.example.com/pricing
Whereas I'm looking to do something like
- Site root
--- About (http://wwww.example.com/about)
------ What we do (http://wwww.example.com/about/what-we-do)
------ Another sub-page (http://wwww.example.com/about/another-sub-page)
--- Contact (http://wwww.example.com/contact)
--- Products (http://wwww.example.com/products)
------ Product One (http://wwww.example.com/products/product-one)
------ Product Two (http://wwww.example.com/products/product-two)
--- Pricing (http://wwww.example.com/pricing)
I've come across solutions like mapping static controllers for each of the pages, but that doesn't seem like a particularly elegant solution.
Or creating a generic route and controller to match requests, like so:
in routes.rb:
map.connect '*path', :controller => 'content', :action => 'show'
in content_controller.rb:
def show
render :action => params[:path].join('/')
end
But that seems even more inelegant, is there another way I'm missing?

is there another way I'm missing?
Yes.
All you have to do is create static pages as you require in /public, either in the root of public, or in a directory structure.
A physical file existing at a path under /public should override any routes you configure to dynamically generated pages.

What DanSingerman said, but also... Just put your static pages on a separate fqdn, possibly hosted on a cdn. The only reason to have rails serve static assets is that you're being lazy and just can't be bothered doing it the right way.

I faced a similar problem for static sub pages, came up with the following solution.
In routes.rb
match 'about/(:page)', :to => 'about#show', as: 'about'
match 'about/what-we-do', :to => 'about#show', as: 'what_we_do'
In about_controller.rb
class AboutController < ApplicationController
def show
unless params[:page].blank?
render "about/#{params[:page].underscore}"
end
end
end
In your views you can reference the alias paths.
<%= link_to 'About', about_path %>
<%= link_to 'What We Do', what_we_do_path %>
So /about will default to rendering the view about.html.erb.
But /about/what-we-do will render the view about/what_we_do.html.erb.
Would something like this help solve your problem?

That is in fact what most CMS's do. If you feel that you are doing a lot of heavy lifting try plugging in a CMS like Refinery to your app. It makes life a little simpler by taking care of some of the SEO aspects. If you are interested in how Refinery CMS handles its pages, have a look at the Pages Controller and the related routes and the magic match all route.

Related

Ruby on Rails: How to link/route from one view/page to another view/page with a different controller

I have a view template within the following file in my Rails application:
app/views/static_pages/mission.html.erb
I want to link to that page from a view template in a different folder (corresponding to a different page in the app):
app/views/home/home.html.erb
I don't know what to write in my mission method in the StaticPagesController. I'm also a newbie at Rails. I also don't know how to write the route for it and I suspect I may need to write a get 'something' in my routes.rb file.
Can anybody help with this?
What you are asking can be accomplished in this way:
In your routes.rb:
get "/path/to/your/mission/page", to: "static_pages#mission", as: "mission"
Then in your static_pages_controller.rb:
class StaticPagesController < ApplicationController
# You can leave the mission method blank, it will render
# the corresponding static_pages/mission.html.erb by default
def mission
end
end
This should set up the static page, so that when you visit: localhost:3000/path/to/your/mission/page, you should be able to see the mission.html.erb page rendered.
To link to the mission page from any other template, you can simply point a link to it, like so:
<%= link_to "Mission Page", mission_path %>
Or, since it's a static page, you can just hardcode the path into the markup:
Mission Page
Since you're new to Rails (welcome to the world of Rails btw :D), I hope you find these really great resources useful:
Official Rails Guides
Rails For Zombies
Railscasts by Ryan Bates
Hope this was helpful!

Rails controller and routes configuration for website

I have an application in RAILS, it is composed of a set of API, a basic website, and an admin dashboard.
For the API routing I have no problems, as they belong to a model and a controller and are compliant with the RAILS RESTful pattern (a controller for each model, and a method for each HTTP method).
What I'm not comfortable with is writing routes and controllers for website.
The main website is at / so the default route is root :to => "home#index"and I have
Routes for the website pages which look like
get "home/index"
get "map/index"
get "api/index"
get "cgu/index"
get "legal/index"
Which I think it is not good, as I have a controller per view and I need to define a get for each views.
Now for the dashboard I tried a different approach.
It is at /dashboard, the default route is match "dashboard" => "dashboard#index" and here is few pages as an examples
get "dashboard/index"
get "dashboard/users"
get "dashboard/users_stats"
get "dashboard/routes"
get "dashboard/routes_stats"
get "dashboard/charts"
get "dashboard/financial"
So for the dashboard I have a massive dashboard_controller, which contains a def method for each dashboard pages. IE:
#dashboard/users
def users
#users = User.all
respond_to do |format|
format.html {render :layout => 'dashboard'}
end
end
the controller of the dashboard is at /controller but for views and assets I have put it in /folder/dashboard/
Here is 2 questions:
What is the best way to build the home website and dashboard ? Should I have a controller per page or a global controller where I have a method per pages ? (Which I find very convenient, less code).
How should I organize my routes to avoid to set a get "something/something" for each page ?
Or it is normal with RAILS that I have a route defined for each of my page ? I'm fairly new.
EDIT:
To clarify, the dashboard is built around an existing application with API that follow RESTFul Rails pattern:
resources :users
resources :routes
But the dashboard is not tied to any existing resources, it only do stats about those resources.
If you have custom controller action names, then yes, you'll need to define every route. If you use Restful routes, then you can define them easily as
resources :users
which will automatically create routes for actions: index, show, edit, update, create and destroy.
This might be helpful: http://guides.rubyonrails.org/routing.html
For your dashboard, which probably is bringing together a lot of resources, so they'll probably be custom methods. I'd suggest focusing on building your app by individual resource. Then, once you've defined them all, build your dashboard.
I agree with everything that other people have said here. You should definitely try to be more RESTful and create more routes like this:
resources :users
However, there is usually a controller that is not RESTful (usually called pages or static) that serves pages such as Privacy, About Us, etc, etc. For those routes, I usually do this:
['api', 'privacy', 'us'].each do |p|
get p, :controller => 'pages', :action => p
end
check this guide out if you haven't;
I think you haven't embraced the MVC concept of Rails.
For example, say you have "users". You should have users_controller.rb, users.rb (model), /views/users (view) under the app directory.
users_controller contains the index, show, create, etc default actions and your custom actions like stats
user.rb contains instance/static/helper methods
/views/users/ contains templates that corresponds to actions in the controller.

Set Up Route for Accessing Private S3 Content

I've been following
https://github.com/thoughtbot/paperclip/wiki/Restricting-Access-to-Objects-Stored-on-Amazon-S3
and
Rails 3, paperclip + S3 - Howto Store for an Instance and Protect Access to try and get Paperclip's expiring links to work. I believe most of what I'm running into is one of the routing variety.
In my pieces_controller I put a method in like this
def download
redirect_to #asset.asset.expiring_url(1000)
end
And then in my routes, I put this:
match "pieces/download"
Then in my view I have:
<%= link_to download_asset_path(piece)%>
It would seem to be far from working, and I'm not sure what is messed up. I know I'm getting routing errors for one, but it's also telling me that my download_asset_path is undefined, which is likely also routing related... I feel like I'm doing everything all wrong.
Tearing my hair out. Thanks!
Try modifying your routes file to:
match 'pieces/download' => 'pieces#download', :as => 'download_asset'
Your match needs to tell which controller#action to go to, and the as option will allow you to name the route download_asset_path.
If your pieces controller is for a Piece resource it could be cleaner like:
resources :pieces do
member do
get :download
end
end
But then you would want to change the link to:
link_to 'Link text', download_piece_path(piece)
For further reading: http://guides.rubyonrails.org/routing.html

Static pages in Ruby on Rails

What are the standard way of making a Ruby on Rails application that will have pages such as
Home
About
Contact
I would appricate if someone had links or an answers rather than just say use a gem because I want to learn how to make simple webapps with such behavior.
Depends on how you want to handle the content in those pages.
Approach #1 - store content in views
If you just want to put all your content in ERB views, then a very simple approach is to create a PagesController whose purpose is to deal with static pages. Each page is represented by one action in the controller.
pages_controller.rb:
class PagesController < ApplicationController
def home
end
def about
end
def contact
end
end
routes.rb:
match '/home' => 'pages#home'
match '/about' => 'pages#about'
match '/contact' => 'pages#contact'
Then create home.html.erb, about.html.erb, and contact.html.erb views under app/views/pages. These views contain whatever content you want on your static pages. They'll by default use your app's application.html.erb layout.
You'll also want to look into page caching to give yourself a boost in performance.
Approach #2 - store content in database
Another approach I've used is to make a very basic CMS for static pages. In this case, pages are represented in the model. It uses the friendly_id gem to handle slugs for each page so that they can be retrieved by a pretty name in the URL (e.g., /about) rather than by ID.
page.rb:
class Page < ActiveRecord::Base
attr_accessible :title, :content
validates_presence_of :title, :content
has_friendly_id :title, :use_slug => true, :approximate_ascii => true
end
pages_controller.rb:
class PagesController < ApplicationController
def show
#page = Page.find(params[:id])
render 'shared/404', :status => 404 if #page.nil?
end
end
show.html.erb:
<%= raw #page.content %>
routes.rb:
match '/:id' => 'pages#show'
Note: put this entry at the end of routes.rb since it matches everything.
Then how you want to create, edit and update pages are up to you - you can have an admin interface, or build it in to your public interface somehow. This approach can benefit from page caching too.
Another option is the high_voltage gem: https://github.com/thoughtbot/high_voltage
This makes it super easy to create static pages where the content is stored in views.
Jeff's approach #1 (storing content in views and having a route and controller action for each static page) is a good one. The only thing I would add is to use the controller macro in your routes.
So, instead of this:
match '/home' => 'pages#home'
match '/about' => 'pages#about'
match '/contact' => 'pages#contact'
You can do this:
controller :pages do
get :home
get :about
get :contact
end
It's two extra lines, yet much so much more elegant, since it eliminates repetition and groups your static page routes together visually.
It also uses the get http verb method instead of match, which is a better practice for Rails routes (and more concise, now that Rails 4 requires the http verb to be specified when using match.
Jeff's Approach #1 works great for me. Here is a trick to make the controller dynamically look up pages. With this, you don't need to touch the controller nor the routes.rb for adding pages. Just drop the pages under app/views/pages and the controller will find it.
class PagesController < ApplicationController
def show
render params[:id]
end
end
Check out Michael Hartl's http://railstutorial.org
which comes in a 2.3.8 and 3.0.x version. It covers this with great examples and leads you through building them very early on and you will also have the opportunity to learn a lot more than this example.
I highly recommend it.
config/routes.rb
get ':id', to: 'pages#show'
app/controllers/pages_controller.rb
class PagesController < ApplicationController
def show
begin
render params[:id]
rescue ActionView::MissingTemplate
render :file => "#{Rails.root}/public/404", :layout => false, :status => :not_found
end
end
end
Then place your static pages in app/views/pages/{name}.html.erb (or whatever template format.)
An adequate answer to your question would basically look like an introduction to the Rails framework: the MVC structure, templating, and routing DSL at least. Jeff has given a good stab, but his answer still assumes a lot of basic Rails knowledge on your part.
I'd suggest though, that if your webapp is really that simple, Rails might be overkill. I'd look into something lighter, like Sinatra, which has a much lower learning curve than Rails and does a great job of this kind of thing without having to deal with complex routing, magic MVC action/template mapping, etc.
I'd suggest adding your pages in the public folder so as to be served directly without having to pass through rails at all. I'm not an expert though so I'm not sure if this could have any cons if the page is static.
For more you can create static pages using Jekyll bootstrap or also Jekyll using Danger blog
Refer it is very helpful.

Customizing map.resources in Rails

Suppose I have a Book model, which contains many Page models.
The routing for this would be as so:
map.resources :books do |book|
book.resources :pages
end
Following the Rails default on this quickly leads to problems. Suppose Book #1 has 10 pages. The first Page in Book #2 will have this route:
/books/2/pages/11
This is a pretty bad route, what would make more sense is this:
/books/2/pages/1
Or even this:
/books/2/1
Is there a way to still use map.resources, but get a result like this:
/books/{book.id}/pages/{page.page_number}
No. You have to use custom routing for that.
Feel free to get inspiration from http://github.com/augustl/kii/blob/master/config/routes.rb
As August says you need to use custom routing for that.
But for the pages, you don't need the full resources routes. Only show will be necessary.
So something like :
map.resources :books do |book|
book.page ':page_id', :action => 'index'
end
Will map the default books url for displaying the index, one book and adding/editing them.
But also a page
/books/{book.id}/{page_id}
Which maps to the index action with the parameter "page_id". You only have to display the appropriate books page ;)
You could also try the shallow-option for your routing!

Resources