Glass is a small library which provides a way to transparently proxy function calls in a pluggable fashion.
Add :glass
to the list of dependencies in mix.exs
:
def deps do
[
{:glass, "~> 0.1.0"}
]
end
This is immediately useful if you're working with multiple applications potentially deployed in different releases/environments but need to call functions between them.
Usually, you could do this via the built-in :rpc
or :erpc
modules (amongst others), but
this requires ceremony and boilerplate to set up and maintain.
Glass aims to live up to its name, and the idea that the BEAM is network-transparent, by completely hiding the fact that you're not just calling a local function.
Once installed, you can use Glass
as simply as adding the following to your code:
# 1) Setup and initialize `Glass`
use Glass
# 2) Define a `GlassProxy` for the target module, which for this example, we can
# imagine is in another Elixir application which is bundled in the same umbrella
# project as the current application, but is *released separately* in a production
# environment so *we can't directly call functions in it*.
defglass ReportService, via: Glass.Methods.RPC
# 3) Be blown away by `Glass` letting you call these functions directly...
def accounts_receivable_report do
current_user()
|> MyApp.Accounting.accounts_receivable()
|> ReportService.to_excel!()
end
Glass is designed to be pluggable and extensible, and comes with a few built-in methods for proxying function calls between modules.
Glass.Methods.Apply
(default): Useful for prototyping locally, this method simply calls the target function directly in the target module viaKernel.apply/3
.Glass.Methods.RPC
: This method uses:rpc
to call the target function in the target module. The node the rpc call is executed on is currently limited to a random node in the cluster (:erlang.nodes()
), but this may be configurable in the future.
You can also define your own method for proxying function calls by implementing the
Glass.Method
behaviour. If you want to customize the way Glass
proxies function calls,
you can define your own module and pass it as the :via
option to defglass
.
defmodule MyApp.Tupleize do
@behaviour Glass.Method
@impl Glass.Method
def handle_proxy(module, function, args) do
{module, function, args}
end
end
defmodule MyApp.Users do
use Glass
defglass ReportService, via: MyApp.Tupleize
...
def users_report do
current_org()
|> list_users()
|> Enum.map(&build_report_rows/1)
|> ReportService.to_csv!()
end
end
iex(1)> MyApp.Users.users_report()
{ReportService, :to_csv!, [ ... ]}
-
:private
(default:true
): Whether or not to generate the proxy module as private. This is useful if you want to prevent direct access to the proxy module and force all calls to go throughGlass
. Mainly affects tab-completion in IEx. -
:debug
(default:false
): Whether or not to generate debug information when proxying function calls. This is useful for debugging and tracing calls between modules. -
:as
: If provided, aliases and binds the createdGlass
proxy to the specified alias. If not provided, anyGlass
proxy must be called via its fully qualified name.
-
Magic: This is a very magical library and should be used with caution. It's designed to be a transparent proxy, but this means that it can be difficult to debug when things go wrong. Use with caution and test thoroughly.
-
Performance: The easier it is to write network-transparent code, the easier it is to write slow network-transparent code.
Be mindful of the performance implications of calling functions between modules in this way as depending on the
:via
method used, there may be a non-trivial serialization-deserialization overhead, or limitations on how much data can be sent between nodes before issues arise. -
Security: This library is designed to be used in a trusted environment where you have control over the nodes in the cluster.
It's not designed to be used in a hostile environment where you need to worry about malicious actors. Be mindful of the security implications of calling functions between modules in this way.
You can mitigate some of these concerns by using the
:via
option to define your own method for proxying function calls with additional security checks, but this is left as an exercise to the reader.
-
Anonymous via functions: Allow for anonymous functions to be passed as the
:via
option todefglass
to allow for more flexible proxying of function calls. -
Customizable debug output: Allow for custom debug output to be generated when proxying function calls.
-
Customizable error handling: Allow for custom error handling to be defined when proxying function calls.
-
Telemetry integration: Allow for
Telemetry
events to be emitted when proxying function calls to allow for better observability of function calls between modules. -
Testing utilities: Allow for easier testing of
Glass
-proxied functions by providing utilities to mock out theGlass
proxying mechanism.
Glass
is released under the MIT License. See the LICENSE file for more information.