├── .formatter.exs ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── README.md ├── examples ├── demo_controller.exs ├── demo_controller_test.exs ├── demo_live.exs ├── demo_live_test.exs └── demo_router.exs ├── lib ├── phoenix_playground.ex ├── phoenix_playground │ ├── endpoint.ex │ ├── error_view.ex │ ├── layouts.ex │ ├── reloader.ex │ ├── routers.ex │ └── test.ex └── php.ex ├── mix.exs ├── mix.lock └── test ├── examples_test.exs ├── phoenix_playground_test.exs └── test_helper.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | import_deps: [:phoenix], 4 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 5 | ] 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-22.04 7 | env: 8 | MIX_ENV: test 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | include: 13 | - pair: 14 | elixir: "1.15" 15 | otp: "24.3.4.10" 16 | - pair: 17 | elixir: "1.16" 18 | otp: "26.2.3" 19 | lint: lint 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - uses: erlef/setup-beam@main 24 | with: 25 | otp-version: ${{ matrix.pair.otp }} 26 | elixir-version: ${{ matrix.pair.elixir }} 27 | version-type: strict 28 | 29 | - uses: actions/cache@v4 30 | with: 31 | path: deps 32 | key: mix-deps-${{ hashFiles('**/mix.lock') }} 33 | 34 | - run: mix deps.get --check-locked 35 | 36 | - run: mix format --check-formatted 37 | if: ${{ matrix.lint }} 38 | 39 | - run: mix deps.unlock --check-unused 40 | if: ${{ matrix.lint }} 41 | 42 | - run: mix deps.compile 43 | 44 | - run: mix compile --warnings-as-errors 45 | if: ${{ matrix.lint }} 46 | 47 | - run: mix test 48 | if: ${{ ! matrix.lint }} 49 | 50 | - run: mix test --warnings-as-errors 51 | if: ${{ matrix.lint }} 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | phoenix_playground-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP 2 | 3 | See . :) 4 | -------------------------------------------------------------------------------- /examples/demo_controller.exs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elixir 2 | Mix.install([ 3 | {:phoenix_playground, github: "phoenix-playground/phoenix_playground"} 4 | # TODO: 5 | # {:phoenix_playground, "~> 0.1.0"} 6 | ]) 7 | 8 | defmodule DemoController do 9 | use Phoenix.Controller, formats: [:html] 10 | use Phoenix.Component 11 | plug :put_layout, false 12 | plug :put_view, __MODULE__ 13 | 14 | def index(conn, params) do 15 | count = 16 | case Integer.parse(params["count"] || "") do 17 | {n, ""} -> n 18 | _ -> 0 19 | end 20 | 21 | render(conn, :index, count: count) 22 | end 23 | 24 | def index(assigns) do 25 | ~H""" 26 |
27 | <%= @count %> 28 | 29 | 30 | 31 |

Now edit <%= __ENV__.file %>:<%= __ENV__.line %> in your editor...

32 |
33 | """ 34 | end 35 | end 36 | 37 | {:ok, _} = PhoenixPlayground.start_link(controller: DemoController) 38 | Process.sleep(:infinity) 39 | -------------------------------------------------------------------------------- /examples/demo_controller_test.exs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elixir 2 | Mix.install([ 3 | {:phoenix_playground, github: "phoenix-playground/phoenix_playground"} 4 | # TODO: 5 | # {:phoenix_playground, "~> 0.1.0"} 6 | ]) 7 | 8 | defmodule DemoController do 9 | use Phoenix.Controller, formats: [:html] 10 | use Phoenix.Component 11 | plug :put_layout, false 12 | plug :put_view, __MODULE__ 13 | 14 | def index(conn, params) do 15 | count = 16 | case Integer.parse(params["count"] || "") do 17 | {n, ""} -> n 18 | _ -> 0 19 | end 20 | 21 | render(conn, :index, count: count) 22 | end 23 | 24 | def index(assigns) do 25 | ~H""" 26 |
27 | Count: <%= @count %> 28 | 29 | 30 | 31 |

Now edit <%= __ENV__.file %> in your editor...

32 |
33 | """ 34 | end 35 | end 36 | 37 | Logger.configure(level: :info) 38 | ExUnit.start() 39 | 40 | defmodule DemoControllerTest do 41 | use ExUnit.Case 42 | use PhoenixPlayground.Test, controller: DemoController 43 | 44 | test "it works" do 45 | conn = get(build_conn(), "/") 46 | assert html_response(conn, 200) =~ "Count: 0" 47 | 48 | conn = get(build_conn(), "/?count=1") 49 | assert html_response(conn, 200) =~ "Count: 1" 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /examples/demo_live.exs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elixir 2 | Mix.install([ 3 | {:phoenix_playground, github: "phoenix-playground/phoenix_playground"} 4 | # TODO: 5 | # {:phoenix_playground, "~> 0.1.0"} 6 | ]) 7 | 8 | defmodule DemoLive do 9 | use Phoenix.LiveView 10 | 11 | def mount(_params, _session, socket) do 12 | {:ok, assign(socket, count: 0)} 13 | end 14 | 15 | def render(assigns) do 16 | ~H""" 17 |
18 | <%= @count %> 19 | 20 | 21 | 22 |

Now edit <%= __ENV__.file %>:<%= __ENV__.line %> in your editor...

23 |
24 | """ 25 | end 26 | 27 | def handle_event("inc", _params, socket) do 28 | {:noreply, assign(socket, count: socket.assigns.count + 1)} 29 | end 30 | 31 | def handle_event("dec", _params, socket) do 32 | {:noreply, assign(socket, count: socket.assigns.count - 1)} 33 | end 34 | end 35 | 36 | {:ok, _} = PhoenixPlayground.start_link(live: DemoLive) 37 | Process.sleep(:infinity) 38 | -------------------------------------------------------------------------------- /examples/demo_live_test.exs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elixir 2 | Mix.install([ 3 | {:phoenix_playground, github: "phoenix-playground/phoenix_playground"} 4 | # TODO: 5 | # {:phoenix_playground, "~> 0.1.0"} 6 | ]) 7 | 8 | defmodule DemoLive do 9 | use Phoenix.LiveView 10 | 11 | def mount(_params, _session, socket) do 12 | {:ok, assign(socket, count: 0)} 13 | end 14 | 15 | def render(assigns) do 16 | ~H""" 17 |
18 | Count: <%= @count %> 19 | 20 | 21 | 22 |

Now edit <%= __ENV__.file %> in your editor...

23 |
24 | """ 25 | end 26 | 27 | def handle_event("inc", _params, socket) do 28 | {:noreply, update(socket, :count, &(&1 + 1))} 29 | end 30 | 31 | def handle_event("dec", _params, socket) do 32 | {:noreply, update(socket, :count, &(&1 - 1))} 33 | end 34 | end 35 | 36 | Logger.configure(level: :info) 37 | ExUnit.start() 38 | 39 | defmodule DemoLiveTest do 40 | use ExUnit.Case 41 | use PhoenixPlayground.Test, live: DemoLive 42 | 43 | test "it works" do 44 | {:ok, view, html} = live(build_conn(), "/") 45 | 46 | assert html =~ "Count: 0" 47 | assert render_click(view, :inc, %{}) =~ "Count: 1" 48 | assert render_click(view, :inc, %{}) =~ "Count: 2" 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /examples/demo_router.exs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elixir 2 | Mix.install([ 3 | {:phoenix_playground, github: "phoenix-playground/phoenix_playground"} 4 | # TODO: 5 | # {:phoenix_playground, "~> 0.1.0"} 6 | ]) 7 | 8 | defmodule CounterLive do 9 | use Phoenix.LiveView 10 | 11 | def mount(_params, _session, socket) do 12 | {:ok, assign(socket, count: 0)} 13 | end 14 | 15 | def render(assigns) do 16 | ~H""" 17 |
18 | <%= @count %> 19 | 20 | 21 | 22 |

Now edit <%= __ENV__.file %>:<%= __ENV__.line %> in your editor...

23 |
24 | """ 25 | end 26 | 27 | def handle_event("inc", _params, socket) do 28 | {:noreply, assign(socket, count: socket.assigns.count + 1)} 29 | end 30 | 31 | def handle_event("dec", _params, socket) do 32 | {:noreply, assign(socket, count: socket.assigns.count - 1)} 33 | end 34 | end 35 | 36 | defmodule ClockLive do 37 | use Phoenix.LiveView 38 | 39 | def mount(_params, _session, socket) do 40 | if connected?(socket) do 41 | :timer.send_interval(1000, :tick) 42 | end 43 | 44 | {:ok, assign(socket, now: now())} 45 | end 46 | 47 | def render(assigns) do 48 | ~H""" 49 |
50 | <%= @now %> 51 | 52 |

Now edit <%= __ENV__.file %>:<%= __ENV__.line %> in your editor...

53 |
54 | """ 55 | end 56 | 57 | def handle_info(:tick, socket) do 58 | {:noreply, assign(socket, now: now())} 59 | end 60 | 61 | defp now do 62 | to_string(NaiveDateTime.local_now()) 63 | end 64 | end 65 | 66 | defmodule Router do 67 | use Phoenix.Router 68 | import Phoenix.LiveView.Router 69 | 70 | pipeline :browser do 71 | plug :accepts, ["html", "json"] 72 | plug :fetch_session 73 | plug :put_root_layout, html: {PhoenixPlayground.Layouts, :root} 74 | plug :protect_from_forgery 75 | plug :put_secure_browser_headers 76 | end 77 | 78 | scope "/" do 79 | pipe_through :browser 80 | 81 | live_session :default, layout: {PhoenixPlayground.Layouts, :live} do 82 | live "/", CounterLive, :index 83 | live "/clock", ClockLive, :index 84 | end 85 | end 86 | end 87 | 88 | {:ok, _} = PhoenixPlayground.start_link(router: Router) 89 | Process.sleep(:infinity) 90 | -------------------------------------------------------------------------------- /lib/phoenix_playground.ex: -------------------------------------------------------------------------------- 1 | defmodule PhoenixPlayground do 2 | # TODO 3 | # @moduledoc """ 4 | # """ 5 | 6 | @doc false 7 | def child_spec(options) do 8 | %{ 9 | id: __MODULE__, 10 | start: {__MODULE__, :start_link, [options]}, 11 | type: :supervisor 12 | } 13 | end 14 | 15 | # TODO 16 | # @doc """ 17 | # """ 18 | def start_link(options) do 19 | options = 20 | Keyword.validate!(options, [ 21 | :live, 22 | :controller, 23 | :router, 24 | port: 4000, 25 | open_browser: true 26 | ]) 27 | 28 | {type, module} = 29 | cond do 30 | live = options[:live] -> 31 | {:live, live} 32 | 33 | controller = options[:controller] -> 34 | {:controller, controller} 35 | 36 | router = options[:router] -> 37 | {:router, router} 38 | 39 | true -> 40 | raise "missing :live, :controller, or :router" 41 | end 42 | 43 | if options[:open_browser] do 44 | Application.put_env(:phoenix, :browser_open, true) 45 | end 46 | 47 | path = module.__info__(:compile)[:source] 48 | basename = Path.basename(path) 49 | 50 | # PhoenixLiveReload requires Hex 51 | Application.ensure_all_started(:hex) 52 | Application.ensure_all_started(:phoenix_live_reload) 53 | 54 | Application.put_env(:phoenix_live_reload, :dirs, [ 55 | Path.dirname(path) 56 | ]) 57 | 58 | Application.put_env(:phoenix_live_view, :debug_heex_annotations, true) 59 | 60 | options = 61 | [ 62 | type: type, 63 | module: module, 64 | basename: basename 65 | ] ++ Keyword.take(options, [:port]) 66 | 67 | children = [ 68 | {Phoenix.PubSub, name: PhoenixPlayground.PubSub}, 69 | PhoenixPlayground.Reloader, 70 | {PhoenixPlayground.Endpoint, options} 71 | ] 72 | 73 | Supervisor.start_link(children, strategy: :one_for_one) 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /lib/phoenix_playground/endpoint.ex: -------------------------------------------------------------------------------- 1 | defmodule PhoenixPlayground.Endpoint do 2 | @moduledoc false 3 | 4 | use Phoenix.Endpoint, otp_app: :phoenix_playground 5 | 6 | defoverridable start_link: 1 7 | 8 | def start_link(options) do 9 | options = 10 | Keyword.validate!( 11 | options, 12 | [ 13 | :type, 14 | :module, 15 | :port, 16 | :basename 17 | ] 18 | ) 19 | 20 | router = 21 | case options[:type] do 22 | :controller -> PhoenixPlayground.ControllerRouter 23 | :live -> PhoenixPlayground.LiveRouter 24 | :router -> options[:module] 25 | end 26 | 27 | options = Keyword.put_new(options, :router, router) 28 | 29 | live_reload_options = 30 | if basename = options[:basename] do 31 | [ 32 | live_reload: [ 33 | web_console_logger: true, 34 | debounce: 100, 35 | patterns: [ 36 | ~r/#{basename}$/ 37 | ], 38 | notify: [ 39 | {"phoenix_playground", [~r/#{basename}$/]} 40 | ] 41 | ] 42 | ] 43 | else 44 | [] 45 | end 46 | 47 | Application.put_env( 48 | :phoenix_playground, 49 | __MODULE__, 50 | [ 51 | adapter: Bandit.PhoenixAdapter, 52 | http: [ip: {127, 0, 0, 1}, port: options[:port]], 53 | server: !!options[:port], 54 | live_view: [signing_salt: "aaaaaaaa"], 55 | secret_key_base: String.duplicate("a", 64), 56 | pubsub_server: PhoenixPlayground.PubSub, 57 | debug_errors: true, 58 | phoenix_playground: Map.new(options) 59 | ] ++ live_reload_options 60 | ) 61 | 62 | super(name: __MODULE__) 63 | end 64 | 65 | @session_options [ 66 | store: :cookie, 67 | key: "_phoenix_playground_key", 68 | signing_salt: "ll+Leuc3", 69 | same_site: "Lax", 70 | # 14 days 71 | max_age: 14 * 24 * 60 * 60 72 | ] 73 | 74 | socket "/live", Phoenix.LiveView.Socket 75 | socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket 76 | 77 | plug Phoenix.LiveReloader 78 | 79 | plug Plug.Parsers, 80 | parsers: [:urlencoded, :multipart, :json], 81 | pass: ["*/*"], 82 | json_decoder: Phoenix.json_library() 83 | 84 | plug Plug.Session, @session_options 85 | 86 | plug :router 87 | 88 | defp router(conn, []) do 89 | config = conn.private.phoenix_endpoint.config(:phoenix_playground) 90 | conn = Plug.Conn.put_private(conn, :phoenix_playground, config) 91 | config.router.call(conn, []) 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/phoenix_playground/error_view.ex: -------------------------------------------------------------------------------- 1 | defmodule PhoenixPlayground.ErrorView do 2 | @moduledoc false 3 | 4 | def render(template, _), do: Phoenix.Controller.status_message_from_template(template) 5 | end 6 | -------------------------------------------------------------------------------- /lib/phoenix_playground/layouts.ex: -------------------------------------------------------------------------------- 1 | defmodule PhoenixPlayground.Layouts do 2 | @moduledoc false 3 | 4 | use Phoenix.Component 5 | 6 | def render("root.html", assigns) do 7 | ~H""" 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <%= @inner_content %> 16 | 17 | 18 | """ 19 | end 20 | 21 | def render("live.html", assigns) do 22 | ~H""" 23 | 25 | 29 | 59 | <%= @inner_content %> 60 | """ 61 | end 62 | 63 | defp phx_vsn, do: Application.spec(:phoenix, :vsn) 64 | defp lv_vsn, do: Application.spec(:phoenix_live_view, :vsn) 65 | end 66 | -------------------------------------------------------------------------------- /lib/phoenix_playground/reloader.ex: -------------------------------------------------------------------------------- 1 | defmodule PhoenixPlayground.Reloader do 2 | @moduledoc false 3 | 4 | use GenServer 5 | 6 | def start_link(_) do 7 | GenServer.start_link(__MODULE__, nil) 8 | end 9 | 10 | @impl true 11 | def init(_) do 12 | :ok = Phoenix.PubSub.subscribe(PhoenixPlayground.PubSub, "phoenix_playground") 13 | {:ok, nil} 14 | end 15 | 16 | @impl true 17 | def handle_info({:phoenix_live_reload, "phoenix_playground", path}, state) do 18 | recompile(path) 19 | {:noreply, state} 20 | end 21 | 22 | defp recompile(path) do 23 | {:ok, quoted} = 24 | path 25 | |> File.read!() 26 | |> Code.string_to_quoted() 27 | 28 | Macro.prewalk(quoted, fn 29 | {:defmodule, _, [mod, _]} = ast -> 30 | mod = 31 | case mod do 32 | {:__aliases__, _, parts} -> Module.concat(parts) 33 | mod when is_atom(mod) -> mod 34 | end 35 | 36 | :code.purge(mod) 37 | :code.delete(mod) 38 | Code.eval_quoted(ast, [], file: path) 39 | :ok 40 | 41 | ast -> 42 | ast 43 | end) 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/phoenix_playground/routers.ex: -------------------------------------------------------------------------------- 1 | defmodule PhoenixPlayground.ControllerRouter do 2 | @moduledoc false 3 | 4 | use Phoenix.Router 5 | import Phoenix.LiveView.Router 6 | 7 | pipeline :browser do 8 | plug :accepts, ["html", "json"] 9 | plug :fetch_session 10 | plug :put_root_layout, html: {PhoenixPlayground.Layouts, :root} 11 | plug :protect_from_forgery 12 | plug :put_secure_browser_headers 13 | end 14 | 15 | scope "/" do 16 | pipe_through :browser 17 | 18 | get "/", PhoenixPlayground.DelegateController, :index 19 | end 20 | end 21 | 22 | defmodule PhoenixPlayground.LiveRouter do 23 | @moduledoc false 24 | 25 | use Phoenix.Router 26 | import Phoenix.LiveView.Router 27 | 28 | pipeline :browser do 29 | plug :accepts, ["html"] 30 | plug :fetch_session 31 | plug :put_root_layout, html: {PhoenixPlayground.Layouts, :root} 32 | plug :put_secure_browser_headers 33 | end 34 | 35 | scope "/" do 36 | pipe_through :browser 37 | 38 | live_session :default, layout: {PhoenixPlayground.Layouts, :live} do 39 | live "/", PhoenixPlayground.DelegateLive, :index 40 | end 41 | end 42 | end 43 | 44 | defmodule PhoenixPlayground.DelegateController do 45 | @moduledoc false 46 | 47 | def init(options) do 48 | options 49 | end 50 | 51 | def call(conn, options) do 52 | %{type: :controller, module: controller} = conn.private.phoenix_playground 53 | controller.call(conn, controller.init(options)) 54 | end 55 | end 56 | 57 | defmodule PhoenixPlayground.DelegateLive do 58 | @moduledoc false 59 | use Phoenix.LiveView 60 | 61 | @impl true 62 | def mount(params, session, socket) do 63 | live().mount(params, session, socket) 64 | end 65 | 66 | @impl true 67 | def render(assigns) do 68 | live().render(assigns) 69 | end 70 | 71 | @impl true 72 | def handle_event(event, params, socket) do 73 | live().handle_event(event, params, socket) 74 | end 75 | 76 | @impl true 77 | def handle_info(message, socket) do 78 | live().handle_info(message, socket) 79 | end 80 | 81 | defp live do 82 | %{type: :live, module: live} = PhoenixPlayground.Endpoint.config(:phoenix_playground) 83 | live 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/phoenix_playground/test.ex: -------------------------------------------------------------------------------- 1 | defmodule PhoenixPlayground.Test do 2 | # TODO 3 | # @moduledoc """ 4 | # """ 5 | 6 | defmacro __using__([{type, module}]) do 7 | module = Macro.expand(module, __ENV__) 8 | 9 | imports = 10 | if type == :live do 11 | quote do 12 | import(Phoenix.LiveViewTest) 13 | end 14 | end 15 | 16 | quote do 17 | import Phoenix.ConnTest 18 | module = unquote(module) 19 | type = unquote(type) 20 | unquote(imports) 21 | 22 | @endpoint PhoenixPlayground.Endpoint 23 | @options [type: type, module: module] 24 | 25 | setup do 26 | start_supervised!({@endpoint, @options}) 27 | :ok 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/php.ex: -------------------------------------------------------------------------------- 1 | defmodule PHP do 2 | def start_link(options) do 3 | PhoenixPlayground.start_link(options) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule PHP.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :php, 7 | version: "0.1.0", 8 | elixir: "~> 1.15", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | def application do 15 | [ 16 | extra_applications: [:logger] 17 | ] 18 | end 19 | 20 | defp deps do 21 | [ 22 | {:jason, "~> 1.0"}, 23 | {:phoenix, "~> 1.0"}, 24 | {:phoenix_live_view, "~> 0.20"}, 25 | {:bandit, "~> 1.0"}, 26 | {:floki, "~> 0.35"}, 27 | # Don't start phoenix_live_reload in case someone just wants PhoenixPlayground.Test. 28 | # Instead, manually start it in PhoenixPlayground.start_link/1. 29 | {:phoenix_live_reload, "~> 1.5", runtime: false} 30 | ] 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "bandit": {:hex, :bandit, "1.4.1", "6ff703b33a967bc20b41ed3840a4c58e62abe62b4cc598cff7429af78e174990", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "5bbc0a4c185358b7d566a3b7b32806723ae139a8704cdc841ad787b677adcb9a"}, 3 | "castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"}, 4 | "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, 5 | "floki": {:hex, :floki, "0.36.1", "712b7f2ba19a4d5a47dfe3e74d81876c95bbcbee44fe551f0af3d2a388abb3da", [:mix], [], "hexpm", "21ba57abb8204bcc70c439b423fc0dd9f0286de67dc82773a14b0200ada0995f"}, 6 | "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, 7 | "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, 8 | "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, 9 | "phoenix": {:hex, :phoenix, "1.7.11", "1d88fc6b05ab0c735b250932c4e6e33bfa1c186f76dcf623d8dd52f07d6379c7", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "b1ec57f2e40316b306708fe59b92a16b9f6f4bf50ccfa41aa8c7feb79e0ec02a"}, 10 | "phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"}, 11 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"}, 12 | "phoenix_live_view": {:hex, :phoenix_live_view, "0.20.14", "70fa101aa0539e81bed4238777498f6215e9dda3461bdaa067cad6908110c364", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "82f6d006c5264f979ed5eb75593d808bbe39020f20df2e78426f4f2d570e2402"}, 13 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, 14 | "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, 15 | "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, 16 | "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, 17 | "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, 18 | "thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"}, 19 | "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, 20 | "websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"}, 21 | } 22 | -------------------------------------------------------------------------------- /test/examples_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Examples do 2 | def run(path) do 3 | {:ok, quoted} = 4 | path 5 | |> File.read!() 6 | |> Code.string_to_quoted() 7 | 8 | quoted 9 | |> remove_mix_install() 10 | |> Code.eval_quoted([], file: path) 11 | end 12 | 13 | defp remove_mix_install(quoted) do 14 | Macro.prewalk(quoted, fn 15 | {{:., _, [{:__aliases__, _, [:Mix]}, :install]}, _, _} -> 16 | nil 17 | 18 | ast -> 19 | ast 20 | end) 21 | end 22 | end 23 | 24 | Examples.run("#{__DIR__}/../examples/demo_controller_test.exs") 25 | Examples.run("#{__DIR__}/../examples/demo_live_test.exs") 26 | -------------------------------------------------------------------------------- /test/phoenix_playground_test.exs: -------------------------------------------------------------------------------- 1 | defmodule PhoenixPlaygroundTest do 2 | use ExUnit.Case, async: true 3 | end 4 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------