diff --git a/lib/phoenix/socket.ex b/lib/phoenix/socket.ex index f2a7de5ddd..aefee8a03a 100644 --- a/lib/phoenix/socket.ex +++ b/lib/phoenix/socket.ex @@ -345,13 +345,18 @@ defmodule Phoenix.Socket do @doc """ Adds key/value pairs to socket assigns. + Accepts a keyword list, a map, or a single-argument function. - A keyword list or a map of assigns must be given as argument to be merged into existing assigns. + When a keyword list or map is provided, it will be merged into the existing assigns. + + If a function is given, it takes the current assigns as an argument and its return + value will be merged into the current assigns. ## Examples iex> assign(socket, name: "Elixir", logo: "💧") iex> assign(socket, %{name: "Elixir"}) + iex> assign(socket, fn %{name: name, logo: logo} -> %{title: Enum.join([name, logo], " | ")} end) """ def assign(%Socket{} = socket, keyword_or_map) @@ -359,6 +364,10 @@ defmodule Phoenix.Socket do %{socket | assigns: Map.merge(socket.assigns, Map.new(keyword_or_map))} end + def assign(%Socket{} = socket, fun) when is_function(fun, 1) do + assign(socket, fun.(socket.assigns)) + end + @doc """ Defines a channel matching the given topic and transports. diff --git a/test/phoenix/socket/socket_test.exs b/test/phoenix/socket/socket_test.exs index 9ac57b4259..03971cd2c0 100644 --- a/test/phoenix/socket/socket_test.exs +++ b/test/phoenix/socket/socket_test.exs @@ -80,6 +80,15 @@ defmodule Phoenix.SocketTest do assert socket.assigns[:foo] == :baz assert socket.assigns[:abc] == :def end + + test "accepts functions" do + socket = %Phoenix.Socket{} + assert socket.assigns[:foo] == nil + socket = assign(socket, :foo, :bar) + assert socket.assigns[:foo] == :bar + socket = assign(socket, fn %{foo: :bar} -> [baz: :quux] end) + assert socket.assigns[:baz] == :quux + end end describe "drainer_spec/1" do