yolo007wizard

yolo007wizard

Turn module docs and specs into a json definition?

Given the following code:

defmodule MathOps do
  @moduledoc """
  This module provides math operations
  """

  @doc """
  This function returns the sum of input

  """
  @spec add(integer, integer) :: map
  def add(a, b) do
    %{"result" => Integer.to_string(a + b)}
  end
end

I’d like to load the file and turn it into a json spec like so:

{
    "module": "MathOps",
    "doc": "This module provides math operations",
    "defs": [
        {
            "name": "add",
            "doc": "This function returns the sum of input",
            "params": [
                {"name": "a", "type": "integer", "default": null},
                {"name": "b", "type": "integer", "default": null}
            ],
            "return_type": "map"
        }
    ]
}

Any ideas/guidance would be greatly appreciated :slight_smile:

Marked As Solved

yolo007wizard

yolo007wizard

Geewiz I finally figured it out and as expected it was something simple. I was starting my local iex session by calling iex directly without any params.

I started iex as iex -S mix and it worked perfectly.

And now fetch_docs actually works hooray!

iex(3)> MathOps.add(1,2)
%{"result" => "3"}
iex(4)> Code.fetch_docs(MathOps)
{:docs_v1, 2, :elixir, "text/markdown",
 %{"en" => "This module provides math operations\n"}, %{},
 [
   {{:function, :add, 2}, 6, ["add(a, b)"],
    %{"en" => "This add function returns the input sum\n\n"}, %{}},
   {{:function, :minus, 2}, 15, ["minus(a, b)"], :none, %{}}
 ]}

And for bonus points I got Exdoc working as well (which provides module specs):

config = ExDoc.Config.build("Elixir", "1", [source_beam: "beam_dir"])
docs = ExDoc.Retriever.docs_from_modules([MathOps], config)

Now I have the info I need to build the json specification :slight_smile:

Also Liked

al2o3cr

al2o3cr

Most of this information is also used in ExDoc templates (used to generate docs like on hexdocs.pm) so that tool’s source could be a good place to start:

https://github.com/elixir-lang/ex_doc/blob/v0.29.1/lib/ex_doc/language.ex

yolo007wizard

yolo007wizard

Did some more reading. ExDoc is using Code.fetch_docs under the hood it seems inside defp docs_chunk. Cool I give it a try:

Code.ensure_loaded?(MathOps)
# true
Code.fetch_docs(MathOps)
# {:error, :module_not_found}

Bummer. Well after more reading I find this: Module documentation not immediatelly available after ensuring then module is compiled - #3 by josevalim. So I go down another rabbit hole trying to ensure all modules are done compiling etc get raw beam file etc and it feels overly complicated now. Hopefully someone can provide insight.

josevalim

josevalim

Creator of Elixir

Any module from dependencies or your lib folder will have the Docs chunk. You can try any Elixir module to get started Code.fetch_docs(String).

Last Post!

yolo007wizard

yolo007wizard

Here is v1 prototype to get a json spec from module docs. I also learned about running iex> c("MathOps.ex", ".") to compile the file.

defmodule MathOps do
  @moduledoc """
  This module provides math operations
  """
  @after_compile {__MODULE__, :_build_json_def}
  def _build_json_def(_env, bytecode) do
    {:docs_v1, _, _, _, module_doc, _, docs} = Code.fetch_docs(__MODULE__)
    {:ok, specs} = Code.Typespec.fetch_specs(bytecode)
    defs =
      for {{_, doc_name, _}, _, [sig], doc, _} <- docs,
        {{spec_name, _}, [{:type, _, :fun, [{:type, _, :product, args}, {_, _, result, _}]}]} <- specs,
        doc != :hidden,
        doc_name == spec_name do
          params =
            for arg <- args,
            {:type, _, type, _} = arg do
              %{"type" => Atom.to_string(type)}
            end
          %{"name"=> Atom.to_string(doc_name), "doc"=> doc, "signature"=> sig, "params"=> params, "return"=> result}
      end
    document = %{"module"=> Atom.to_string(__MODULE__), "doc"=> module_doc["en"], "defs"=> defs}
    IO.inspect(document)
  end

  @doc """
  This function adds
  """
  @spec add(integer, integer) :: map()
  def add(a, b) do
    %{"result" => Integer.to_string(a + b)}
  end

end

Looks kinda bad in my opinion but it works :stuck_out_tongue: . On compile it spits out clean looking map. Couple minor things yet and then json dump to file.

%{
  "module" => "Elixir.MathOps",
  "doc" => "This module provides math operations\n",
  "defs" => [
    %{
      "doc" => %{"en" => "This function adds\n"},
      "name" => "add",
      "params" => [%{"type" => "integer"}, %{"type" => "integer"}],
      "return" => "map",
      "signature" => "add(a, b)"
    }
  ]
}

Thinking later on this could be a mix compile task but haven’t learned about it yet.

Where Next?

Popular in Questions Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

We're in Beta

About us Mission Statement