ruby - Rails 4: Undefined method `total_price' for nil:NilClass, Order Controller -


i'm having trouble having order go through. have posted error bellow. think issue has create method in ordercontroller.rb, have total_price method defined but.. other i'm not sure how fix issue. appreciated. thank you.

enter image description here

class ordertransaction   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    private    attr_reader :order, :nonce end 

class order < activerecord::base   belongs_to :user   has_many :order_items    def total_price     order_items.inject(0) { |sum, item| sum + item.total_price }   end end 

class orderscontroller < applicationcontroller   before_filter :initialize_cart    def index     @orders = order.order(created_at: :desc).all   end    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 root_path, notice: "thank placing order."       else         flash[:warning] = <<eof order id #{@order_form.order.id}. <br/> went wrong. eof         redirect_to new_payment_order_path(@order_form.order)       end     else       render "carts/checkout"     end   end    def update     @order = order.find params[:id]     @previous_state = @order.state      if @order.update state_order_params       notify_user_about_state       redirect_to orders_path, notice: "order updated."     end   end     def new_payment     @order = order.find params[:id]     @client_token = braintree::clienttoken.generate   end    def pay     @order = order.find params[:id]     transaction = ordertransaction.new @order, params[:payment_method_nonce]     transaction.execute     if transaction.ok?       redirect_to root_path, notice: "thank placing order."     else       render "orders/new_payment"     end   end    private    def notify_user     @order_form.user.send_reset_password_instructions     ordermailer.order_confirmation(@order_form.order).deliver   end    def notify_user_about_state     ordermailer.state_changed(@order, @previous_state).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    def state_order_params     params.require(:order).permit(:state)   end end 

class orderitem < activerecord::base   belongs_to :order   belongs_to :product    def total_price     self.quantity * self.product.price   end end 

  class orderform   include activemodel::model    attr_accessor :user, :order # credit_card   attr_writer :cart    def save     set_password_for_user      if valid?       persist       true     else       false     end   end    def has_errors?     user.errors.any?   end    private    def valid?     user.valid?   end    def persist     user.save     @order = order.create! user: user      build_order_items   end    def set_password_for_user     user.password = digest::sha1.hexdigest(user.email + time.now.to_s)[0..8]   end    def build_order_items     @cart.items.each |item|       @order.order_items.create! product_id: item.product_id, quantity: item.quantity     end   end  end 

class orderitem < activerecord::base   belongs_to :order   belongs_to :product    def total_price     self.quantity * self.product.price   end end 

as standard note, nilclass error means haven't defined variable you're trying manipulate.

the key solving problem therefore find why variable isn't defined, , populate it.


def execute     @result = braintree::transaction.sale(       amount: order.total_price,       payment_method_nonce: nonce     ) end 

this rails says variable not populated.

however, many problems in programming, cause of issue may not defined...

i thought problem weren't calling @order. however, class initializes order, shouldn't problem. have @ how you're invoking class:

transaction = ordertransaction.new @order, params[:payment_method_nonce] 

this surmises @order defined.

i surmise isn't.

here's i'd do:

def create     @order_form = orderform.new(       user: user.new(order_params[:user]),       cart: @cart     )      if @order_form.save       notify_user       @order = @order_form.order #-> not efficient should create @order       if charge_user         redirect_to root_path, notice: "thank placing order."       else         flash[:warning] = <<eof order id #{@order_form.order.id}. <br/> went wrong. eof         redirect_to new_payment_order_path(@order_form.order)       end     else       render "carts/checkout"     end end 

personally, think highlights deeper problem code structure:

  • you're creating orderform object , yet processing @order_form.order
  • your controller full of tiny methods bloat big time
  • your controller orders, yet builds orderform objects

i'd best make controller thin possible:

#app/controllers/orders_controller.rb class orderscontroller < applicationcontroller    def new        @order = current_user.order.new    end    def create       @order = current_user.order.new order_params       if @order.save           @order.charge       end    end     private     def order_params       params.require(:order).permit(:x, :y, :z, order_products_attributes: [:product, :qty])    end end 

i'd have more modular model structure:

#app/models/order.rb class order < activerecord::base    belongs_to :user    has_many :order_products    has_many :products, through: :order_products, extend productqty    has_many :payments, inverse_of: :order     scope :cart, -> { order_products }     def total_price       products.pluck(:price, :qty) #-> need work out    end     def charge        payment = payments.create        payment.execute ? payment.success : payment.error #-> conditional    end end  #app/models/order_product.rb class orderproduct < activerecord::base    #columns id | order_id | product_id | qty | created_at | updated_at    belongs_to :order    belongs_to :product  end  #app/models/payment.rb class payment < activerecord::base    belongs_to :order, inverse_of: :payments     def execute       braintree::transaction.sale(amount: order.total_price)    end end  #app/models/product.rb class product < activerecord::base    has_many :order_products    has_many :orders, through: :order_products end  #app/models/concerns/product_qty.rb module productqty      #load     def load        products.each |qty|           proxy_association.target << qty        end     end      #private     private      #products     def products        return_array = []        through_collection.each_with_index |through,i|            associate = through.send(reflection_name)            associate.assign_attributes({qty: items[i]})            return_array.concat array.new(1).fill( associate )        end        return_array     end      #######################     #      variables      #     #######################      #association     def reflection_name             proxy_association.source_reflection.name     end      #foreign key     def through_source_key             proxy_association.reflection.source_reflection.foreign_key     end      #primary key     def through_primary_key             proxy_association.reflection.through_reflection.active_record_primary_key     end      #through name     def through_name        proxy_association.reflection.through_reflection.name     end      #through     def through_collection        proxy_association.owner.send through_name     end      #captions     def items        through_collection.map(&:qty)     end      #target     def target_collection        proxy_association.target     end  end 

i wanted include cart somewhere, i'll have time.

for now, you'd able following:

@order = current_user.orders.find params[:id] @order.products.each |product|    product.qty #-> 5  @order.total_price #-> prices * qtys 

--

this not complete or tested, hope shows how improve code structure dramatically, making modular. ie keep many methods tied objects possible.

in short, should able following:

@order = current_users.orders.find params[:id] if @order.payments.any?    @payment = @order.payment.first    @payment.success? end 

Comments