Walerian Sobczak
Walerian Sobczak

Reputation: 849

How to write to standard input in Elixir?

I have this module for checking the parity of numbers:

defmodule Parity do
  def start() do
    continue()
  end

  defp continue() do
    get_number()
    |> parse()
    |> even?()
    |> print_output()
    continue()
  end

  defp get_number() do
    IO.gets("Type a number: ")
  end

  defp parse(input) do
    String.trim(input)
    |> Integer.parse
  end

  defp even?({ number, _ }) do
    Integer.mod(number, 2) == 0
  end

  defp print_output(_is_even = true) do
    IO.puts("This number is even!")
  end

  defp print_output(_is_odd) do
    IO.puts("This number is odd!")
  end
end

And it works like this:

$ mix run -e Parity.start
Type a number: 4
This number is even!
Type a number: 3
This number is odd!
Type a number: 68
This number is even!
Type a number: 1
This number is odd!
Type a number: ...

Now I want to have another module, which would be able to write numbers to the standard input for the Parity module. So I need to have a something for executing Parity.start() and then providing numbers to IO.gets() function. I tried something like this:

defmodule Bot do
  def start() do
    Parity.start()
    provide_number()
  end

  defp provide_number() do
    random_number()
    |> IO.puts
    provide_number()
  end

  defp random_number() do
    Enum.random(0..100)
  end
end

And it doesn't work.

It only starts Parity module, but there is no input from the provide_number() function.

Upvotes: 3

Views: 1212

Answers (1)

t56k
t56k

Reputation: 6981

If you want to write to stdin (like the equivalent of faking a CLI input) you pretty much need a way to exchange info between different system processes. Porcelain does that pretty well:

defmodule OtherModule do
  alias Porcelain.Process, as: Proc
  alias Porcelain.Result

  proc = %Proc{pid: pid} =
    Porcelain.spawn_shell("mix run -e Parity.start", in: :receive, out: {:send, self()})

  Proc.send_input(proc, "1")
  receive do
    {^pid, :data, :out, data} -> IO.inspect data   #=> "This number is odd!"
  end
end

Written without testing, but that should give you a rough idea. Porcelain has a much better handle on this than System.cmd/3. Alternatively, if you don't need stdin, you should run each module on their own Elixir node:

iex --sname parity -S mix

Then in your OtherModule you can write something like:

pid = Node.spawn_link :"parity@computer-name", fn -> ... end

Upvotes: 3

Related Questions