Closed JanisErdmanis closed 1 year ago
Hi @JanisErdmanis, No worries, that's actually intended behavior.
In Julia, importing a module does not run all the code defined inside it at runtime. Instead, it only loads the module into memory and makes its exported functions and variables available for use in the current scope.
If you want to run all the code defined inside a module, you can use the include()
function. This function reads and evaluates the contents of a file as if they were entered directly into the REPL or script.
You can read more about this here: https://docs.julialang.org/en/v1/manual/code-loading/
So when integrating Oxygen into a custom module, you'll need to wrap the code that registers routes with some function. You can use the __init__
function to have it called automatically or create your own function that would need to get called manually. Alternatively, you can load your custom module with include()
like you're already doing here: https://github.com/PeaceFounder/PeaceFounder.jl/blob/master/src/PeaceFounder.jl#L24
This line is what executes all the code in your module and registers your routes automatically. If you take a similar approach with oxygen, it should work as expected.
Below is a brief example of how you could get this to work in your package. Break out the api operations into its own module and then include it, which will register all your routes.
This still does not work as I do load PeaceFounder
as a package. However, I just found that I can use @eval
at the __init__
block to include my service module:
module Example
function __init__()
fname = (@__DIR__) * "/Service.jl"
@eval include($fname)
end
serve() = Service.serve()
end
and that works as expected. Could there be any downsides to using this approach?
Also, thanks for pointing out to code-loading section. That has made me confused lately.
Honestly, I like that solution you came up with. It's short, clear, and is scalable ( it should automatically execute any other code referenced from that module ).
The only downside I'd be wary of is any weird precompilation issues from using eval in your package. Granted, this should be ok since you're only evaluating your own code that's tracked in git. This usually becomes an issue when people try to evaluate stuff that can change during run-time.
Here's the specific use case and discussion I found: https://discourse.julialang.org/t/precompilation-init-and-eval/70188
On a side note, the following package isn't using this new approach we're talking about, but it could still be helpful to see a package that uses Oxygen called RemoteHPC by Louis Ponet: https://github.com/louisponet/RemoteHPC.jl/blob/master/src/api.jl#L38
I am honestly hesitant to use @eval
+ include
approach I proposed earlier. The reason is that execution at the runtime is nonessential when compared with using HTTP
directly. In my opinion, there is a missing Julia feature but I am not sure what it is.
Another approach I am considering is to include the Oxygen
as a local module:
module OxygenExample
include(Base.find_package("Oxygen"))
using .Oxygen
using HTTP
get("/") do request::HTTP.Request
return "hello world!"
end
serve() = Oxygen.serve(port=8081)
end
which works fine, as expected. The only downside I see is that I need to manually add Oxygen's dependencies for that to work, but I expect that could be automated within a function.
That's a pretty wild approach. I had no idea we could add packages as local modules in Julia!
Although, I would caution you against going that route unless you absolutely had to. If I ever have to add a new dependency and publish it - it could break your package and require you to republish your app with the new dependency. I personally wouldn't want my package to be coupled so tightly to another package.
Granted, you could get around this by explicitly adding strict version rules in the compatibility section of your Project.toml file. This would prevent any potential breaking changes from getting introduced at the cost of your users not having access to the latest updates from Oxygen (until you manually update your compatibility section).
But at the end of the day, It's your package and you should choose whatever approach best meets your requirements. I'm just thankful that you see Oxygen as a viable alternative to HTTP.jl and am excited to see what you can do with it! Let me know if you have any other questions or feature requests. Package authors are the power users of our little community and I want to make sure to support them in any way that I can.
I love the idea of writing HTTP request handlers and simultaneously registering them to a router. I would also love to try to use Swagger to document my service currently written in bare
HTTP.jl
, as can be seen here. However, I have stumbled upon an issue where my handlers are not registered when it's imported from the module.For example, let's consider a module:
Using this module as
import Example; Example.serve()
would serve a blank page at/
.Interestingly, I found that if I import the module as a file
include("src/Example.jl"); Example.serve()
, the handler works as expected. Also, I found that moving the handler to the__init__
fixes the issue; however, that is not a viable solution as that would either make__init__
function unbearably long or would make code less local in comparison with direct usage of HTTP.