Smuggle v1.0.0 Smuggle View Source

Pack Elixir data into self-extracting archives to paste elsewhere.

Ever had a large data structure you needed to attach to a ticket to make troubleshooting easier? Worse, was it full of references, pids, and structures with custom Inspect protocol installations that made the output of IO.inspect/2 not valid Elixir?

Smuggle makes it easy to pipe that value to a self-extracting archive you can paste anywhere for someone else to try on their system. It's as easy as:

Smuggle.dump(value)

The output will be Elixir code to reproduce your value on another system. It'll be:

(fn v -> v |> Base.decode64!(ignore: :whitespace) |> :erlang.binary_to_term() end).(~S"""
g20AAABBT2gsIGdvb2Qgb24geW91IGZvciBjaGVja2luZy4gSXQgY291bGQgaGF2ZSBiZWVuIGFu
eXRoaW5nLCByaWdodD8=
""")

See Usage below for more details.

Do you need to take a dependency on this? The first few dozen times I did it, I copied or re-invented the encode/1 and decode/1 pipelines, but I'm enjoying having it around now. If you're in a hurry and don't want to touch mix, loot what you need from below:

  def encode(term) do
    term
    |> :erlang.term_to_binary(compressed: 9)
    |> Base.encode64()
    |> Stream.unfold(&String.split_at(&1, 76))
    |> Enum.take_while(&(&1 != ""))
    |> Enum.join("\n")
  end

  def decode(encoded) do
    encoded
    |> Base.decode64!(ignore: :whitespace)
    |> :erlang.binary_to_term()
  end

Usage

If your target is an iex prompt:

Smuggle.dump(value)
# copy
# paste into other iex

The output of Smuggle.dump(true) is:

(fn v -> v |> Base.decode64!(ignore: :whitespace) |> :erlang.binary_to_term() end).(~S"""
g2QABHRydWU=
""")

Paste that back into the iex prompt of a local or remote console, and you'll get the value back:

Erlang/OTP 23 [erts-11.0.3] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [hipe]

Interactive Elixir (1.10.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> (fn v -> v |> Base.decode64!(ignore: :whitespace) |> :erlang.binary_to_term() end).(~S"""
...(1)> g2QABHRydWU=
...(1)> """)
true

It's more impressive when it's an %Ecto.Changeset{}, but you get the gist. Speaking of which, sometimes you're trying to paste vital troubleshooting information into a GitHub issue, comment, or gist. It's polite to wrap that in a <details>, and Smuggle does all the hard work:

Smuggle.dump(value, wrapper: :gfm)
# copy
# paste into GitHub

On GitHub, the self-extracting archive will be hidden behind a friendly summary so it's easy to skip:

gfm-closed

That friendly open source maintainer who'll check it out, though, can click once to see the whole thing:

gfm-open

Link to this section Summary

Types

Encoded terms.

Self-extraction code.

Functions

Decode a compressed binary in Base64.

Dump self-extracted.

Encode a term to a compressed binary in Base64.

Generate self-extraction code.

Wrap in a details tag for GitHub-flavoured markdown.

No-op wrapper.

Link to this section Types

Encoded terms.

Self-extraction code.

Link to this section Functions

Link to this function

decode(encoded)

View Source
decode(encoded()) :: term()

Decode a compressed binary in Base64.

Link to this function

dump(term, opts \\ [])

View Source
dump(term(), [{:wrapper, atom()}]) :: :ok

Dump self-extracted.

Encode a term to a compressed binary in Base64.

Link to this function

gen_sfx(encoded)

View Source
gen_sfx(encoded()) :: sfx()

Generate self-extraction code.

Wrap in a details tag for GitHub-flavoured markdown.

No-op wrapper.