elvanja
Hi, I’m trying to see if there’s a way to find all the modules that implement a behaviour, but from an escript.
This is one way I tried extracting those:
defp modules_implementing_behaviour(behaviour) do
for {module, _} <- :code.all_loaded(),
behaviour in (module.module_info(:attributes)
|> Keyword.get_values(:behaviour)
|> List.flatten()) do
module
end
end
Another attempt was this (also required adding :mix to :external_applications in mix.exs):
# copied from https://github.com/findmypast/behaviour-introspection
defp modules_implementing_behaviour(behaviour) do
# Ensure the current projects code path is loaded
Mix.Task.run("loadpaths", [])
# Fetch all .beam files
Path.wildcard(Path.join([Mix.Project.build_path(), "**/ebin/**/*.beam"]))
# Parse the BEAM for behaviour implementations
|> Stream.map(fn path ->
{:ok, {mod, chunks}} = :beam_lib.chunks('#{path}', [:attributes])
{mod, get_in(chunks, [:attributes, :behaviour])}
end)
# Filter out behaviours we don't care about and duplicates
|> Stream.filter(fn {_mod, all_behaviours} ->
is_list(all_behaviours) && behaviour in all_behaviours
end)
|> Stream.map(fn {module, _} -> module end)
|> Enum.uniq()
end
None of those actually work like that. The first one did kind of work if I did Code.ensure_loaded?(AModuleThatImplementsTargetBehaviour), but it defeats the purpose since I’d need to manually list all the related modules.
The problem I’m trying to solve is a project that knows how to perform a data sync between two databases. It has a number of sync scenarios to choose from. The whole idea is to create an escript that accepts a param that determines which data sync scenario we want, starts the application, finds that scenario and executes it. Not even sure that the escript is the correct way to do it. Thought of releases but can’t find a nice way to wrap it all up as an executable. Docker is something that would work but I was hoping to find a way without that. I can even make it a service that exposes some API and can be triggered via http, but that seems like an overkill for something so simple.
Anyway, thank you for your time ![]()
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #ai
- #phoenix_html
- #elixirconf-us
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
elvanja
Update, a slight modification, without using
Mixis marginally better:This works, but in reality it’s only because
libis right where the code expects it. So it looks like the relatedlibmodule isn’t included in the escript.I tried several ways to include all code from
libin that escript related module, no luck. Not even:ok = Application.ensure_started(:app)seems to be working. Even just aliasing those modules doesn’t work, they need to be explicitly used. This seems to work after all:So I guess this is all about how to actually include the main app into escript. If it’s even possible…
dimitarvp
Since dev mode starts the BEAM in interactive mode, my first stab at the problem would be to make sure all your app’s modules are loaded:
…and then retry with your coding snippets from above.
(
:code.all_available()docs for reference.)This is not a bulletproof solution since f.ex. Phoenix projects with Postgres enums can produce modules defined in the global namespace if one is not careful (i.e. not belonging to your project’s namespace) but I’d think it’s good enough as a start.
NOTE 1: You’ll need OTP 23 for
:code.all_available().NOTE 2: Maybe this thread can help as well: Run code for each module implementing a Behaviour
NOTE 3: Maybe this answer in a thread can help as well? How to detect if a module implemented genserver callbacks - #2 by NobbZ
dimitarvp
This got interesting to me because I’ve struggled with it in the past so I made a more complete example. I have a few meduim-sized Elixir projects whose source customers from 2018 and earlier have allowed me to keep. I tested the below module on them and it works quite fine in
iexat least.Curious to find out if it works for you. I realize your case is a bit different but, again, I feel this code is a good first step. Would ditching the escript idea for a Mix task work?
lud
Not sure if it is a good idea but you could abuse
defprotocolanddefimplto create a “dispatch protocol” that would call the appropriate module for the desired scenario.elvanja
I’ve created a small project to demonstrate the problem, see GitHub - elvanja/escript_testbed · GitHub. One funny thing I noticed is that running it as a mix task also doesn’t work, unless I change one of the scenario modules. In that case, that module is compiled, and mix task is able to find it. But, if there are no changes, there’s also no result. So even in mix tasks I’m not getting the code in
libunless directly specified somehow.elvanja
When one invokes the scenario modules directly in
iex -S mix runit all runs nicely. However, even executingEscriptTestbed.CLI.main(["list"])in the shell doesn’t yield any results. Need to try this out with OTP 23 yet.elvanja
I feel that the problem actually lies in code in
libnot being loaded, but maybe this might help. Need to try it out.lud
It should work unless you want to load code from lib/ dynamically without calling
mix escript.buildagain.dimitarvp
I think you are sorted.
I again used the module I proposed above and put a note that you need OTP 23 for the PR to work.
https://github.com/elvanja/escript_testbed/pull/1
elvanja
Tried this, no success:
And then use in CLI via one of:
All return empty list. It’s likely though that I’m not using it correctly!