Add to Cart Function - No Method Error (undefined method) - ruby-on-rails

Hi guys so i am attempting a "add to cart function". I keep receiving "cart undefined method or variable" error and i can't seem to figure out where i'm going wrong.
Here's my cart.rb model
class Cart < ActiveRecord::Base
has_many :tutors
def add_tutor(tutor_id)
tutor = Tutor.where('tutor_id = ?', tutor_id)
if tutor
Cart.tutors << tutor
end
save
end
end
Here's my carts_controller.rb
class CartsController < ApplicationController
def show
#cart = current_cart
end
def add_to_cart
current_cart.add_tutor(params[:tutor_id])
redirect_to tutors_path
end
end
methods defined in application_controller.rb
def current_cart
if session[:cart_id]
#current_cart ||= Cart.find(session[:cart_id])
end
if session[:cart_id].nil?
#current_cart = Cart.create!
session[:cart_id] = #current_cart.id
end
#current_cart
end
code in routes.rb for the button
post '/add_to_cart/:tutor_id' => 'carts#add_to_cart', :as => 'add_to_cart'
and the code for the button to add to cart
<%= button_to "Shortlist Tutor", add_to_cart_path(:tutor_id => :tutor_id), :method => :post %>
Whenever i try the "Shortlist Tutor" button, i receive a NoMethodError in CartsController#add_to_cart, undefined method `tutors' for Cart(id: integer):Class
and the highlighted error is on the cart.rb model file and the line being
Cart.tutors << tutor
Any help regarding this error is greatly appreciated. Thank you for your time and advice!

In Cart.tutors << tutor Cart is a class. To add tutors to Cart, Cart must be an object. In your case current_cart is an object but Cart is class. so replace Cart in cart.rb with self as shown below
class Cart < ActiveRecord::Base
has_many :tutors
def add_tutor(tutor_id)
tutor = Tutor.where('tutor_id = ?', tutor_id)
if tutor
self.tutors << tutor
end
save
end
end

Related

I am having this error: "You cannot call create unless the parent is saved",

I have seen this error, I understand the problem or so I hope I do, the problem being my order_items are saving before an Order Id has been created. The problem of being a nube is having a clue about the problem but not idea about how to implement the solution, your patience is appreciated.
The error I am getting.
ActiveRecord::RecordNotSaved (You cannot call create unless the parent is saved):
app/models/shopping_bag.rb:22:in add_item'
app/controllers/order_items_controller.rb:10:increate'
My OrderItems controller
class OrderItemsController < ApplicationController
def index
#items = current_bag.order.items
end
def create
current_bag.add_item(
book_id: params[:book_id],
quantity: params[:quantity]
)
redirect_to bag_path
end
def destroy
current_bag.remove_item(id: params[:id])
redirect_to bag_path
end
end
My Orders controller
class OrdersController < ApplicationController
before_action :authenticate_user!, except:[:index, :show]
def index
#order = Order.all
end
def new
#order = current_bag.order
end
def create
#order = current_bag.order
if #order.update_attributes(order_params.merge(status: 'open'))
session[:bag_token] = nil
redirect_to root_path
else
render new
end
end
private
def order_params
params.require(:order).permit(:sub_total, :status, :user_id)
end
end
My shopping bag Model
class ShoppingBag
delegate :sub_total, to: :order
def initialize(token:)
#token = token
end
def order
#order ||= Order.find_or_create_by(token: #token, status: 'bag') do | order|
order.sub_total = 0
end
end
def items_count
order.items.sum(:quantity)
end
def add_item(book_id:, quantity: 1)
book = Book.find(book_id)
order_item = order.items.find_or_create_by(
book_id: book_id
)
order_item.price = book.price
order_item.quantity = quantity
ActiveRecord::Base.transaction do
order_item.save
update_sub_total!
end
end
def remove_item(id:)
ActiveRecord::Base.transaction do
order.items.destroy(id)
update_sub_total!
end
end
private
def update_sub_total!
order.sub_total = order.items.sum('quantity * price')
order.save
end
end
Thank you, your time is appreciated.
From docs about find_or_create_by:
This method always returns a record, but if creation was attempted and failed due to validation errors it won’t be persisted, you get what create returns in such situation.
Probably this is the situation - the record was not persisted in a database, but only created in memory. Looking at your code, I think you want to use a bang-version of the method (find_or_create_by!), which will raise an error in such situation.
To use parent attributes in child when using nested_attributes you can use inverse_of. Here is the documentation which may help you understand why parents need to be created first.
UPDATED with example: This will create forums first and then posts.
class Forum < ActiveRecord::Base
has_many :posts, :inverse_of => :forum
end
class Post < ActiveRecord::Base
belongs_to :forum, :inverse_of => :posts
end

