How to add a custom action to the controller in Rails 3 - ruby-on-rails

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

Related

Routing and form in Rails

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.

url action is not working in my rails

I made a salaries controller inside the folder employee.
In my routes:
namespace :employee do
resources :salaries
end
Now in my salaries controller I added a new method action_list:
class Employee::SalariesController < ApplicationController
def action_list
end
end
From view inside index I want to call action_list like:
<%= form_for :form, :url => {:action => 'action_list'}, :method => :post,
:html => {:id => 'form1', :onsubmit => "return checkCheckBoxes();"} do |f| %>
When I submit the form I get the following error:
No route matches [POST] "/employee/salaries/action_list"
What could be the problem? It works fine for other controllers without using a namespace.
What am I doing wrong?
have you added action_list onto your routes
namespace :employee do
resources :salaries do
post :action_list, :on => :collection
end
end
Add a route for the action_list action:
namespace :employee do
resources :salaries do
post 'action_list'
end
end
Read more about adding restful routes here.

Ruby on Rails: How to override the 'show' route of a resource?

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

Routing error on Ruby on Rails 3 voting system

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.

How to add a custom RESTful route to a Rails app?

I'm reading these two pages
resources
Adding more RESTful actions
The Rails Guides page shows
map.resources :photos, :new => { :upload => :post }
And its corresponding URL
/photos/upload
This looks wonderful.
My routes.rb shows this
map.resources :users, :new => { :signup => :get, :register => :post }
When I do: [~/my_app]$ rake routes
I see the two new routes added
signup_new_user GET /users/new/signup(.:format)
register_new_user POST /users/new/register(.:format)
Note the inclusion of /new! I don't want that. I just want /users/signup and /users/register (as described in the Rails Routing Guide).
Any help?
When you expose a controller as a resource, following actions are automatically added:
show
index
new
create
edit
update
destroy
These actions can be categorized in to two groups:
:member actions
The URL for the member action has the id of the target resource. E.g:
users/1/edit
users/1
You can think of :member action as an instance method on a class. It always applies on an existing resource.
Default member actions: show, edit, update, destroy
:collection actions
The URL for the :collection action does not contain the id of the target resource. E.g:
users/login
users/register
You can think of :collection action as a static method on a class.
Default collection actions: index, new, create
In your case you need two new actions for registration. These actions belong to :collection type( as you do not have the id of the user while submitting these actions). Your route can be as follows:
map.resources :users, :collection => { :signup => :get, :register => :post }
The URL for the actions are as follows:
users/signup
users/register
If you want to remove a standard action generated by Rails use :except/:only options:
map.resources :foo, :only => :show
map.resources :foo, :except => [:destroy, :show]
Edit 1
I usually treat the confirmation action as a :member action. In this case params[id] will contain the confirmation code.
Route configuration:
map.resources :users, :member => { :confirm => :get}
URL
/users/xab3454a/confirm
confirm_user_path(:id => #user.confirmation_code) # returns the URL above
Controller
class UsersController < ApplicationController
def confirm
# assuming you have an attribute called `confirmation_code` in `users` table
# and you have added a uniq index on the column!!
if User.find_by_confirmation_code(params[id])
# success
else
# error
end
end
end
This can be taken as just another syntax -- something good to know may be.
Syntax 1:
resources :users do
member do
get 'signup'
post 'register'
end
end
Rake Route Output will include
signup_users GET /users/signup(.:format) {:action=>"signup", :controller=>"users"}
register_users POST /users/register(.:format) {:action=>"register", :controller=>"use
rs"}
Syntax 2:
If you have only one collection route
resources :users do
get 'signup', :on => :collection
end
If i'm understanding your question right, you just want to rename the urls of the new and create actions.
This would be done like so:
map.resources :users, :path_names => {:new => 'signup', :create => 'register'}
If you really would like to add new routes with corresponding controller actions, then Damiens answer is the way to go.
The new option allows you to create new routes for creating new objects. That's why they're prefixed with that term.
What you're looking for is the :collection option.
map.resources :users, :collection => { :signup => :get, :register => :post }
Which will create the /users/signup and /users/register urls.

Resources