Maps module name prefixes to directory paths.
A mount is a list of mount points, each one associating a module prefix with
the directory where the modules under that prefix belong. In a Mix project it
is defined by the :mount entry of the :modkit configuration in mix.exs:
def project do
[
# ...
modkit: [
mount: [
{MyApp, "lib/my_app"},
{Mix.Tasks, "lib/mix/tasks", flavor: :mix_task}
]
]
]
endOnce defined, a mount gives the preferred file path of any module it covers:
iex> mount = Modkit.Mount.define!([{MyApp, "lib/my_app"}])
iex> Modkit.Mount.preferred_path(mount, MyApp.Some.Worker)
{:ok, "lib/my_app/some/worker.ex"}Mount points are tried from the most precise prefix to the least precise one, so a module always resolves to the deepest prefix that covers it.
Summary
Functions
Derives a mount for test modules from the given mount.
Builds a mount from a list of mount points.
Builds a mount from a list of mount points. Raises an ArgumentError when a
point is invalid.
Transforms a module file path (without extension) into the corresponding test file path (also without extension).
Returns the file path where the given module belongs according to the mount.
Returns whether the given mount point covers the module, that is whether the
point prefix is a prefix of the module name. The module is given as a list of
segments as returned by Module.split/1.
Returns the mount point covering the given module.
Functions
Derives a mount for test modules from the given mount.
Each mount point is replaced by two points targeting the test directory: one
keeping the original prefix, and one with a Test suffix on the prefix. For
instance {MyApp, "lib/my_app"} produces {MyApp, "test/my_app"} and
{MyAppTest, "test/my_app_test"}. Paths resolved from the derived mount use
the .exs extension:
iex> mount = Modkit.Mount.define!([{MyApp, "lib/my_app"}])
iex> test_mount = Modkit.Mount.as_test(mount)
iex> Modkit.Mount.preferred_path(test_mount, MyApp.WorkerTest)
{:ok, "test/my_app/worker_test.exs"}Points mounted on :ignore are kept as they are.
Builds a mount from a list of mount points.
Accepts the same points and options as define!/2 but returns
{:ok, mount}, or {:error, exception} when a point is invalid.
Builds a mount from a list of mount points. Raises an ArgumentError when a
point is invalid.
Each point is a {prefix, path} or {prefix, path, opts} tuple:
prefix- a module whose namespace is mounted on the path.path- the directory for the modules under the prefix, or:ignoreto exclude those modules from path resolution.opts- accepts a:flavoroption with one of the following values::elixir(the default) - path segments are the snake cased module segments, as inlib/my_app/some/worker.ex.:mix_task- segments are joined with dots, as inlib/mix/tasks/mod.new.ex.:phoenix- controllers, views, components, live views, channels and sockets are placed in the conventional Phoenix sub-directories, as inlib/my_app_web/controllers/user_controller.ex. Namespaces are nested under that sub-directory, as inlib/my_app_web/controllers/admin/user_controller.ex.
Options
:names- custom snake case forms for words found in module names, given as{word, snake_form}pairs in a keyword list or a map. For instance withnames: [RabbitMQ: "rabbitmq"], the moduleMyApp.RabbitMQConsumerresolves tomy_app/rabbitmq_consumer.exinstead ofmy_app/rabbit_mq_consumer.ex.
Transforms a module file path (without extension) into the corresponding test file path (also without extension).
The transformation replaces a leading lib/ or test/ segment with test/,
or prepends test/ otherwise, then appends _test to the last segment.
iex> Modkit.Mount.path_as_test("lib/my_app/foo")
"test/my_app/foo_test"
iex> Modkit.Mount.path_as_test("dev/my_app/foo")
"test/dev/my_app/foo_test"
iex> Modkit.Mount.path_as_test("test/support/my_app/foo")
"test/support/my_app/foo_test"
Returns the file path where the given module belongs according to the mount.
The path joins the mount point directory with the snake cased module segments
that follow the point prefix. The extension is .ex, or .exs for mounts
derived with as_test/1.
Returns {:ok, path} on success, :ignore when the module resolves to an
:ignore point, {:error, :not_mounted} when no point covers the module,
and {:error, :not_elixir} for module names without the Elixir. namespace,
such as Erlang module names.
Examples
iex> mount = Modkit.Mount.define!([{MyApp, "lib/my_app"}, {MyApp.Schemas, "lib/schemas"}])
iex> Modkit.Mount.preferred_path(mount, MyApp.Some.Worker)
{:ok, "lib/my_app/some/worker.ex"}
iex> Modkit.Mount.preferred_path(mount, MyApp.Schemas.User)
{:ok, "lib/schemas/user.ex"}
iex> Modkit.Mount.preferred_path(mount, Other.Module)
{:error, :not_mounted}
Returns whether the given mount point covers the module, that is whether the
point prefix is a prefix of the module name. The module is given as a list of
segments as returned by Module.split/1.
Returns the mount point covering the given module.
The mount can be given as a Modkit.Mount struct or a list of
Modkit.Mount.Point structs, and the module as a module name or a list of
segments as returned by Module.split/1.
Returns {:ok, point} for the first point whose prefix covers the module,
:ignore when that point is mounted on :ignore, and
{:error, :not_mounted} when no point matches.
Examples
iex> mount = Modkit.Mount.define!([{MyApp, "lib/my_app"}, {MyApp.Ecto, :ignore}])
iex> {:ok, point} = Modkit.Mount.resolve(mount, MyApp.Worker)
iex> point.path
"lib/my_app"
iex> Modkit.Mount.resolve(mount, MyApp.Ecto.Repo)
:ignore
iex> Modkit.Mount.resolve(mount, Other.Worker)
{:error, :not_mounted}