Freshly/spicerack

View on GitHub
around_the_world/lib/around_the_world.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true

require "active_support/concern"
require "active_support/core_ext/object/blank"
require "active_support/descendants_tracker"

require_relative "around_the_world/errors"
require_relative "around_the_world/method_wrapper"
require_relative "around_the_world/proxy_module"
require_relative "around_the_world/version"

module AroundTheWorld
  extend ActiveSupport::Concern

  included do
    singleton_class.extend(AroundTheWorld::ClassMethods)
  end

  module ClassMethods
    protected

    # Defines a method that gets called +around+ the given instance method.
    #
    # @example
    #   class SomeClass
    #     around_method :make_something_happen!, :did_something_happened? do |*args| # use |...| for ruby 2.7+
    #       # For Ruby <= 2.6:
    #       things_happened = super(*args)

    #       #Or, for Ruby <= 2.7:
    #       things_happened = super(...)

    #       if things_happened
    #         "Something happened!"
    #       else
    #         "Nothing to see here..."
    #       end
    #     end
    #
    #     def dont_look_in_here
    #       do_some_things
    #     end
    #   end
    #
    #   SomeClass.new.dont_look_in_here
    #   => "Something happened!"
    #
    # @example
    #   around_method :dont_look_in_here, prevent_double_wrapping_for: :memoization do |...|
    #     @memoized ||= super(...)
    #   end
    #
    #   around_method :dont_look_in_here, prevent_double_wrapping_for: :memoization do |...|
    #     @memoized ||= super(...)
    #   end
    #   # => AroundTheWorld::DoubleWrapError:
    #          "Module AroundTheWorld:ProxyModule:memoization already defines the method :dont_look_in_here"
    #
    #   around_method :dont_look_in_here do |...|
    #     do_something_else
    #     super(...)
    #   end
    #   # => no error raised
    #
    # @example
    #   class SomeClass
    #     include AroundTheWorld
    #
    #     class << self
    #       def a_singleton_method; end
    #
    #       around_method :a_singleton_method do |...|
    #         super(...)
    #         "It works for class methods too!"
    #       end
    #     end
    #   end
    #
    #   SomeClass.a_singleton_method
    #   => "It works for class methods too!"
    #
    # @api public
    # @param method_names [Array<Symbol, String>] A list of methods to be wrapped
    # @param :prevent_double_wrapping_for [Object]
    #   If defined, this prevents wrapping the method twice for a given purpose. Accepts any argument.
    # @param :allow_undefined_method [Boolean] When false, an error is raised if the wrapped method is not
    #   explicitly defined by the target module or class. Default: false
    def around_method(*method_names, prevent_double_wrapping_for: nil, allow_undefined_method: false, &block)
      method_names.each do |method_name|
        MethodWrapper.wrap(
          method_name: method_name,
          target: self,
          prevent_double_wrapping_for: prevent_double_wrapping_for,
          allow_undefined_method: allow_undefined_method,
          &block
        )
      end
    end
  end
end