Browse Source

feat: add initial notes application

pull/37/head
Inhji Y. 1 year ago
parent
commit
e5174a3e9c
  1. 23
      apps/db/priv/repo/migrations/20200430065350_create_notes.exs
  2. 5
      apps/notes/.formatter.exs
  3. 24
      apps/notes/.gitignore
  4. 21
      apps/notes/README.md
  5. 70
      apps/notes/lib/notes.ex
  6. 20
      apps/notes/lib/notes/application.ex
  7. 16
      apps/notes/lib/notes/book.ex
  8. 18
      apps/notes/lib/notes/note.ex
  9. 12
      apps/notes/lib/notes/note_path.ex
  10. 8
      apps/notes/lib/notes/tree.ex
  11. 37
      apps/notes/mix.exs
  12. 8
      apps/notes/test/notes_test.exs
  13. 1
      apps/notes/test/test_helper.exs
  14. 15
      apps/tomie/lib/tomie/application.ex
  15. 10
      apps/tomie_web/lib/tomie_web/live/note_live/components/note_component.ex
  16. 11
      apps/tomie_web/lib/tomie_web/live/note_live/index.ex
  17. 33
      apps/tomie_web/lib/tomie_web/live/note_live/new_notebook.ex
  18. 15
      apps/tomie_web/lib/tomie_web/live/note_live/show_note.ex
  19. 11
      apps/tomie_web/lib/tomie_web/live/note_live/show_notebook.ex
  20. 5
      apps/tomie_web/lib/tomie_web/router.ex
  21. 1
      apps/tomie_web/lib/tomie_web/templates/layout/root.html.leex
  22. 10
      apps/tomie_web/lib/tomie_web/templates/note/form.html.leex
  23. 19
      apps/tomie_web/lib/tomie_web/templates/note/index.html.leex
  24. 1
      apps/tomie_web/lib/tomie_web/templates/note/new_notebook.html.leex
  25. 13
      apps/tomie_web/lib/tomie_web/templates/note/note.html.leex
  26. 7
      apps/tomie_web/lib/tomie_web/templates/note/show_note.html.leex
  27. 5
      apps/tomie_web/lib/tomie_web/templates/note/show_notebook.html.leex
  28. 8
      apps/tomie_web/lib/tomie_web/views/note_view.ex
  29. 1
      apps/tomie_web/mix.exs
  30. 5
      mix.lock

23
apps/db/priv/repo/migrations/20200430065350_create_notes.exs

@ -0,0 +1,23 @@
defmodule Db.Repo.Migrations.CreateNotes do
use Ecto.Migration
def change do
create table(:notebooks) do
add :title, :string
end
create table(:notes) do
add :title, :string
add :content, :text
add :root, :boolean
add :notebook_id, references(:notebooks)
end
create table(:note_paths) do
add :depth, :integer
add :ancestor, references(:notes)
add :descendant, references(:notes)
end
end
end

5
apps/notes/.formatter.exs

