NoMethodError: undefined method - ruby-on-rails

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)

Related

Trouble testing for an error thrown from AASM gem

I'm having trouble testing for a thrown AASM error.
Here is my controller method:
# controllers/jobs_controller.rb
def change_state
respond_to do |format|
if #job.close && #job.save
format.html { redirect_to #job, notice: 'Job has been closed.' }
format.json { render :show, status: :ok, location: #job }
else
format.html { render :show, notice: 'Job could not be closed.' }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
My spec looks like this:
# spec/controllers/jobs_controller_spec.rb
describe "POST #change_state" do
it "cannot transition job from closed" do
job.aasm_state = "closed"
job.save!
post :change_state, params: {id: job.hash_id, user_id: user.id}
expect { response }.to raise_error(AASM::InvalidTransition)
end
end
The test fails (with the expected/desired failure):
Failure/Error: if #job.close && #job.save
AASM::InvalidTransition:Event 'close' cannot transition from 'closed'.
I'm just unable to figure out the right syntax to pass the test. I've tried a few variations of the expect line but can't seem to piece it together.
Any guidance is appreciated.
The exception is happening before the expect statement. Try it:
expect {
post(:change_state, params: { id: job.hash_id, user_id: user.id })
}.to(
raise_error(AASM::InvalidTransition)
)

Rails update model from another controller

So I have this 2 models, Folder and Event. Folder have_one Event and Event belongs to Folder.
I want to be able to update a folder's event with new attributes at the same time as the folder. Something like this.
def update
respond_to do |format|
if #folder.update(folder_params)
#folder.event.update_attributes(status: #folder.status)
format.html { redirect_to #folder, notice: 'Folder was successfully updated.' }
format.json { render :show, status: :ok, location: #folder }
else
format.html { :edit }
format.json { render json: #folder.errors, status: :unprocessable_entity }
end
end
end
But I understand this is not very MVC compliant and also does not work. So any idea on how I can make this work ?
Thanks in advance
You can do something like:
class Folder < ActiveRecord::Base
after_save :update_event_status, on: update #Or after_update :update_event_status
def update_event_status
event.update_attributes(status: status)
end
end
write method in Model and call it in controller method. like below code.
class Folder < ActiveRecord::Base
def update_event_status(event,status)
event.update_attributes(status: status)
end
end
def update
respond_to do |format|
if #folder.update(folder_params)
update_event_status(#folder.event,#folder.status) #call methode here
format.html { redirect_to #folder, notice: 'Folder was successfully updated.' }
format.json { render :show, status: :ok, location: #folder }
else
format.html { :edit }
format.json { render json: #folder.errors, status: :unprocessable_entity }
end
end
end
Try this.

How to add different edit/update method into rails controller?

The controller:
def edit
end
def update
respond_to do |format|
if #item.update(item_params)
format.html { redirect_to #item, notice: 'Item was successfully updated.' }
format.json { render :show, status: :ok, location: #item }
else
format.html { render :edit }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
I want to add another methods like this. For updating only 1 column of item and i want to dont show other columns to user.
I have all neccesary views.
def another_edit
end
def another_update
respond_to do |format|
if #item.update(params[:item].permit(:amount))
format.html { redirect_to #item, notice: 'Item was successfully updated.' }
format.json { render :show, status: :ok, location: #item }
else
format.html { render :edit }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
I thought my routes are wrong. Then tried
resources :items do
collection do
get 'add', on: :edit
end
end
and
get 'items/another_edit'
Is it possible or not? Please help me.
You need to add those endpoints in routes as "member" (not collection):
resources :items do
get :another_edit, on: :member
put :another_update, on: :member
end
Then routes will be built with "id" parameter inside.

Nested Routes - Broken Form, NoMethodError - undefined method

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.

Undefined constant Rails 4

by copying and trying to modify a scaffold manually in Rails 4, I got an error when I want to delete an article: uninitialized constant Article::Tag.
Another error when I want to edit an Article: "undefined local variable or method `article_params'".
I don't know from where it can comes from, the highlited error which are displayed are #article.destroy and if #article.update(article_params). I don't understand at all considering I think having copied perfectely the scaffold...
def destroy
#article.destroy
respond_to do |format|
format.html { redirect_to articles_url }
format.json { head :no_content }
end
end
def update
respond_to do |format|
if #article.update(article_params)
format.html { redirect_to #article, notice: 'Article was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #article.errors, status: :unprocessable_entity }
end
end
end
Thansk you for your help

Resources