Custom Plugins

By now, you know that most of the features from this library are added as plugins.

Plugins are activated for a single session, once you call .plugin, and calls can be concatenated in order for load multiple plugins:

# enabling follow redirects plugin
session = HTTPX.plugin(:follow_redirects)

# enabling new session with cookies plugin
session_with_cookies = session.plugin(:cookies)

Plugins are a pattern following a convention for “controlled patching”. In order for you to create a plugin you can follow the following steps:

  1. Creating the plugin module

This part is straightforward

# The simplest plugin which does nothing
module MyPlugin
end

custom_session = HTTPX.plugin(MyPlugin)

1.1 (Optionally) register it

Plugins can be called via a symbol (like the :cookies example from above). For that to happen, you have to register them. There a few rules for that, described in the gist below:

# 1. Create your plugin module under lib/httpx/plugins
#
# for this example: lib/httpx/plugins/custom.rb
#
module HTTPX::Plugins
  module Custom
  end
  register :custom, Custom
end

# now you can:
custom_session = HTTPX.plugin(:custom)

That’s it, your “minimal viable product”.

  1. Decorate existing components

Methods can be added to some of the internal components, by defining certain modules within your plugin:

  • InstanceMethods: methods will be added to the session.
  • HeadersMethods: methods will be added to the Headers of the above.
  • RequestMethods: methods will be added to the session Requests.
  • ResponseMethods: methods will be added to the session Responses.
  • RequestBodyMethods: methods will be added to the session Request Body.
  • ResponseBodyMethods: methods will be added to the session Response Body.
  • ConnectionMethods: methods will be added to the session Connections
  • OptionsMethods: methods will be added to the session Options
module HTTPX::Plugins
  module Custom
    module InstanceMethods
      def foo
        "foo"
      end
    end
  end
  # ...
end

HTTPX.plugin(:custom).foo #=> "foo"
  1. Add extra options

Options can be passed through all layers, and reused inside of your plugins. You have to define them first:

session = HTTPX.plugin(:custom, bar: 1) #=> "unknown option: bar"

In order for you to do that, you have to define the “transformer” method on the OptionsMethods, which you do by creating a method with the “option_” prefix, followed by the option:

module HTTPX::Plugins
  module Custom
    module OptionsMethods
      # creates :bar option
      def option_bar(value)
        # must be an integer
        Integer(value)
      end
    end

    module InstanceMethods
      def foo
        @options.bar
      end
    end
  end
  # ...
end

HTTPX.plugin(:custom).foo #=> nil
HTTPX.plugin(:custom, bar: 2).foo #=> 2
HTTPX.plugin(:custom).with(bar: 1).foo #=> 1
HTTPX.plugin(:custom).with(bar: "a").foo #=> invalid value for Integer(): "a" (ArgumentError)

In case you want to provide a sensible default for an option, you can use the “hook” method extra_options:

# same example from above
module HTTPX::Plugins
  module Custom
    def self.extra_options(options)
      options.merge(bar: 2)
    end
  # ...
  end
end

HTTPX.plugin(:custom).foo #=> 2
  1. Load/Configure

Your custom plugin might depend on external libraries. For example, you might want to integrate with some internal token generation gem, and assign those tokens on each request. The plugin system provides two “hooks” one can use for this goal:

  • load_dependencies(session_class): called at the beginning of the loading process, can be used, p.ex. to require dependencies, or some other form of pre-processing;
  • configure(session_class): called at the end of the loading process, can do some post-processing; also used to load other plugins implicitly;

For our example, you then use a combination of all above methods to load the gem, load the :auth plugin for convenience auth helpers, and off we go:

module HTTPX::Plugins
  module Custom
    class << self
      def load_dependencies(*)
        require "internal_auth"
      end

      def configure(klass)
        klass.plugin(:auth)
      end
    end

    module InstanceMethods
      def authenticate
        token = InternalAuth.generate

        bearer_auth(token)
      end
    end
  end
  # ...
end

HTTPX.plugin(:custom).authenticate.get("https://internal-smth/action") #=> "..Authorization: Bearer custom-token..."

You’re kindly invited to look at the existing plugins to understand the possibilities of what could be done.

Next: Integrations