@ -0,0 +1,5 @@
# Used by "mix format"
[
import_deps: [:ecto],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

24
apps/notes/.gitignore

@ -0,0 +1,24 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
notes-*.tar

21
apps/notes/README.md

@ -0,0 +1,21 @@
# Notes
**TODO: Add description**
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `notes` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:notes, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/notes](https://hexdocs.pm/notes).

70
apps/notes/lib/notes.ex

@ -0,0 +1,70 @@
defmodule Notes do
@moduledoc """
Documentation for `Notes`.
"""
import Ecto.Query, warn: false
alias Notes.{Book, Note, Tree}
def get_note!(id) do
Db.Repo.get!(Note, id)
end
def get_rootnode!(notebook_id) do
Note
|> where([n], n.root == true)
|> where([n], n.notebook_id == ^notebook_id)
|> Db.Repo.one!()
end
def create_note(parent, attrs \\ %{}) do
{:ok, note} =
attrs
|> Map.put_new(:notebook_id, parent.notebook_id)
|> do_create_note()
Tree.insert(note.id, parent.id)
end
defp do_create_note(attrs \\ %{}) do
%Note{}
|> Note.changeset(attrs)
|> Db.Repo.insert()
end
def list_notebooks() do
Book
|> Db.Repo.all()
|> Db.Repo.preload([:notes])
end
def get_notebook!(id, tree_opts \\ []) do
notebook = Db.Repo.get!(Book, id)
root_note = Db.Repo.get_by!(Note, notebook_id: notebook.id, root: true)
{:ok, tree} = Tree.descendants(root_note.id, [nodes: true] ++ tree_opts)
{notebook, tree}
end
def create_notebook(attrs \\ %{}) do
with {:ok, notebook} <- do_create_notebook(attrs),
{:ok, root_note} <-
do_create_note(%{
notebook_id: notebook.id,
content: "ROOT",
title: "ROOT",
root: true
}),
{:ok, _tree} <- Tree.insert(root_note.id, root_note.id) do
{:ok, notebook}
else
error -> error
end
end
defp do_create_notebook(attrs \\ %{}) do
%Book{}
|> Book.changeset(attrs)
|> Db.Repo.insert()
end
end

20
apps/notes/lib/notes/application.ex

@ -0,0 +1,20 @@
defmodule Notes.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
children = [
# Starts a worker by calling: Notes.Worker.start_link(arg)
# {Notes.Worker, arg}
Notes.Tree
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Notes.Supervisor]
Supervisor.start_link(children, opts)
end
end

16
apps/notes/lib/notes/book.ex

@ -0,0 +1,16 @@
defmodule Notes.Book do
use Ecto.Schema
import Ecto.Changeset
schema "notebooks" do
field :title, :string
has_many :notes, Notes.Note, foreign_key: :notebook_id
end
def changeset(book, attrs \\ %{}) do
book
|> cast(attrs, [:title])
|> validate_required([:title])
end
end

18
apps/notes/lib/notes/note.ex

@ -0,0 +1,18 @@
defmodule Notes.Note do
use Ecto.Schema
import Ecto.Changeset
schema "notes" do
field :title, :string
field :content, :string
field :root, :boolean, default: false
belongs_to :notebook, Notes.Book
end
def changeset(note, attrs \\ %{}) do
note
|> cast(attrs, [:title, :content, :notebook_id, :root])
|> validate_required([:content, :notebook_id])
end
end

12
apps/notes/lib/notes/note_path.ex

@ -0,0 +1,12 @@
defmodule Notes.NotePath do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
schema "note_paths" do
field :depth, :integer, default: 0
belongs_to :parent_note, Notes.Note, foreign_key: :ancestor
belongs_to :note, Notes.Note, foreign_key: :descendant
end
end

8
apps/notes/lib/notes/tree.ex

@ -0,0 +1,8 @@
defmodule Notes.Tree do
use CTE,
otp_app: :notes,
adapter: CTE.Adapter.Ecto,
repo: Db.Repo,
nodes: Notes.Note,
paths: Notes.NotePath
end

37
apps/notes/mix.exs

@ -0,0 +1,37 @@
defmodule Notes.MixProject do
use Mix.Project
def project do
[
app: :notes,
version: "0.1.0",
build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",
elixir: "~> 1.10",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger],
mod: {Notes.Application, []}
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:ecto_sql, "~> 3.1"},
# {:closure_table, "~> 0.1"},
{:closure_table, path: "~/Devel/elixir/closure_table"},
{:db, in_umbrella: true}
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
]
end
end

8
apps/notes/test/notes_test.exs

@ -0,0 +1,8 @@
defmodule NotesTest do
use ExUnit.Case
doctest Notes
test "greets the world" do
assert Notes.hello() == :world
end
end

1
apps/notes/test/test_helper.exs

@ -0,0 +1 @@
ExUnit.start()

15
apps/tomie/lib/tomie/application.ex

