No method error when trying to call a function wit

2019-08-20 04:26发布

问题:

I have a model Token with three fields user_id,product_id and unique_token.In the controller i instantiate a @token object with user_id and product_id values collected from the form.Then i call save_with_payment function with that object,where within the function i want to generate random string 3 times and save in unique_token field.The problem is self.tokens.create!( unique_token: Digest::SHA1.hexdigest("random string") ) give me no method error undefined method tokens.What am i doing wrong here?To clarify what i want to accomplish,I want to be able to retrieve list of generated unique_tokens associated to that user_id or product_id like User.find(1).tokens or Product.find(1).tokens.The model association is User has_many Tokens Product has_many Tokens.Note: unique_token field is from Token model originally,user_id and product_id are just ref primary keys.Much Thanks!

def create
  @token=Token.new(params[:token])
  if @token.save_with_payment
    redirect_to :controller => "products", :action => "index"
  else
    redirect_to :action => "new"
  end
end

class Token < ActiveRecord::Base
  require 'digest/sha1'

  def save_with_payment
 #  if valid?
 #   customer = Stripe::Charge.create(amount:buck,:currency => "usd",card:stripe_card_token,:description => "Charge for bucks")
#self.stripe_customer_token = customer.id
    3.times do
      self.tokens.create!(unique_token: Digest::SHA1.hexdigest("random string"))
    end 
    save!
  end
end

回答1:

There is no tokens method on the Token class. Since you're creating three tokens you don't need the @token instance. Just have save_with_payment be a class method:

def create
  if Token.save_with_payment(params[:token])
    redirect_to :controller => "products", :action => "index"
  else
    redirect_to :action => "new"
  end
end

class Token < ActiveRecord::Base
  require 'digest/sha1'

  def self.save_with_payment(attributes)
    attributes.merge!(unique_token: Digest::SHA1.hexdigest("foo"))
    3.times do
      self.create!(attributes)
    end
  end

end

Hope this helps.

You might want to wrap the loop in a begin/rescue, too. Otherwise if the 2nd or 3 create! fails you end up with tokens AND redirecting to "new".

Response to 1st comment: That won't work if you use a class method. You can't call valid? because you're not in the context of an instance of Token. I don't recommend sticking with an instance method. If you do change it to a class method you'll want to wrap it in a transaction block:

def self.save_with_payment(attributes)
  transaction do
    attributes.merge!(unique_token: Digest::SHA1.hexdigest("foo"))
    3.times do
      self.create!(attributes)
    end
  rescue
    false
  end
end

That should roll back the SQL transactions if any of the create! calls fail and return false to the controller create action.

I'd pull that customer code out of Token (Token shouldn't care about creating/retrieving a customer) and put it in the controller action. Pass the pertinent information into save_with_payments. Like:

self.save_with_payments(customer, attributes)
  ...
end