CRUD Generators for Phoenix applications suitable for both admin and user-facing pages with integrated real-time UI updates.
The package can be installed by adding mandarin
to your list
of dependencies in mix.exs
:
def deps do
[
{:mandarin, "~> 0.1.0"}
]
end
Documentation can be found be found at https://hexdocs.pm/mandarin.
Mandarin doesn't have any code that runs at runtime. Instead, it generates code inside your project. However, code generated by Mandarin depends on a number of external packages, which do ship code that runs at runtime:
-
bootstrap5components
- a replacement for the defaultCoreComponents
module generated by the Phoenix generators. Whilebootstrap5components
was developped in order to be compatible with Mandarin, it is an independent project and can be used in applications that weren't generated with the Mandarin generators -
flop
andflop_phoenix
- utilities to convert query parameters into Ecto queries to simplify filtering and pagination when listing resources -
all the packages that code generated by the
phx.gen.auth
generator depends on (those dependencies are added automatically by Mandarin, though)
The code generated by Mandarin depends on a number of other packages, but those are already pulled in by the default Phoenix generators.
Designing a context and schemas with the default command-line phoenix generators is cumbersome and prone to mistakes. It's often better to encapsulate the context design in a script. The functions in this module allow you to carefully design a context and the relationships between schemas and then generate everything all at once (HTML, context, schema, routes, etc.) when you're ready.
If things don't turn out like you expect, you can revert all changes
with the mandarin.gen.delete_context MyContext
generator, which
will remove all files and routes created by the Mandarin generators.
That way, you can prototype a context very quickly, and only commit to it when it's just perfect for your use case.
To showcase Mandarin, we will re-implement parts of a web app created by Pedro Gaspar to showcase his FlaskAppBuilder framework, which runs on top of the Python web framework Flask.
However, while the FlaskAppBuilder framework generates the web pages based on introspection of Python's classes representing the models in the database, Mandarin will generate actual code, including the Ecto schemas (which are the equivalent to the Python models). This means that Mandarin can't easily make use of already existing ecto schemas, unlike some alternatives like Backpex.
While I love macros, generators allow for better customization compared to macros. Elixir provides a way of overriding functions, which means one could actually implement the functionality using macros and have the user re-implement it by overriding the functions with custom code. However, it's easier to modify existing code than to write it by scratch, and for the purposes of generating a CRUD interface, where one would expect the need for some customization, having the code already written and only having to modify it is the best option in my opinion.
The bootstrap framework is quite extensible with custom themes,
such as the Bootswatch themes that are included in the
bootstrap5components
package by default.
It provides sane defaults for those who are not expert designers.
And more importantly, I hate tailwind with a passion.
However, it should be easy to use Mandarin with an alternative
CSS framework as long as you re-implement the functions in the
bootstrap5components
package.
I'm planning on creating a MandarinComponents
behaviour so that
users who want an alternative components module know what to implement.
Probably not, but the performance impact will be quite low in small apps and it can help in the case where users are concurrently editing the same resource. It's simple enough that the implemention comes down to few lines of code and it's easy to remove from the generated code if the performance impact turns out to be significant.
Backpex is a very interesting package, which I have been following quite closely. It can generate a CRUD inteface to any number of Ecto schemas without "poluting" the application with lots of code (unlike Mandarin which generates thousands of lines of code). The main problem with projects like Backpex is that one ends up wanting to customize some of the functionality, especially UI-related functionality. And in these cases, adding the appropriate extension points is not very easy: add too few and the project is not extensible enough; add too many and the project becomes really hard to maintain and even the user-code becomes somewhat of a mess. While I think that Backpex is good for a basic admin interface with good CRUD functionality, I don't think it can serve as a good basis for user-facing functionality.
If you're ok with using Phoenix generators, I guess you're probably ok with the idea of generating a CRUD interface which is actually more advanced (and in my opinion better designed) than the default CRUD interface provided by Phoenix.
Torch does something very similar to what Mandarin does. It uses generators to quickly dump the necessary code to get a CRUD interface up and running and which the user can customize later. I believe that the CRUD interface generated by Mandarin is better and just as easy to extend.
I think that choosing between Torch and Mandarin comes down to user-preference.
The functionality provided by Mandarin is pretty much complete. Next releases should focus on bug fixing, performance improvements and adding tests to the generated code.
Mandarin will not be updated to keep up with newer versions of the Phoenix generators. This package should be viewed as an alternative to the default generators, not as an improvement. The default generators have made a couple choices I disagree with.
In the future, Mandarin could make use of my code_gen
package,
which is already used by the bootstrap5components
in order to
generate less code by default (while allowing the user to pull
in that code if needed).
I'll keep that option open if anyone really wants it and will accept
pull requests that make it possible.