elixir-maru / maru

Elixir RESTful Framework
https://maru.readme.io
BSD 3-Clause "New" or "Revised" License
1.32k stars 85 forks source link

Proper way to `use` `before` and `rescue_from` defined within other module? #122

Open mrusme opened 5 years ago

mrusme commented 5 years ago

I'm struggling to figure out how to make use of of things like before and rescue_from when they are defined inside another module, e.g.:

ExternalModule

defmodule ExternalModule.Server do
    defmacro __using__(_) do
        quote do
            use Maru.Server, otp_app: Confex.fetch_env!(:external_module, :server)[:app]

            def init(_type, opts) do
                Confex.Resolver.resolve(opts)
            end
        end
    end
end
defmodule ExternalModule do
    import Plug
    use ExternalModule.Server
    use ExternalModule.Helpers.Response

    defmacro __using__(_) do
        quote do
            before do
                plug Plug.Logger
                plug Corsica, origins: "*"
                plug Plug.Parsers,
                    pass: ["*/*"],
                    json_decoder: Jason,
                    parsers: [:urlencoded, :json, :multipart]
            end

            rescue_from Unauthorized, as: e do
                IO.inspect e
                ...
            end

            rescue_from [MatchError, RuntimeError], as: e do
                IO.inspect e
                ...
            end

            rescue_from Maru.Exceptions.InvalidFormat, as: e do
                IO.inspect e
                ...
            end

            rescue_from Maru.Exceptions.NotFound, as: e do
                IO.inspect e
                ...
            end

            rescue_from :all, as: e do
                IO.inspect e
                ...
            end
        end
    end
end

Now, inside my project I add ExternalModule as a dependency I and I create the following files:

MyModule

use Mix.Config

config :my_module, MyModule.Server,
    adapter: Plug.Cowboy,
    plug: MyModule.API,
    scheme: :http,
    ip: {0,0,0,0},
    port: {:system, :integer, "PORT", 8882}

config :my_module,
    maru_servers: [MyModule.Server]

config :external_module, :server,
    app: :my_module
defmodule MyModule.Server do
    use ExternalModule.Server
end
defmodule MyModule.API do
    use ExternalModule
    use MyModule.Server

    resources do
        get do
            json(conn, %{hello: :world})
        end

        mount MyModule.API.EndpointOne
    end
end

The application compiles successfully, but when I'm trying to access the endpoint that should be mounted with MyModule.API.EndpointOne a 500 is being returned. Apparently the mount is being ignores, as are the rescue_from definitions within the ExternalModule. However, the get do that returns hello: :world is being run when performing a GET /.

I'm not sure if this is a bug or simply me holding it wrong. However, some help (and maybe documentation) on this topic would be very much appreciated! :-)

Thanks!

falood commented 5 years ago

Hey @mrusme I tried to reproduce the issue, but I miss too many part such as how you put it in a supervisor tree and how MyModule.API.EndpointOne and ExternalModule.Helpers.Response look like.

It will more helpful for me If you can put your code in a simple standalone repo.

mrusme commented 5 years ago

@falood thank you for getting back! :) Actually you can check the real code here and here. paperwork.ex is the library where I'm trying to have the Maru.Server definition and all rescue_froms implemented, while service-notes is one service in which I'm trying to utilise those. I would e.g. like to get rid of this and only have it here.