Routing trouble on Rails 3 (related to singular/plural) - ruby-on-rails

I've read around the resources on how routing in Rails 3 works, but am running into some difficulties.
In my app there are Blogs and my routes.rb contains:
resources :blogs
root :to => "home#index"
URLs containing 'blogs' are work fine.
However what I'd like to do is have 'blog' in the URLs. Specifically, /blog/:id (for show) and /blog (for index).
If I add the line:
match 'blog' => 'blogs#index'
Then /blog does show the index, however it breaks my blog edit form as the action URL changes from /blog/:id to /blog.:id
Any ideas on how to use blog instead of blogs, and also allow the blog index to be on /blog? I've tried quite a few things (like resource :blog, and also resources :blogs, :as => 'blog') and not getting anywhere. Assuming there's a way to do this without manually defining each route for show, edit, destroy and index.

You need use the :path option
resources :blogs, :path => 'blog'

Remove your match line, and change your resouces line to:
resources :blogs, :path => 'blog'
Check: http://edgeguides.rubyonrails.org/routing.html#translated-paths

Related

Resources vs namespace in Rails routes

What is the difference between resources and namespace?
I have a Rack app inside a gem that I want to call from the Rails app.
namespace :app do
get 'go', to: Gem::Controller.new
end
Since I have controller called AppController, can I use this one?
resources :app do
collection do
get 'go', to: Gem::Controller.new
end
end
Which way is better?
As per the Rails guide routing section
Resources:
Resource routing allows you to quickly declare all of the common
routes for a given resourceful controller. Instead of declaring
separate routes for your index, show, new, edit, create, update and
destroy actions, a resourceful route declares them in a single line of
code.
Namespace:
You may wish to organize groups of controllers under a namespace. Most
commonly, you might group a number of administrative controllers under
an Admin:: namespace. You would place these controllers under the
app/controllers/admin directory, and you can group them together in
your router.
Eg:
namespace :admin do
resources :articles, :comments
end
But, I think what you meant was to choose between collection and namespacing.
It's like this, namespacing would be a better option if you are planning to have more routes for that app. Else, you can just use it as a collection.
resources is a shortcut for generating seven routes needed for a REST interface.
so resources :app would generate the following seven routes(patch and put routes are same):
get "apps" => "apps#index", :as => 'apps'
get "apps/:id" => "apps#show", :as => 'app'
get "apps/new" => "apps#new", :as => 'new_app'
post "apps" => "apps#create", :as => 'apps'
get "apps/:id/edit" => "apps#edit", :as => 'edit_app'
patch "apps/:id" => "apps#update", :as => 'app'
put "apps/:id" => "apps#update", :as => 'app'
delete "apps/:id" => "apps#destroy", :as => 'app'
and then it would generate another route because of get 'go', to: Gem::Controller.new:
/apps/go
In case of namespace the apps seven REST routes won't be created but a named route for apps/go would be generated.

Rails Routing -- Adding Custom Route

