reph icon indicating copy to clipboard operation
reph copied to clipboard

Project Architecture

Open micahrye opened this issue 8 years ago • 1 comments

The more I have played with your project the more I like it for getting up and going, but I also have found some limitations.

Most notably is the use of std_json_io. When using std_json_io the function StdJsonIo.json_call will error if your JS code contains the fetch module, but is fine with axios, which you include in your project.

The error is at the line of code using StdJsonIo.json_call, output is:

[error] #PID<0.580.0> running DemoAppWeb.Endpoint terminated
Server: localhost:4000 (http)
Request: GET /about
** (exit) an exception was raised:
    ** (MatchError) no match of right hand side value: {:error, "fetch is not defined"}
        (demo_app) lib/demo_app_web/controllers/react_controller.ex:14: DemoAppWeb.ReactController.index/2
        (demo_app) lib/demo_app_web/controllers/react_controller.ex:1: DemoAppWeb.ReactController.action/2
        (demo_app) lib/demo_app_web/controllers/react_controller.ex:1: DemoAppWeb.ReactController.phoenix_controller_pipeline/2
        (demo_app) lib/demo_app_web/endpoint.ex:1: DemoAppWeb.Endpoint.instrument/4
        (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
        (demo_app) lib/demo_app_web/endpoint.ex:1: DemoAppWeb.Endpoint.plug_builder_call/2
        (demo_app) lib/plug/debugger.ex:99: DemoAppWeb.Endpoint."call (overridable 3)"/2
        (demo_app) lib/demo_app_web/endpoint.ex:1: DemoAppWeb.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) /Users/micahrye/code/elixir/workbench/web/demo_app/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

While I do not have a full archtecture plan, I was doing some hacking around related to the error above and found a way to not get errors related to JS files with fetch and a major speed up of load time.

I started looking at the use of react-stdio, which you have also contribued code too.

If instead of using std_json_io and instead read from a file that contains the output of react-stdio we get a big speed up and no errors with respect to fetch. See resutls:

  • std_json_io avg response time 50ms
  • read react-stdio output from file response time 400μs

In the second approach I use react-stdio directly and write its' output to file:

echo '{ "component": "/Users/micahrye/code/elixir/workbench/web/demo_app/_build/dev/lib/demo_app/priv/static/server/js/app.js", "props": {"initial_state": {}, "location": ""} }' | react-stdio  > app.js

You can then place the app.js file into _build/{dev, test, prod}/lib/demo_app/priv/static/server/js/.

In the ReactController I then modify it to be:

def index(conn, _params) do
    initial_state = %{}
    html =
      Application.app_dir(:demo_app, "priv")
      |> Kernel.<>("/static/server/js/app.js")
      |> File.read!()
    
    render(conn, "index.html", html: html, initial_state: initial_state)
  end

Which will pull the app.js file we created from the right environment folder in _build.

While this is def hacky, and there may be negatives that I do not realize, it points to a potentially better solution that does not have the fetch error and is much more performant.

micahrye avatar Jan 19 '18 19:01 micahrye

This will work, assuming the happy path:

    react_stdio_args = %{
      component: Application.app_dir(:myapp, "priv/static/server/js/app.js"),
      props: %{
        "location" => conn.request_path,
        "initial_state" => initial_state
      }
    }
    {:ok, react_stdio_arg_str} = Poison.encode(react_stdio_args)

    html =
      'echo \'#{react_stdio_arg_str}\' | react-stdio'
      |> :os.cmd()
      |> to_string()

    render(conn, "index.html", html: html, initial_state: initial_state)

Now it is all handled in Phoenix.

Works with

  • Running mix complied for prod, PORT=4001 MIX_ENV=prod mix phx.server
  • Running Distillery release, PORT=4001 _build/prod/rel/demo_app/bin/demo_app foreground

Now you can remove std_json_io from mix.exs and config/config.exs if wanted. This approach is slower than the original, the fastest would be to enable production release to open with File.read! which was by far fastest.

Also, since you put in the time to make a nice project, and I would like to see this be popular, it might be nice to start a topic for the project on Elixir Form for libraries.

This could be nice for open/visible conversation that does not fit into the github "issues."

micahrye avatar Jan 19 '18 20:01 micahrye