defmodule Mix.Tasks.Arch.Explore.Ecto do @moduledoc """ Mix Task to collect information of Ecto schema modules and their relationships. Usage: mix arch.explore.ecto [options] The following options are accepted: * `--include-deps` - Wildcard glob style to filter dependencies For more information see: [Basic usage guide](guides/introduction/basic_usage.md#include-dependencies-in-the-analysis) """ @shortdoc "Dumps ecto schemas and associations into Archeometer DB" use Mix.Task alias Archeometer.Explore.Ecto alias Archeometer.Util.DumpStats alias Archeometer.Explore.Project @impl Mix.Task def run(argv) do case get_args(argv) do [ include_deps: deps_filter ] -> if Archeometer.Repo.db_ready?(:full) do Mix.shell().info("Starting ecto schemas analysis...\n") Mix.shell().info("Listing ecto schemas...") schema_modules = Ecto.ecto_schemas(append_deps(deps_filter)) Mix.shell().info("Saving ecto schemas...") schema_modules |> Enum.map(&Macro.to_string/1) |> DumpStats.add_ecto_schemas() Ecto.clear_references() Mix.shell().info("Saving ecto associations...") schema_modules |> Ecto.get_associations() |> DumpStats.add_ecto_associations() Mix.shell().info("Done!") else Mix.shell().error("Please run 'mix arch.explore' first") end {:validate_error, error_type} -> Mix.shell().error("Error: #{error_type}") {:error, error_type} -> Mix.shell().error("Error: #{error_type}") print_help() end end defp get_args(argv) do {opts, _args, invalid} = OptionParser.parse( argv, strict: [ include_deps: :keep ] ) case invalid do [] -> deps_filter = Keyword.get_values(opts, :include_deps) validate_options(deps_filter) _ -> {:error, :wrong_arguments} end end defp validate_options(deps_filter) do case Project.filter_deps(deps_filter) do [] -> {:validate_error, "Filter doesn't match any dependency."} _ -> [ include_deps: deps_filter ] end end defp append_deps(deps_filter) do deps = Project.filter_deps(deps_filter) if deps, do: Keyword.keys(deps), else: [] end defp print_help() do Mix.shell().info(""" Usage: mix arch.explore.ecto [opts] opts: --include-deps 'deps_filter' Where `deps_filter` is a glob Unix-like pattern, matches dependencies name --include-deps can be used more than once. - `*` matches none or many tokens - `?` matches exactly one token - `[abc]` matches a set of tokens - `[a-z]` matches a range of tokens - `[!...]` matches anything but a set of tokens """) end end