Nested Routes - Broken Form, NoMethodError - undefined method - ruby-on-rails

To learn Rails, I'm working on a simple calendar app. To get started, I created two scaffolds and the proper associations/routes:
calendars
content_items
app/models/calendar.rb
class Calendar < ActiveRecord::Base
has_many :content_items
end
app/models/content_item.rb
class ContentItem < ActiveRecord::Base
belongs_to :calendar
end
routes.rb
resources :calendars do
resources :content_items
end
controllers/content_items_controller.rb
def create
#content_item = ContentItem.new(content_item_params)
#calendar = Calendar.find(params[:calendar_id] )
respond_to do |format|
if #content_item.save
format.html { redirect_to calendar_content_item_path(#calendar,#content_item), notice: 'Content item was successfully created.' }
format.json { render :show, status: :created, location: #content_item }
else
format.html { render :new }
format.json { render json: #content_item.errors, status: :unprocessable_entity }
end
end
end
When I created the nested routes, I began to run into errors when trying to create new content_items. Whenever I submit the form, I get this error:
NoMethodError in ContentItems#create
undefined method `content_items_path'
The error is coming from:
views/content_items/index.html.erb
<%= form_for [#calendar, #content_item] do |f| %>
UPDATE
Using the code posted below by #Gaurav GuptA fixed the form issue, but led to a new errror. Whenever I visit /calendars/1/content_items, I recieve an error - but only after creating an entry. With an empty database, it works fine.
ActionController::UrlGenerationError
No route matches {:action=>"show", :calendar_id=>#, :controller=>"content_items", :format=>nil, :id=>nil} missing required keys: [:id]
I believe this is because the content_item is being saved without a calendar_id. How do I set the content_item to save with the calendar_id it belongs to?
UPDATE 2
It now saves with a calendar_id, but when an item is saved, the links for edit/shpw/destroy throw errors.
No route matches {:action=>"show", :calendar_id=>#<ContentItem id: 1, , content_type: "Test", content_text: "Tetst\r\n", calendar_id: 1, created_at: "2015-05-26 07:06:42", updated_at: "2015-05-26 07:06:42", content_image_file_name: nil, content_image_content_type: nil, content_image_file_size: nil, content_image_updated_at: nil>, :controller=>"content_items", :format=>nil, :id=>nil} missing required keys: [:id]
It highlights this part of the file:
<td><%= link_to 'Show', calendar_content_item_path(content_item) %></td>
GitHub link: https://github.com/JeremyEnglert/baked

Try to change your create action of content_items_controller as:
def create
#calendar = Calendar.find(params[:calendar_id] )
#content_item = #calendar.content_items.new(content_item_params) #Edit
respond_to do |format|
if #content_item.save
format.html { redirect_to calendar_content_item_path(#calendar,#content_item), notice: 'Content item was successfully created.' }
format.json { render :show, status: :created, location: #content_item }
else
format.html { render :new }
format.json { render json: #content_item.errors, status: :unprocessable_entity }
end
end
end
Hope this will work for you.

Ok so with the routes you have:
resources :calendars do
resources :content_items
end
You get a path like this for example:
/calendars/50
/calendars/50/content_items/77
Which are then routed to:
app/controllers/calendars_controller.rb
app/controllers/content_items_controller.rb
Your error comes from your create action in your ContentItemsController. The params you are posting to the action include your calendar id "calendar_id"=>"1"
but nothing is there to receive it.
You need to get the instance of Calendar associated with the item you are creating.
#calendar = Calendar.find(params[:calendar_id] )
Then after you save #content_item pass to the calendar_content_item_path (as indicated in your routes) both objects:
format.html { redirect_to calendar_content_item_path(#calendar,#content_item), notice: 'Content item was successfully created.' }
In your create method in the ContentItemsController should look like this:
def create
#calendar = Calendar.find(params[:calendar_id] )
#content_item = #calendar.content_items.build(params[:content])
respond_to do |format|
if #content_item.save
format.html { redirect_to calendar_content_item_path(#calendar,#content_item), notice: 'Content item was successfully created.' }
format.json { render :show, status: :created, location: #content_item }
else
format.html { render :new }
format.json { render json: #content_item.errors, status: :unprocessable_entity }
end
end
end
I have also noticed that you are passing the calendar id to your strong params. That would through an error since the calendar's id is not assigned by the user.
You can read more on Strong Parameters.

Related

Bad Redirect On Create Method - Rails 4

I am trying to change the redirect when I create a listing, but I keep getting a No route matches {:action=>"manage_photos", :controller=>"listings"} missing required keys: [:id]. What am I missing? My route is nested and there is a manage_photos method in my controller. Not sure where to go from here.
routes.rb
resources :listings do
member do
get 'like'
get 'unlike'
get 'duplicate'
get 'gallery'
delete 'gallery' => 'listings#clear_gallery'
get 'manage_photos'
get 'craigslist'
get "add_to_collection"
get 'request_photos'
get 'rsvp'
end
end
rake routes:
manage_photos_listing GET /listings/:id/manage_photos(.:format) listings#manage_photos
listings_controller:
create method:
def create
#listing = Listing.new(listing_params)
respond_to do |format|
if #listing.save
format.html { redirect_to manage_photos_listing_path, notice: 'Listing was successfully created.' }
format.json { render json: #listing, status: :created, location: #listing }
else
format.html { render action: "new", notice: "Correct the mistakes below to create the new listing" }
format.json { render json: #listing.errors, status: :unprocessable_entity }
end
end
end
manage_photos method:
def manage_photos
#listing = Listing.find(params[:id])
end
error:
Since it is a nested resource you have to add the id of the parent element. Try:
manage_photo_listing(#listing)
so it can actually use the id of #listing and build the route

Create and Update Redirect with Nested Resources Rails 4

to avoid deep nesting routes I have done the following:
routes.rb
resources :clients, controller: 'clients' do
resources :sites, controller: 'clients/sites', except: [:index]
end
resources :sites, controller: 'clients/sites', except: [:index] do
resources :site_contacts, controller: 'sites/site_contacts', except: [:index]
end
My problem right now is after I Create a new Site Contact I Get an error that states
undefined method `site_contact_url' for #<Sites::SiteContactsController:0x007f9dcb980100>
Did you mean? site_contact_params
my associations are all set up and correct but I am not sure how to handle the re-directs in the site contact create and update methods (below)
site_contacts_controller.rb
def create
#site = Site.friendly.find(params[:site_id])
#site_contact = SiteContact.new(site_contact_params)
#site_contact.site = #site
respond_to do |format|
if #site_contact.save
format.html { redirect_to #site_contact, notice: 'Site contact was successfully created.' }
format.json { render :show, status: :created, location: #site_contact }
else
format.html { render :new }
format.json { render json: #site_contact.errors, status: :unprocessable_entity }
end
end
end
def update
#site = Site.friendly.find(params[:site_id])
respond_to do |format|
if #site.update(site_contact_params)
format.html { redirect_to #site_contact, notice: 'Site contact was successfully updated.' }
format.json { render :show, status: :ok, location: #site_contact }
else
format.html { render :edit }
format.json { render json: #site_contact.errors, status: :unprocessable_entity }
end
end
end
I originally changed #site_contact to #site in both create and update methods and it was throwing the error, I changed them back to original for now as it clearly wasn't working.
Im lost here and am having a hell of a time trying to find a solution. Any help would be greatly appreciated!
Thanks in advance, please let me know if you require any further details.
EDIT #1: Adds rake routes output for sites
site_site_contacts POST /sites/:site_id/site_contacts(.:format) sites/site_contacts#create
new_site_site_contact GET /sites/:site_id/site_contacts/new(.:format) sites/site_contacts#new
edit_site_site_contact GET /sites/:site_id/site_contacts/:id/edit(.:format) sites/site_contacts#edit
site_site_contact GET /sites/:site_id/site_contacts/:id(.:format) sites/site_contacts#show
PATCH /sites/:site_id/site_contacts/:id(.:format) sites/site_contacts#update
PUT /sites/:site_id/site_contacts/:id(.:format) sites/site_contacts#update
DELETE /sites/:site_id/site_contacts/:id(.:format) sites/site_contacts#destroy
Here are the client/sites rake routes output
client_sites POST /clients/:client_id/sites(.:format) clients/sites#create
new_client_site GET /clients/:client_id/sites/new(.:format) clients/sites#new
edit_client_site GET /clients/:client_id/sites/:id/edit(.:format) clients/sites#edit
client_site GET /clients/:client_id/sites/:id(.:format) clients/sites#show
PATCH /clients/:client_id/sites/:id(.:format) clients/sites#update
PUT /clients/:client_id/sites/:id(.:format) clients/sites#update
DELETE /clients/:client_id/sites/:id(.:format) clients/sites#destroy
So I do see an issue here in the outputs, it should be redirecting to client/:client_id/sites/:site_id/site_contacts/:id as there is no sites controller, or no sites/show it is nested to clients.
EDIT #2 New ERROR
No route matches {:action=>"show", :controller=>"sites/site_contacts", :site_id=>#<SiteContact id: 3, site_id: 1, name: "Shawn Wilson", tel_number: "403-615-5915", tel_ext: "", email: "swilson#taurenltd.com", contact_type: "Management", after_hours: true, notes: "Primary", created_at: "2016-09-23 06:47:42", updated_at: "2016-09-23 06:47:42", slug: "shawn-wilson-b7a18908-34ae-4385-bcf7-17c97547dabb">} missing required keys: [:id]

NoMethodError: undefined method

I am working through Agile Web Development with ruby on rails. While running a test, I get the following:
Error: LineItemsControllerTest#test_should_update_line_item:
NoMethodError: undefined method 'product_id' for nil:NilClass
test/controllers/line_items_controller_test.rb:13:in `block in <class:LineItemsControllerTest>
Here is my test file
require 'test_helper'
class LineItemsControllerTest < ActionController::TestCase
test "should create line_item" do
assert_difference('LineItem.count') do
post :create, product_id: products(:ruby).id
end
assert_redirected_to cart_path(assigns(:line_item).cart)
end
test "should update line_item" do
patch :update, id: #line_item, line_item: { product_id: #line_item.product_id }
assert_redirected_to line_item_path(assigns(:line_item))
end
end
Could someone kindly explain why I get a NoMethodError: undefined method while the book says it should be fine?
Thank you!
Update 1
As per Boltz0r's comment below, here are my create and update methods. I tried comparing what I have versus what is in the book and can't seem to find the problem.
def create
product = Product.find(params[:product_id])
#line_item = #cart.add_product(product.id)
respond_to do |format|
if #line_item.save
format.html { redirect_to #line_item.cart, notice: 'Line item was successfully created.' }
format.json { render :show, status: :created, location: #line_item }
else
format.html { render :new }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #line_item.update(line_item_params)
format.html { redirect_to #line_item, notice: 'Line item was successfully updated.' }
format.json { render :show, status: :ok, location: #line_item }
else
format.html { render :edit }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
I've checked the actual example from the book so will now post the answer so maybe somebody else could see it. If you going to test is update operation you should have something to update first. That's why the test from the book and any other real-life one has setup method to pre-create test item:
setup do
#line_item = line_items(:one)
end
What that error means is that in here:
patch :update, id: (at)line_item, line_item: { product_id: (at)line_item.product_id }
the (at)line_item is nil. so it means you probably have an error either on the create method or maybe in the update method (you are not getting the right line_item)

testing nested route in functional tests

I have a nested route:
resources :stories do
resources :comments
end
this is my create method in controller:
def create
#story = Story.find(params[:story_id])
#comment = #story.comments.build(params[:comment])
#comments = #story.comments.all
respond_to do |format|
if #comment.save
format.html { redirect_to #story, notice: t('controllers.comments.create.flash.success') }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render template: "stories/show" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
And here is it's test:
setup do
#comment = comments(:one)
#story = stories(:one)
end
....
test "should create comment" do
assert_difference('Comment.count') do
post :create, :story_id => #story.id, comment: { content: #comment.content, name: #comment.name, email: #comment.email }
end
assert_redirected_to #story_path
end
that ends up with this error:
1) Failure:
test_should_create_comment(CommentsControllerTest) [/home/arach/workspace/Web/ruby/nerdnews/test/functional/comments_controller_test.rb:25]:
Expected response to be a redirect to <http://test.host/stories/980190962/comments> but was a redirect to <http://test.host/stories/980190962>
I don't know why the test expect to redirect to stories/:id/comments. I tried other things like story_comment_path but it didn't help either. story_path without # also ends up with another error:
ActionController::RoutingError: No route matches {:action=>"show", :controller=>"stories"}
same error happens for story_path, :story_id => #story.id. Any idea why this happens?
I think it should be story_path(#story.id). See here.

Using Best_in_Place to edit attributes

I am using the Best_in_Place gem to make areas on my site in place editable. I have two models: Student and Education, with the relationship being that each Student has_many Educations. I have the Best_in_place functionality working when editing attributes that are directly in the Student model, such as
<%=best_in_place #student, :name =>
However, I can't get it to update the attributes of an education.. with a line like
<%=best_in_place #education, :college =>
in the view of students/show,
I get the error
Started PUT "/educations" for 127.0.0.1 at 2012-03-25 13:06:57 -0400
ActionController::RoutingError (No route matches [PUT] "/educations")
and not only does it not work, the editable spot disappears completely. I can't figure out what's causing the problem, everything seems to be the same for both models/controllers. My routes are very simple:
resources :students
resources :educations
root :to => 'pages#home'
devise_for :students
as are my controllers:
def update
#student = Student.find(params[:id])
respond_to do |format|
if #student.update_attributes(params[:student])
format.html { redirect_to #student, notice: 'Student was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
vs
def update
#education = Education.find(params[:id])
respond_to do |format|
if #education.update_attributes(params[:education])
format.html { redirect_to #education, notice: 'Education was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #education.errors, status: :unprocessable_entity }
end
end
end
If I rake routes, I get:
educations GET /educations(.:format) educations#index
POST /educations(.:format) educations#create
new_education GET /educations/new(.:format) educations#new
edit_education GET /educations/:id/edit(.:format) educations#edit
education GET /educations/:id(.:format) educations#show
PUT /educations/:id(.:format) educations#update
DELETE /educations/:id(.:format) educations#destroy
students GET /students(.:format) students#index
POST /students(.:format) students#create
new_student GET /students/new(.:format) students#new
edit_student GET /students/:id/edit(.:format) students#edit
student GET /students/:id(.:format) students#show
PUT /students/:id(.:format) students#update
DELETE /students/:id(.:format) students#destroy
Any pointers would be a huge help.
Two Things:
Your view syntax is off. You're closing your tags with => instead of %>. So try this instead:
<%=best_in_place #student, :name %>
<%=best_in_place #education, :college %>
Try updating the respond_to blocks for format.json to respond_with_bip(#student). That should enable the user to edit via javascript, while leaving the user on the page to see their updates via AJAX.
See ReadMe for best_in_place Controller response with respond_with_bip
See updates below:
def update
#student = Student.find(params[:id])
respond_to do |format|
if #student.update_attributes(params[:student])
format.html { redirect_to #student, notice: 'Student was successfully updated.' }
format.json { respond_with_bip(#student) }
else
format.html { render action: "edit" }
format.json { render json: #student.errors, status: :unprocessable_entity }
end
end
end
def update
#education = Education.find(params[:id])
respond_to do |format|
if #education.update_attributes(params[:education])
format.html { redirect_to #education, notice: 'Student was successfully updated.' }
format.json { respond_with_bip(#education) }
else
format.html { render action: "edit" }
format.json { render json: #education.errors, status: :unprocessable_entity }
end
end
end

Resources