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.
Related
I believe we are all familiar with the normal posts and comments models to be displayed. Now imagine those relations hold. I would like to add replies to my comments so we have routing as follows
resources :posts do
resources :comments do
resources :replies do
end
end
I tried many different ways of implementing this in my views but I had no luck! It just does not work when my actual controller is post and I want to access replies. I tried searching for this but did not find the suitable name for it. Is there any resource that have information about how to implement this system or a code snippet on how to make it work on n levels instead of just 2?
http://nithinbekal.com/posts/rails-shallow-nesting/
You can use shallow nesting instead.
resources :posts, shallow: true do
resources :comments do
resources :replies do
end
end
This way you only have a nested route when you need to create a nested object [new and create action] or see all the related objects [index view]. Look at the first three lines below to see what i mean. And then you have normal routes for the resources that dont need to know the relation.
comment_replies GET /comments/:comment_id/replies(.:format) replies#index
POST /comments/:comment_id/replies(.:format) replies#create
new_comment_reply GET /comments/:comment_id/replies/new(.:format) replies#new
edit_reply GET /replies/:id/edit(.:format) replies#edit
reply GET /replies/:id(.:format) replies#show
PATCH /replies/:id(.:format) replies#update
PUT /replies/:id(.:format) replies#update
DELETE /replies/:id(.:format) replies#destroy
post_comments GET /posts/:post_id/comments(.:format) comments#index
POST /posts/:post_id/comments(.:format) comments#create
new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new
edit_comment GET /comments/:id/edit(.:format) comments#edit
comment GET /comments/:id(.:format) comments#show
PATCH /comments/:id(.:format) comments#update
PUT /comments/:id(.:format) comments#update
DELETE /comments/:id(.:format) comments#destroy
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format)
Rule of thumb:
Resources should never be nested more than 1 level deep.
I suggest you to read this article by Jamis Buck. You probably want to do something like:
resources :posts do
resources :comments
end
resources :comments
resources :replies
end
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
Here is a portion of my routes file:
resource :vendor do post 'modify_account' end
Running rake routes will generate the following line
modify_account_vendor POST /vendor/modify_account(.:format) {:action=>"modify_account", :controller=>"vendors"}
vendor POST /vendor(.:format) {:action=>"create", :controller=>"vendors"}
new_vendor GET /vendor/new(.:format) {:action=>"new", :controller=>"vendors"}
edit_vendor GET /vendor/edit(.:format) {:action=>"edit", :controller=>"vendors"}
GET /vendor(.:format) {:action=>"show", :controller=>"vendors"}
PUT /vendor(.:format) {:action=>"update", :controller=>"vendors"}
DELETE /vendor(.:format) {:action=>"destroy", :controller=>"vendors"}
/vendor(.:format) {:action=>"account", :controller=>"vendors"}
I can load the form and verify the URL is correct in the form's action, but when I submit, the app throws an error:
No route matches "/vendor/modify_account"
Any suggestions as to what I can do to troubleshoot? Firebug verifies that the error page is of type POST.
I had a simular issue with a singleton resource, check that you are passing in the url to the form_tag, we use simple_form and have the following
<%= simple_form_for #enterprise_account, :url => enterprise_account_path do |f| -%>
As it turns out, I believe the issue was with my choice of "action verb" - I should have been using PUT instead of POST. This is my new routes file, and the only thing I had to change was the _url helper method names.
resource :vendor do put 'account', :action => 'modify_account' end
How do I create a link using link_to, that links to a method in my controller. The link I want to create is something like this:
/meetings/10/contacts/2/send_invite
The send_invite method is in the Contacts controller. I'm unsure what the next step as far as how to setup the routes file. I've tried nesting resources but no luck so far. Also, what is the link to specify in the link_to? I've tried various combinations such as send_invite_path, meeting_contact_send_invite_path, but everything throws an error.
My routes file looks like this:
resources :meetings do
resources :contacts, :only => [:send_invite], :as => :send_invite
end
But then when I try to call send_invite_path, it doesn't work.
expanding on austin's answer. This nesting in config/routes.rb
resources :meetings do
resources :contacts do
member do
post :send_invite
end
end
end
would give you these routes
send_invite_meeting_contact POST /meetings/:meeting_id/contacts/:id/send_invite(.:format) {:action=>"send_invite", :controller=>"contacts"}
meeting_contacts GET /meetings/:meeting_id/contacts(.:format) {:action=>"index", :controller=>"contacts"}
POST /meetings/:meeting_id/contacts(.:format) {:action=>"create", :controller=>"contacts"}
new_meeting_contact GET /meetings/:meeting_id/contacts/new(.:format) {:action=>"new", :controller=>"contacts"}
edit_meeting_contact GET /meetings/:meeting_id/contacts/:id/edit(.:format) {:action=>"edit", :controller=>"contacts"}
meeting_contact GET /meetings/:meeting_id/contacts/:id(.:format) {:action=>"show", :controller=>"contacts"}
PUT /meetings/:meeting_id/contacts/:id(.:format) {:action=>"update", :controller=>"contacts"}
DELETE /meetings/:meeting_id/contacts/:id(.:format) {:action=>"destroy", :controller=>"contacts"}
meetings GET /meetings(.:format) {:action=>"index", :controller=>"meetings"}
POST /meetings(.:format) {:action=>"create", :controller=>"meetings"}
new_meeting GET /meetings/new(.:format) {:action=>"new", :controller=>"meetings"}
edit_meeting GET /meetings/:id/edit(.:format) {:action=>"edit", :controller=>"meetings"}
meeting GET /meetings/:id(.:format) {:action=>"show", :controller=>"meetings"}
PUT /meetings/:id(.:format) {:action=>"update", :controller=>"meetings"}
DELETE /meetings/:id(.:format) {:action=>"destroy", :controller=>"meetings"}
You will need to define the route in your routes.rb file and have setup your routes to be nested. As far as the name goes you can define that in your routes.rb file as well by using :as => "some_name" that will allow you to use link_to some_name_path.
For more info on routes: http://guides.rubyonrails.org/routing.html
Specifically on nested resources: http://guides.rubyonrails.org/routing.html#nested-resources
I have model called Search and a resource named :search. I'd like to name my controller SearchController, rather than SearchesController. But when I initialize an instance of Search, Rails assumes its route has to be "/searches".
Is there anything I can do to stop this?
This should work:
resources :search, :as => :searches
Route urls start with /search, point to search controller and use default naming convention:
searches GET /search(.:format) {:controller=>"search", :action=>"index"}
POST /search(.:format) {:controller=>"search", :action=>"create"}
new_search GET /search/new(.:format) {:controller=>"search", :action=>"new"}
edit_search GET /search/:id/edit(.:format) {:controller=>"search", :action=>"edit"}
search GET /search/:id(.:format) {:controller=>"search", :action=>"show"}
PUT /search/:id(.:format) {:controller=>"search", :action=>"update"}
DELETE /search/:id(.:format) {:controller=>"search", :action=>"destroy"}
The reason for error is that when form has only access to model instance it tries to find a route helper based on pluralized model name. In this case it tried to use searches_path. Things should work if we keep the default route names and change only urls and controller.
Relevant documentation (under "Relying on named routes")