module Protobuf::Rpc::ServiceFilters::InstanceMethods

def call_or_send(callable, *args, &block)


__send__ assuming that we respond_to it. Return the call's return value.
Call the object if it is callable, otherwise invoke the method using
def call_or_send(callable, *args, &block)
  return_value = case
                 when callable.respond_to?(:call) then
                   callable.call(self, *args, &block)
                 when respond_to?(callable, true) then
                   __send__(callable, *args, &block)
                 else
                   raise "Object #{callable} is not callable"
                 end
  return return_value
end

def filters


Get back to class filters.
def filters
  self.class.filters
end

def invoke_filter?(rpc_method, filter)


if the filter should not be invoked, true if invocation should occur.
options for every filter. Each option check is expected to return false
should be called. Specifically checks the :if, :unless, :only, and :except
Predicate which uses the filter options to determine if the filter
def invoke_filter?(rpc_method, filter)
  return invoke_via_only?(rpc_method, filter) \
    && invoke_via_except?(rpc_method, filter) \
    && invoke_via_if?(rpc_method, filter) \
    && invoke_via_unless?(rpc_method, filter)
end

def invoke_via_except?(rpc_method, filter)


Value should be a symbol/string or an array of symbols/strings.

This option is the opposite of :only.
other target rpc endpoint methods not listed should be invoked.
return false to indicate that the filter should not be invoked. Any
If the target rpc endpoint method is listed under an :except option,
def invoke_via_except?(rpc_method, filter)
  except = [ filter.fetch(:except) { [] } ].flatten
  return except.empty? || ! except.include?(rpc_method)
end

def invoke_via_if?(rpc_method, filter)


or an object that responds to `call`.
Value can either be a symbol/string indicating an instance method to call

return value to determine if we should invoke the target filter.
Used by `invoke_filter?` which expects a true/false
Invoke the given :if callable (if any) and return its return value.
def invoke_via_if?(rpc_method, filter)
  if_check = filter.fetch(:if) { lambda { |service| return true } }
  do_invoke = case
              when if_check.nil? then
                true
              else
                call_or_send(if_check)
              end
  return do_invoke
end

def invoke_via_only?(rpc_method, filter)


Value should be a symbol/string or an array of symbols/strings.

option should not be invoked. This option is the opposite of :except.
it should be invoked. Any target rpc endpoint methods not listed in this
If the target rpc endpoint method is listed in the :only option,
def invoke_via_only?(rpc_method, filter)
  only = [ filter.fetch(:only) { [] } ].flatten
  return only.empty? || only.include?(rpc_method)
end

def invoke_via_unless?(rpc_method, filter)


or an object that responds to `call`.
Value can either be a symbol/string indicating an instance method to call

return value to determine if we should invoke the target filter.
of it's return value. Used by `invoke_filter?` which expects a true/false
Invoke the given :unless callable (if any) and return the opposite
def invoke_via_unless?(rpc_method, filter)
  unless_check = filter.fetch(:unless) { lambda { |service| return false } }
  skip_invoke = case
                when unless_check.nil? then
                  false
                else
                  call_or_send(unless_check)
                end
  return ! skip_invoke
end

def rescue_filters

def rescue_filters
  self.class.rescue_filters
end

def run_around_filters(rpc_method)


end
end
end
my_endpoint
filter3 do
filter2 do
filter1 do

It is similar to this call chain:
When the my_endpoint method is invoked using Service#callable_rpc_method,

end
end
# do stuff
def my_endpoint

around_filter :filter1, :filter2, :filter3
class MyService

Let's say you have a class defined with the following filters:

with the inner-most method called being the actual rpc endpoint.
Around filters are invoked in the order they are defined, outer to inner,

condition), simply do not yield.
method to be invoked. If the endpoint should not be run (due to some
simply build a method that yields control when it expects the underlying
Reverse build a chain of around filters. To implement an around chain,
def run_around_filters(rpc_method)
  final = lambda { __send__(rpc_method) }
  filters[:around].reverse.inject(final) { |previous, filter|
    if invoke_filter?(rpc_method, filter)
      lambda { call_or_send(filter[:callable], &previous) }
    else
      previous
    end
  }.call
end

def run_filters(rpc_method)


be used instead of the other run methods directly.
Entry method to call each filter type in the appropriate order. This should
def run_filters(rpc_method)
  run_rescue_filters do
    continue = run_unwrapped_filters(filters[:before], rpc_method, true)
    if continue
      run_around_filters(rpc_method)
      run_unwrapped_filters(filters[:after], rpc_method)
    end
  end
end

def run_rescue_filters

def run_rescue_filters
  if rescue_filters.keys.empty?
    yield
  else
    begin
      yield
    rescue *rescue_filters.keys => ex
      callable = rescue_filters.fetch(ex.class) {
        mapped_klass = rescue_filters.keys.detect { |child_klass| ex.class < child_klass }
        rescue_filters[mapped_klass]
      }
      call_or_send(callable, ex)
    end
  end
end

def run_unwrapped_filters(unwrapped_filters, rpc_method, stop_on_false_return = false)


is either a before or after filter, not an around filter.
Loop over the unwrapped filters and invoke them. An unwrapped filter
def run_unwrapped_filters(unwrapped_filters, rpc_method, stop_on_false_return = false)
  unwrapped_filters.each do |filter|
    if invoke_filter?(rpc_method, filter)
      return_value = call_or_send(filter[:callable])
      return false if stop_on_false_return && return_value == false
    end
  end
  return true
end