defmodule Mix.Tasks.Drab.Install do use Mix.Task @shortdoc "Installs Drab in the current Phoenix project" @moduledoc """ Installs Drab in the current Phoenix application. It does inject lines of code to the existing *.ex, *.exs and *.eex files, so it is wise to commit before continuing. It assumes that the Phoenix application was generated by `mix phx.new`. In case you have a non standard application, it is better to do it with manual install. Usage: mix drab.install """ @impl true def run(_args) do validate_phoenix_version() app = Mix.Drab.app_name() Mix.shell().info("Checking prerequisites for #{inspect(app)}") app_html = find_file("lib", "app.html.eex") user_socket = find_file("lib", "user_socket.ex") config = find_file("config", "config.exs") dev_config = find_file("config", "dev.exs") if Mix.shell().yes?("The installer is going to modify those files. OK to proceed?") do update("app.html.eex", app_html) update("user_socket.ex", user_socket) update("config.exs", config, app) update("dev.exs", dev_config) Mix.shell().info """ Drab has been successfully installed in your Phoenix application. Now it is time to create your first commander, for example, for PageController: mix drab.gen.commander Page """ end end defp update(file, path, app \\ nil) defp update("app.html.eex", file, _app) do inject = "<%= Drab.Client.run(@conn) %>" inject_to_file(file, " ", " #{inject}\n ") end defp update("user_socket.ex", file, _app) do inject = "use Drab.Socket" inject_to_file(file, " use Phoenix.Socket", " use Phoenix.Socket\n #{inject}") end defp update("dev.exs", file, _app) do inject = "templates/.*(eex|drab)$" inject_to_file file, "templates/.*(eex)$", inject end defp update("config.exs", file, app) do phoenix = """ \n# Configures default Drab file extension config :phoenix, :template_engines, drab: Drab.Live.Engine\n """ drab = """ # Configures Drab config :drab, #{inspect(Mix.Drab.find_endpoint_in_config_exs(app))}, otp_app: #{inspect(app)} """ unless inject_string_already_there(file, drab) do # File.write!(file, phoenix <> drab, [:append]) logger = "# Configures Elixir's Logger\n" inject_to_file(file, logger, drab <> phoenix <> logger) end end defp inject_to_file(file, search_for, replace_with) do unless inject_string_already_there(file, replace_with) do f = File.read!(file) replaced = String.replace(f, search_for, replace_with) if replaced == f do Mix.shell().error("Installer could not update #{file}. Please install Drab manually.") else File.write!(file, replaced, [:write]) end end end defp inject_string_already_there(file, inject) do f = File.read!(file) contains = String.contains?(f, inject) if contains do Mix.shell().info(" Drab is already installed in #{file}, skipping.") end contains end defp find_file(dir, name) do file = case Path.wildcard("#{dir}/**/#{name}") do [] -> ask_for_file_path(name) [filename] -> filename names -> choose_file(names, name) end Mix.shell().info " #{file}" file end defp ask_for_file_path(name) do Mix.shell().error("Can't find #{name}. Please specify the full path.") path = String.trim(Mix.shell().prompt(">")) if File.exists?(path) do path else ask_for_file_path(name) end end defp choose_file(names, name) do Mix.shell().error("Multiple #{name} found, please copy/paste the full path of correct one.") Mix.shell().info(Enum.join(names, "\n")) path = String.trim(Mix.shell().prompt(">")) if File.exists?(path) do path else choose_file(names, name) end end defp validate_phoenix_version do unless phoenix13?() do Mix.raise """ Only Phoenix 1.3 is supported with the installer, please proceed with manual install. """ end end defp phoenix13?(), do: Regex.match?(~r/^1.3/, to_string(Application.spec(:phoenix, :vsn))) end