Rails - DRY'ing up new/edit pages in nested resources - ruby-on-rails

My new and edit pages for multiple levels of nested resources all worked fine when I had a flat resource structure. Since nesting resources for the purpose of making a more logical structure, these pages have been a bit broken.
I have a single form template for each model which starts as this:
<%= simple_form_for #contact, html: {:class => "well form-vertical"} do |f| %>
This works perfectly for the non-nested resources (such as Contact, as above), allowing create and update actions to work as expected.
With the nested resources however (such as Service, as below), the new action stops working. When I browse to the 'new' page, I get the error:
Error 500: undefined method `services_path' for #<#<Class:0x0b3512b4>:0xb42b2c58>
My routes.rb for the relevant section is as follows:
resources :contacts, shallow: true, :except => [ :destroy ] do
resources :accounts, shallow: true, :except => [ :destroy ] do
resources :services, :except => [ :destroy ]
end
end
The controller actions for new and edit for contacts and services are:
Contact:
def new
#contact = Contact.new
...
def edit
#contact = Contact.find(params[:id])
Service:
def new
#service = Service.new(account_id: params[:account_id])
...
def edit
#service = Service.find(params[:id])
The relevant output from rake routes is:
account_services GET /accounts/:account_id/services(.:format) services#index
POST /accounts/:account_id/services(.:format) services#create
new_account_service GET /accounts/:account_id/services/new(.:format) services#new
edit_service GET /services/:id/edit(.:format) services#edit
service GET /services/:id(.:format) services#show
PUT /services/:id(.:format) services#update
contacts GET /contacts(.:format) contacts#index
POST /contacts(.:format) contacts#create
new_contact GET /contacts/new(.:format) contacts#new
edit_contact GET /contacts/:id/edit(.:format) contacts#edit
contact GET /contacts/:id(.:format) contacts#show
PUT /contacts/:id(.:format) contacts#update

<%= simple_form_for(#contact, :html => {:class => "well form-vertical"}) do |f| %> Use instead of
<%= simple_form_for #contact, html: {:class => "well form-vertical"} do |f| %>

As it turns out, this was entirely the wrong approach to be taking to this problem. I instead rewrote my create routes to POST straight to /:controller and it then worked with just using simple_form_for #contact, html: {class: "well form-vertical"} do |f|

Related

Rails form url configuration to avoid No route matches [PATCH]

I am a bit confused, I have admin section in my project. These are the routes.
Rails.application.routes.draw do
devise_for :users
resources :users
namespace :admin do
get '/' => 'dashboard#index'
resources :dashboard, only: [:index]
resources :categories do
collection do
get :set_active
end
resources :sections, only: [:edit, :new, :create, :update]
end
end
And I have a form.
If I start it like this.
<%= form_for #category, :html => { :multipart => true } do |f| %>
It fails (because it tries to search bad url without admin).
undefined method category_path' for #ActionView::Base:0x00000000019320`
So I defined url.
<%= form_for #category, url: admin_categories_path do |f| %>
It works great when i create new category, but if I want to edit the category with the same form partial, it fails:
No route matches [PATCH] "/admin/categories"
but routes exists
admin_categories GET /admin/categories(.:format) admin/categories#index
POST /admin/categories(.:format) admin/categories#create
new_admin_category GET /admin/categories/new(.:format) admin/categories#new
edit_admin_category GET /admin/categories/:id/edit(.:format) admin/categories#edit
admin_category GET /admin/categories/:id(.:format) admin/categories#show
PATCH /admin/categories/:id(.:format) admin/categories#update
PUT /admin/categories/:id(.:format) admin/categories#update
DELETE /admin/categories/:id(.:format) admin/categories#destroy
and I have methods in my model for index, new, create, update, edit, show.
I have no idea how to set up it properly.
One small detail that is missed #category to your route path, so it will build url
/admin/categories/1 where 1 is #category.id Rails builds url from model
<%= form_for([:admin, #category]) do |f| %>
So rails generated route for you /admin/categories/:id but you are trying to access /admin/categories/ with no :id
further reading

link_to update for a nested resource not working

The link in _applicant.html.erb looks like this in the browser: http://localhost:3000/needs/3/applicants.1
and when clicked on this shows up in the browser:
Routing Error
No route matches [PUT] "/needs/3/applicants.1"
I want it to update the acceptance column for this particular applicant row. Basically I want it to send data to the update method of the applicants controller. How can I modify the code to do this?
_applicant.html.erb
<%= link_to 'Accept Applicant', need_applicants_path(applicant.need_id, applicant.id), :method => :put, :action => "update", :applicant => {:acceptance => true} %>
got this from running rake routes:
PUT /needs/:need_id/applicants/:id(.:format) applicants#update
routes.rb:
resources :needs, except: [:new] do
resources :applicants
end
applicants_controller.rb
class ApplicantsController < ApplicationController
def update
#need = Need.find(params[:need_id])
#applicant = #need.applicants.find(params[:id])
if #applicant.update_attributes(params[:applicant])
flash[:success] = 'Your applicant has been accepted/rejected!'
redirect_to #need
else
#need = Need.find(params[:need_id])
render 'needs/show'
end
end
end
I think there are two possible fixes here:
First,
http://localhost:3000/needs/3/applicants.1
should probably read
http://localhost:3000/needs/3/applicants/1
The error is in this line:
<%= link_to 'Accept Applicant', need_applicants_path(applicant.need_id, applicant.id), :method => :put, :action => "update", :applicant => {:acceptance => true} %>
where...
need_applicants_path(applicant.need_id, applicant.id)
You can try passing in two instance objects like so:
need_applicants_path(Need.find(applicant.need_id), applicant)
Second, another possible solution is to explicitly set the PUT path in your routes.
In your config/routes.rb add the line
put 'need/:need_id/applicant/:id/update
then run
rake routes
and see what the PUT path is

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.

DRY routing for one polymorph resource with nested resources

Given the following models:
class Blog < ActiveRecord::Base
has_many :posts
end
class SiteBlog < Blog
end
class ProjectBlog < Blog
end
class Post <ActiveRecord::Base
belongs_to :blog
end
And the following routes:
resources :blogs do
resources :posts
end
In say a form partial, the following will work fine if #blog is a Blog:
form_for [#blog, #post] ...
However, if #blog is a ProjectBlog or SiteBlog, it bombs since it will be looking for a URL helper such as project_blog_posts.
I guess something like this would solve this:
[:project_blogs, :site_blogs].each |blogs| do
resources blogs do
resources :posts
end
end
I'm wondering whether there's a way to use the routes for subclassed models (e.g. ProjectBlog) to use the routes of the parent model (Blog). The "as" option only deals with the last object passed like [#blog, #post] to form_for.
Update
As requested below, here are the routes:
resources :blogs, only: [:show] do
resources :posts, only: [:new, :create, :edit, :update]
end
blog_posts POST /blogs/:blog_id/posts(.:format) posts#create
new_blog_post GET /blogs/:blog_id/posts/new(.:format) posts#new
edit_blog_post GET /blogs/:blog_id/posts/:id/edit(.:format) posts#edit
blog_post PUT /blogs/:blog_id/posts/:id(.:format) posts#update
blog GET /blogs/:id(.:format) blogs#show
Update 2:
The tip from an answer below:
form_for [#blog, #post], url: blog_posts_path(#blog, #post) do |f|
This works for "new" actions only, for "edit" actions, I'd get - as expected - a bad URL:
params[:action] # => "edit"
blog_posts_path(#blog, #post) # => "/blogs/publikationsreihe-tafelrunde/posts.5"
So the "if" I mentioned would fix this:
form_for [#blog, #post], url: params[:action]=='new' ? blog_posts_path(#blog, #post) : blog_post_path(#blog, #post) do |f|
But this looks incredibly clumsy, there must be a better way.
Easily solvable by passing the resource url to the form:
<%= form_for [#blog, #post], :url => blog_posts_path(#blog, #post) do |f| %>
...
<%- end %>

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.

Resources