lib/tasks/noizu_entities.ex

defmodule Mix.Tasks.Nz.Gen.Entity do
  use Mix.Task
  @shortdoc "Setup new Entities"
  @author Application.compile_env(Mix.Project.config()[:app], :author, "General")
  @org Application.compile_env(Mix.Project.config()[:app], :group, "General")

  def run([name | params]) do
    create_entity(name, params)
  end

  def create_entity(name, params) do
    app_name = Mix.Project.config()[:app] |> Atom.to_string()

    filename = "lib/#{app_name}_entities/#{name}.ex"
    content = entity_template(name, params)

    with :ok <- File.write(filename, content) do
      IO.puts("Generated")
    end
  end

  defp app_name() do
    Mix.Project.config()[:app]
    |> Atom.to_string()
    |> String.split("_")
    |> Enum.map(&String.capitalize(&1))
    |> Enum.join("")
  end

  defp entity_name(name, _) do
    String.split(name, ".")
    |> Enum.map(&String.capitalize(&1))
    |> Enum.join(".")
  end

  defp prep_params(params) do
    Enum.group_by(params, fn param ->
      cond do
        String.starts_with?(param, "sref=") -> :sref
        String.starts_with?(param, "field=") -> :field
        String.starts_with?(param, "store=") -> :store
        :else -> :misc
      end
    end)
  end

  defp extract_sref(params) do
    Enum.find_value(params, fn param ->
      case String.split(param, "=") do
        ["sref", x] -> x
        _ -> nil
      end
    end)
  end

  defp extract_fields(nil), do: []

  defp extract_fields(params) do
    Enum.map(
      params,
      fn
        "field=" <> f ->
          case String.split(f, ":") do
            [name, type] ->
              {:field, {name, type}}
          end

        _ ->
          nil
      end
    )
    |> Enum.filter(& &1)
  end

  defp extract_storage(nil), do: []

  defp extract_storage(params) do
    Enum.map(
      params,
      fn
        "store=ecto" -> {:ecto, :storage}
        "store=redis" -> {:redis, :storage}
        "store=mnesia" -> {:mnesia, :storage}
        _ -> nil
      end
    )
    |> Enum.filter(& &1)
  end

  defp add_ecto(name, fields) do
    ecto = Enum.map(fields, fn {_, {field, type}} -> "#{field}:#{type}" end)
    ecto = ["Schema" <> "." <> name, Macro.underscore(name) | ecto]
    apply(Mix.Tasks.Phx.Gen.Schema, :run, [ecto])
  end

  def entity_template(name, params) do
    app_name = app_name()
    name = entity_name(name, params)
    sm = app_name <> "." <> name
    params = prep_params(params)
    # Sref, Fields, STorage
    sref = extract_sref(params[:sref])
    fields = extract_fields(params[:field])
    storage = extract_storage(params[:store])

    # Ecto Setup
    storage[:ecto] && add_ecto(name, fields)

    # Payload Snippet
    entity_fields =
      Enum.map(fields, fn {_, {field, _type}} -> "field :#{field}" end)
      |> Enum.join("\n    ")

    entity_persistence =
      Enum.map(
        storage,
        fn {store, settings} ->
          "@persistence {#{inspect(store)}, #{inspect(settings)}}"
        end
      )
      |> Enum.join("\n   ")

    """
      #-------------------------------------------------------------------------------
      # Author: #{@author}
      # Copyright (C) #{DateTime.utc_now().year} #{@org} All rights reserved.
      #-------------------------------------------------------------------------------
    
      defmodule #{sm} do
        use Noizu.Entities
    
        @vsn 1.0
        #{(sref && "@sref \"#{sref}\"") || ""}
        #{entity_persistence}
        def_entity do
          identifier :integer
          #{entity_fields}
        end
    
        defmodule Repo do
          use Noizu.Repo
        end
      end
    """
  end
end