Skip to content

Instantly share code, notes, and snippets.

@andreaseriksson
Last active March 25, 2025 14:01
Show Gist options
  • Select an option

  • Save andreaseriksson/e454b9244a734310d4ab74d8595f98cd to your computer and use it in GitHub Desktop.

Select an option

Save andreaseriksson/e454b9244a734310d4ab74d8595f98cd to your computer and use it in GitHub Desktop.

Revisions

  1. andreaseriksson revised this gist Mar 3, 2023. 1 changed file with 26 additions and 7 deletions.
    33 changes: 26 additions & 7 deletions convert_to_verified_routes.ex
    Original file line number Diff line number Diff line change
    @@ -40,15 +40,19 @@ defmodule Mix.Tasks.ConvertToVerifiedRoutes do
    end

    def ask_about_replacement(content, route, learnings) do
    route = String.trim(route)

    if verified_route = find_verified_route_from_string(route) do
    replacement = Map.get(learnings, route) || ask_for_direct_match(route, verified_route) || ask_for_fallback(route, verified_route)

    if replacement && String.starts_with?(replacement, "~p\"/") do
    if replacement && (String.starts_with?(replacement, "~p\"/") || String.starts_with?(replacement, "url")) do
    replace_content(
    String.replace(content, route, replacement),
    Map.put(learnings, route, replacement)
    )
    end
    else
    {:ok, content, learnings}
    end
    end

    @@ -57,10 +61,8 @@ defmodule Mix.Tasks.ConvertToVerifiedRoutes do
    """
    What is the verified route for (type "skip" for skipping):
    #{IO.ANSI.red}#{route}#{IO.ANSI.reset}
    Start with:
    ~p"/..
    """)
    response = String.trim("#{response}")
    response != "" && response
    @@ -69,13 +71,10 @@ defmodule Mix.Tasks.ConvertToVerifiedRoutes do
    def ask_for_direct_match(route, verified_route) do
    if Mix.shell().yes?(
    """
    Should we replace
    #{IO.ANSI.red}#{route}#{IO.ANSI.reset}
    with
    #{IO.ANSI.green}#{verified_route}#{IO.ANSI.reset}
    """) do
    verified_route
    end
    @@ -103,8 +102,13 @@ defmodule Mix.Tasks.ConvertToVerifiedRoutes do
    |> case do
    %{path: "" <> path} ->
    path = interpolate_path_with_vars(path, arguments)
    path = maybe_add_params(path, arguments)

    ~s(~p"#{path}")
    if String.contains?(route_helper, "_url") do
    ~s[url(~p"#{path}")]
    else
    ~s(~p"#{path}")
    end
    _ ->
    nil
    end
    @@ -124,4 +128,19 @@ defmodule Mix.Tasks.ConvertToVerifiedRoutes do
    String.replace(memo, slot, "##{argument}", global: false)
    end)
    end

    def maybe_add_params(path, arguments) do
    case Enum.filter(arguments, &String.contains?(&1, ": ")) do
    [] ->
    if "params" in arguments do
    query_params = "{params}"
    "#{path}?##{query_params}"
    else
    path
    end
    query_params ->
    query_params = "{#{inspect(query_params) |> String.replace("\"", "") }}"
    "#{path}?##{query_params}"
    end
    end
    end
  2. andreaseriksson revised this gist Mar 2, 2023. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions convert_to_verified_routes.ex
    Original file line number Diff line number Diff line change
    @@ -3,11 +3,12 @@ defmodule Mix.Tasks.ConvertToVerifiedRoutes do

    use Mix.Task

    @regex ~r/(Routes\.)(.*)_path\(.*?\)/
    @web_module LiveSaasKitWeb
    @regex ~r/(Routes\.)(.*)_(path|url)\(.*?\)/
    @web_module MyAppWeb

    def run(_) do
    Path.wildcard("lib/**/*.*ex")
    |> Enum.concat(Path.wildcard("test/**/*.*ex*"))
    |> Enum.sort()
    |> Enum.filter(&(File.read!(&1) |> String.contains?("Routes.")))
    |> Enum.reduce(%{}, fn filename, learnings ->
  3. andreaseriksson revised this gist Mar 2, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion convert_to_verified_routes.ex
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,7 @@ defmodule Mix.Tasks.ConvertToVerifiedRoutes do
    content = File.read!(filename)

    case replace_content(content, learnings) do
    {:ok, _content, learnings} ->
    {:ok, content, learnings} ->
    File.write!(filename, content)
    learnings
    _ ->
  4. andreaseriksson created this gist Mar 2, 2023.
    126 changes: 126 additions & 0 deletions convert_to_verified_routes.ex
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    defmodule Mix.Tasks.ConvertToVerifiedRoutes do
    @shortdoc "Fix routes"

    use Mix.Task

    @regex ~r/(Routes\.)(.*)_path\(.*?\)/
    @web_module LiveSaasKitWeb

    def run(_) do
    Path.wildcard("lib/**/*.*ex")
    |> Enum.sort()
    |> Enum.filter(&(File.read!(&1) |> String.contains?("Routes.")))
    |> Enum.reduce(%{}, fn filename, learnings ->
    test_filename(filename, learnings)
    end)

    :ok
    end

    def test_filename(filename, learnings) do
    Mix.shell().info(filename)

    content = File.read!(filename)

    case replace_content(content, learnings) do
    {:ok, _content, learnings} ->
    File.write!(filename, content)
    learnings
    _ ->
    learnings
    end
    end

    def replace_content(content, learnings) do
    case Regex.run(@regex, content) do
    [route|_] -> ask_about_replacement(content, route, learnings)
    _ -> {:ok, content, learnings}
    end
    end

    def ask_about_replacement(content, route, learnings) do
    if verified_route = find_verified_route_from_string(route) do
    replacement = Map.get(learnings, route) || ask_for_direct_match(route, verified_route) || ask_for_fallback(route, verified_route)

    if replacement && String.starts_with?(replacement, "~p\"/") do
    replace_content(
    String.replace(content, route, replacement),
    Map.put(learnings, route, replacement)
    )
    end
    end
    end

    def ask_for_fallback(route, _verified_route) do
    response = Mix.shell().prompt(
    """
    What is the verified route for (type "skip" for skipping):
    #{IO.ANSI.red}#{route}#{IO.ANSI.reset}
    Start with:
    ~p"/..
    """)
    response = String.trim("#{response}")
    response != "" && response
    end

    def ask_for_direct_match(route, verified_route) do
    if Mix.shell().yes?(
    """
    Should we replace
    #{IO.ANSI.red}#{route}#{IO.ANSI.reset}
    with
    #{IO.ANSI.green}#{verified_route}#{IO.ANSI.reset}
    """) do
    verified_route
    end
    end

    def find_verified_route_from_string(route) do
    parts =
    route
    |> String.replace("Routes.", "")
    |> String.split("(")

    with [route_helper, arguments|_] <- parts do
    arguments =
    arguments
    |> String.replace_trailing(")", "")
    |> String.split(",")
    |> Enum.map(&String.trim/1)

    @web_module.Router.__routes__()
    |> Enum.find(fn %{helper: helper, plug_opts: plug_opts} ->
    is_atom(plug_opts) &&
    Enum.member?(arguments, ":#{plug_opts}") &&
    String.starts_with?(route_helper, helper)
    end)
    |> case do
    %{path: "" <> path} ->
    path = interpolate_path_with_vars(path, arguments)

    ~s(~p"#{path}")
    _ ->
    nil
    end
    end
    end

    def interpolate_path_with_vars(path, arguments) do
    arguments = Enum.slice(arguments, 2..10)

    path
    |> String.split("/")
    |> Enum.filter(&String.starts_with?(&1, ":"))
    |> Enum.with_index()
    |> Enum.reduce(path, fn {slot, idx}, memo ->
    argument = Enum.at(arguments, idx)
    argument = "{#{argument}}"
    String.replace(memo, slot, "##{argument}", global: false)
    end)
    end
    end