Trying to create a POST request but getting No route matches [GET] - ruby-on-rails-3

I'm trying to do something similar to Railscasts #255 but I'm getting a No Route error:
In Ryan's routes.rb file:
post "versions/:id/revert" => "versions#revert", :as => "revert_version"
In in the controller where he uses the route, versions_controller.rb
link = view_context.link_to(link_name, revert_version_path(#version.next, :redo => !params[:redo]), :method => :post)
redirect_to :back, :notice => "Undid #{#version.event}. #{link}"
In my routes.rb
post "/approve/:id" => "listings#approve", :as => "listing_approve"
and view where I use my link:
<%= link_to 'Approve Content', listing_approve_path(#listing), :method => :post %>
My tests return to me a ActionController::RoutingError: No route matches [GET] "/approve/1"
If I leave the method as a GET everything works.. Using rails 3.1.0rc5. Any guidance as to what I'm doing wrong here would be very much appreciated..
EDIT: routes.rb file (the last line is set as match right now to work)
RLR::Application.routes.draw do
root :to => "home#index"
devise_for :users, :controllers => { :registrations => "registrations" }
devise_for :users
match '/user' => "layouts#index", :as => :user_root
resources :users, :only => :show
resources :layouts, :only => [:index, :show]
resources :listings
resources :features
resources :orders
match "/preview/:id" => "listings#preview", :as => "listing_preview", :via => "get"
match "/approve/:id" => "listings#approve", :as => "listing_approve"
end

Hmmmm, it looks right to my eye. The test sounds like it is generating a GET instead of a POST though, so it might be a problem with the link_to call. You've got :method => :post there, so it should be fine. http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to seems to indicate that link_to will generate some javascript to make a POST call on click (and that users with javascript disabled will get a normal GET link unless you use :href="#"), so it might be because your test engine isn't running the javascript.
You can fix this by changing it to a button that submits a hidden form, but that might not be the visual representation you want.

It might be a precedence thing - the first matching route definition in routes.rb is used, so if you have a resources route or something like that it may be matching on that first.

I got the same problem in my rails application and I solved it the same way you did by doing a via: :get on the match instead of a via: :post. I think for some reason when you send a request in the format of /something/:id it will automatically assume its a [GET] request and search for a get route. This of course will cause problems in your routes if you have it as a :POST.
If anyone has a better solution or idea as to why you cannot send a post request in the format '/something/:id' let me know please.

Related

Devise, can't log out

In a Rails app, I use devise to manage my users and my link to destroy a session no longer work. It was working, and now I have add active admin, It doesn't.
My link is
<%= link_to "Déconnexion", destroy_user_session_path, :method => :delete, :class => 'button' %>
My routes.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks"}
My rake routes
destroy_user_session DELETE /users/sign_out(.:format)
And it try to open the view /users/sign_out, so I have :
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User with id=sign_out
Does Active_admin and Devise crash together?
It will be weird 'cause active use devise, no?
Edit:
For the next person who will have this issue, I solved it by adding the next line to /config/initializers/devise.rb.
config.sign_out_via = :get
Not exactly the finest way, but it does the job.
Posting Jeff Paquette's comment as an answer.
Update the config/initializers/active_admin.rb with:
config.logout_link_method = :delete
Please make changes in your routes.rb :-
devise_scope :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks"} do
get "/users/sign_out", :to => "devise/sessions#destroy"
end
I am also getting same problem, only this can resolve me after 1hr time wasting.
Thanks.

Update users details without a password by admin. Rails 3.x Devise

I would like to create a link to approve a users signup,
1) First solution<%= link_to "Approve", edit_user_registration_path(:id => user.id, :approved => true), :method => :put %>
However it drops back No route matches [PUT] "/users/edit"
2) Also I was thinking about an extra action that will respond to a specific route and update the user signup, say match '/users/approve_user', :controller => 'users', :action => 'approve_user', :collection => { :my_action => :put}
and in the view:
%- link_to "Approve", users_approve_user_path(:id => user.id), :method => :put
However, it gives back that Couldn't find User with ID=approve_user
Any help will be appreciated
I think you should use custom routing with devise
This is direct from devise documentation (https://github.com/plataformatec/devise), configuring routes
devise_scope :user do
get "sign_in", :to => "devise/sessions#new"
end
so in your case try something like
devise_scope :user do
post "Approve", :to => "users/approve_user"
end
and please note, by default device users controller comes under the name space 'devise'.

rails 3 absolute link to generated url

I am looking into generating urls which look like https://mywebsite.com/user/check_email_confirmation/46cee41bc2044104aff8cf7746687ea8
for the famous "please click this link to confirm your email".
So I have a route which I intend to use for this in my routes.rb:
resources :user do
collection do
get 'check_email_confirmation/:generated_url', :to => 'user#check_email_confirmation'
end
end
Now, my trouble is to generate the full address above. I cah generate the SHA (46cee41bc2044104aff8cf7746687ea8) part, but how do I write the "https://mywebsite.com/user/check_email_confirmation/" part in my view?
Added view's code:
<p>Please click <%= url_for(:action => 'check_email_confirmation', :controller => 'user', :only_path => false, :id => #confirmation_url) %> to confirm your e-mail address.</p>
Replace id: with generated_url:.
You could also simplify the url_foring with naming the route by as: in routes
get 'check_email_confirmation/:generated_url', to: 'user#check_email_confirmation', as: :confirmation
More on this at http://viget.com/extend/rails-named-routes-path-vs-url
get "check_email_confirmation/:id" => "users#check_email_confirmation", :as => "check_email_confirmation"

Routing with id and handle in Rails

I'm trying to set up rails to use both the ID and the Handle (which is just an URL safe version of the title) of a blog post in the route.
match '/articles/:id/:handle', :to => 'articles#show'
resources :articles
This works, of course -- but I can't seem to set up the to_param method in the model os the longer URL -- with the handle attached, is the default.
This doesn't work (not that I really expected it to):
def to_param
"#{id}/#{handle}"
end
I get a No route matches {:action=>"edit", :controller=>"articles", error. I also tried just using the handle, but then Rails generates links to the resource just using the handle and not the ID. I know I can do it with a - in stead of a /, but I prefer the /. Any way to make this work? If I have to add some extra paremeters to my link_to helpers, that's okay.
Did you try to pass a Hash to link_to?
link_to "Link", {:id => #article.id, :handle => #article.handle}
Update
You have to modify your routes:
match '/articles/:id/:handle', :to => 'articles#show', :as => :article_with_handle
and use the following helper to generate the link:
link_to "Link", article_with_handle_path(:id => #article.id, :handle => #article.handle)
You can override the helper to simplify things:
def article_with_handle_path(article)
super(:id => article.id, :handle => article.handle)
end
and use it like this:
link_to "Link", article_with_handle_path(#article)
Okay, here's what I did to remove the query string problem from the answer above:
Changed the route to this:
match '/articles/:id/:handle' => 'articles#show', :as => :handle
Removed the to_param method from the model and then generated the link like this:
link_to 'Show', handle_path(:handle => article.handle, :id => article.id) %>
That works, but could be condensed, obviously, with the helper above. Just change the one line to: args[1] = handle_path(:id => args[1].id, :handle => args[1].handle)

Rails 3 routing

Ok, I'm relatively new to rails and am working through the book Rails for PHP programmers. To make things interesting I'm using Rails 3 and haml to work through the book, and the book is written using Rails 2.X and erb so some of the examples are outdated.
To the question:
The route example in the book is
map.presentation 'meetings/:meeting_id/presentations/:action/:id',
:controller => "presentations",
:action => "show",
:meeting_id => /\d+/
So that was a pre-Rails 3 route. My adaptation of the above into a Rails 3 route is:
match 'meetings/:meeting_id/presentations/:action/:id', :to => 'presentations#show', :constraints => {:id => /\d/}
My adaptation works for the destroy action, but not for the edit action... and due to my inexperience I don't know what I'm doing wrong. From the article here (http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/) it looks like I'm doing it right, but something isn't right.
The link_to helpers that produce the urls are as follows
= link_to 'edit', :controller => 'presentations',
:action => 'edit',
:meeting_id => presentation.meeting.id,
:id => presentation.id
# Incorrectly Produces: http://localhost:3000/presentations/edit?meeting_id=2&id=1
= link_to 'destroy', { :controller => 'presentations',
:action => 'destroy',
:meeting_id => presentation.meeting.id,
:id => presentation.id },
:confirm => 'Are you sure?',
:method => :delete
# Correctly Produces: http://localhost:3000/meetings/2/presentations/destroy/1
Your help would go a long way to clearing up my routing haziness. Thanks so much!
EDIT
Here's the non-working routes.rb file in its entirety:
UserGroup::Application.routes.draw do
get "presentations/new"
get "presentations/edit"
get "sessions/new"
get "users/index"
get "users/show"
get "users/new"
get "users/edit"
get "meetings/index"
match '/meetings/show/:id', :to => 'meetings#show', :as => 'meeting'
match '/meetings/new', :to => 'meetings#new', :as => 'new_meeting'
match '/meetings/edit/:id', :to => 'meetings#edit', :as => 'edit_meeting'
match 'meetings/:meeting_id/presentations/:action/:id', :to => 'presentations', :constraints => {:id => /\d/}
#default route
match ':controller(/:action(/:id(.:format)))'
end
EDIT 2
Thanks to codykrieger I took a look at the rest of the routes file (I know, duh right?). Apparently, all those get "..." routes are added when using the rails generator and help to make some default connections in the app. I commented out the get "presentations/edit" line and, wonder of wonders, my routing adaptation actually does work.
This works:
UserGroup::Application.routes.draw do
get "presentations/new"
#get "presentations/edit"
get "sessions/new"
get "users/index"
get "users/show"
get "users/new"
get "users/edit"
get "meetings/index"
match '/meetings/show/:id', :to => 'meetings#show', :as => 'meeting'
match '/meetings/new', :to => 'meetings#new', :as => 'new_meeting'
match '/meetings/edit/:id', :to => 'meetings#edit', :as => 'edit_meeting'
match 'meetings/:meeting_id/presentations/:action/:id', :to => 'presentations', :constraints => {:id => /\d/}
#default route
match ':controller(/:action(/:id(.:format)))'
end
I played around with the order of the routes in the file and also found that if I put my route above all the auto-generated routes, without commenting out the get "presentations/edit" line, my route still has the intended effect.
This also works:
UserGroup::Application.routes.draw do
match 'meetings/:meeting_id/presentations/:action/:id', :to => 'presentations', :constraints => {:id => /\d/}
get "presentations/new"
get "presentations/edit"
get "sessions/new"
get "users/index"
get "users/show"
get "users/new"
get "users/edit"
get "meetings/index"
match '/meetings/show/:id', :to => 'meetings#show', :as => 'meeting'
match '/meetings/new', :to => 'meetings#new', :as => 'new_meeting'
match '/meetings/edit/:id', :to => 'meetings#edit', :as => 'edit_meeting'
#default route
match ':controller(/:action(/:id(.:format)))'
end
I'm thinking that the latter is the better way to go and I should make my custom routing declarations above the standard generated ones, but I'm not sure. If someone out there wants to comment on which practice is better, or if it's better to entirely remove the generated ones if I want to specify my own, I'd love to hear it.
Thanks all :)
Routing is done on a first match basis (top to bottom) in the routes.rb file.
So you can keep all routes as you have done above if you want, and Rails will use the first one that matches. However, its messy, potentially buggy and totally unnecessary. So the "match..." lines in your code should be enough, and you can remove the "get..." lines.
Furthermore, your "match...presentations...." line has a bug, it should be
...{:id => /\d+/}
else it will validate only 1-digit length ids.
Finally, as far as your overall routes.rb is concerned, you do not need to do
link_to 'edit', :controller => 'presentations',
:action => 'edit',
:meeting_id => presentation.meeting.id,
:id => presentation.id
Instead, you should use edit_meeting_path, new_meeting_path etc. For instance
<%= link_to "New meeting", new_meeting_path %>
<%= link_to "Edit meeting", edit_meeting_path(#meeting) %>
Finally, you should read Rails Routing from the Outside In for best practices on how to design nested routes etc.