2017-10-20 8 views
0

J'ai ces modèles et les codes de commande:NoMethodError: méthode non définie '' pour rien: NilClass

class Item < ActiveRecord::Base 
has_many :receivers_collateral_items, through: :received_trades, source: :wanted_item 
has_many :receivers_wanted_items, through: :received_trades, source: :collateral_item 
has_many :requesters_collateral_items, through: :requested_trades, source: :collateral_item 
has_many :requesters_wanted_items, through: :requested_trades, source: :wanted_item 
validates :year, :price, presence: true, numericality: true 
validates :shares, numericality: 
        { only_integer: true, greater_than_or_equal_to: 0, 
        less_than_or_equal_to: :build_shares } 

accepts_nested_attributes_for :receivers_collateral_items, :receivers_wanted_items, 
           :requesters_collateral_items, :requesters_wanted_items, 
           allow_destroy: true 
end 




class Trade < ActiveRecord::Base 
belongs_to :trade_requester, class_name: "User" 
belongs_to :trade_recipient, class_name: "User" 
belongs_to :wanted_item, class_name: "Item" 
belongs_to :collateral_item, class_name: "Item" 
validates :trade_requester, :trade_recipient, :wanted_item, :collateral_item, presence: true 
validates :shares, numericality: 
        { only_integer: true, greater_than_or_equal_to: 0, 
        less_than_or_equal_to: :max_shares } 

accepts_nested_attributes_for :wanted_item, :collateral_item, allow_destroy: true 

def max_shares 
    if wanted_item.shares > collateral_item.shares 
     collateral_item.shares 
    else 
     wanted_item.shares 
    end 
end 
end 


class TradesController < ApplicationController 
def create 
    @trade = current_user.requested_trades.build(trade_params) 
end 

private 
    def trade_params 
    params.require(:trade).permit(:trade_requester_id, :trade_recipient_id, :wanted_item_id, :collateral_item_id, :shares) 
    end 
end 

Mais je deviens NoMethodError: undefined method 'shares' for nil:NilClass et un rollback de bd. Étapes pour reproduire l'erreur:

t = Trade.new 
t.trade_requester_id = User.find(1) 
t.trade_recipient_id = User.find(2) 
t.wanted_item_id = Item.second 
t.collateral_item_id = Item.first 
t.shares = 100 
t.save 

La trace de la pile indique qu'il vient de la, il me semble validation Tradenumericality, mais que la classe shares est appelée sur devrait exister. Je devrais être en mesure d'appeler t.wanted_item et obtenir cet article, mais à la place, je reçois nil. t.wanted_item_id, cependant, renvoie cet identifiant. Pourquoi est-ce?

+1

indiquent les étapes à répéter l'erreur code au lieu d'une description. – max

+0

@max 't = Trade.new',' t.trade_requester_id = 1', 't.trade_recipient_id = 2', 't.wanted_item_id = 2',' t.collateral_item_id = 1',' t.shares = 100' , puis 't.save' qui est ce qui déclenche réellement l'erreur de la méthode no – sabaeus

+1

Ajoutez-le au corps de la question - pas un commentaire. – max

Répondre

2

Ceci est probablement dû au fait que le nouvel enregistrement manque un wanted_item ou un collateral_item. Si l'une de ces deux associations renvoie nil, une exception sera levée. Pour résoudre ce problème, utilisez try pour les anciennes versions de Ruby ou l'opérateur de navigation sécurisé (&.) pour Ruby 2.3 ou version ultérieure.

Cela devrait résoudre votre problème (avant Ruby 2.3):

def max_shares 
    if wanted_item.try(:shares).to_i > collateral_item.try(:shares).to_i 
    wanted_item.shares 
    else 
    collateral_item.try(:shares).to_i 
    end 
end 

La méthode try retourne la valeur de wanted_item.shares et collateral_item.shares si elles ne sont pas nil. Si l'un d'eux est nil alors try va attraper l'exception et retourner nil. Le to_i convertit nil à zéro.

Alors collateral_item.try(:shares).to_i retourne la valeur de shares si collateral_item existe, et il retournera à zéro si collateral_item est nil. Si vous utilisez Ruby 2.3 ou version ultérieure, vous devez remplacer _item.try(:shares).to_i par _item&.shares.to_i. Ce dernier est plus propre et faster in execution.

Mise à jour

Pour la réplication, assurez-vous assignez des documents à vos associations et non à leurs ids:

t = Trade.new 
t.trade_requester = User.find(1) 
t.trade_recipient = User.find(2) 
t.wanted_item = Item.second 
t.collateral_item = Item.first 
t.shares = 100 
t.save! 
+0

quel est le point d'utilisation de '''' quand la seule chose à faire est de supprimer la trace de la pile? Un 'Trade' ne sauvegardera pas dans la base de données à moins que tout ne soit validé. ce que j'essaie de comprendre c'est * pourquoi * ces 'Items' sont' nil'. – sabaeus

+1

Bonne question. Cela peut être dû au fait que les utilisateurs avec ces identifiants n'existent pas. Que retournent 't.wanted_item' et' t.collateral_item' avant 't.save'? –

+0

quand je vérifie l'enregistrement que j'essaie d'enregistrer avec 't', j'obtiens' # '. ce qui est attendu. mais quand je vérifie 't.wanted_item_id' ou' t.collateral_item_id', j'obtiens 'nil'. ce qui est inattendu – sabaeus