0

I am trying to wrap my controller logic within the same conditional. Not sure how to achieve this as I am a bit new to ruby on rails. The pseudo code below is what I am trying to achieve

class Api::BaseController
  include Api::AuthConcern if SAME_CONDITION
  before_action -> { some_function() } if SAME_CONDITION

  if SAME_CONDITION
    rescue_from Api::AuthConcern::Unauthenticated do |e|
      render status: 401, json: { error: e.error }
    end

    rescue_from Api::AuthConcern::PermissionDenied do |e|
      render status: 403, json: { error: e.error }
    end
  end
end
6
  • Can you explain what problem this code is supposed to solve? It makes no sense at all as rescue_from is a class method used to handle exceptions. You have the if: and unless: options for callbacks (not the same thing as the ruby keyword) that take a lambda or a method name. Commented Sep 15, 2021 at 9:14
  • @max sorry for the confusion. What I am trying to do is actually wrap rescue, include, and before_action within a feature flag. If o activate the feature flag, then I’d like to enable the code wrapped in the conditional. Commented Sep 15, 2021 at 9:18
  • Is this flag class level? If so why not just place the code into a module (concern)? Commented Sep 15, 2021 at 9:21
  • The class body in Ruby is just a block of code thats evaluated in the context of the instance of Class so you can use whatever control structures you want. But I belive you're really going to regret adding a bunch of complexity as its going to be hard to test and lead to expected behavior as classes are persisted across requests by web servers that do forking. Commented Sep 15, 2021 at 9:24
  • 1
    A better idea is most like to just have your concern raise an exception and rescue it with a method which classes that include the module can override. Commented Sep 15, 2021 at 9:27

1 Answer 1

1

You ain't gonna need it. Just use methods instead:

module Api
  # Avoid adding "Concern" to your module names
  # it provides absolutely no information about what the object does
  module AuthConcern
    extend ActiveSupport::Concern

    included do
      rescue_from Unauthenticated { |e| respond_to_unauthenticated(e.error) }
      rescue_from PermissionDenied { |e| respond_to_permission_denied(e.error) } 
    end

    def respond_to_unauthenticated(message)
       render status: 401, json: { error: message }
    end

    def respond_to_permission_denied(message)
       render status: 403, json: { error: message }
    end
  end
end

This lets any class that includes the module customize the behavior by simply overriding the method:

module Api
  class FooController < BaseController
    # ...
  

    private  

    def respond_to_unauthenticated(error)
      render plain: 'Oh noes, you broke it!'
    end
  end
end

If you need to add more logic to to how a module augments the class with you can use the macro pattern. This is simply a class method that adds methods or behavior to the class:

module Api
  module AuthConcern
    extend ActiveSupport::Concern

    # ....

    module ClassMethods
      def authenticate!(**kwargs)
        before_action :authenticate!, **kwargs
      end
    end
  end
end
module Api
  class FooController < BaseController
    authenticate! except: :index
  end
end

This pattern is found all over Ruby in everything from attr_accessor to Rails callbacks and assocations.

Sign up to request clarification or add additional context in comments.

1 Comment

On a side note - do not use class Api::BaseController. Using the scope resolution operator does not set the correct module nesting and leads to suprising constant lookup and autoloading bugs. github.com/rubocop/ruby-style-guide#namespace-definition

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.