NoMethodError in LineItemsController#create - ruby-on-rails-3

I'm working with the Agile Web Development with Rails (4th edition) book and I bumped into a piece of code that is not working. I tried hard to figure out why it's not working, but I didn't make it.
BACKGROUD INFORMATION
The following classes are involved:
class Cart < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
end
class LineItem < ActiveRecord::Base
belongs_to :product
belongs_to :cart
end
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
session[:cart_id] = cart.id
end
end
NOW
here is where it fails:
class LineItemsController < ApplicationController
def index
#line_items = LineItem.all`
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #line_items }
end
end
# GET /line_items/1
# GET /line_items/1.xml
def show
#line_item = LineItem.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #line_item }
end
end
# GET /line_items/new
# GET /line_items/new.xml
def new
#line_item = LineItem.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #line_item }
end
end
# GET /line_items/1/edit
def edit
#line_item = LineItem.find(params[:id])
end
# POST /line_items
# POST /line_items.xml
def create
#cart = current_cart
product = Product.find(params[:product_id])
#line_item = **#cart.line_items.build(:product => product)**`
ERROR MESSAGE
*NoMethodError in LineItemsController#create
undefined method `line_items' for 9:Fixnum
Rails.root: /home/tmiskiew/depot*
*app/controllers/line_items_controller.rb:53:in `create'
Request
Parameters:
{"product_id"=>"4",
"authenticity_token"=>"dR4nL5zI+R7qIIPwNkl3EoaI1KyFWRokvh92m3PwD8o="}*
Anyone an idea what's wrong with #cart.line_items.build?
I'm working rails 3.0.9 and ruby 1.8.7
Thanks
Thomas

Have a look at your error
undefined method line_items' for 9:Fixnum
This says that #cart is a number, not an Cart object (#cart = current_cart from create action returns a number),
current_cart function returns a number because
Cart.find(session[:cart_id]) returns recordNotFound and your rescue block from current_cart function will be executed.
Remember that every function in Ruby returns the result of the last executed line, so you will get returned the cart.id
Edit: For first comment, try to rewrite method
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
session[:cart_id] = cart.id
cart # this will get returned
end
end

Here's how I fixed it...hope it works for you too.
/app/models/cart.rb:
def add_product(product_id)
current_item = line_items.find_by_product_id(product_id)
if current_item
current_item.quantity += 1
else
current_item = line_items.build(:product_id => product_id)
end
return current_item
end
Add the return in. I'm not 100% sure why this was failing, but I think the function only returns the quantity, rather than a line item unless the if statement evaluates to true.

I have similar error, it turns out I have a typo in Cart.rb:
class Cart < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
end
typo in :destory... and it cause the undefined method... hope this help.

I was having the same problem, and I felt really stuck on it.
I fixed it by rollbacking the database and migrating it again.
Commands on terminal:
rake db:rollback
rake db:migrate

Related

Which Active record association should I use