@ -20,13 +20,12 @@ defmodule Tomie.Application do
opts = Application.get_env(:tomie, Oban)
# Prevent running queues or scheduling jobs from an iex console.
# if Code.ensure_loaded?(IEx) and IEx.started?() do
# opts
# |> Keyword.put(:crontab, false)
# |> Keyword.put(:queues, false)
# else
# opts
# end
opts
if Code.ensure_loaded?(IEx) and IEx.started?() do
opts
|> Keyword.put(:crontab, false)
|> Keyword.put(:queues, false)
else
opts
end
end
end

10
apps/tomie_web/lib/tomie_web/live/note_live/components/note_component.ex

@ -0,0 +1,10 @@
defmodule TomieWeb.NoteLive.NoteComponent do
use TomieWeb, :live_component
def render(assigns), do: TomieWeb.NoteView.render("note.html", assigns)
def update(%{note: note}, socket) do
{:ok, children} = Notes.Tree.descendants(note.id, nodes: true, depth: 1)
{:ok, socket |> assign(note: note, children: children)}
end
end

11
apps/tomie_web/lib/tomie_web/live/note_live/index.ex

@ -0,0 +1,11 @@
defmodule TomieWeb.NoteLive.Index do
use TomieWeb, :live
def render(assigns), do: TomieWeb.NoteView.render("index.html", assigns)
def mount(_params, _session, socket) do
notebooks = Notes.list_notebooks()
{:ok, socket |> assign(notebooks: notebooks)}
end
end

33
apps/tomie_web/lib/tomie_web/live/note_live/new_notebook.ex

@ -0,0 +1,33 @@
defmodule TomieWeb.NoteLive.NewNotebook do
use TomieWeb, :live
alias Notes.Book
def render(assigns), do: TomieWeb.NoteView.render("new_notebook.html", assigns)
def mount(_params, _session, socket) do
changeset = Book.changeset(%Book{})
{:ok, assign(socket, changeset: changeset)}
end
def handle_event("validate", %{"book" => params}, socket) do
changeset =
%Book{}
|> Book.changeset(params)
|> Map.put(:action, :insert)
{:noreply, assign(socket, changeset: changeset)}
end
def handle_event("save", %{"book" => params}, socket) do
case Notes.create_notebook(params) do
{:ok, notebook} ->
{:noreply,
push_redirect(socket,
to: Routes.live_path(socket, TomieWeb.NoteLive.ShowNotebook, notebook)
)}
{:error, changeset} ->
{:noreply, assign(socket, changeset: changeset)}
end
end
end

15
apps/tomie_web/lib/tomie_web/live/note_live/show_note.ex

@ -0,0 +1,15 @@
defmodule TomieWeb.NoteLive.ShowNote do
use TomieWeb, :live
def render(assigns), do: TomieWeb.NoteView.render("show_note.html", assigns)
def mount(%{"id" => id, "note_id" => note_id}, _session, socket) do
note = Notes.get_note!(note_id)
{:ok, socket |> assign(note: note, notebook_id: id)}
end
def handle_params(%{"id" => _id, "note_id" => note_id}, _url, socket) do
note = Notes.get_note!(note_id)
{:noreply, socket |> assign(note: note)}
end
end

11
apps/tomie_web/lib/tomie_web/live/note_live/show_notebook.ex

@ -0,0 +1,11 @@
defmodule TomieWeb.NoteLive.ShowNotebook do
use TomieWeb, :live
def render(assigns), do: TomieWeb.NoteView.render("show_notebook.html", assigns)
def mount(%{"id" => id}, _session, socket) do
{notebook, nodes} = Notes.get_notebook!(id, depth: 1)
{:ok, socket |> assign(notebook: notebook, nodes: nodes)}
end
end

5
apps/tomie_web/lib/tomie_web/router.ex

