diff --git a/.committee.exs b/.committee.exs index 51f72ef1..91e94e2f 100644 --- a/.committee.exs +++ b/.committee.exs @@ -13,8 +13,10 @@ defmodule YourApp.Commit do To test: `mix committee.runner [pre_commit | post_commit]` """ def pre_commit do - {_format_output, 0} = System.cmd("mix", ["format"] ++ staged_files([".ex", ".exs"])) - {_add_output, 0} = System.cmd("git", ["add"] ++ staged_files()) + existing_staged_files = staged_files([".ex", ".exs"]) |> Enum.filter(&File.exists?(&1)) + + {_format_output, 0} = System.cmd("mix", ["format"] ++ existing_staged_files) + {_add_output, 0} = System.cmd("git", ["add"] ++ existing_staged_files) {:ok, "SUCCESS!"} end end diff --git a/apps/xest/tui.exs b/apps/xest/tui.exs new file mode 100644 index 00000000..64aeba25 --- /dev/null +++ b/apps/xest/tui.exs @@ -0,0 +1,23 @@ +defmodule Counter do + @behaviour Ratatouille.App + + import Ratatouille.View + + def init(_context), do: 0 + + def update(model, msg) do + case msg do + {:event, %{ch: ?+}} -> model + 1 + {:event, %{ch: ?-}} -> model - 1 + _ -> model + end + end + + def render(model) do + view do + label(content: "Counter is #{model} (+/-)") + end + end +end + +Ratatouille.run(Counter) diff --git a/apps/xest_tui/.formatter.exs b/apps/xest_tui/.formatter.exs new file mode 100644 index 00000000..d2cda26e --- /dev/null +++ b/apps/xest_tui/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/apps/xest_tui/.gitignore b/apps/xest_tui/.gitignore new file mode 100644 index 00000000..5216c8e5 --- /dev/null +++ b/apps/xest_tui/.gitignore @@ -0,0 +1,27 @@ +# 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"). +xest_tui-*.tar + + +# Temporary files for e.g. tests +/tmp diff --git a/apps/xest_tui/README.md b/apps/xest_tui/README.md new file mode 100644 index 00000000..364fe6e3 --- /dev/null +++ b/apps/xest_tui/README.md @@ -0,0 +1,21 @@ +# Xest.TUI + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `xest_tui` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:xest_tui, "~> 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/xest_tui](https://hexdocs.pm/xest_tui). + diff --git a/apps/xest_tui/lib/xest/tui.ex b/apps/xest_tui/lib/xest/tui.ex new file mode 100644 index 00000000..b7b3c211 --- /dev/null +++ b/apps/xest_tui/lib/xest/tui.ex @@ -0,0 +1,18 @@ +defmodule Xest.TUI do + @moduledoc """ + Documentation for `Xest.TUI`. + """ + + @doc """ + Hello world. + + ## Examples + + iex> Xest.TUI.hello() + :world + + """ + def hello do + :world + end +end diff --git a/apps/xest_tui/lib/xest/tui/application.ex b/apps/xest_tui/lib/xest/tui/application.ex new file mode 100644 index 00000000..e88fd233 --- /dev/null +++ b/apps/xest_tui/lib/xest/tui/application.ex @@ -0,0 +1,20 @@ +defmodule Xest.TUI.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + @impl true + def start(_type, _args) do + children = [ + # TUI process + {Ratatouille.Runtime.Supervisor, runtime: [app: Xest.TUI.TUI]} + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Xest.TUI.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/apps/xest_tui/lib/xest/tui/tui.ex b/apps/xest_tui/lib/xest/tui/tui.ex new file mode 100644 index 00000000..d929aad7 --- /dev/null +++ b/apps/xest_tui/lib/xest/tui/tui.ex @@ -0,0 +1,79 @@ +defmodule Xest.TUI.TUI do + @behaviour Ratatouille.App + + alias Ratatouille.{EventManager, Window} + alias Ratatouille.Runtime.{Subscription, State} + + require DateTime + import Ratatouille.View + + alias Xest.BinanceExchange + alias Xest.Models + + @impl true + def init(_context) do + {DateTime.utc_now(), 0, Xest.BinanceExchange.model(Xest.BinanceExchange)} + end + + @impl true + def subscribe(_model) do + Subscription.interval(1_000, :tick) + end + + @impl true + def update({now, counter, exchange}, msg) do + case msg do + {:event, %{ch: ?q}} -> + :ok = Window.close() + + {:event, %{ch: ?+}} -> + {now, counter + 1, exchange} + + {:event, %{ch: ?-}} -> + {now, counter - 1, exchange} + + :tick -> + {DateTime.utc_now(), counter, exchange} + + _ -> + IO.inspect(msg) + {now, counter, exchange} + end + end + + # TODO : TUI design... + + # Various windows: + # - HTTP requests (a nice log view) + # - websockets (a nice log view) + # - Events (from both rest polling and websockets, a debugging interface for commanded) + # - Exchange (model) State (from Events, but other untracked sources) - default view + + @impl true + def render({now, counter, exchange}) do + view do + panel title: "Clock Example ('q' to quit)" do + label(content: "The time is: #{DateTime.to_string(now)}") + end + + panel title: "Counter" do + label(content: "Counter is #{counter} (+/-)") + end + + render_exchange(exchange) + end + end + + defp render_exchange(%Models.Exchange{} = exchange) do + panel title: "Exchange" do + label(content: "code: #{exchange.status.code} ") + label(content: "status: #{exchange.status.message} ") + end + end + + defp render_events() do + end + + defp render_adapters() do + end +end diff --git a/apps/xest_tui/mix.exs b/apps/xest_tui/mix.exs new file mode 100644 index 00000000..fc4f88fc --- /dev/null +++ b/apps/xest_tui/mix.exs @@ -0,0 +1,36 @@ +defmodule Xest.TUI.MixProject do + use Mix.Project + + def project do + [ + app: :xest_tui, + version: "0.1.0", + build_path: "../../_build", + config_path: "../../config/config.exs", + deps_path: "../../deps", + lockfile: "../../mix.lock", + elixir: "~> 1.11", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger], + mod: {Xest.TUI.Application, []} + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # TUI tooling + {:ratatouille, "~> 0.5.0"}, + {:xest, 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 diff --git a/apps/xest_tui/test/test_helper.exs b/apps/xest_tui/test/test_helper.exs new file mode 100644 index 00000000..869559e7 --- /dev/null +++ b/apps/xest_tui/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/apps/xest_tui/test/xest/tui_test.exs b/apps/xest_tui/test/xest/tui_test.exs new file mode 100644 index 00000000..6e6d4d77 --- /dev/null +++ b/apps/xest_tui/test/xest/tui_test.exs @@ -0,0 +1,8 @@ +defmodule Xest.TUITest do + use ExUnit.Case + doctest Xest.TUI + + test "greets the world" do + assert Xest.TUI.hello() == :world + end +end diff --git a/config/config.exs b/config/config.exs index a7b8d6e6..c831a86c 100644 --- a/config/config.exs +++ b/config/config.exs @@ -12,6 +12,9 @@ use Mix.Config config :xest_web, generators: [context_app: :xest] +config :xest_tui, + generators: [context_app: :xest] + # Configures the endpoint config :xest_web, XestWeb.Endpoint, url: [host: "localhost"], diff --git a/config/dev.exs b/config/dev.exs index f60b23dd..5a7719ea 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -68,7 +68,3 @@ config :phoenix, :stacktrace_depth, 20 # clear screen when test_watch running again config :mix_test_watch, clear: true - -# to make sure we format before committing -# TODO : add credo and coveralls -config :pre_commit, commands: ["format"]