I'm using rails 5.1.4 , so I have a table USER within I store all the users, then a user can send friend request to other users, and I want to store that request into other table named FRIEND, like user_1 ( who send the request) - user_2 (who is requested for) , but you will understand that those two user (user_1, user_2) are coming from USER table.
It means: first I have this table Users which contains all the users. And what I want to say is that any user in this table user can be friend with any other user from the same table user.
I'm not 100% certain what your asking in your question but it sounds like what you're looking to do is to have a table called Users and another table called Friends and to be able to store a value for the friend request. I believe what you want to look into is a "has_many :through association" using a "join table". Check out this video it walks you through it http://blog.teamtreehouse.com/what-is-a-has_many-through-association-in-ruby-on-rails-treehouse-quick-tip
here's more info on the Active Record association http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
EDIT:
Based on your below post what you want to do is just a simple has_many association. In your User model add has_many :friends, dependent: :destroy and in your Friend model add belongs_to :users now go to your routes file and make friends a nested route of user by doing
# nested routes for users
resources :users do
resources :friends
end
in your friends table make sure you add a foreign key for users lik ethis in the migration t.references :user, foreign_key: true
now in your friends_controller you need to update the actions like this. Replace the example "patent" with your "friend" object.
class PatentsController < ApplicationController
# first loads #user and then #patent for the below actions.
before_action :set_user, only: [:index, :show, :new, :edit, :create, :update]
before_action :set_patent, only: [:show, :edit, :update, :destroy]
# GET /patents
# GET /patents.json
def index
#patents = #user.patents
end
# GET /patents/1
# GET /patents/1.json
def show
end
# GET /patents/new
def new
#patent = #user.patents.new
end
# GET /patents/1/edit
def edit
end
# POST /patents
# POST /patents.json
def create
#patent = #user.patents.new(patent_params)
respond_to do |format|
if #patent.save
format.html { redirect_to user_patents_path(#patent.user), notice: 'Patent was successfully created.' }
format.json { render :show, status: :created, location: #patent }
else
format.html { render :new }
format.json { render json: #patent.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /patents/1
# PATCH/PUT /patents/1.json
def update
respond_to do |format|
if #patent.update(patent_params)
format.html { redirect_to user_patents_path(#patent.user), notice: 'Patent was successfully updated.' }
format.json { render :show, status: :ok, location: #patent }
else
format.html { render :edit }
format.json { render json: #patent.errors, status: :unprocessable_entity }
end
end
end
# DELETE /patents/1
# DELETE /patents/1.json
def destroy
#patent.destroy
respond_to do |format|
format.html { redirect_to user_url(#user), notice: 'Patent was successfully destroyed.' }
format.json { head :no_content }
end
end
#######################
# private methods
#######################
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:user_id])
end
def set_patent
#patent = Patent.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def patent_params
params.require(:patent).permit(:patent_title, :patent_office, :patent_number, :patent_status, :patent_date, :patent_description, :user_id)
end
end

Rails 3 - Building a nested resource within another nested resource (articles -> comments -> votes)

In my app there is an association problem, which I'm unable to fix.
My app is quite simple: There's an Article model; each article has_many comments, and each of those comments has_many votes, in my case 'upvotes'.
To explain the way I designed it, I did a comments scaffold, edited the comment models and routes to a nested resource, everything works fine. Now, I basically did the same process again for 'upvotes' and again edited model and routes to make this a nested resource within the comment nested resource. But this fails at the following point:
NoMethodError in Articles#show
Showing .../app/views/upvotes/_form.html.erb where line #1 raised:
undefined method `upvotes' for nil:NilClass
My _form.html.erb file looks like this:
<%= form_for([#comment, #comment.upvotes.build]) do |f| %>
<%= f.hidden_field "comment_id", :value => :comment_id %>
<%= image_submit_tag "buttons/upvote.png" %>
<% end %>
Why is 'upvotes' undefined in this case, whereas here:
<%= form_for([#article, #article.comments.build]) do |form| %>
rest of code
everything works totally fine? I copied the same mechanism but with #comment.upvotes it doesn't work.
My upvotes_controller:
class UpvotesController < ApplicationController
def new
#upvote = Upvote.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #upvote }
end
end
def create
#article = Article.find(params[:id])
#comment = #article.comments.find(params[:id])
#upvote = #comment.upvotes.build(params[:upvote])
respond_to do |format|
if #upvote.save
format.html { redirect_to(#article, :notice => 'Voted successfully.') }
format.xml { render :xml => #article, :status => :created, :location => #article }
else
format.html { redirect_to(#article, :notice =>
'Vote failed.')}
format.xml { render :xml => #upvote.errors, :status => :unprocessable_entity }
end
end
end
end
I'm sorry for this much code.., my articles_controller: (extract)
def show
#upvote = Upvote.new(params[:vote])
#article = Article.find(params[:id])
#comments = #article.comments.paginate(page: params[:page])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #article }
end
end
And my 3 models:
class Article < ActiveRecord::Base
attr_accessible :body, :title
has_many :comments
end
class Comment < ActiveRecord::Base
attr_accessible :content
belongs_to :user
belongs_to :article
has_many :upvotes
end
class Upvote < ActiveRecord::Base
attr_accessible :article_id, :comment_id, :user_id
belongs_to :comment, counter_cache: true
end
Upvote migration file:
class CreateUpvotes < ActiveRecord::Migration
def change
create_table :upvotes do |t|
t.integer :comment_id
t.integer :user_id
t.timestamps
end
end
end
My routes:
resources :articles do
resources :comments, only: [:create, :destroy] do
resources :upvotes, only: [:new, :create]
end
end
Sorry for that much code. If anyone might answer this, they would be so incredibly awesome!
Thank you in advance!
Why is 'upvotes' undefined in this case, whereas here:
This is because you're calling upvotes on a nil object, the comment doesn't exist yet.
Best thing to do would be looking into nested attributes:
http://guides.rubyonrails.org/2_3_release_notes.html#nested-attributes
http://guides.rubyonrails.org/2_3_release_notes.html#nested-object-forms
Your error message, says that you try call upvotes on nil. Specifically it is a part of code #comment.upvotes.build in your /app/views/upvotes/_form.html.erb view.
You have to fix show action in you ArticlesController, by adding #comment (with contents) variable.
def show
#upvote = Upvote.new(params[:vote])
#article = Article.find(params[:id])
#comments = #article.comments.paginate(page: params[:page])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #article }
end
end
Also strange things are happening in UpvotesController, in create action.
#article = Article.find(params[:id])
#comment = #article.comments.find(params[:id])
#upvote = #comment.upvotes.build(params[:upvote])
Firstly you had fetched one #article using params[:id], then you had fetched all comments of that #article (throught association), where comments id is the same as #article id. Please review your code, it is inconsistent and will not work correctly.
Everything fixed and works fine now. Took a different approach and simply used Upvote.new instead of nesting it into the comments and building associations, edited my routes as well. Implemented Matthew Ford's idea
I would suspect you have many comments on the article page, the comment variable should be local e.g #article.comments.each do |comment| and then use the comment variable to build your upvote forms.
Thanks everybody for your help!

Polymorphic Comments with Ancestry Problems

I am trying to roll together two Railscasts: http://railscasts.com/episodes/262-trees-with-ancestry and http://railscasts.com/episodes/154-polymorphic-association on my app.
My Models:
class Location < ActiveRecord::Base
has_many :comments, :as => :commentable, :dependent => :destroy
end
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
My Controllers:
class LocationsController < ApplicationController
def show
#location = Location.find(params[:id])
#comments = #location.comments.arrange(:order => :created_at)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #location }
end
end
end
class CommentsController < InheritedResources::Base
def index
#commentable = find_commentable
#comments = #commentable.comments.where(:company_id => session[:company_id])
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
#comment.user_id = session[:user_id]
#comment.company_id = session[:company_id]
if #comment.save
flash[:notice] = "Successfully created comment."
redirect_to :id => nil
else
render :action => 'new'
end
end
private
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
end
In my locations show view I have this code:
<%= render #comments %>
<%= render "comments/form" %>
Which outputs properly. I have a _comment.html.erb file that renders each comment etc. and a _form.html.erb file that creates the form for a new comment.
The problem I have is that when I try <%= nested_comments #comments %> I get undefined method 'arrange'.
I did some Googling and the common solution to this was to add subtree before the arrange but that throws and undefined error also. I am guessing the polymorphic association is the problem here but I am at a loss as to how to fix it.
Dumb mistake... forgot to add the ancestry gem and required migration which I thought I had already done. The last place I checked was my model where I eventually discovered my error.

Mass Assignment Security Error when testing new action with Agile Web tutorial using Rails 3.2

I am going through the Agile Web tutorial with some slight changes. When I run functional tests in Rails 3.2, I am getting the following error:
test_should_get_new(OrdersControllerTest):
ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: cart, deal
Here is the orders_controller_test.rb code:
test "should get new" do
cart = Cart.create
session[:cart_id] = cart.id
LineItem.create(cart: cart, deal: deals(:one))
get :new
assert_response :success
end
Here is the orders fixtures:
one:
name: MyString
address: MyText
email: MyString
pay_type: Check
Here is the line items fixtures:
one:
deal: one
order: one
Here is the deals fixture:
one:
title: MyString
description: MyText
image_url: MyString
price: 9.99
Here is the order controller code:
def new
#cart = current_cart
if #cart.line_items.empty?
redirect_to store_url, notice: "Your cart is empty"
return
end
#order = Order.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #order }
end
end
I tried using FactoryGirl but still got the same error message. Here is the code:
test "should get new" do
cart = FactoryGirl.build(:cart)
session[:cart_id] = cart.id
LineItem.create(cart: cart, deal: deals(:one))
get :new
assert_response :success
end
And the FactoryGirl code:
FactoryGirl.define do
factory :cart do
end
end
For FactoryGirl I also tried 'create' instead of 'build' and got the same error message.
Although I could turn off the mass assignment error in config, I would rather not since I prefer to test properly.
Any suggestions please?
Instead of LineItem.create(cart: cart, deal: deals(:one)) try
item = LineItem.create
item.cart = cart
item.deal = deals(:one)
or in your LineItem model, add:
attr_accessible :cart, :deal
In your 'models', 'order.rb' add the attr_accessible line
class Order < ActiveRecord::Base
has_many :line_items, dependent: :destroy
attr_accessible :name, :address, :email, :pay_type
end

Rails 3.1 respond_to :html with :except

I have the following in my controller:
respond_to :html, :except => :some_action
respond_to :json, :xml
If you hit the :some_action route in a browser (tested with Chrome), you get a 406 Not Acceptable response back. Is there a way to "catch" this in Rails and do something else (like a redirect)?
Additionally, I'm trying to avoid using the block form of respond_to. I'm just curious if there is some way to handle this case.
Check this out: http://ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with
There's a bit about action overriding:
class UsersController < ApplicationController::Base
respond_to :html, :xml, :json
# Override html format since we want to redirect to a different page,
# not just serve back the new resource
def create
#user = User.create(params[:user])
respond_with(#user) do |format|
format.html { redirect_to users_path }
end
end
end