Rails Transaction error when placing order

I'm trying to place an order with braintree payment gem and everything is working fine execept one little thing
i get this error while placing the order
undefined method `total_price' for nil:NilClass
in the order_transaction.rb file which is located in app/views/services/
this is the file
class OrderTransaction
attr_reader :order,:nonce
def initialize order, nonce
#order=order
#nonce=nonce
end
def execute
#result = Braintree::Transaction.sale(
amount: #order.total_price,
payment_method_nonce: #nonce)
end
def ok?
#result.success?
end
end
and this is the method in orders_controller.rb which is creating the transaction
def charge_user
transaction=OrderTransaction.new #order,params[:payment_method_nonce]
transaction.execute
transaction.ok?
end
i dont get whats happening i have the method total_price in order model which is this
class Order < ApplicationRecord
belongs_to :user
has_many :order_items
def total_price
order_items.inject(0){|sum, item| sum +item.total_price }
end
end
i have also used attr_reader to read the order and the nonce in order_transaction.rb file so why is it showing undefined method for nil class i'm really confused can someone help me out
this is the orders controller in controller folder
class OrdersController < ApplicationController
before_filter :initialize_cart
def create
#order_form=OrderForm.new(user: User.new(order_params[:user]),cart: #cart)
if #order_form.save
notify_user
if charge_user
redirect_to products_path,notice:"Thank You for placing the order sir."
end
else
render "carts/checkout",notice:"Something went wrong please try again."
end
end
def custom
#order_form=OrderForm.new(user: current_user,cart: #cart)
if #order_form.save
notify_user
if charge_user
redirect_to products_path,notice:"Thank You for placing the order sir."
end
else
render "carts/checkout",notice:"Something went wrong please try again sir."
end
end
private
def notify_user
OrderMailer.order_confirmation(#order_form.order).deliver
end
def order_params
params.require(:order_form).permit(
user:[:name,:phone,:address,:city,:country,:postal_code,:email])
end
def charge_user
transaction=OrderTransaction.new #order,params[:payment_method_nonce]
transaction.execute
transaction.ok?
end
end

Rails. Save parent, before save children

When I try create line_item, I get this error: ActiveRecord::RecordNotSaved (You cannot call create unless the parent is saved).
When I wrong? How fix it?
line_items controller
def create
#product = Product.find_by_id(params[:line_item][:product_id])
#cart = current_cart
#line_item = #cart.add_product(line_item_params)
end
cart model
has_many :line_items, dependent: :destroy
def add_product(line_item_args)
current_line_item.quantity += line_item_args[:quantity].to_i
current_line_item.save
if current_item
current_item.quantity += line_item.quantity.to_i
else
current_item = line_items.create!(line_item_args)
end
current_item
end
UPD
working helper method current_cart from application controller. problem was in it.
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
session[:cart_id] = cart.id
cart
end
i don't know how you are working with the cart but i'm amoust sure current cart is not persited on de database, check if its true.

Cart Checkout >> Send an Email with Cart Items included

I have an "Add To Cart" function working fine based on Sessions so no user is currently logged in.
Here's the Model
class Cart < ActiveRecord::Base
has_many :tutors
def add_tutor(tutor_id)
tutor = Tutor.find(tutor_id)
if tutor
self.tutors << tutor
end
save
end
end
Here's the Controller
class CartsController < ApplicationController
def show
#cart = current_cart
end
def add_to_cart
current_cart.add_tutor(params[:tutor_id])
redirect_to tutors_path
end
end
What i would like to do is when the user checkouts, they will be redirected to a page with a simple form requesting for their name/email/phone-number, and when that email is sent to me, it includes the items in their cart.
I am currently using gem 'mail_form' and this is what i have set-up so far.
Model checkout.rb
class Checkout < MailForm::Base
attribute :name, :validate => true
attribute :email, :validate => /\A([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})\z/i
attribute :hp, :validate => true
def headers
{
:subject => "Tutor Checkout",
:to => "xxx#xxx.com"
}
end
end
Controller checkouts_controller.rb
class CheckoutsController < ApplicationController
def new
#checkout = Checkout.new
end
def create
#checkout = Checkout.new(params[:checkout])
#checkout.request = request
if #checkout.deliver
flash[:notice] = 'Thank you! We will be in touch with you shortly!'
else
flash[:error] = 'There was an error in sending your message!'
render :new
end
end
end
and under views/checkouts/new.html.erb its just a form requesting for their information.
What i really have no idea on where to start is creating the association between Carts and Checkouts and how do i even include the items in the cart to be sent along with the email?
Any help or advice would be greatly appreciated! Thank you!

NoMethodError in StoreController#display_cart

I am following the book Agile Web Development with Rails - A Pragmatic Guide. On Iteration C1, creating a cart, I am getting the following error:
NoMethodError in StoreController#display_cart
undefined method `items' for #<Hash:0xb5cf041c>
Extracted source (around line #15):
13 def display_cart
14 #cart = find_cart
15 #items = #cart.items
16 end
17
18 private
Here are the source files:
routes.rb
Rails.application.routes.draw do
get 'store' => 'store#index'
get 'add_item_to_cart' => 'store#add_to_cart'
get 'display_cart' => 'store#display_cart'
resources :products
store_controller.rb
class StoreController < ApplicationController
def index
#products = Product.salable_items
end
def add_to_cart
product = Product.find(params[:id])
#cart = find_cart
#cart.add_product(product)
redirect_to display_cart_path
end
def display_cart
#cart = find_cart
#items = #cart.items
end
private
def find_cart
session[:cart] ||= Cart.new
end
end
cart.rb
class Cart
attr_reader :items
attr_reader :total_price
def initialize
#items = []
#total_price = 0.0
end
def items
#items
end
def add_product(product)
#items << LineItem.for_product(product)
#total_price += product.price
end
end
line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :product
def self.for_product(product)
item = self.new
item.quantity = 1
item.product = product
item.unit_price = product.price
item
end
end
I reach the action display_cart from the add_to_cart action of StoreController. Even though I have a def items in cart.rb, why am I getting a NoMethodError?
Assuming you have stored cart id in the session you can do following changes in your method
def find_cart
Cart.find_by_id(session[:cart_id]) ||= Cart.new
end
Try this to return your Cart instance:
def find_cart
Cart.find(session[:cart]
rescue ActiveRecord::RecordNotFound
cart = Cart.new
session[:cart] = cart.id
end
This is almost the same code as in your book. Semantically you could call that function current_cart.
By storing only the carts id and using the ActiveRecord find() method, rescuing with calling Cart.new , you will always return a cart instance.
Edit
if you do not implement Cart as a Model, you will (logically) never be able to instantiate an object of it.
This is a good article about handling sessions in Ruby on Rails. As you will see, the session itself is a Hash by 'nature', further down you will find some alternative solutions for storing sessions in your rails app.
The problem is in
def find_cart
session[:cart] ||= Cart.new
end
your session[:cart] is a Hash, and thus does not get Cart.new, and when you call #cart.items it gets called on Hash not on a Cart object
In your display_cart method: you're doing #cart = find_cart which may return a hash instead of an object as in the find_cart methhod you're doing session[:cart] ||= Cart.new which may return a Hash.

Resources