module Filterable
  extend ActiveSupport::Concern

  class_methods do
    def filterable_by(*scopes)
      @@filterer = Filterer.new(scopes)
    end

    def filter(name)
      @@filterer.filter(name, self)
    end

    def filter!(name)
      @@filterer.filter!(name, self)
    end

    private

    class Filterer
      def initialize(scopes)
        @scopes = Array.wrap(scopes).map(&:to_s)
      end

      # Returns the original scope if filter has not been whitelisted
      def filter(name, scope)
        return scope.all unless filter_is_whitelisted?(name)

        scope.send(name)
      end

      # Raises an exception unless filter has been whitelisted
      def filter!(name, scope)
        raise "Cannot filter #{scope} by `#{name}`" unless filter_is_whitelisted?(name)

        scope.send(name)
      end

      private

      def filter_is_whitelisted?(name)
        @scopes.include?(name)
      end
    end
  end
end
