BeamPatch (beam_patch v0.2.2)

View Source

Patch Elixir & Erlang modules at runtime

Example

require BeamPatch

assert String.jaro_distance("same", "same") == 1.0

BeamPatch.patch_and_load! String do
  @modifier 2

  @override original: [rename_to: :jaro_distance_orig]
  def jaro_distance(a, b), do: jaro_distance_orig(a, b) * @modifier
end

assert String.jaro_distance("same", "same") == 2.0

Changing function visibility

BeamPatch.patch_and_load! String do
  # Rename the original and make it public
  @override original: [rename_to: :jaro_distance_orig, export?: true]
  # As expected, `defp` keyword makes `jaro_distance/2` private
  defp jaro_distance(a, b), do: jaro_distance_orig(a, b) * 2

  # Define a completely new function as a part of `String`'s interface
  def my_jaro_distance(a, b),
    do: jaro_distance(a, b) * jaro_distance_orig(a, b)
end

refute function_exported?(String, :jaro_distance, 2)
assert String.jaro_distance_orig("same", "same") == 1.0
assert String.my_jaro_distance("same", "same") == 2.0

Split patching and loading

defmodule CompileTimePatch do
  # A common pattern is patching at compilation time,
  # but loading the patched module at runtime
  @patch BeamPatch.patch_quoted!(
           String,
           quote do
             def hello, do: :world
           end
         )

  def load, do: BeamPatch.load!(@patch)
end

CompileTimePatch.load()
assert String.hello() == :world

Installation

def deps do
  [
    {:beam_patch, "~> 0.2.2"}
  ]
end

Summary

Functions

Load a patched module.

Load a patched module.

Patch (without loading) a module with body given via do end block.

Patch and load a module with body given via do end block.

Patch a module with a quoted body.

Patch a module with a quoted body.

Patch and load a module with a quoted body.

Patch and load a module with a quoted body.

Functions

load(patch)

@spec load(BeamPatch.Patch.t()) :: :ok | {:error, BeamPatch.Error.t()}

Load a patched module.

load!(patch)

@spec load!(BeamPatch.Patch.t()) :: :ok

Load a patched module.

Raises BeamPatch.ModuleLoadError.t/0 on error.

patch!(module, bytecode \\ nil, list)

(macro)

Patch (without loading) a module with body given via do end block.

This is sugar for patch_quoted!/2 function.

Raises one of BeamPatch.Error.t/0 on error.

patch_and_load!(module, bytecode \\ nil, list)

(macro)

Patch and load a module with body given via do end block.

This is sugar for patch_quoted_and_load!/2 function.

Raises one of BeamPatch.Error.t/0 on error.

patch_quoted(module, bytecode \\ nil, body)

@spec patch_quoted(module(), bytecode :: binary() | nil, body :: Macro.t()) ::
  {:ok, BeamPatch.Patch.t()} | {:error, BeamPatch.Error.t()}

Patch a module with a quoted body.

patch_quoted!(module, bytecode \\ nil, body)

@spec patch_quoted!(module(), bytecode :: binary() | nil, body :: Macro.t()) ::
  BeamPatch.Patch.t()

Patch a module with a quoted body.

Raises one of BeamPatch.Error.t/0 on error.

patch_quoted_and_load(module, bytecode \\ nil, body)

@spec patch_quoted_and_load(module(), bytecode :: binary() | nil, body :: Macro.t()) ::
  {:ok, BeamPatch.Patch.t()} | {:error, BeamPatch.Error.t()}

Patch and load a module with a quoted body.

patch_quoted_and_load!(module, bytecode \\ nil, body)

@spec patch_quoted_and_load!(module(), bytecode :: binary() | nil, body :: Macro.t()) ::
  :ok

Patch and load a module with a quoted body.

Raises one of BeamPatch.Error.t/0 on error.