First commit
This commit is contained in:
parent
a14860401e
commit
b45a818fa4
4
.formatter.exs
Normal file
4
.formatter.exs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,5 +1,12 @@
|
|||||||
# The directory Mix will write compiled artifacts to.
|
# The directory Mix will write compiled artifacts to.
|
||||||
/_build/
|
/_build/
|
||||||
|
/.elixir_ls/
|
||||||
|
/priv/input/
|
||||||
|
/priv/output/
|
||||||
|
|
||||||
|
/config/*.secret.exs
|
||||||
|
/config/*.pem
|
||||||
|
/config/*.key
|
||||||
|
|
||||||
# If you run "mix test --cover", coverage assets end up here.
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
/cover/
|
/cover/
|
||||||
|
4
.iex.exs
Normal file
4
.iex.exs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
alias Translator.Languages
|
||||||
|
alias Translator.Batcher
|
||||||
|
alias Translator.Parser
|
||||||
|
alias Translator.Parser.{Base, JSON}
|
21
README.md
Normal file
21
README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Translator
|
||||||
|
|
||||||
|
**TODO: Add description**
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||||
|
by adding `translator` to your list of dependencies in `mix.exs`:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def deps do
|
||||||
|
[
|
||||||
|
{:translator, "~> 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/translator](https://hexdocs.pm/translator).
|
||||||
|
|
12
config/config.exs
Normal file
12
config/config.exs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
# Configures Elixir's Logger
|
||||||
|
config :logger, :console,
|
||||||
|
format: "$time $metadata[$level] $message\n",
|
||||||
|
metadata: [:request_id]
|
||||||
|
|
||||||
|
config :translator,
|
||||||
|
parsers: [Translator.Parser.YAML, Translator.Parser.JSON],
|
||||||
|
availiable_languages: ~w(en zh ar es hi de ru be uk kk bn pl pt it fr)
|
||||||
|
|
||||||
|
import_config "#{Mix.env()}.exs"
|
3
config/dev.exs
Normal file
3
config/dev.exs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Config
|
||||||
|
|
||||||
|
import_config "dev.secret.exs"
|
1
config/prod.exs
Normal file
1
config/prod.exs
Normal file
@ -0,0 +1 @@
|
|||||||
|
import Config
|
1
config/test.exs
Normal file
1
config/test.exs
Normal file
@ -0,0 +1 @@
|
|||||||
|
import Config
|
19
lib/translator/application.ex
Normal file
19
lib/translator/application.ex
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
defmodule Translator.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: Translator.Worker.start_link(arg)
|
||||||
|
# {Translator.Worker, arg}
|
||||||
|
]
|
||||||
|
|
||||||
|
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||||
|
# for other strategies and supported options
|
||||||
|
opts = [strategy: :one_for_one, name: Translator.Supervisor]
|
||||||
|
Supervisor.start_link(children, opts)
|
||||||
|
end
|
||||||
|
end
|
28
lib/translator/batcher.ex
Normal file
28
lib/translator/batcher.ex
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
defmodule Translator.Batcher do
|
||||||
|
alias Translator.Languages
|
||||||
|
|
||||||
|
@spec parse(String.t()) :: {:ok, map} | {:error, String.t()}
|
||||||
|
def parse(path) do
|
||||||
|
batch = Path.basename(path)
|
||||||
|
|
||||||
|
folder =
|
||||||
|
Path.join(path, Languages.pattern())
|
||||||
|
|> Path.wildcard()
|
||||||
|
|> List.first()
|
||||||
|
|
||||||
|
case folder do
|
||||||
|
nil ->
|
||||||
|
{:error, "Couldn't find input folder in batch '#{path}'"}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
name: batch,
|
||||||
|
folder: path,
|
||||||
|
input: folder,
|
||||||
|
language: Path.basename(folder),
|
||||||
|
files: Path.wildcard(Path.join(folder, "**"))
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
4
lib/translator/languages.ex
Normal file
4
lib/translator/languages.ex
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
defmodule Translator.Languages do
|
||||||
|
def list(), do: Application.get_env(:translator, :availiable_languages)
|
||||||
|
def pattern(), do: "{" <> Enum.join(list(), ",") <> "}"
|
||||||
|
end
|
72
lib/translator/parser.ex
Normal file
72
lib/translator/parser.ex
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
defmodule Translator.Parser do
|
||||||
|
@type filename :: String.t()
|
||||||
|
@type extension :: String.t()
|
||||||
|
@type data :: map
|
||||||
|
@type parser :: atom
|
||||||
|
@type availiable_parsers_map :: %{required(extension) => parser}
|
||||||
|
|
||||||
|
@spec load(filename) :: {:error, any} | {:ok, any}
|
||||||
|
def load(filename) do
|
||||||
|
case select_parser(filename) do
|
||||||
|
{:error, message} -> {:error, message}
|
||||||
|
{:ok, parser} -> load(filename, parser)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec load(filename, parser) :: {:error, any} | {:ok, any}
|
||||||
|
def load(filename, parser) do
|
||||||
|
with {:ok, contents} <- File.read(filename),
|
||||||
|
{:ok, result} <- parser.parse(contents) do
|
||||||
|
{:ok, result}
|
||||||
|
else
|
||||||
|
{:error, error} -> {:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec save(filename, data) :: {:error, any} | {:ok, any}
|
||||||
|
def save(filename, data) do
|
||||||
|
case select_parser(filename) do
|
||||||
|
{:error, message} -> {:error, message}
|
||||||
|
{:ok, parser} -> save(filename, data, parser)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec save(filename, data, parser) :: :ok | {:error, any}
|
||||||
|
def save(filename, data, parser) do
|
||||||
|
with {:ok, contents} <- parser.generate(data),
|
||||||
|
:ok <- File.write(filename, contents) do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
{:error, error} -> {:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec select_parser(filename) :: {:ok, atom} | {:error, any}
|
||||||
|
defp select_parser(filename) do
|
||||||
|
case select_parser!(filename) do
|
||||||
|
nil -> {:error, "Couldn't find availiable parser for this extension"}
|
||||||
|
value -> {:ok, value}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec select_parser!(filename) :: atom
|
||||||
|
defp select_parser!(filename),
|
||||||
|
do: Map.get(availiable(), String.trim_leading(Path.extname(filename), "."))
|
||||||
|
|
||||||
|
@spec availiable() :: availiable_parsers_map
|
||||||
|
defp availiable() do
|
||||||
|
parsers = Application.get_env(:translator, :parsers)
|
||||||
|
|
||||||
|
parsers
|
||||||
|
|> Enum.reduce(%{}, fn parser, avaliable_extensions_map ->
|
||||||
|
{:ok, extensions} = parser.extensions()
|
||||||
|
|
||||||
|
parser_extensions_map =
|
||||||
|
extensions
|
||||||
|
|> Enum.map(&{&1, parser})
|
||||||
|
|> Map.new()
|
||||||
|
|
||||||
|
Map.merge(avaliable_extensions_map, parser_extensions_map)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
20
lib/translator/parser/base.ex
Normal file
20
lib/translator/parser/base.ex
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
defmodule Translator.Parser.Base do
|
||||||
|
@typedoc """
|
||||||
|
Text contents of the file for parse in Map
|
||||||
|
"""
|
||||||
|
@type contents :: String.t()
|
||||||
|
|
||||||
|
@typedoc """
|
||||||
|
Data in Map format for encoding to text contents of the file
|
||||||
|
"""
|
||||||
|
@type data :: map
|
||||||
|
|
||||||
|
@typedoc """
|
||||||
|
List of the file extensions for parser
|
||||||
|
"""
|
||||||
|
@type extensions :: [String.t()]
|
||||||
|
|
||||||
|
@callback parse(contents) :: {:ok, data} | {:error, any}
|
||||||
|
@callback generate(data) :: {:ok, contents} | {:error, any}
|
||||||
|
@callback extensions() :: {:ok, extensions}
|
||||||
|
end
|
25
lib/translator/parser/json.ex
Normal file
25
lib/translator/parser/json.ex
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
defmodule Translator.Parser.JSON do
|
||||||
|
@extensions ["json"]
|
||||||
|
|
||||||
|
@type contents :: String.t()
|
||||||
|
@type data :: map
|
||||||
|
@type extensions :: [String.t()]
|
||||||
|
|
||||||
|
@behaviour Translator.Parser.Base
|
||||||
|
|
||||||
|
@impl Translator.Parser.Base
|
||||||
|
@spec parse(contents) :: {:ok, data} | {:error, atom | Jason.DecodeError.t()}
|
||||||
|
def parse(contents) do
|
||||||
|
Jason.decode(contents)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Translator.Parser.Base
|
||||||
|
@spec generate(data) :: {:ok, contents} | {:error, any}
|
||||||
|
def generate(data) do
|
||||||
|
Jason.encode(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Translator.Parser.Base
|
||||||
|
@spec extensions() :: {:ok, extensions}
|
||||||
|
def extensions(), do: {:ok, @extensions}
|
||||||
|
end
|
41
lib/translator/parser/yaml.ex
Normal file
41
lib/translator/parser/yaml.ex
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
defmodule Translator.Parser.YAML do
|
||||||
|
@extensions ["yml", "yaml"]
|
||||||
|
|
||||||
|
@type contents :: String.t()
|
||||||
|
@type data :: map
|
||||||
|
@type extensions :: [String.t()]
|
||||||
|
|
||||||
|
@behaviour Translator.Parser.Base
|
||||||
|
|
||||||
|
@impl Translator.Parser.Base
|
||||||
|
@spec parse(contents) :: {:ok, data} | {:error, atom | Jason.DecodeError.t()}
|
||||||
|
def parse(contents) do
|
||||||
|
YamlElixir.read_from_string(contents)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Translator.Parser.Base
|
||||||
|
@spec generate(data) :: {:ok, contents} | {:error, any}
|
||||||
|
def generate(data) do
|
||||||
|
{:ok, to_yaml(data)}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp to_yaml(data, indentation \\ "") do
|
||||||
|
data
|
||||||
|
|> Map.keys()
|
||||||
|
|> Enum.map(fn key ->
|
||||||
|
Map.fetch!(data, key)
|
||||||
|
|> value_to_yaml(key, indentation)
|
||||||
|
end)
|
||||||
|
|> Enum.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp value_to_yaml(value, key, indentation) when is_number(value) or is_bitstring(value),
|
||||||
|
do: "#{indentation}#{key}: #{value}"
|
||||||
|
|
||||||
|
defp value_to_yaml(value, key, indentation) when is_map(value),
|
||||||
|
do: "#{indentation}#{key}:\n#{to_yaml(value, "#{indentation} ")}"
|
||||||
|
|
||||||
|
@impl Translator.Parser.Base
|
||||||
|
@spec extensions() :: {:ok, extensions}
|
||||||
|
def extensions(), do: {:ok, @extensions}
|
||||||
|
end
|
4
lib/translator/translator.ex
Normal file
4
lib/translator/translator.ex
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
defmodule Translator do
|
||||||
|
defdelegate translate(text, to, from), to: YandexTranslate
|
||||||
|
defdelegate translate(text, to), to: YandexTranslate
|
||||||
|
end
|
25
lib/translator/watcher.ex
Normal file
25
lib/translator/watcher.ex
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
defmodule Translator.Watcher do
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
@spec start_link(any) :: :ignore | {:error, any} | {:ok, pid}
|
||||||
|
def start_link(args) do
|
||||||
|
GenServer.start_link(__MODULE__, args)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec init(keyword) :: {:ok, %{watcher_pid: pid}}
|
||||||
|
def init(args) do
|
||||||
|
{:ok, watcher_pid} = FileSystem.start_link(args)
|
||||||
|
FileSystem.subscribe(watcher_pid)
|
||||||
|
{:ok, %{watcher_pid: watcher_pid}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info({:file_event, watcher_pid, {path, events}}, %{watcher_pid: watcher_pid} = state) do
|
||||||
|
# YOUR OWN LOGIC FOR PATH AND EVENTS
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_info({:file_event, watcher_pid, :stop}, %{watcher_pid: watcher_pid} = state) do
|
||||||
|
# YOUR OWN LOGIC WHEN MONITOR STOP
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
end
|
44
mix.exs
Normal file
44
mix.exs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
defmodule Translator.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :translator,
|
||||||
|
version: "0.1.0",
|
||||||
|
elixir: "~> 1.10",
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
deps: deps()
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
mod: {Translator.Application, []},
|
||||||
|
extra_applications: applications(Mix.env())
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp applications(:dev), do: applications(:all) ++ [:remix]
|
||||||
|
defp applications(_all), do: [:logger]
|
||||||
|
|
||||||
|
# Run "mix help deps" to learn about dependencies.
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
# Translators
|
||||||
|
{:yandex_translate, "~> 0.4.0"},
|
||||||
|
|
||||||
|
# Parsers/generators
|
||||||
|
{:jason, "~> 1.2.0"},
|
||||||
|
{:yaml_elixir, "~> 2.4.0"},
|
||||||
|
|
||||||
|
# File system monitoring
|
||||||
|
{:file_system, "~> 0.2.8"},
|
||||||
|
|
||||||
|
# Remix for autorestart
|
||||||
|
{:ex_doc, "~> 0.21.3", only: :dev, runtime: false},
|
||||||
|
{:credo, "~> 1.4.0-rc.1", only: :dev, runtime: false},
|
||||||
|
{:remix, "~> 0.0.2", only: :dev}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
19
mix.lock
Normal file
19
mix.lock
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
%{
|
||||||
|
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
|
||||||
|
"castore": {:hex, :castore, "0.1.5", "591c763a637af2cc468a72f006878584bc6c306f8d111ef8ba1d4c10e0684010", [:mix], [], "hexpm", "6db356b2bc6cc22561e051ff545c20ad064af57647e436650aa24d7d06cd941a"},
|
||||||
|
"credo": {:hex, :credo, "1.4.0-rc.2", "5a618536557c6bacc1ad0b13eb93c271f29946394b23a23e5f5c5a327e10ae52", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "a40447f2fb853824e5acbd0f69100a0a2794c6b62d5774d6566e7601ad727cca"},
|
||||||
|
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
|
||||||
|
"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"},
|
||||||
|
"file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"},
|
||||||
|
"jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"},
|
||||||
|
"joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},
|
||||||
|
"jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"},
|
||||||
|
"makeup": {:hex, :makeup, "1.0.1", "82f332e461dc6c79dbd82fbe2a9c10d48ed07146f0a478286e590c83c52010b5", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49736fe5b66a08d8575bf5321d716bac5da20c8e6b97714fec2bcd6febcfa1f8"},
|
||||||
|
"makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"},
|
||||||
|
"mint": {:hex, :mint, "1.0.0", "ca5ab33497ba2bdcc42f6cdd3927420a6159116be87c8173658e93c8746703da", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "b8943ef1e630879538dd6620bfc189d4d75fab3ad39f3fe9c50539879f7efd84"},
|
||||||
|
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
|
||||||
|
"remix": {:hex, :remix, "0.0.2", "f06115659d8ede8d725fae1708920ef73353a1b39efe6a232d2a38b1f2902109", [:mix], [], "hexpm", "5f5555646ed4fca83fab8620735150aa0bc408c5a17a70d28cfa7086bc6f497c"},
|
||||||
|
"yamerl": {:hex, :yamerl, "0.7.0", "e51dba652dce74c20a88294130b48051ebbbb0be7d76f22de064f0f3ccf0aaf5", [:rebar3], [], "hexpm", "cb5a4481e2e2ad36db83bd9962153e1a9208e2b2484185e33fc2caac6a50b108"},
|
||||||
|
"yaml_elixir": {:hex, :yaml_elixir, "2.4.0", "2f444abc3c994c902851fde56b6a9cb82895c291c05a0490a289035c2e62ae71", [:mix], [{:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "4e25a6d5c873e393689c6f1062c5ec90f6cd1be2527b073178ae37eae4c78bee"},
|
||||||
|
"yandex_translate": {:hex, :yandex_translate, "0.4.0", "a8851bdd0899d29334c9028aecc70c9921cae5dff86e512e30e72a0bceb6613a", [:mix], [{:castore, "~> 0.1.5", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.2.0", [hex: :jason, repo: "hexpm", optional: false]}, {:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:mint, "~> 1.0.0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "d4cdb333d8e447d0de20e0314ffc5fdd0a38421800866801007baca9102936f1"},
|
||||||
|
}
|
1
test/test_helper.exs
Normal file
1
test/test_helper.exs
Normal file
@ -0,0 +1 @@
|
|||||||
|
ExUnit.start()
|
8
test/translator_test.exs
Normal file
8
test/translator_test.exs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
defmodule TranslatorTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest Translator
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
assert Translator.hello() == :world
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user