1
votes

I migrated my Rails application from 3.2.13 to 4.0.0, but while running the application, I am getting the error:

Started GET "/signup.html" for 127.0.0.1 at 2016-06-29 17:28:16 +0530 ActiveRecord::SchemaMigration Load (0.2ms) SELECT "schema_migrations".* FROM "schema_migrations" Processing by AccountsController#new as HTML Parameters: {"plan"=>"year"} Completed 500 Internal Server Error in 51ms ** [Airbrake] Notice was not sent due to configuration: Environment Monitored? false API key set? true

NoMethodError - undefined method each' for nil:NilClass:
activemerchant (1.47.0) lib/active_merchant/billing/model.rb:11:in
initialize' app/controllers/accounts_controller.rb:83:in load_billing' activesupport (4.0.0) lib/active_support/callbacks.rb:437:in _run__3203841093432473566__process_action__callbacks' activesupport (4.0.0) lib/active_support/callbacks.rb:80:in run_callbacks'
actionpack (4.0.0) lib/abstract_controller/callbacks.rb:17:in
process_action' actionpack (4.0.0) lib/action_controller/metal/rescue.rb:29:in `process_action'
.......................................

This is my controller code :

class AccountsController < ApplicationController

  #inherit_resources
  ssl_required :new, :create
  before_filter :load_billing, :only => [:new, :create]

  def new
    @account_name = Rails.application.config.custom.accounts.send(params[:account_name]).name
    @account_signup_message = Rails.application.config.custom.accounts.send(params[:account_name]).signup_message
  rescue
    nil
  end

  def create
    @address.first_name = @creditcard.first_name
    @address.last_name = @creditcard.last_name
    @account.address = @address
    @account.creditcard = @creditcard

    if @account.new_record?
      if @account.save
        flash[:notice] = 'Account was created.'
        sign_in(@user, :bypass => true)
        redirect_to session[:previous_url] || user_reports_path(@user)
      else
        render :action => 'new'
      end
    else
      @user.account_id = @account.id
      if @user.save
        flash[:notice] = 'User was created.'
        sign_in(@user, :bypass => true)
        redirect_to session[:previous_url] || user_reports_path(@user)
      else
        render :action => 'new'
      end
    end
  end

  protected

  def load_billing
    @creditcard = ActiveMerchant::Billing::CreditCard.new(params[:account].blank? ? nil : params[:account][:creditcard]) #This is the line it is showing error.
    @address = SubscriptionAddress.new(params[:account].blank? ? nil : params[:account][:address])
  end

end

routes.rb:

get '/signup'   => 'accounts#new'

accounts/new.html.erb:

<%= semantic_form_for(@account, :url => account_create_path, :html => { :multipart => true, :class => 'billing'}) do |f| %>

  <%= f.inputs :for => :user do |u| %>
     #name and email fields are mentioned.
  <% end %>

  <%= f.inputs :for => :creditcard do |c| %>
     #name, card_no, cvv, expire_date etc;
  <% end %>
<% end %>

This is my activemerchant (1.47.0) lib/active_merchant/billing/model.rb:

require "active_merchant/billing/compatibility"
require "active_merchant/empty"

module ActiveMerchant
  module Billing
    class Model
      include Compatibility::Model
      include Empty

      def initialize(attributes = {})
        attributes.each do |key, value|
          send("#{key}=", value)
        end
      end

      def validate
        {}
      end

      private

      def errors_hash(array)
        array.inject({}) do |hash, (attribute, error)|
          (hash[attribute] ||= []) << error
          hash
        end
      end
    end
  end
end

include Empty is the error showing line in the above code. And there is no model.rb file for activemerchant version 1.20.4 (my previous). Please help me.

3
From looking at your error message I can tell that the issue is in a callback on lib/active_merchant/billing/model.rb:11. On that line, whatever you are calling .each on doesn't exist. Post the code from that file if you are still confused - ruby_newbie
I updated in the question. Please check. - user3189916

3 Answers

2
votes

"include Empty is the error showing line in the above code. " No it is raising an exception on line 11 which is " attributes.each do |key, value|" because attributes is nil.

So change this line:

@creditcard = ActiveMerchant::Billing::CreditCard.new(params[:account].blank? ? nil : params[:account][:creditcard])

to this:

@creditcard = ActiveMerchant::Billing::CreditCard.new(params[:account].blank? ? {} : params[:account][:creditcard])

now you will be sending through something that won't raise if you call .each on it.

foo = {}
 => {} 
2.3.0 :002 > foo.each {|key, value| puts "#{key}=#{value}" }
 => {} 

But just so that you understand:

In the initialize method this line

def initialize(attributes = {})

initializes an instance of the class with a default parameter of an empty hash if no other parameter is being passed in. In your ternary on this line:

@creditcard = ActiveMerchant::Billing::CreditCard.new(params[:account].blank? ? nil : params[:account][:creditcard]) 

this:

params[:account].blank? ? nil : params[:account][:creditcard]

says that if there are no account params, send nil as a parameter to initialize. The nil value overwrites the default({}) value and you get undefined method each on nil class because you are calling:

attributes.each do |key, value|

but you have defined attributes as nil.

1
votes

ActiveMerchant.new requires an hash of attributes and their values(default is empty hash), but you are passing nil which is causing the error. Check this implementation for better understanding. I think that you need not pass anything(not even nil) if params[:account][:creditcard] is not present.

0
votes

You forgot to close square brackets on before_action call.