For my public libs I have created demo apps. During development the demo app should depend on the development version of the library; so I use path: ../my_lib. However, when the demo app is cloned by a user it should depend on the Hex version instead.
Currently I set an environment flag which toggles the location of the dependency, but it keeps nagging in my head: this should be easier. Maybe detecting the existence of a file not committed to Git…?
Anyone having a better idea?
Ps. Detecting :dev runtime environment is not gonna cut it, as the user will probably run the demo in :dev too.
This may not work for you but taking inspiration from Doggo, I package the demo app alongside the library in the same repo. While there some annoyances around grepping and how some editor tools handle nested mix.exs, it’s generally really convenient for everyone: 1) You can simply leave the dep as {:my_lib, path: ".."} in your demo’s mix.exs, 2) the demo repo is source controlled alongside the lib, and 3) when users only have to clone one repo to get everything.
I can imagine this isn’t always viable so I’m also interested in other options.
I do this in the demo for one of my projects, in “mix.exs”:
defp deps do
[
{:phoenix, "~> 1.7.14"},
...,
my_lib_dep()
]
end
defp my_lib_dep() do
if File.exists?(Path.join(__DIR__, "../my_lib/mix.exs")) do
{:my_lib, path: "../my_lib"}
else
{:my_lib, "~> 0.1.0"}
end
end
Or you can append an entire list to the end if you have multiple deps. The nice thing about “mix.exs” is that it’s code, and is executed every time you run mix.
I didn’t do it this way because it feels wrong to reach out into an unsuspecting user’s filesystem to execute code (at least from an unsanctioned directory). While I’m fully aware it’s super edge case, you never know what people have on their machines. For example, maybe they clone the example app into a directory where a long time ago they were experimenting with a mix project with the same name as your lib. Confusion ensues. It’s certainly an option, but I wish there was a more “official” way.
If you don’t like that option, you can always use a environment variable to achieve the same, the important point is as @Nicodemus mentioned: you can execute elixir code inside of mix.exs, which is a very powerful concept.
@BartOtten already mentioned the env variable approach which I do like better. I’m certainly aware mix.exs is just code which I thought was implied by my saying I already considered @Nicodemus’s approach (well, same goes for checking an env var, really).
Indeed, after reading it again I misunderstood the question.
Addressing your previous concerns, just opening a project with a LSP carries risk of executing stuff you are unaware of, especially since elixir metaprogramming allows execution of code at compile-time.
Current implementation is like this. I did commit an .envrc file which sets the path. It seems checking path existence is the only ‘fully native’ solution. Combining it with env override and a warning might hit the sweet spot.
defp routex_dep() do
if path = System.get_env("ROUTEX_PATH") do
IO.puts(">> !! USING LOCAL ROUTEX PATH !! <<")
{:routex, path: path}
else
{:routex, ">= 0.0.0"}
end
end
Ya, same here. There are easy ways to make it work but none that I can think of feel “right.” Though I do quite like the “Doggo way.” It does also mean shipping the demo with package but it’s not like it gets compiled into peoples’ apps or anything. And of course if you want multiple demos then it’s a pain.
No, of course not! I have a problem with being either overly diplomatic or overly blunt. When I say “super edge case” that is my diplomatic way of saying: “this will definitely 100% absolutely for sure ruin someone’s day one day but if you don’t care about that one person then go ahead” (apparently I throw some passive aggression in there as well)
The dotfile is a decent approach since I’m realizing this problem can be summed up as: “Only compile this dep from source if it I’M the one working on it.” This is exactly the use-case for dotfiles. It’s simple to throw these instructions in a README or CONTRIBUTING doc for those looking to hack on your lib. I do still prefer collocation, though (seems you do as well!) and will be keeping it up in my own situation.
To clarify for those who didn’t look at Doggo, the example app lives in the root of the package alongside lib, priv, etc… ie, there’s no parent directory contained the two. I’m just making sure as I’ve been misunderstood more than a couple of times today. Blame Halloween? I dunno.
OK, I’m convinced, and will probably switch to the dotfile solution instead of path existence check.
I considered colocation, but I have many different demos for my library and don’t want them all colocated. I’d rather keep them very closely to a “stock” mix phx.gen codebase for easier understanding by newbies.