I asked this question on Code Review but they suggested I ask it here
I'm trying to create custom routes for every web page in my show action.
Movie.all.each do |movie|
match "/#{movie.title.downcase}", to: 'movies#show', via: 'get',
as: :"#{movie.title.downcase.gsub(/[:.]/, '').gsub(' ', '_')}"
end
This code matches every web page to my not found page.
resources :movies, except: [:show]
It should match the web page to the actual page.
I only have a show.html.erb file in my views folder. Could that be the problem?
Is it possible to use string interpolation on view file names?
I can't use Rails REST routing because I need to have better URLS.
/Movie Title
is a better URL then
/movies/1
That is why I need to optimize this code. It works, but it could use improvement.
I have no idea how to fix this. Here are a couple things I thought of.
Using Rails route redirection to another action
Using wildcard as view names (*.html.erb)
Remember: My only goal is to change the URL name to the movie's title
This can be easily achieved using FriendlyId gem
Here's the link:
https://github.com/norman/friendly_id/blob/master/README.md
Add it to your Gemfile
Then add following to your model:
class Movie < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: :slugged
Then run this on rails console:
Movie.find_each(&:save)
To save slugs for existing movies
The documentation is quite good, feel free to comment if you have any trouble.
Related
I'm trying to write a simple CMS in rails that will allow text urls rather than the standard /posts/show/<id>.
I have a model that has a title and body, I would like to replace spaces in the title and use it as the url.
I also want to have the url at the root level, not past /pages/show.
Take a look at this gem friendly_id, is used for create url from fields of the model storing it on database. Otherwise, you can create the route that you want example get 'pages/show', home#index and pointing wherever you want.
While the friendly_id gem is really nice, it's quite heavy for such a simple need. You might consider a lighter alternative approach : https://github.com/johnotander/urls_for_humans
You can override your rails urls by setting them like this:
get 'posts' => 'posts#index'
In your routes.
But since you want to format the url for the show action (which depends on a specific id for each item in posts) your best bet is to use the awesome friendly_id gem.
See: https://github.com/norman/friendly_id
Some resources:
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid
http://blog.teamtreehouse.com/creating-vanity-urls-in-rails
I have figured out how to do what I wanted to do.
I am setting a route to the root url with a parameter like this (config/routes.rb):
get ':title', to: 'pages#show', as: :page
Now I have a pages controller that was generated by rails generate scaffold Page title:string body:text
which has a function set_page which I changed to do this (app/controllers/pages_controller.rb):
def set_page
#page = Page.find_by("title" => params[:title])
end
I have these models:
Category(:name)
Post(:category_id, :title, :body)
The category model only has a handful amount of categories, Currently my possible routes are:
/tasks/15015
/categories/5/posts/5641841
I'd like to have a friendly url in this format, but i can't figure out a way to not go completely wildcard and mess up the other routes:
/politics/518-obama-health-care
As i said, i tried this but i'm looking for something less wildcard and more integrated with the current categories if possible:
match '(:category_name)/(:id)' => 'posts#show'
Any other ideas are welcome!
Just put it last, so it doesn't mess with any of your other routes. Then put a before_filter (only for show) in your PostsController that checks to make sure that :category_name is valid and if it's not raise an ActionController::RoutingError
I'm attempting to create "pretty URLs" for linking to posts in a blog. I want to maintain access to the blog entries via entries/23 and 2011/07/some-post-slug-here as I only generate a slug once an entry has been published (just in case the title of the posts changes, and, though not a strict requirement, I would prefer to be able to edit/delete posts via the entries/23 style URL. Currently, the appropriate part of what I have in my config/routes.rb:
root :to => 'entries#index'
resources :entries
match ':year/:month/:slug' => 'entries#show', :constraints => {
:year => /[0-9][0-9][0-9][0-9]/,
:month => /[0-9][0-9]/,
:slug => /[a-zA-Z0-9\-]+/
}, :as => :vanity_entry
and I use this (in my application helper) function for creating the links:
def appropriate_entry_path entry
if entry.published
vanity_entry_path entry.published_on.year.to_s, entry.published_on.month.to_s, entry.slug
else
entries_path entry
end
end
def appropriate_entry_url entry
if entry.published
vanity_entry_url entry.published_on.year.to_s, entry.published_on.month.to_s, entry.slug
else
entries_url entry
end
end
Basically, I check if the article is published (and therefore has a slug) and then use that URL/path helper, or otherwise use the default one.
However, when trying to use this, I get the following from Rails:
No route matches {:slug=>"try-this-one-on-for", :month=>"7", :controller=>"entries", :year=>"2011", :action=>"show"}
I have tried a few different solutions, including overriding to_param in my Entry model, however then I would have to create match routes for the edit/delete actions, and I would like to keep my routes.rb file as clean as possible. Ideally, I would also love to lose the appropriate_entry_path/appropriate_entry_url helper methods, but I'm not sure that this is possible?
Is there any thing I am missing regarding routing that might make this easier and/or is there any specific way of doing this that is the cleanest?
Thanks in advance.
You might want to take a look at friendly_id. It's a gem for creating seo friendly slugs :)
I found the issue with what I had been doing, the regex for :month in the route wanted two numbers, whereas I was only passing in one number. Anyways, I decided that the URLs look nicer (in my opinion) without the month padded to 2 digits, so I updated my route accordingly.
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.
I have my index page which posts a single entry instead of the usual scaffold default of all of the entries. I told it to link to an action and it just responds to "Couldn't find Post with ID=all". It is the same as the default index method and index view. I assume this has something to do with routing but being no I have no clue. Any ideas?
The "all" name is misleading. If you want a page to display all the posts then the index page is perfect for that. If you want to show a subset of the posts then I recommend adding another action to your controller with a better name and then this to the routes.rb file:
map.resources :posts, :collection => { :some => :get }
Which you then can reference by using some_posts_path or some_posts_url.
For more information read the Official Ruby on Rails Routing guide.