@ -42,6 +42,11 @@ defmodule TomieWeb.Router do
live "/bookmarks/:id", BookmarkLive.Show
live "/bookmarks/:id/edit", BookmarkLive.Edit
live "/notebooks", NoteLive.Index
live "/notebooks/new", NoteLive.NewNotebook
live "/notebooks/:id", NoteLive.ShowNotebook
live "/notebooks/:id/note/:note_id", NoteLive.ShowNote
live "/tags", TagLive.Index
live "/tags/new", TagLive.New
live "/tags/:id", TagLive.Show

1
apps/tomie_web/lib/tomie_web/templates/layout/root.html.leex

@ -23,6 +23,7 @@
<% end %>
</li>
<li><%= active_link @conn, "Bookmarks", to: Routes.live_path(TomieWeb.Endpoint, TomieWeb.BookmarkLive.Index) %></li>
<li><%= active_link @conn, "Notes", to: Routes.live_path(TomieWeb.Endpoint, TomieWeb.NoteLive.Index) %></li>
<li><%= active_link @conn, "Listens", to: Routes.live_path(TomieWeb.Endpoint, TomieWeb.ListenLive.Index) %></li>
<li><%= active_link @conn, "Tags", to: Routes.live_path(TomieWeb.Endpoint, TomieWeb.TagLive.Index) %></li>
<li><%= active_link @conn, "Jobs", to: Routes.live_path(TomieWeb.Endpoint, TomieWeb.JobLive.Index) %></li>

10
apps/tomie_web/lib/tomie_web/templates/note/form.html.leex

@ -0,0 +1,10 @@
<%= f = form_for @changeset, "#",
[phx_change: :validate, phx_submit: :save] %>
<fieldset>
<%= label f, :title %>
<%= text_input f, :title, required: true %>
<%= error_tag f, :title %>
</fieldset>
<%= submit "submit", class: "button" %>
</form>

19
apps/tomie_web/lib/tomie_web/templates/note/index.html.leex

@ -0,0 +1,19 @@
<nav>
<ul>
<li><%= live_patch "New Notebook", to: Routes.live_path(@socket, NoteLive.NewNotebook) %></li>
</ul>
</nav>
<%= for notebook <- @notebooks do %>
<div class="card">
<%= live_patch to: Routes.live_path(@socket, NoteLive.ShowNotebook, notebook) do %>
<h3>
<%= notebook.title %>
</h3>
<p>
<%= note_count(notebook) %> Notes
</p>
<% end %>
</div>
<% end %>

1
apps/tomie_web/lib/tomie_web/templates/note/new_notebook.html.leex

@ -0,0 +1 @@
<%= render "form.html", assigns %>

13
apps/tomie_web/lib/tomie_web/templates/note/note.html.leex

@ -0,0 +1,13 @@
<div class="card">
<%= live_patch to: Routes.live_path(@socket, NoteLive.ShowNote, @note.notebook_id, @note) do %>
<h3><%= @note.title %> (<%= @note.id %>)</h3>
<p><%= @note.content %></p>
<% end %>
</div>
<div class="pl-4 border-l-2 border-solid border-on-surface-not-even">
<%= for child <- @children do %>
<%= live_component @socket, TomieWeb.NoteLive.NoteComponent, note: child %>
<% end %>
</div>

7
apps/tomie_web/lib/tomie_web/templates/note/show_note.html.leex

@ -0,0 +1,7 @@
<nav>
<ul>
<li><%= live_patch "Back", to: Routes.live_path(@socket, NoteLive.ShowNotebook, @notebook_id) %></li>
</ul>
</nav>
<%= live_component @socket, TomieWeb.NoteLive.NoteComponent, note: @note %>

5
apps/tomie_web/lib/tomie_web/templates/note/show_notebook.html.leex

@ -0,0 +1,5 @@
<h1> <%= @notebook.title %></h1>
<%= for node <- @nodes do %>
<%= live_component @socket, TomieWeb.NoteLive.NoteComponent, note: node %>
<% end %>

