Rails 3 - Missing Index Paths? - ruby-on-rails

I'm having a pretty weird problem with one of my rails apps. I think I'm probably doing something really silly that I just haven't been able to identify. My problem is that, I seem to be missing about half of my index paths.
For example, if my controller is "foos" for a model foo, I'll have the:
foos POST /foos(.:format) {:action=>"create", :controller=>"foos"}
But no GET option which would usually be as:
foos GET /foos(.:format) {:action=>"index", :controller=>"foos"}
Below I'll show you my actually code, to help me recover my missing index routes.
routes.rb:
resource :announcements, :controller => "announcements" do
resources :comments
member do
post 'vote'
end
end
routes for the announcements part:
announcements POST /announcements(.:format) {:action=>"create", :controller=>"announcements"}
new_announcements GET /announcements/new(.:format) {:action=>"new", :controller=>"announcements"}
edit_announcements GET /announcements/edit(.:format) {:action=>"edit", :controller=>"announcements"}
GET /announcements(.:format) {:action=>"show", :controller=>"announcements"}
PUT /announcements(.:format) {:action=>"update", :controller=>"announcements"}
DELETE /announcements(.:format) {:action=>"destroy", :controller=>"announcements"}
As you can see there is no get / index. In my controller, I have the simply index method defined...
def index
#announcements = Announcement.all
respond_to do |format|
format.html
format.xml { render :xml => #announcements }
end
end
I really don't understand why I don't have this index path. It's happening on several other controllers as well. Any help would be appreciated.
Edit: In the console, app.announcements_path returns a method missing error, in addition to the others that have missing index paths.

This is because you're using the singularized version of resources (resource). There is no index action route generated for these. You should change this to be the pluralized version, and remove :controller from the line too.

Related

Ruby on Rails: link_to action, no route matches

I'm getting into Rails and trying to add a "vote" feature on a blog setup from here: http://guides.rubyonrails.org/getting_started.html
In app/controllers/posts_controller.rb I created this:
def incvotes
#post = Post.find(params[:id])
post.update_attributes(:upvotes => 1 )
format.html { redirect_to(#post, :notice => 'Vote counted.') }
format.xml { head :ok }
end
In app/views/posts/index.html.erb I created this:
<%= link_to 'Vote', :controller => "posts", :action => "incvotes", :id => post.id %>
But the link is giving the error
No route matches {:controller=>"posts", :action=>"incvotes", :id=>1}
I'm missing something here, but not sure what.
rake routes:
incvotes_post POST /posts/:id/incvotes(.:format) {:action=>"incvotes", :controller=>"posts"}
posts GET /posts(.:format) {:action=>"index", :controller=>"posts"}
POST /posts(.:format) {:action=>"create", :controller=>"posts"}
new_post GET /posts/new(.:format) {:action=>"new", :controller=>"posts"}
edit_post GET /posts/:id/edit(.:format) {:action=>"edit", :controller=>"posts"}
post GET /posts/:id(.:format) {:action=>"show", :controller=>"posts"}
PUT /posts/:id(.:format) {:action=>"update", :controller=>"posts"}
DELETE /posts/:id(.:format) {:action=>"destroy", :controller=>"posts"}
home_index GET /home/index(.:format) {:action=>"index", :controller=>"home"}
root /(.:format) {:action=>"index", :controller=>"home"}
try
= link_to "vote", incvotes_post_path(post), :method=>:post
and if that doesn't work, try changing the method to :put
My guess is that you probably do not have a definition in your routes file for the action you just defined in the controller. Both an action in the controller and an action in the routes file must be defined for Rails to generate urls correctly.
Your routes file probably has something like this:
resources :posts
But you want to add more than the standard actions generated by the resources keyword, so try something like this:
resources :posts do
member do
post 'incvotes'
end
end
This tells routes that you have another action in your posts controller called incvotes that accepts HTTP post requests as long as they are pointed at a member route with the correct action (/posts/14 is a member route, while /posts/ is a 'collection' route). So you will have a new route probably like /posts/14/incvotes that you can post a form to and everything should start working properly.
EDIT:
Actually I guess since you are just adding 1 to an attribute on a model, you don't need a POST action (which are normally associated with posting forms as with create and update). To send a post, you might need to change the HTML in the view to include a form and have it post to the correct url. So you can try that, or you can change your routes file to read get 'incvotes' instead of post 'incvotes'. Sorry for the confusion, hope that helps!
The incvotes_post route only accepts a HTTP POST, and a link always produces a HTTP GET.
Use a form with a button instead (or do a POST using AJAX).
Try using button_to instead link_to:
In your view:
<%= button_to 'Vote', incvotes_post_path(post) %>
In your config/routes.rb add the route to incvotes action as post:
resources :posts do
member do
post 'incvotes'
end
end
And in your controller, create the incvotes action:
def incvotes
# Something
redirect_to :posts
end

Rails 3 Engine Problem with Routes

I have an engine, with this routes file:
Rails.application.routes.draw do
resources :comments, :controller => 'opinio/comments'
end
When I run the rake routes task, I get the correct output
comments GET /comments(.:format) {:action=>"index", :controller=>"opinio/comments"}
POST /comments(.:format) {:action=>"create", :controller=>"opinio/comments"}
new_comment GET /comments/new(.:format) {:action=>"new", :controller=>"opinio/comments"}
edit_comment GET /comments/:id/edit(.:format) {:action=>"edit", :controller=>"opinio/comments"}
comment GET /comments/:id(.:format) {:action=>"show", :controller=>"opinio/comments"}
PUT /comments/:id(.:format) {:action=>"update", :controller=>"opinio/comments"}
DELETE /comments/:id(.:format) {:action=>"destroy", :controller=>"opinio/comments"}
My controller is pretty simple:
class Opinio::CommentsController < ApplicationController
include Opinio::Controllers::InternalHelpers
def index
resource.comments.page(params[:page])
end
def create
#comment = resource.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
flash[:notice] = I18n.translate('opinio.comment.sent', :default => "Comment sent successfully.")
else
flash[:error] = I18n.translate('opinio.comment.error', :default => "Error sending the comment.")
end
end
end
But when I try using any action that goes to the engine's controller I get the following error:
uninitialized constant Comment::CommentsController
I sincerely don't know where Rails is magically adding this Comment namespace on the controller, and I don't have a clue of how to solve this.
Wow, this deserves an answer so nobody ever do such stupidity like I did.
Basically, I added this to my engine's module:
mattr_accessor :name
##name = "Comment"
and internally, there is already a method name on every module, which I accidentally overrided, and causing all the errors. AS tried to load the missing constant, but when called for name inside my Opinio model, it got "Comment" instead of Opinio.
A reminder for myself and any others out there.
Don`t use obvious names and attributes without checking if they already exist first.

Rails: URL after validation fails when creating new records via form

Lets say I am creating a new Foo using a form and a standard Rails restful controller, which looks something like this:
class FoosController < ApplicationController
...
def index
#foos = Foo.all
end
def new
#foo = Foo.new
end
def create
#foo = Foo.create(params[:foo])
if #foo.save
redirect_to foos_path, :notice => 'Created a foo.'
else
render 'new'
end
end
...
end
So, if I use the standard restful controller (as above), then when I'm creating the Foo I am at example.com/foos/new, and if I submit the form and it saves correctly I'm at example.com/foos showing the index action. However, if the form is not filled correctly the form is rendered again and error messages are shown. This is all plain vanilla.
However, if errors are shown, the form page will be rendered but the URL will be example.com/foos, because the CREATE action posts to that url. However, one would expect to find Foos#index at example.com/foos, not the form they just submitted now with error messages added.
This seems to be Rails standard behavior, but it doesn't make a lot of sense to me. Obviously I could redirect back to new instead of rendering new from the create action, but the problem with that is the error messages etc. would be lost along with the partially complete Foos in memory.
Is there a clean solution for this problem, a way to send people back to example.com/foos/new when there are errors in the new Foo form they submitted?
Thanks!
To answer your comment on another answer:
I'm wondering if there's a way, without rewriting the controller at all, to tell rails that you want the URL to match the rendered template, rather than the controller action that called it.
I don't think so; URLs are tied directly to routing, which is tied into a controller and action pair--the rendering layer doesn't touch it at all.
To answer your original question, here's information from another similar question I answered.
As you've found, by default when you specify resources :things, the POST path for creating a new thing is at /things. Here's the output for rake routes:
things GET /things(.:format) {:action=>"index", :controller=>"things"}
POST /things(.:format) {:action=>"create", :controller=>"things"}
new_thing GET /things/new(.:format) {:action=>"new", :controller=>"things"}
edit_thing GET /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"}
thing GET /things/:id(.:format) {:action=>"show", :controller=>"things"}
PUT /things/:id(.:format) {:action=>"update", :controller=>"things"}
DELETE /things/:id(.:format) {:action=>"destroy", :controller=>"things"}
It sounds like you want something more like this:
create_things POST /things/new(.:format) {:action=>"create", :controller=>"things"}
things GET /things(.:format) {:action=>"index", :controller=>"things"}
new_thing GET /things/new(.:format) {:action=>"new", :controller=>"things"}
edit_thing GET /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"}
thing GET /things/:id(.:format) {:action=>"show", :controller=>"things"}
PUT /things/:id(.:format) {:action=>"update", :controller=>"things"}
DELETE /things/:id(.:format) {:action=>"destroy", :controller=>"things"}
Although not recommended, you can get this result with the following route:
resources :things, :except => [ :create ] do
post "create" => "things#create", :as => :create, :path => 'new', :on => :collection
end
You would also need to modify your forms to make them POST to the correct path.
You could hook into rails routing by adding this in an initializer:
https://gist.github.com/903411
Then just put the regular resources in your routes.rb:
resources :users
It should create the routes and behaviour you are looking for.
You can set up the routing manually, if you're that concerned about what URL is going to show. For what you want, you can have a GET to /foos/new render your form, and a POST to the same URL do the creation:
map.with_options :controller => :foos do |foo|
foo.new_foo '/foos/new', :conditions => {:method => :get}, :action => :new
foo.create_foo '/foos/new', :conditions => {:method => :post}, :action => :create
foo.foos '/foos', :conditions => {:method => :get}, :action => :index
end
This should work without requiring any changes to your controller (yay!) - all three actions from your example are taken care of. The few disclaimers:
This is based on my routing for a 2.3.8 app - some syntax (semantics?) changes are probably required to get it into Rails 3 routing style.
My attempts to mix this style of routing with map.resources have failed horribly - unless you're more familiar with this than me, or Rails 3 routing is better (both easily possible), you'll have to do this for every route to the controller.
And finally, don't forget to add /:id, (.:format), etc. to the routes that need them (none in this example, but see #2).
Hope this helps!
Edit: One last thing - you'll need to hard-code the URL in your form_for helper on /foos/new.html.erb. Just add :url => create_foo_path, so Rails doesn't try to post to /foos, which it will by default (there might be a way to change the creation URL in the model, but I don't know of it, if there is one).
You could use Rack::Flash to store the parameters you wanted in the user's session and then redirect to your form url.
def create
#foo = Foo.new(params[:foo])
if #foo.save
redirect_to foos_path, :notice => 'Created a foo.'
else
flash[:foo] = params[:foo]
flash[:errors] = #foo.errors
redirect_to new_foo_path #sorry - can't remember the Rails convention for this route
end
end
def new
# in your view, output the contents of flash[:foo]
#foo = Foo.new(flash[:foo])
end

Rails Resources are giving me headache

I am a .NET developer moving to Ruby on Rails. I have been programming in ASP.NET MVC and now trying to apply the same concepts to Rails. I created index action and now when I say home/index it automatically redirects me the "show" action which does not exists. My routes.rb file has this particular line:
resources :home
home is the home_controller.
What am I doing wrong?
class HomeController < ApplicationController
def show
end
# show all the articles
def index
#articles = Array.new[Article.new,Article.new,Article.new]
respond_to do |format|
format.html
format.xml { render :xml => #articles }
end
end
def new
#article = Article.new
respond_to do |format|
format.html
format.xml { render :xml => #article }
end
end
def create
#article = Article.new(params[:post]);
if #article.save
format.html { redirect_to(#post,
:notice => 'Post was successfully created.') }
end
end
def confirm
end
end
You can run "rake routes" to check out what rails thinks about your routes and which urls will be dispatched to which controllers.
In your case, I get:
home_index GET /home(.:format) {:action=>"index", :controller=>"home"}
home_index POST /home(.:format) {:action=>"create", :controller=>"home"}
new_home GET /home/new(.:format) {:action=>"new", :controller=>"home"}
edit_home GET /home/:id/edit(.:format) {:action=>"edit", :controller=>"home"}
home GET /home/:id(.:format) {:action=>"show", :controller=>"home"}
home PUT /home/:id(.:format) {:action=>"update", :controller=>"home"}
home DELETE /home/:id(.:format) {:action=>"destroy", :controller=>"home"}
So to get to the index action, you need to go to "/home". If you go to "/home/index", it will think "index" is the ID of the resource, thus dispatching to the show action.
However, in Rails, it's custom to use plural names for controllers, and naming them after the resource they represent (this is usually a model, but it doesn't have to be). So in your case the name of the controller should be "ArticlesController" and your routes.rb should contain "resources :articles". Rails is very anal about plural and singular names.
The big advantage of using the plural name of the resource you're accessing, is that you can now use short notations, like "redirect_to #article", "form_for #article do |f|", etc.
So, resources in Rails are supposed to be telling about what you want your actually getting. This helps maintenance too, since other developers have to guess less. If you find yourself needing more than one ArticlesController, consider using namespaces, or try to figure out if one of those controllers are actually another resource (even though they store their data in the same database table).
More information about the routers can be found in the Rails Guide: http://guides.rubyonrails.org/routing.html

ActiveRecord::RecordNotFound in MembersController#show

I am sorry for my bad english first.
I just installed ruby and rails few hours ago (you wouldn't believe it took me 3 days to install ruby,rvm,rails and etc, on this ubuntu 10.04 machine) and I am trying to implement basic Member scaffold. My version of rails is 3.0.0 and my ruby is 1.9.2.
When I #rails generate scaffold Member email:string password:string it created various files. I also did #rake db:migrate to implement database in mysql.
So within member controller, I saw that I have to go through 127.0.0.1:3000/members/ to get to the basic scaffold setup.
I just changed
def new
#member = Member.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #member }
end
end
above statements in member controller into
def register
#member = Member.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #member }
end
end
U see, I just changed the new into register, and now, when I try to get into
127.0.0.1:3000/members/register
The ActiveRecord::RecordNotFound error shows up.
How can I resolve this problem?
I just want to make
127.0.0.1:3000/members/register
to be a page where user can register..
btw, this RoR seems to be very complicated, and api documents seems to be too broad to be understood for beginners. I ordered a RoR book last week, so I will see how it goes...
By using the scaffold generator members gets mapped as a resource. Look in the config/routes.rb
resources :members
When entities are mapped as resources they get a set of default routes. You can see all your mapped routes by doing rake routes
members GET /members(.:format) {:action=>"index", :controller=>" members"}
members POST /members(.:format) {:action=>"create", :controller=> "members"}
new_member GET /members/new(.:format) {:action=>"new", :controller=>"members"}
edit_member GET /members/:id/edit(.:format) {:action=>"edit", :controller=>"members"}
member GET /members/:id(.:format) {:action=>"show", :controller=>"members"}
member PUT /members/:id(.:format) {:action=>"update", :controller=>"members"}
member DELETE /members/:id(.:format) {:action=>"destroy", :controller=>"members"}
When you rename the new action to register there no longer is a valid route for that mapping.
What you could do is to leave the action as new and just add the following route in your routes.rb
match 'members/register' => 'members#new'
This way you do not break other things in the scaffold. If you really want to rename the action to register I would suggest not using scaffolds.
You need to add 'register' method to routes, like:-
map.connect '/members/register', :controller => 'members', :action => 'register'.
After adding the above to routes.rb restart the server.
Thanks, Anubhaw
I had the very same problem when creating a new html.erb.
Even my routes.rb match 'controller/action' => 'controller#action' were correct.
Later I found that the problem was that the resources :controller were above the match.
This is the correct order that worked for me:
match 'controller/action' => 'controller#action' resources :controller
Thanks to pavlo for asking this question, and to maz because his answer gave me the hint that the resources were involved in the error.

Resources