Search code examples
ruby-on-railsnomethoderrorsidekiqairbrake

Rails - NoMethodError


I am receiving NoMethodErrors when my DeltaSynWorker runs. This happens in an application that was built as a revision of a currently working application. I am not the original programmer, and I am coming at it from a Java background (I mention this because it is possible I am missing something very obvious to others). I cannot figure out why NoMethodeError is being thrown when the code is very similar to code that is currently working fine in a different web application.

The Error:

NoMethodError: undefined method `client' for #<ApiRequestBuilder:0x0000000705a8f0>

delta_sync_worker.rb

class DeltaSyncWorker < SyncWorker

  include Sidekiq::Worker
  sidekiq_options queue: "delta_syncs"

  def perform(subscriber_id, client_id)

      sleep(10)
      current_subscriber = ApiSubscriberDecorator.decorate(Subscriber.find(subscriber_id))
      Time.zone = current_subscriber.time_zone
      client = ApiClientDecorator.decorate(Client.find(client_id))
      arb = ApiRequestBuilder.new(URI.parse(SR_HOST + '/servlet/sync/process'))

      if arb.client(:subscriber => current_subscriber, :client => client)

        arb.transmit

        if arb.success?
          current_subscriber.touch(:sync_updated_at)
          decorated_client = ClientDecorator.decorate(client.model)        
          ConfirmationsSyncWorker.perform_in(1.hours, current_subscriber.id)
        else
          error_params = {:subscriber => current_subscriber.id, :response_body =>            arb.response.body, :request_body => arb.request.body, :sync_type => "DeltaSyncWorker"}
          Airbrake.notify(:error_class => "sync_error", :error_message => "Sync Error: #{arb.response.message}", :params => error_params)
        end
      end
    end
  end

api_request_builder.rb

require 'nokogiri'
class ApiRequestBuilder < AbstractController::Base
  include AbstractController::Rendering
  include AbstractController::Layouts
  include AbstractController::Helpers
  include AbstractController::Translation
  include AbstractController::AssetPaths

  self.view_paths = "app/api"

  attr_accessor :request_body, :request, :response, :append_request_headers, :request_method, :url

  def initialize(url, *args)
    @url = url
    if args
      args.each do |arg|
        arg.each_pair{ |k,v| instance_variable_set("@#{k.to_s}", v) }
      end
    end
  end

  # this will search for an api request template in api/requests, render that template and set any instance variables
  def method_missing(meth, *args, &block)

    if lookup_context.template_exists?("requests/#{meth.to_s}")

      if args
        args.each do |arg|
          arg.each_pair{|k,v| instance_variable_set("@#{k.to_s}", v) }
        end
      end

      @request_body = (render :template => "requests/#{meth.to_s}")
    else
      super
    end

  end

  def transmit
    @request_method ||= "Post"
    @request = "Net::HTTP::#{@request_method}".constantize.new(@url.path)
    @request['x-ss-user'] = @subscriber.sr_user if @subscriber &&        @subscriber.sr_user.present?
    @request['x-ss-pwd'] =  @subscriber.sr_password if @subscriber && @subscriber.sr_password.present?

    unless @append_request_headers.nil?
      @append_request_headers.each_pair{ |k,v| @request[k] = v }
    end
    @request.body = @request_body if request_body? && @request.request_body_permitted?

    @http = Net::HTTP.new(@url.host, @url.port)
    @http.use_ssl = true
    @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    @response = @http.request(@request)

  end

  def success?
    if @response.code == 200.to_s
      return true
    else
      return false
    end
  end

  def request_body?
    unless @request_body.nil?
      return true
    else
      return false
    end
  end
end

I have been looking at other NoMethodError questions here, but I cannot find an answer I feel applies to my situation. Any help is greatly appreciated. Thanks!


Solution

  • method_missing will catch sent messages for which there is no method defined, and the call to super at the end will pass it up to Ruby's standard method_missing behavior, which is what you are seeing (NoMethodError). However, this only happens if the if condition is not met, which is what I suspect is happening here, and would explain why it works in some situations but not in others. The call to :client, having found no matching methods along the inheritance chain, will look for a template called "requests/client" - try adding this template and see if that fixes the issue.