I have been using a multi-endpoint setup in my app, just because I think it makes the most sense for a multi-subdomain app. This worked well enough with the old style of route helpers, since I could call them with their fully qualified module names.
Now with the introduction of ~p, things have become more complicated. The crux of the issue is: there can only be one ~p sigil defined per module. This means that it is not possible to use verified routes for multiple routers in the same template.
The result is that I have resorted to using just string literals in the places where cross-domain links are present.
My proposal is to make the sigil that is defined customizable, with a default to p when no other has been defined.
Usage would look something like this:
use Phoenix.VerifiedRoutes,
endpoint: MyApp.Endpoint,
router: MyApp.Router,
statics: MyApp.statics,
sigil: "MYAPP"
And then you would be able to use the sigil like this: ~MYAPP"/my_path".
This would make it possible for several sigils to be defined in a single module.
I’d be happy to provide a PR if there is agreement that this would be a good addition to Phoenix.
[edit: apparently path/3 and url/3 exist and I just didn’t know about them. In light of that it seems unlikely that this proposal will be accepted]
This is not valid syntax. As far as I remember, sigils are limited to a single lating letter, restricting their namespace to a size of 52.
If at all, you might be lucky using the “options” argument to the sigil. I am not aware though if the order is guaranteed to be kept, or if there are already options defined by sigil_p/2 that might conflict.
Though I have not yet used verified routes at all, as I didn’t have had any usecase for using route helpers at all in the last 2 years.
I usually do not generate paths on the server side.
So here I have to throw in some assumptions:
I’m pretty sure there is an alternative way to use verified paths rather than just using the sigil. And from what I can find, it seems as if the sigil is bound to a specific endpoint within a module when Phoenix.VerifiedRoutes was used with the :endpoint and :router options, while usable with any endpoint when the phoenix module was imported and then sigil_p/2 is used within a path/3 macro.
You should be able to build your own macros on top of this:
defmacro myapp_p(og_ast) do
quote do
path(MyApp.Endpoint, MyApp.Router, unquote(og_ast))
end
end
Plus/Minus some quoting adjustements and perhaps some useage helper that deals with importing Phoenix.VerifiedRoutes for you.
Not sure how I overlooked this , still getting used to all the new goodies in Phoenix / LV lately (as you have probably noticed from my presence on Slack). Thanks!
Ooof, I missed that one, still I am not a friend of that.
sigil_p/2 seems to allows for interpolation, while uppercase sigils do usually not do that by convention.
Personally I would probably prefer my explicit macro (maybe even a self-written uppercase multiletter sigil like yours, if you insist on that) over yet another auto-generated macro which every developer has to guess what it means.
With an explicitely self-implemented macro I get “docs on mouseover” for free in most editors. Sigils don’t receive this benefit.
And if they are generated by phoenix, you can not even use CTRL/CMD + click without falling into a deep rabbit hole.
Discussion on that change was pretty heated so I don’t think you’re the only one. Still, it’s there.
by convention
I think with new capabilities there will be new conventions.
Personally I’m very much in favour of the new sigil capabilities. There are a thousand new sigils that one could imagine, and the all upper caps means that you will readily be able to see where magic is happening.
The whole point of the all uppercase sigil was to allow interpolation in the way the implementation wants it. We already have an example in wide use today with Heex. ~H"{magic_assign}" in attributes
As a thought experiment, an interesting alternative would be to provide the desired router as a suffix.
You could hack around how sigils allow lists of characters at the end, to support things like Regex modifier flags such as ~r/(^(?<line>.*?)$/m for multi-line regexes.
Sadly, ~p"my_path"MyApp.Router would not work because of how the . gets parsed; these modifiers only accept ASCII letters and digits. So we’re back to doing something like configuring an alias in your Router, but with even nastier metaprogramming to implement:
I am a mostly satisfied user of ~p but this is a very valid criticism and one I have half-come across. I’m not using multiple endpoints but I do have an admin. subdomain and was at first pretty confused how I was supposed to have links into my main app from admin only using ~p. So far I haven’t actually needed to do that but feel I’m going to run into this soon.
Using path/3 as @LostKobrakai pointed out is a perfectly acceptable solution as far as I’m concerned. To throw out an idea to keep things more, uh, “sigily”, there could be a modifier before the path. ~p requires that paths start with a / so, that could be exploited to allow:
Of course I’m sure people are then going to want to be able to specify both the subdomain and the host so there would likely have to be a syntax for that as well.