I'm almost certain that someone has asked this question before, but I can't seem to hit on the right series of words to find it.
I have a resource, Games, with all of the normal resource-y paths. Create, Edit, etc.
I've created a new action within GamesController called json that I want to be able to access at mydomain.com/games/json but the routing keeps picking up 'json' as the ID and routing it to the 'show' action instead.
Presumably this is because of the default route:
match ':controller(/:action(/:id))'
I've tried a number of things, but no matter what I do it keeps routing to 'show.' I've been attempting to figure it out using this guide, but for someone that's pretty new to rails its quite a bit to take in and apply.
I'd like to say that for any controller /json would take you to the json action (instead of show with id 'json'), but I'd settle for having to specify it for every controller individually.
Any help is greatly appreciated. (Even if that's just pointing me to the already answered question.) In all cases I've been placing the route I'm attempting to create above the default route.
My routes:
root :to => 'home#index'
resources :events, :players, :sets, :matches, :characters, :videos, :games, :event_instances, :game_instances
resource :user_session
resource :account, :controller => "users"
resources :users
match '/login', :to => 'user_sessions#new', :as => 'login'
match '/logout', :to => 'user_sessions#destroy', :as => 'logout'
match '/register', :to => 'users#create', :as => 'register'
match '/games/json', :to => 'games#json', :as => 'gameList'
match ':controller(/:action(/:id))'
Ok so what you need to do is put your custom json route above your other resources routes and make it default like so:
get '/:controller/json(/:id)', action: 'json'
resources :events, :players, :sets, :matches, :characters, :videos, :games, :event_instances, :game_instances
...
Your confusion was linked to the way the routes are built and urls are parsed
Think of your routes.rb file as a set of consecutive filters to be applied to the url requested by the client.
If you put a default route above another it will catch all request that match the given pattern => this means that when you were adding all your resources routes above your json route, all urls matching the /#{resources}/:id pattern (and /games/json is one of them) were caught before they could reach the json custom route.
By putting your custom route above the others, you make it catch any request matching the pattern, that means in that case all requests with
/#{controller_name or resource_name)}/json(/:id)
NB:
I am not quite sure this is the best design pattern to use for your routes and I'd rather go with conventional REST routes with basic CRUD actions that would implement the respond_to pattern to answer specific json requests.
See docs for this: http://apidock.com/rails/ActionController/MimeResponds/InstanceMethods/respond_to
Adding such 'catch all' routes on top of all your resources may become risky when your app grows.
But you may have your reasons to go this way, in that case, I think my answer matches what you need.
resources :games do
collection do
get :json
end
end
if you also wanted to add an action that would take an :id path segment and return, say, json representation of a specific game, you'd do that with
resources :games do
collection do
get :json
end
member do
get :json
end
end
I'd add that when I was brand new to Rails (less than 1.5 years ago), the routing dsl was really hard for me to get my head around for some reason. But it's worth studying that guide, despite the fact that it's a bit overwhelming at first. Otherwise you'll end up with a bunch of match '/route/to/somewhere' => 'controller#action', :as => :some_helper, which ends up being a pain to read & maintain and will make it harder to have a consistent "grammar" for your urls.
Edit: looking at your now-posted routes file, the problem is that you declare the games/json route after the resources :games declaration. Route-matching precedence proceeds from the top of the file down, so the resourceful route for games matches first, and the specific route declared later is never checked. If you use the syntax above to make your json route a part of the resource declaration, this won't happen.

Rails custom route not working

Feel like I'm doing this right, but apparently not.
I have a restful resource, Posts, with index, show, new, update, edit, etc actions in the controller. In routes, I have
resources :posts
I wanted to make the index action occur at the URL '/archive' instead of '/posts'
So I added this line in the routes.rb file, after the resources one:
match '/archive', to: "posts#index"
But when I click on a link to posts_path, it still goes to /post (though if I type in /archive as a url, it works -- not ideal, though). Confused. Could this have to do with my having installed friendly_id?
resources :posts, except: [:index]
get 'archive' => 'posts#index', as: :posts
You need to use something like match '/archive', :to => 'posts#index', :as => 'archived'. Then you will have a new route to the tune of archived_posts_path. The method posts_path does not dynamically changed based on custom matchers. You can always run rake routes to see a list of routes for your site.

Rails 3: How do I route a controllers index and show actions to root while sending new/edit/destroy to /admin?

First off, I'm using rails 3.0.8 with friendly_id 3.2.1.1.
I'd like to be able to view the posts at website.com/:title, so drop the "/posts".
But I'd also like to have an /admin view. From there, a user should be able to create/edit/destroy posts. I already have a admin.html.erb view with links to various actions.
Right now my routes file look like:
MyApp::Application.routes.draw do
root :to => 'posts#index'
resources :posts
match "/:id" => "posts#show"
match "/admin" => "posts#admin"
end
This works for website.com/:title, but for website.com/admin I get an error:
Couldn't find Post with ID=admin
.... which makes sense. But I'm not sure how to solve this problem.
The rules are run through top to bottom. So put your admin rule on top of the resource definition.
If you put /admin first then it will work (as noted by cellcortex). You can also use :constraints if you can neatly separate your :id from 'admin'; for example, if your :id values are numeric, then something like this should work:
match '/:id' => 'posts#show', :constraints => { :id => /\d+/ }
match '/admin' => 'posts#admin'
In a simple case like yours, putting things in the right order will work fine. However, if your routing is more complicated, then the :constraints approach might work better and avoid a some confusion and chaos.
Use this
resource :posts, :path => '/'
with this all of your article will be directly under root
So in Posts Class you may add this:
def to_param
"#{id}-#{title.parameterize}"
end

Difference between :as option in Rails 2 and Rails3 routing?

In Rails 2.X we have:
map.resources :posts, :controller => 'posts', :as => 'articles'
This essentially creates an alias for our posts routes. For example, this sends "domain.com/articles/" to the posts controller index action.
In Rails3, however, the :as option behaves differently. For example:
resources :posts, :controller => 'posts', :as => 'articles'
sets a named route rather than an alias, and going to "domain.com/articles/" gives an error:
No route matches {:controller=>"posts"}
How do I get the old (Rails 2) :as behavior using the new (Rails 3) api?
PS: Please don't tell me to simply rename my controller. That's not an option for me.
From some cursory reading of the RoR guide on routing, I think you might need to try:
resources :articles, :controller => "posts"
(http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use)
You might also need to add :as => "articles", but that named helper might already be set up since you are adding :articles resources.
You can accomplish this same behavior using the path option:
resources :posts, :path => '/articles/'
Now for example /posts/new becomes /articles/new.

Resources