tfwright

tfwright

I have a library (MyLib) that I use for part of another app’s functionality (MyApp). I’d like to add some conditional code (an Ecto schema definition) to one of the library’s modules only if MyApp has a particular config that points to one of MyApp’s modules (also an Ecto schema). I was hoping to call a function on the MyApp module to get the info about it I need, but it appears to break compilation in certain circumstances because MyApp hasn’t been compiled yet (I was trying to call __schema__/2 to get info about an association).

One option would be to require the MyApp config specify all the data MyLib needs, but that feels a bit clunky and redundant. Is there a way to force MyApp to compile first so that MyLib can access its module’s functions during compilation time? Or is there another, better way to go about what I’m trying to do?

Appreciate any tips/suggestions.

Showing Posts 1 to 10

kip

kip

ex_cldr Core Team

You may need to use Code.ensure_compiled/1 to force compilation order if it isn’t being detected the way you want.

The documentation notes that this is not a function you should commonly reach for. However forcing compilation order at compile time is one reasonable use.

tfwright

tfwright OP

This doesn’t seem to work for my use case, perhaps I using it incorrectly? My current implementation looks like this:

MyLib.MyModule
  if Application.get_env(:my_lib, :some_config) do
    case Code.ensure_compiled(Application.get_env(:my_lib, :some_config)) do
      {:module, module} -> # do something with module
      {:error, _} -> Logger.warn("couldn't compile")
    end
  end

Instead of breaking compilation #do something with module fails to run and I get the warning instead. The docs says the function

halts the compilation of the caller until the module given to ensure_compiled/1 becomes available or all files for the current project have been compiled. If compilation finishes and the module is not available, an error tuple is returned.

I’m not sure what “project” technically means here. I had hoped it meant something like “MyApp and all deps except the current one”, but it looks like it just means “all files in MyLib except this one”?

kip

kip

ex_cldr Core Team

It would be helpful to know:

  • What the result of Application.get_env(:my_lib, :some_config) is
  • What the error return was (instead of ignoring it add it to the log message)
tfwright

tfwright OP

Elixir.MyApp.ConfigModule

unavailable

kip

kip

ex_cldr Core Team

Thanks. That return code pretty much says its compiled everything and that module wasn’t found. Just to be really sure, the module is actually defined as defmodule MyApp.ConfigModule not defmodule Elixir.MyApp.ConfigModule?

Is :my_lib a dependency of the project? So that it will be compiled first, before the current project?

tfwright

tfwright OP

Yep.

Is :my_lib a dependency of the project?

Yes, exactly.

So when I compile the project (MyApp) I see

==> my_lib
Compiling 3 files (.ex)

[warn]  unavailable
Generated my_lib app
==> my_app
Compiling 82 files (.ex)
Generated my_app app
kip

kip

ex_cldr Core Team

Ah, I see. Compilation order is dependencies first, then the current project. Where current project is the one that has mix.exs in it when you did mix.compile. Dependencies are compiled first and they do not create a transitive association with the “current” project. Understandably so - libraries aren’t built to make assumptions about the projects that use them.

TLDR; A dependency depending on the consumer of the dependency isn’t going to work

kip

kip

ex_cldr Core Team

The common pattern in Elixir to handle this kind of situation is to introduce a new module into the current project and then use the code from the dependency so you can create a relationship. This is what I do heavily in the ex_cldr libs for pretty much the same reason you have - conditional compilation of library code based upon configuration in consumer code.

kip

kip

ex_cldr Core Team

To explain additionally why conditional compilation in a dependency isn’t a good idea I should point out that the dependency could be used my multiple different consuming libraries in the same runtime environment and therefore unpredictable behaviour would ensue.

tfwright

tfwright OP

I had a feeling that something like this would be the correct option. Otherwise the config is going to extremely unwieldy and redundant. I’m just not quite sure the right approach since coming from Ruby typically we’re able to just change everything at runtime (:scream:), exactly the kind of thing I’m learning Elixir to get away from…

I’m thinking I need to add some sort of boilerplate module that the client app can use in their own module, overriding the defaults somehow without actually changing how the original module is compiled. Does that sound generally like the right track to investigate?

The lib I’m working on is open source, and the current code I’m working on is here, if you’re curious.

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
subsaharancoder
I’ve followed the Phoenix LiveView file upload code here Uploads — Phoenix LiveView v1.0.0-rc.7 and so far everything works just fine wit...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews