lib/xapi/view.ex

defmodule Xmeta.Xapi.View do
  defmacro __using__(opts) do
    unless opts[:title], do: raise("plaease set :title in  Module: #{__MODULE__}")

    quote bind_quoted: [opts: opts] do
      Xmeta.Xapi.Env.put_module(__MODULE__, :xpi_api)

      if opts[:schema] do
        use Params.Schema, Xmeta.Xapi.View.make_schema(opts[:schema])
      end

      @config opts
      
      def get_context_data(conn, params) do
        %{}
      end

      def call(conn, params) do
        {valid?, changes, errors} = valid_schema(conn,params)

        ctx = %{conn: conn}

        if valid? do
          ctx = ctx |> put_in([:data],changes)
          rsp = Xmeta.Xapi.View.check_middleware(ctx, params, __MODULE__)
          if rsp[:status] == 0, do: get_context_data(ctx, params), else: rsp
        else
          %{status: 400, data: [], msg: Xmeta.Xapi.View.make_errors(errors)}
        end 
      end

      def config, do: @config
      defp get_schema, do: @config[:schema]

      defp valid_schema(conn, params) do
        schema = get_schema()
        unless schema do
          {true, params, []}
        else
          %Ecto.Changeset{valid?: valid?, changes: changes, errors: errors} = __MODULE__.from(params)
          {valid?,changes,errors}
        end
      end

      def get_middleware() do
        config = config()
        middleware = config[:middleware]
        if middleware, do: middleware, else: []
      end

      defoverridable get_context_data: 2, call: 2, valid_schema: 2,get_middleware: 0
    end
  end

  def make_schema(schema) do
    Enum.reduce(Map.to_list(schema), %{}, fn(x,acc) -> 
      {key,data} = x
      if is_atom(data) do
        put_in(acc,[key],data)
      else
        data = for x <- data, check_key?(x), do: x
        put_in(acc,[key],data)
      end
    end)
  end

  defp check_key?(c) do
    {key,_} = c
    if key in [:name,:des] do
      false
    else
      true
    end
  end

  def make_errors(errors) do
    for x <- errors do
      {name,{msg,_}} = x
      %{} |> put_in([name],msg)
    end
  end

  def check_middleware(ctx, params, module) do
    mids = get_module_middleware(module)
    Enum.reduce(mids,%{status: 0, data: [], msg: ""}, fn(m,acc) -> 
      if acc[:status] != 0 do
        acc
      else
        m.call(ctx,params)
      end
    end)
  end

  defp get_module_middleware(module) do
    slist = Module.split(module)
    site_mod = get_module_root(slist,1)
    router_mod = get_module_root(slist,2)
    site_mod.get_middleware() ++ router_mod.get_middleware() ++ module.get_middleware()
  end

  defp get_module_root(slist, num) do
    new_slist = for x <- 0..num do
      Enum.at(slist,x)
    end
    Module.concat(new_slist)
  end
end