Ruby - Check if controller defined

I am using Solidus with Ruby on Rails to create a webshop and I have multiple modules for that webshop.

So, I defined a me controller into an module called 'solidus_jwt_auth' with the followin code:

module Spree
  module Api
    class MeController < Spree::Api::BaseController
      def index
        ...
      end

      def orders
        ...
      end

      def addresses
        ...
      end
    end
  end
end

I want to extend this in another module called 'solidus_prescriptions' so I created a decorator for this with the following code me_decorator:

if defined? Spree::Api::MeController.class
  Spree::Api::MeController.class_eval do
    def prescriptions
      ...
    end

    def create_prescription
      ...
    end

    private

    def prescription_params
      params.require(:prescription).permit(
          *Spree::CustomerPrescription.permitted_attributes
      )
    end
  end
end

And for this I wrote unit tests in solidus_prescription module and integration tests in webshop. The unit tests are working fine, but the integration tests are giving the following error:

Error: MeEndpointsTest#test_me/prescriptions_post_endpoint_throws_an_error_when_wrong_params: AbstractController::ActionNotFound: The action 'create_prescription' could not be found for Spree::Api::MeController test/integration/me_endpoints_test.rb:68:in `block in '

Which means that he can not find the MeController defined in another module. How can I make the check if the MeController is defined since the code bellow does not help me with anything:

if defined? Spree::Api::MeController.class
end

Upvotes: 1

Views: 458

Answers (2)

m. simon borg
m. simon borg

Reputation: 2575

if defined? should do exactly what you want it to do in theory. The problem is you're checking if defined? Spree::Api::MeController.class. The #class of your class is Class. So what you're really getting is if defined? Class which will always be true!

This issue is most likely not that the conditional is failing but that it's never getting read. Rails lazy loads most of the code you write, meaning the file is not read until it's called somewhere in execution.

The decorator module should just contain the methods you want to add, without the conditionals or the use of class_eval. Then in the original class you can include it.

module Spree
  module Api
    class MeController < Spree::Api::BaseController
      include MeDecorator
    end
  end
end

If for any reason you're not certain MeDecorator will be defined, don't use defined?, because defined? MeDecorator will not actually go looking for it if it's not defined and load the necessary file. It will return nil if the constant has no value. Just rescue a NameError

module Spree
  module Api
    class MeController < Spree::Api::BaseController
      begin
        include MeDecorator
      rescue NameError => e
        logger.error e
      end
    end
  end
end

Upvotes: 1

This worked in the end:

def class_defined?(klass)
  Object.const_get(klass)
rescue
  false
end

if class_defined? 'Spree::Api::MeController'
 ....
end

Upvotes: 1

Related Questions