One useful feature with a
rails new my_app is,
my_app is used only in one place. If you change there, there will not be any other references anywhere else. This is quite useful in one way. You can create a skeleton app with multiple repetitive steps - and clone your new app from there.
However, that is not possible with a new phoenix application. You need a rename script with some regex before you can start using starter application. I find it to be a hack - and - not developer friendly. For example, there is a small feature added to the starter app, it is impossible to sync with our new app.
Any reasons why it is designed like this? I am not making any value judgments but want to understand the thought process.
Any best practices regarding preparation of starter apps?
One useful feature with a
You’re planning to share code between projects by copy-pasting it, so that seems like a highly subjective judgement
IMO the motivation is partly necessity - global state like what’s needed to make something like
Rails.application work isn’t best practice - but mostly about allowing more than one app to coexist in a single VM, either with an umbrella app or without.
I understand what you are saying. But, leaving the utopic principle - Here are the things that I want to do:
- I want to add tailwind to the project
- I want to run the phx.gen.auth generator - and - then - make their views with tailwind.
- install credo and configure it.
- add flash message components.
- Add modal, form fields and other styled function/live components so that I can get going faster.
Now, I obviously would want to copy-paste here.
I am not speaking about need of global state. I am just speaking about naming conventions. For example, instead of
my_app_web - what would be the harm if we use
web folders inside
my_app - that will make the namespacing a lot more simpler. There must be reasons. I am curious to know them.
What’s stopping you from writing a code generator mix task (similar to
mix ecto.gen.migration, or
mix phx.gen.auth) to add all of those elements? (Tailwind, starter components, etc.)
If you need the base project name, it’s returned by
You can do something like this:
app = Keyword.fetch!(Mix.Project.config(), :app) # returns :my_app or whatever you named it in mix.exs
If you need different formatted versions of the app name to inject into files you generate you can then do things like:
Macro.camelize(app |> to_string()) # "MyApp"
Or, in context of an EEx file you might use in a code generator:
defmodule <%= Macro.camelize(app |> to_string()) %>Web.Components.Modal do
Agree to a degree. But maintaining a generator is a lot more involved than maintaining a running application. Isn’t it?
You can probably use
git rebase to bring an existing app up to date with a freshly generated skeleton. I haven’t try that though.
Yes. Or something like degit with a local Git repo, and the OTP app is called something like
:tpl_app_name, the main module
TplAppName and you can
sed and rename files from there with a couple commands.
So you can just maintain your template as a standard application, and create a copy when you want.
Have a look at the 1.3 release thread for the reasoning:
Thanks for the link. Even bigger thanks for editing the title. This is much more what I have in mind. Maybe the one I wrote is a little bit provocative.
I name my top-level modules
Web, rather than
MyAppWeb. (For apps I write, not for libraries.) This makes it much easier to copy files between projects. I realize that it will make umbrella apps more difficult, so I understand why the generator doesn’t do it this way. I just do a quick rename after running the generator.
So, the views so far can be summarized as
a. Best option - have a rename script.
b. If you are certain that you will not need an umbrella app, name the top level modules in a way that does not include the AppName.
c. If you are looking for high frequency app generation, consider building a generator.
Any additional inputs? Views?
I am using a method which likes @eahanson said, but for umbrella project.
# 1. create an empty umbrella project $ mix new --umbrella elixir_umbrella_starter $ cd elixir_umbrella_starter/apps # 2. create an app called `core` for the business domain. $ mix phx.new.ecto --binary-id core # 3. create an app called `web_api` for mobile apps: $ mix phx.new.web \ --binary-id \ --no-assets \ --no-html \ --no-live \ --no-dashboard \ web_api # 4. create an app called `dashboard` for admin panel: $ mix phx.new.web --binary-id --no-dashboard dashboard
After executinng above steps, you will have a project which contains multiple apps, and the apps’ name are suitable for a general web project.
Then, you can patch this project to fit your own needs, such as, adding TaliwindCSS, etc.
You have a starter, now.
About future maintenance:
- patch above project according to the diff result at https://www.phoenixdiff.org/
- patch above project according to
- If you bundle all these apps into one release, make sure you are running the multiple endpoints in a conflict-free way:
- run multiple endpoints on different ports
- run multiple endpoints on one port with different domain. one possible solution is master_proxy.
- run multiple endpoints on one port with the same domain, but with different path.
- If you bundle every web application into its own release, everything will be fine.
An example repo for this idea - GitHub - plastic-archives/elixir-umbrella-starter: A possible implementation of umbrella starter.
The commits are self-explained, check it out.
- Like @al2o3cr said, you can’t use pattern when you want to running multiple apps which use above starter. Because in this case, you have multiple
- other cons that I have not thought of yet,
Saša Jurić on Twitter: “Elixir tip of the day: avoid umbrella apps. You’re welcome.” / Twitter
is an interesting suggestion from @sasajuric.
So, move to normal mix app - rename the core folders - and - continue - unless we know for clear we want an umbrella app (I mean advanced case.)
That leads to an interesting scenario - breaking convention in majority cases.
It is very handy. I’ve used it in past. Works like breeze.
However, it is interesting to note that @josevalim and @chrismccord in their book Programming Phoenix 1.4: Productive |> Reliable |> Fast by Chris McCord, Bruce Tate and José Valim (pragprog.com) do convert the app to an umbrella app in the later part of the book. It would be very interesting to hear their views on the usefulness or lack thereof of umbrella apps in the current scheme of things.
As a general observation, I think Elixir leans into the code-generation side of the universe, so we have a Phoenix app (or even a regular mix app) being generated from a template, and there’s some of the same impetus behind macros in general.
My quick take on the workflow is that instead of modifying/cloning an app name/directory after it has been created, with Elixir I think the “better” way to achieve this would be to re-create new apps using the
mix task. In PHP or Rails (from what I remember), there wasn’t as much of a concern with namespaces when considering 2 independent apps – Laravel I think used the ubiquitous “App” as the namespace for all generated apps, and in the PHP world, that was totally fine. However, this could cause conflicts on the BEAM because multiple apps could be loaded up and each requires its own unique identifier. I remember when I first realized this could be a problem was when I had naively attempted to develop isolated services as their own apps and I ran into trouble when I tried to use one app from the other and they had the same OTP name.
Maybe there’s a tutorial out there, but I ended up customizing the Phoenix app template and using a custom mix task so we could spin up new apps more easily that incorporated the things that our organization needed to do.
Dear @fireproofsocks, Thanks for the detailed explanation.
Dear friends, Thanks for participating in the discussion. It has been a valuable learning experience.