Question

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!

Was it helpful?

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.

OTHER TIPS

I know Ive seen this before and I feel like it wasn't what it appeared to be, but ya basically method missing is just intercepting the method call and when you call arb.client, it is caught by method missing and therefore tries to render api/client.xml.arb or api whatever the file type is. -- Note that there should be a file in the initializers directory named somethig like api_template_handler.rb or arb_temmplate_handler.rb, which is what allows rails to see that template type in the view directory -- make sure that is there first. Also sidenote, _client.xml.api is a partial used by the other api request in that directory (full sync),

To debug Id start by, above the following line

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

Add a log statement

Rails.logger.debug "Can I see View?" + lookup_context.template_exists?("requests/#{meth.to_s}")

If true, then the problem is the error isnt getting caught properly because of method missing. If false, then the sidekiq worker isnt loading rails properly, or the view path isn't being added onto the rails view paths.

If true, Im guessing it might have something to do with the client model not being loaded, or an attribute on the client model not existing, that the builder is trying to call, and the error is somehow bubbling up to the api request builder class.

Oh also, just general stuff, but make sure redis and sidekiq are running, and restart passenger if its non local environment.

Let me know how it goes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top