All I'm trying to add is a button that, when pressed, increments an "accuracy" value by 1. I've looked at many, many, StackOverflow solutions but none of them have worked for me so far. I'm working with a colleague's code, and if it helps, most of it is taken from a Ruby on Rails textbook ("Learn Rails by Example") and may be hacked together.
my view looks like:
<%= link_to 'Accurate2', :action => :vote_up, :id => #post.id%>
my code looks like:
def vote_up
#in posts_controller.rb
#post = Post.find(params[:id])
#post.rate_it( 1, current_user.id ) #with "acts_as_rateable" plugin
#post.save
my routing looks like:
resources :users do
resources :comments
resources :posts
end
resources :posts do
resources :comments
match "vote_up", :on => :collection
match "vote_down", :on => :collection
end
the error I receive is:
http://localhost:3000/posts/vote_up?id=1
CanCan::AccessDenied in PostsController#vote_up
You are not authorized to access this page.
Parameters:
{"id"=>"1"}
You're using the CanCan gem which apparently doesn't give you the necessary authorization. Check this for details:
https://github.com/ryanb/cancan/wiki/defining-abilities
Also, as a sidenote, I would recommend changing these:
match "vote_up", :on => :collection
match "vote_down", :on => :collection
to:
member do
post "vote_up"
post "vote_down"
end
and making the appropriate changes in the view:
<%= button_to 'Vote up', {:action => :vote_up, :id => #post.id} %>
The reason is that actions like these shouldn't be done with GET requests since the user may accidentally submit them multiple times by refreshing the page etc.
Related
I have a controller for a resource, BuddiesController. My routes config file up until now has been
resources :buddies
match ':controller(/:action(/:id))', :via => [:get, :post]
I didn't realize what the ' resources :buddies ' line was doing until I read up on routing in Rails just now, because the behavior has been identical with what I expected until now. The problem was that I wanted to add a non-CRUD action to the controller: 'search'. Every time I used link_to(:action => 'search'), I would get an exception saying that action 'show' could not be found despite the url being ' localhost:3000/buddies/search ' as expected. I have several questions arising from this:
Firstly, the form I used in 'new' stopped working:
%= form_for(#buddy, {:action => :create, :method => :post, :html => {:role => "form"}}) do |f| %>
because buddies_path couldn't be found. How could I manually add a buddies_path to my routes?
Secondly, I revised the form to use:
<%= form_for(#buddy, :url => {:action => :create, :id => #buddy.id}, :html => {:role => "form", :id => #buddy.id}) do |f| %>
but this has caused the form to give me password and email confirmation not matching errors even if they match. What's going on here?
Lastly, what is the best way to add a search action to my resource?
#routes.rb
resources :buddies
collection do
get :search
end
end
now when you run rake routes | grep 'buddies' you will get output something like this :
now you need to define this search action in your buddies controller .
#buddies_controller.rb
Class BuddiesController < ApplicationController
def search
end
end
Have your search form in app/views/buddies/search.html.erb
Now in order to open your search form / to hit your search action you need to use
<%= link_to 'Search XYZ', search_buddies_path %>
against buddies#search you can see search_buddies
In routes.rb:
resources :buddies do
collection do
post :search
end
end
This might make your routing works.
I want to add another action to my controller, and I can't figure out how.
I found this on RailsCasts, and on most StackOverflow topics:
# routes.rb
resources :items, :collection => {:schedule => :post, :save_scheduling => :put}
# items_controller.rb
...
def schedule
end
def save_scheduling
end
# items index view:
<%= link_to 'Schedule', schedule_item_path(item) %>
But it gives me the error:
undefined method `schedule_item_path' for #<#<Class:0x6287b50>:0x62730c0>
Not sure where I should go from here.
A nicer way to write
resources :items, :collection => {:schedule => :post, :save_scheduling => :put}
is
resources :items do
collection do
post :schedule
put :save_scheduling
end
end
This is going to create URLs like
/items/schedule
/items/save_scheduling
Because you're passing an item into your schedule_... route method, you likely want member routes instead of collection routes.
resources :items do
member do
post :schedule
put :save_scheduling
end
end
This is going to create URLs like
/items/:id/schedule
/items/:id/save_scheduling
Now a route method schedule_item_path accepting an Item instance will be available. The final issue is, your link_to as it stands is going to generate a GET request, not a POST request as your route requires. You need to specify this as a :method option.
link_to("Title here", schedule_item_path(item), method: :post, ...)
Recommended Reading: http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
Ref Rails Routing from the Outside In
Following should work
resources :items do
collection do
post 'schedule'
put 'save_scheduling'
end
end
You can write routes.rb like this:
match "items/schedule" => "items#schedule", :via => :post, :as => :schedule_item
match "items/save_scheduling" => "items#save_scheduling", :via => :put, :as => :save_scheduling_item
And the link_to helper can not send post verb in Rails 3.
You can see the Rails Routing from the Outside In
Currently I have a route that looks like this:
resources :posts
I want to override the 'show' action so that I can display a url like this:
posts/:id/:slug
I am currently able to do this by adding a custom match route:
resources :posts
match 'posts/:id/:slug' => 'posts#show'
However, when I use the link_to helper, it does not use my custom show route.
<%= link_to 'show', post %> # renders /posts/123
How can I define my show route so that I can still use the link_to helper?
Update: As you can read in the following answers, you can override the route to the 'show' action, but it's probably more work than it's worth. It's easier to just create a custom route:
# config/routes.rb
match 'posts/:id/:slug' => 'posts#show', as: 'post_seo'
# app/views/posts/index.html.erb
<%= link_to post.title, post_seo_path(post.id, post.slug) %>
You have two routes which point to posts#show (you should be able to confirm this by running rake routes), and your link is using the wrong one.
When you call link_to('show', post) the URL of the link is generated by calling url_for(post) which (eventually, after passing through several other methods on the way) calls post_path(post). Since the route to posts#show that was created by your call to resources(:posts) is named post, that is the route that post_path generates.
You also currently have inconsistent routes for the show, update and destroy actions which will probably cause you problems later on.
You can fix this by changing your routes to the following:
resources :posts, :except => ['show', 'update', 'destroy']
get 'posts/:id/:slug' => 'posts#show', :as => 'post'
put 'posts/:id/:slug' => 'posts#update'
delete 'posts/:id/:slug' => 'posts#destroy'
Unfortunately you still can't use link_to('show', post) just yet, because it relies on being able to use post.to_param as the single argument needed to build a path to a post. Your custom route requires two arguments, an id and a slug. So now your link code will need to look like this:
link_to 'show', post_path(post.id, post.slug)
You can get around that problem by defining your own post_path and post_url helpers in app/helpers/posts_helper.rb:
module PostsHelper
def post_path(post, options={})
post_url(post, options.merge(:only_path => true))
end
def post_url(post, options={})
url_for(options.merge(:controller => 'posts', :action => 'show',
:id => post.id, :slug => post.slug))
end
end
Which means we're finally able to use:
link_to 'show', post
If that all seems like too much work, a common alternative is to use URLs that look more like posts/:id-:slug, in which case you can stick with the standard RESTful routes and just override the to_param method in your Post class:
def to_param
"#{id}-#{slug}"
end
You'll also need to do a little bit of work splitting up params[:id] into an ID and a slug before you can look up the relevant instance in your show, edit, update and destroy controller actions.
resources :posts, except: :show do
get ":slug" => :show, as: "", on: :member
end
and define helper
def post_path post
"/posts/#{post.id}/#{post.slug}"
end
db/migrate/add_slug_to_articles.rb
add_column :articles, :slug, :string
add_index :articles, :slug
models/article.rb
class Article < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :slugged
def should_generate_new_friendly_id?
new_record?
end
end
Or...
class Article < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :history
end
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid
https://github.com/norman/friendly_id
I need a bit of help with converting routing from Rails 2 to Rails 3.
In app/views/layouts/application.html.erb, I have:
<%= link_to "Reports", reports_path %><br>
There is a ReportsController, and in app/views/reports/index.html.erb, I have this:
<%= link_to "Clients With Animals", :action => "getAnimals", :controller => "clients" %>
Then, in config/routes.rb, I have this (Rails 3)
match '/reports' => "reports#index"
match '/clients/getAnimals', to: "clients#getAnimals"
I get this error when I click on the "getAnimals" link on the reports page:
ActiveRecord::RecordNotFound in ClientsController#show
Couldn't find Client with id=getAnimals
I don't want "getAnimals" to be the ID - I want it to be the action, instead.
How do I do that?
Assuming you also have a resources :clients entry, you want to make sure match '/clients/getAnimals', to: "clients#getAnimals" is above it (Rails will match whatever it hits first).
However, the better way may be to put it in the resource:
resources :clients do
get 'getAnimals', :on => :collection
end
I've got a very simple Rails 3 app with a User model run by Devise and a Notes model. Right now the url and routing looks like this:
# url
/users/MEMBERNAME/notes/TITLE-OF-NOTE
# routing
resources :users do
resources :notes
end
But I would like the urls to look like this, how would the routing look like in this case?
# url
/MEMBERNAME/TITLE-OF-NOTE
Update:
Thanks, now I discovered a new problem though. In my forms I have this code:
<%= form_for([#user, #note]) do |f| %>
and in my controller I redirect like this:
format.html { redirect_to([#user, #note], :notice => 'Note was successfully created.') }
In both those cases when I use #user, #note the old urls are still present. Do you know how to translate the form and the redirects to use the member/title structure?
Thanks in advance!
You can use a custom route here:
get "/:user_id/:id", :to => "notes#show", :as => :short_user_note
Hope this helps!
Update:
To use the newly created named route:
# => /USER_NAME/NOTE_NAME
redirect_to short_user_note_path(#user, #note)
# => /user/USER_NAME/note/NOTE_NAME
redirect_to user_note_path(#user, #note)
# OR
redirect_to url_for([#user, #note])
# OR
redirect_to [#user, #note]
So, the general rule is if you pass an array of active_record objects like below to #redirect_to, #url_for or #form_for methods, the #polymorphic_url method is called internally, and generates the standard RESTful route.
You can the routes behaviour you're after with working route helpers in Rails 3 with like the following:
resources :users, :path => ''
resources :users, :path => '', :only => [] do
resources :notes, :path => '', :except => [:index]
end
See my blog post for the details on how I arrived at this solution.