Replacing default routes for a resource in Rails 3 - ruby-on-rails-3

I have a resource defined like so:
resources :referrals, :except => [:show, :edit, :destroy]
and I'd like to replace (not just add a named route) a default route that Rails produces, specifically the one for the update action.
Here is my rake routes:
referrals GET /referrals(.:format) {:action=>"index", :controller=>"referrals"}
POST /referrals(.:format) {:action=>"create", :controller=>"referrals"}
new_referral GET /referrals/new(.:format) {:action=>"new", :controller=>"referrals"}
referral PUT /referrals/:id(.:format) {:action=>"update", :controller=>"referrals"}
share /share(.:format) {:controller=>"referrals", :action=>"new"}
special /special(.:format) {:controller=>"referrals", :action=>"index"}
thanks /thanks(.:format) {:controller=>"pages", :action=>"thanks"}
/:shortlink(.:format) {:controller=>"referrals", :action=>"update"}
/:linktext(.:format) {:controller=>"referrals", :action=>"update"}
root /(.:format) {:controller=>"pages", :action=>"home"}
I'd like either the
/:shortlink(.:format)
or
/:linktext(.:format)
to hit the update action, but not the
/referrals/:id(.:format)
This is to implement a form of non-password "security". When the PUT goes to the update action, I want certain things to happen, but I don't want to require authorization to do this, and I don't want to allow easy guessing of the url based on controller name and simple low-numbered ids.
How can I fully replace the default route given by rails?

resources :referrals, :except => [:show, :edit, :destroy, :update]
match "/whatever_you_want/:variable_here" => "referrals#updated", :as=> the_link, :via=> :put
then in your controller you will access the param with
params[:variable_here]
here the param is whatever you want to compare against in the db, and the path will be create like this:
the_link_path or the_link_url
the :via part will constrain the path per HTTP method so only put request will match
more info here
http://guides.rubyonrails.org/routing.html

Related

Devise routing, problems customising Devise routes

After customising Devise routes, I have some issues with the routing.
Currently setup (but failing):
/me/account loads Devise::Registration#edit form
/me/account/:what routes to account_controller#edit
My routes (shortcut):
devise_for :users do
...
end
devise_scope :user do
scope "/me/account" do
get "/" => "users/registrations#edit", :as => :my_account
get "/:what" => "accounts#edit", :as => :my_account_edit
end
end
resources :accounts, :only => [:edit, :update]
Rake routes output:
activate_account GET /reactivate(.:format) users#reactivate
my_account GET /me/account(.:format) users/registrations#edit
my_account_edit GET /me/account/:what(.:format) accounts#edit
cancel GET /me/account/cancel(.:format) users/registrations#cancel
DELETE /me/account(.:format) users/registrations#destroy
edit_account GET /accounts/:id/edit(.:format) accounts#edit
account PATCH /accounts/:id(.:format) accounts#update
PUT /accounts/:id(.:format) accounts#update
Account
Since /me/account is actually showing registrations#edit ( Devise ) and all the /me/account/helpme are custom form fields
This has issues:
No notices shown on /me/account on update or failure
On failure the form is not repopulated with earlier filled in form values
Its not updating the form
/me/account/helpme goes , on form submit , to /accounts/1 ( the current user id ) and throws error
No route matches {:action=>"edit", :controller=>"accounts", :id=>"1", :what=>nil} missing required keys: [:what]
These issues are totally driving me insane. Anyone can provide me some suggestions to fix (one or more ) of these routing issues?
About form submit error.
You need to override url in your form to submit to:
<%= form_for #resource, url: my_account_edit(what: params[:what]) do |f| %>
This should be done in your views or in devise-generated views.
If you didn't generated devise views, then, just run in terminal:
rails g devise:views
EDIT
And you should tell us how your form in views looks like, and how controller handles updates of custom fields.

Singular route serving parameter :object_id instead of :id