8
apps/tomie_web/lib/tomie_web/views/note_view.ex

@ -0,0 +1,8 @@
defmodule TomieWeb.NoteView do
use TomieWeb, :view
alias TomieWeb.NoteLive
def note_count(notebook) do
Enum.count(notebook.notes) - 1
end
end

1
apps/tomie_web/mix.exs

@ -58,6 +58,7 @@ defmodule TomieWeb.MixProject do
{:telemetry_metrics, "~> 0.4"},
{:tomie, in_umbrella: true},
{:bookmarks, in_umbrella: true},
{:notes, in_umbrella: true},
{:tags, in_umbrella: true},
{:db, in_umbrella: true},
{:listens, in_umbrella: true}

5
mix.lock

@ -3,6 +3,7 @@
"cachex": {:hex, :cachex, "3.2.0", "a596476c781b0646e6cb5cd9751af2e2974c3e0d5498a8cab71807618b74fe2f", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "aef93694067a43697ae0531727e097754a9e992a1e7946296f5969d6dd9ac986"},
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"},
"chartkick": {:hex, :chartkick, "0.4.0", "d490135346cc726bc0f971ef657cdb6504bc8c8bb01f760ad5dfaa86f6c67f5f", [:mix], [{:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}, {:uuid, "~> 1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm", "6c3493a5326066dd0eecd4ff5775916067c70aa67d1dc8f502724dd4d37f1319"},
"closure_table": {:hex, :closure_table, "0.1.6", "1c7ef93209cb5d2933edacf44d19a71d43ce9146a52de0b08bf2fcf907042ab7", [:mix], [{:ecto, "~> 3.1", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "758b992ae1ba9bdce3171857aae85b86ef09f27d1101bfa9878847b6aaf2f4a5"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
"cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"},
@ -11,9 +12,9 @@
"db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
"ecto": {:hex, :ecto, "3.4.2", "6890af71025769bd27ef62b1ed1925cfe23f7f0460bcb3041da4b705215ff23e", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3959b8a83e086202a4bd86b4b5e6e71f9f1840813de14a57d502d3fc2ef7132"},
"ecto": {:hex, :ecto, "3.4.3", "3a14c2500c3964165245a4f24a463e080762f7ccd0c632c763ea589f75ca205f", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b6f18dea95f2004d0369f6a8346513ca3f706614f4ede219a5f3fe5db5dd962"},
"ecto_autoslug_field": {:hex, :ecto_autoslug_field, "2.0.1", "2177c1c253f6dd3efd4b56d1cb76104d0a6ef044c6b9a7a0ad6d32665c4111e5", [:mix], [{:ecto, ">= 2.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:slugger, ">= 0.2.0", [hex: :slugger, repo: "hexpm", optional: false]}], "hexpm", "a3cc73211f2e75b89a03332183812ebe1ac08be2e25a1df5aa3d1422f92c45c3"},
"ecto_sql": {:hex, :ecto_sql, "3.4.2", "3d842665a81ba2137b62aa70151afe81dae44824cd09b2076a255937ab4e2dc9", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f2b064102467e1525314a464b6fea0707ff28ee132a15006727ccf51b73492ff"},
"ecto_sql": {:hex, :ecto_sql, "3.4.3", "c552aa8a7ccff2b64024f835503b3155d8e73452c180298527fbdbcd6e79710b", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ec9e59d6fa3f8cfda9963ada371e9e6659167c2338a997bd7ea23b10b245842b"},
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm", "b14f1dc204321429479c569cfbe8fb287541184ed040956c8862cb7a677b8406"},
"ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"},
"ex_utils": {:hex, :ex_utils, "0.1.7", "2c133e0bcdc49a858cf8dacf893308ebc05bc5fba501dc3d2935e65365ec0bf3", [:mix], [], "hexpm", "66d4fe75285948f2d1e69c2a5ddd651c398c813574f8d36a9eef11dc20356ef6"},

Loading…
Cancel
Save