def mixin(args)
      # normalize the supplied argument (array or hash) into a hash of
      # { visibility => exports } pairs, where `visibility` is a symbol and
      # `exports` is a hash of { dependency_name => method_name } pairs
      if args.is_a?(Array)
        args = { @default_visibility => args }
      elsif !args.is_a?(Hash)
        raise ArgumentError, "invalid mixin argument: expected array or hash, got: #{args.class}"
      end

      # slurp each array of name (symbol) or name => alias (hash) imports into
      # a normalized hash of { dependency_name => method_name } pairs e.g.:
      #
      # before:
      #
      #   [:foo, { :bar => :baz }, :quux]
      #
      # after:
      #
      #   { :foo => :foo, :bar => :baz, :quux => :quux }

      # XXX transform_values isn't available in ruby 2.3 and we don't want to
      # pull in ActiveSupport just for one method (on this occasion)
      #
      # args = DEFAULT_EXPORTS.merge(args).transform_values do |exports|
      #     exports = [exports] unless exports.is_a?(Array)
      #     exports.reduce({}) do |a, b|
      #         a.merge(b.is_a?(Hash) ? b : { b => b })
      #     end
      # end

      args = DEFAULT_EXPORTS.merge(args).each_with_object({}) do |(key, exports), merged|
        exports = [exports] unless exports.is_a?(Array)
        merged[key] = exports.reduce({}) do |a, b|
          a.merge(b.is_a?(Hash) ? b : { b => b })
        end
      end

      @module_cache.get!(args) { module_for(args) }
    end
def mixin(args)
      # normalize the supplied argument (array or hash) into a hash of
      # { visibility => exports } pairs, where `visibility` is a symbol and
      # `exports` is a hash of { dependency_name => method_name } pairs
      if args.is_a?(Array)
        args = { @default_visibility => args }
      elsif !args.is_a?(Hash)
        raise ArgumentError, "invalid mixin argument: expected array or hash, got: #{args.class}"
      end

      # slurp each array of name (symbol) or name => alias (hash) imports into
      # a normalized hash of { dependency_name => method_name } pairs e.g.:
      #
      # before:
      #
      #   [:foo, { :bar => :baz }, :quux]
      #
      # after:
      #
      #   { :foo => :foo, :bar => :baz, :quux => :quux }

      # XXX transform_values isn't available in ruby 2.3 and we don't want to
      # pull in ActiveSupport just for one method (on this occasion)
      #
      # args = DEFAULT_EXPORTS.merge(args).transform_values do |exports|
      #     exports = [exports] unless exports.is_a?(Array)
      #     exports.reduce({}) do |a, b|
      #         a.merge(b.is_a?(Hash) ? b : { b => b })
      #     end
      # end

      args = DEFAULT_EXPORTS.merge(args).each_with_object({}) do |(key, exports), merged|
        exports = [exports] unless exports.is_a?(Array)
        merged[key] = exports.reduce({}) do |a, b|
          a.merge(b.is_a?(Hash) ? b : { b => b })
        end
      end

      @module_cache.get!(args) { module_for(args) }
    end
Takes an array or hash specifying the dependencies to export, and returns
 a module which defines getters for those dependencies.

   class Test
     # hash (specify visibilities)
     include Services.mixin private: :foo, protected: %i[bar baz], public: :quux

     # or an array of imports using the default visibility (:private by default)
     include Services.mixin %i[foo bar baz quux]

     def test
       foo + bar + baz + quux # access the dependencies
     end
   end

 The visibility of the generated getters can be controlled by passing a hash
 with { visibility => imports } pairs, where imports is an array of import
 specifiers. An import specifier is a symbol (method name == dependency name)
 or a hash with { dependency_name => method_name } pairs (aliases). If
 there's only one import specifier, its enclosing array can be omitted e.g.:

   include Services.mixin(private: :foo, protected: { :baz => :quux })

 is equivalent to:

   include Services.mixin(private: [:foo], protected: [{ :baz => :quux }])