Say I have an object called invoice. In routes.rb I have
resources :invoices do
get "pay"
end
When I run rake routes, the route is generated as
invoice_pay GET /invoices/:invoice_id/pay(.:format) invoices#pay
and the parameter is :invoices_id instead of :id
If I use a match statement:
match "invoices/:id/pay" => "invoices#pay", :via => :get
I get:
GET /invoices/:id/pay(.:format) invoices#pay
It seems to me that the route should be pay_invoice_path(#invoice), however, I have not found suitable documentation on this. Any suggestions?
i think what you are trying to do is
resources :invoices do
get "pay", :on => :member
end
have a look at the guides: http://guides.rubyonrails.org/routing.html

Rails routes, kind of synonym routes

Statement
Imagine we have an inventory app, there it is a Movement model, it represent any movement like products purchase or products sale. So we have default REST routes.
movements GET /movements(.:format) {:action=>"index", :controller=>"movements"}
POST /movements(.:format) {:action=>"create", :controller=>"movements"}
new_movement GET /movements/new(.:format) {:action=>"new", :controller=>"movements"}
edit_movement GET /movements/:id/edit(.:format) {:action=>"edit", :controller=>"movements"}
movement GET /movements/:id(.:format) {:action=>"show", :controller=>"movements"}
PUT /movements/:id(.:format) {:action=>"update", :controller=>"movements"}
DELETE /movements/:id(.:format) {:action=>"destroy", :controller=>"movements"}
For mnemonic proposes, we want to have some descriptive routes, like:
new_purchase /purchase/new(.:format) {:controller=>"movements", :action=>"new_purchase"}
edit_purchase /purchase/:id/edit(.:format) {:controller=>"movements", :action=>"edit_purchase"}
If you can see purchase's are same model likemovement's, actually are processed by MovementsController, but there with have different flow and treatment, this specified by create_purchase instead of create.
Questions
How should I add restful routes for purchase's? Taking care of specify HTTP methods like GET, POST, PUT, DELETE, etc.
How should I write form_for tags? Using movement model we can write: <%= form_for(#movement) do |f| %> but how is to call purchase paths for create or update methods?
How should I specify validation rules for purchase's? I have specified some rules on Movement model, but they are not applied for purchase's when a form is submitted.
You could use some thing like this in your routes file
match '/purchase/new(.:format)' => 'movements#new_purchase' :via => :get
match '/purchase/:id/edit(.:format)' => 'movements#edit_purchase' :via => :post
and you can mention others like delete, put in :via
for more info look at this link
If you want to change create to create_purchases go to the movements controller and change the definition names.
for the second question you could do something like this
form_tag(:controller => "controller_name", :action => "action_name", :method => "get")
I don't think that you should be looking for a way to RESTfully build routes for a model that handles an object in two separate ways. That sounds to me like you're really looking for two objects. You can create a Purchase model that inherits its properties from a parent Movement model.
Alternatively, you could assume that they are the same object and handle them the same way with semantic paths that make sense. That said, what you're looking for may be a path alteration for the default RESTful routes of the Movement controller actions. Also, a typical rails convention is to use plural path routes, which makes semantic sense for index pages and identifying objects that are a subset of a table of objects in a database, so I'm using purchases instead of purchase.
resources :movements, :path => "/purchases"
will produce
movements GET /purchases(.:format) {:action=>"index", :controller=>"movements"}
POST /purchases(.:format) {:action=>"create", :controller=>"movements"}
new_movement GET /purchases/new(.:format) {:action=>"new", :controller=>"movements"}
edit_movement GET /purchases/:id/edit(.:format) {:action=>"edit", :controller=>"movements"}
movement GET /purchases/:id(.:format) {:action=>"show", :controller=>"movements"}
PUT /purchases/:id(.:format) {:action=>"update", :controller=>"movements"}
DELETE /purchases/:id(.:format) {:action=>"destroy", :controller=>"movements"}
Using this model, the form_for helper method can still be used just like you would normally. Assuming #movement is properly defined, the path with the purchases root will be called properly.

Managing users with Devise

I am attempting to manage users in my app that uses Devise for the authentication. I followed the steps here in order to create a UsersController to allow me to do so. However, when I attempt to sign out, it says that the path /d/users/sign_out cannot be found. Is there anything else I need to add to get this to work?
EDIT: My routes look like:
devise_for :users. :path_prefix => 'd'
resources :users do
# stuff here
end
When I run rake routes, it gives me, for the destroy_user_session_path:
destroy_user_session DELETE /d/users/sign_out(.:format) {:controller=>"devise/sessions", :action=>"destroy"}
When I remove the path_prefix part, it attempts to 'show' a user with the ID sign_out
link_to "sign_out", destroy_user_session_path, method: :delete
do you have the method :delete in your view?

Questions about rails3 routes

I'm upgrading my app to rails 3, and I am a bit confused about some of the routes. The resourceful ones are easy enough, but how can I set a generic rule for all actions in a specific controller. I tried something like this:
get 'custom/:action/' => {:controller => :custom}
But that didn't work. It seems the new format is "controller#action", but how can I specify the action to be variable?
Also, other than using named routes or resources, is it possible to do shorthand notation to name routes in a specific controller?
i.e. rather than:
get '/tasks', :controller => :home, :action => :tasks, :as => 'tasks_home'
get '/accounts', :controller => :home, :action => :accounts, :as => 'accounts_home'
is it possible to do something a little cleaner, like:
controller => :home do
get :tasks
get :accounts
end
And that would automatically created the named routes?
You can use action as a variable like this:
resource :custom do
match ':action'
end
This will generate
/custom/:action(.:format) customs#:action
custom POST /custom(.:format) customs#create
new_custom GET /custom/new(.:format) customs#new
edit_custom GET /custom/edit(.:format) customs#edit
GET /custom(.:format) customs#show
PUT /custom(.:format) customs#update
DELETE /custom(.:format) customs#destroy
So it will handle your action as a variable URL-s and will add some default CRUD actions as well.
Note that the controller name here is in plural. If you would like to use a route for a controller which name is in singular, use resources instead of resource.
The answer to the second question is almost identical to the first one, use resource:
resource :home do
get :tasks
get :accounts
end
generates:
tasks_home GET /home/tasks(.:format) homes#tasks
accounts_home GET /home/accounts(.:format) homes#accounts
home POST /home(.:format) homes#create
new_home GET /home/new(.:format) homes#new
edit_home GET /home/edit(.:format) homes#edit
GET /home(.:format) homes#show
PUT /home(.:format) homes#update
DELETE /home(.:format) homes#destroy
Note that the matched controller names are in plural again, because of the convention.
Looks like this is related to the persisted field being set to false on nested ActiveResource objects: https://github.com/rails/rails/pull/3107