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) callable.call(self, *args, &block) when respond_to?(callable, true) __send__(callable, *args, &block) else fail "Object #{callable} is not callable" end 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) 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 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) { ->(_service) { return true } } do_invoke = case when if_check.nil? true else call_or_send(if_check) end 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 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) { ->(_service) { return false } } skip_invoke = case when unless_check.nil? false else call_or_send(unless_check) end !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 = -> { __send__(rpc_method) } filters[:around].reverse.reduce(final) do |previous, filter| if invoke_filter?(rpc_method, filter) -> { call_or_send(filter[:callable], &previous) } else previous end 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) do mapped_klass = rescue_filters.keys.find { |child_klass| ex.class < child_klass } rescue_filters[mapped_klass] end 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 true end