Packages
absinthe
1.10.0
1.11.0
1.10.2
1.10.1
1.10.0
1.9.1
1.9.0
1.8.0
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.8
1.6.7
retired
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.5
1.5.0-rc.4
1.5.0-rc.3
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.5.0-beta.2
1.5.0-beta.1
1.5.0-beta.0
1.5.0-alpha.4
1.5.0-alpha.3
1.5.0-alpha.2
1.5.0-alpha.1
1.5.0-alpha.0
1.4.16
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
retired
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.3
1.4.0-rc.2
1.4.0-rc.1
1.4.0-rc.0
1.4.0-beta.5
1.4.0-beta.4
1.4.0-beta.3
1.4.0-beta.2
1.4.0-beta.1
1.3.2
1.3.1
1.3.0
1.3.0-rc.0
1.3.0-beta.2
1.3.0-beta.1
1.3.0-beta.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.2.0-beta.0
1.2.0-alpha0
1.2.0-alpha.2
1.2.0-alpha.1
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
0.5.2
0.5.1
0.5.0
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.2.3
0.2.2
0.2.1
0.1.0
GraphQL for Elixir
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/absinthe/subscription/pipeline_serializer.ex
defmodule Absinthe.Subscription.PipelineSerializer do
@moduledoc """
Serializer responsible for packing and unpacking pipeline stored in the Elixir registry.
The purpose of this logic is saving memory by deduplicating repeating options - (ETS
backed registry stores them flat in the memory).
"""
@packed_keys [:variables, :context]
alias Absinthe.{Phase, Pipeline}
@type options_label :: {:options, non_neg_integer()}
@type packed_phase_config :: Phase.t() | {Phase.t(), options_label()}
@type options_map :: %{options_label() => Keyword.t()}
@type packed_pipeline :: {:packed, [packed_phase_config()], options_map()}
@spec pack(Pipeline.t() | {module(), atom, list()}) :: packed_pipeline()
def pack({module, function, args})
when is_atom(module) and is_atom(function) and is_list(args) do
{module, function, args}
end
def pack(pipeline) do
{packed_pipeline, reverse_map} =
pipeline
|> List.flatten()
|> Enum.map_reduce(%{}, &maybe_pack_phase/2)
options_map = Map.new(reverse_map, fn {options, label} -> {label, options} end)
{:packed, packed_pipeline, options_map}
end
@spec unpack(Pipeline.t() | packed_pipeline()) :: Pipeline.t()
def unpack({:packed, pipeline, pack_map}) do
Enum.map(pipeline, fn
{phase, [:pack | index]} ->
{phase, pack_map |> Map.fetch!(index) |> maybe_unpack_options(pack_map)}
phase ->
phase
end)
end
def unpack({module, function, args}) do
apply(module, function, args)
end
def unpack([_ | _] = pipeline) do
pipeline
end
defp maybe_pack_phase({phase, options}, reverse_map) do
{options, reverse_map} = maybe_pack_options(options, reverse_map)
{packed, reverse_map} = pack_value(options, reverse_map)
{{phase, packed}, reverse_map}
end
defp maybe_pack_phase(phase, reverse_map) do
{phase, reverse_map}
end
defp maybe_pack_options(options, reverse_map) do
for key <- @packed_keys, reduce: {options, reverse_map} do
{options, reverse_map} ->
value = Keyword.get(options, key, %{})
if value == %{} do
{options, reverse_map}
else
{packed, reverse_map} = pack_value(value, reverse_map)
{Keyword.put(options, key, packed), reverse_map}
end
end
end
defp pack_value(key, reverse_map) do
case reverse_map do
%{^key => index} ->
{[:pack | index], reverse_map}
%{} ->
new_index = map_size(reverse_map)
reverse_map = Map.put(reverse_map, key, new_index)
{[:pack | new_index], reverse_map}
end
end
defp maybe_unpack_options(options, pack_map) do
for key <- @packed_keys, reduce: options do
options ->
case Keyword.get(options, key) do
[:pack | index] -> Keyword.put(options, key, Map.fetch!(pack_map, index))
_ -> options
end
end
end
end