tmbb

tmbb

Mandarin + Forage - An admin tool for phoenix

I’ve been working on two packages (not on hex.pm yet) to build admin interfaces for phoenix apps:

  • bureaucrat - which contains a bunch of generators to create very feature-rich CRUD views for your resources (with filtering, sorting and proper pagination out of the box)

  • forage (the name comes from the rummage package) - which does the bulk of the work by allowing easy creating of dynamic ecto queries based on plug query parameters. It also contains widgets which make it easy to create search filters, pagination widgets, etc. It acts as a bridge between the form in the frontend and the ecto queries in the backend. Unlike rummage, it’s coupled to plug applications, while rummage tries to be frontend-independent. I don’t think that in the current state of the elixir ecosystem decoupling it from plug is worth it.

Documentation is severely lacking, but those who are interested can look at an example project here: https://github.com/tmbb/bureaucrat_demo_app

The packages lack a lot of documentation, and I’ll work on that over the next weeks now that I’m happy with the functionality.

Roadmap

I’m planning on doing the following:

  1. Documentation, documentation, documentation…

  2. In particular, documenting forage’s functionality, because it can be useful outside Bureaucrat. I do try very hard to make it work as a black box, that is, as longa s you use the correct widgets in the frontend things should just work, but people who want to extend it need some guidance.

  3. Make Paginator (a library which I use for pagination) support ecto 3.

  4. Improving forage so that it is less dependent on the Repo. Everything should work with “raw” ecto queries independent of the Repo. That will probably require some PRs to the paginator package which I use for pagination.

  5. Make bureaucrat play better with many-to-many relations. That will require working mostly on forage, and only minor changes to Bureaucrat itself

  6. Make bureaucrat independent of JQuery, and find a better way to packaging the necessary Javascript. The current generated templates get all JS and CSS from a CDN and embed some inline Javascript to make the Select2 widgets work.

  7. Write a proper tutorial, based on [this tutorial](https://flask-appbuilder.readthedocs.io/en/latest/quickminimal.html, for the Flask application framework (which is a python project)

Most Liked

tmbb

tmbb

As it turns out, bureaucrat was already of an elixir package on Hex.

I’ve changed the name to mandarin and published it as it is so that no one else takes the name in the mean time (renaming this project again is a chore I’d like to avoid). To publish mandarin, I’ve had to publish forage too. None of the packages are exactly ready for use in production. They have no tests and the documentation is still lacking.

“Mandarin” was the name given to the official of the Chinese empire by Portuguese explorers in the Far East in the 1500s. It is commonly said to come from the Portuguese word “mandar”, which means “to give orders/boss around”, but this etymology turns out to be fake (although it’s much cooler than the real one). Actually it comes from a Malay word for “boss” in use at the time. Basically a Mandarin is a Chinese bureaucrat, so it is a good replacement name.

Could a nice mod please change the title of this thread to “Mandarin + Forage - An admin tool for Phoenix”?

EDIT: Mandarin has the obvious advantage that it is derived from Portuguese (actually from “Old Portuguese”, because nowadays the word is written as “mandarim”), unlike Bureaucrat, which comes from French. As all French words, it’s impossible to write correctly on the first try, which can lead to countless typos in your code, so maybe this change is for the best :smile: C’est la vie… On the other hand, spelling rules in Portuguese are (mostly) consistent and Portuguese words are easy to write. Which only proves the superiority of the Portuguese language over everything else.

The above is obviously a joke

tmbb

tmbb

Now I believe I’m getting really close to 1.0. The main new features (already reflected in the GitHub repo) are the following:

  • Updated the generators in order to be more compatible with what Phoenix 1.6 generates

  • Made sure all tests pass (the generators weren’t buggy themselves, but the tests hadn’t been updated to reflect changes in the generators)

  • Use “vertical feature folders” which keep your controllers, views and templates together in the same directory, which makes it easy to modify related parts of the source code

  • As a consequence of the above, there is now a mix mandarin.uninstall MyContext which completely purges all the context diurectories and files and all references to that context from the router. This is very useful when your database design is still in flux and you might want to tear down the whole mandarin files and build them again

  • There is now a programatic interface to mandarin generators using the Mandarin.Design module, which allows you to generate the interface for a bunch of schemas from the comfort of an Elixir script instead of having to fiddle with the command line. To keep the design close to the normal Phoenix generators, I’m going to keep the command line interface as the main form of interaction with mandarin, but that might change in a putative 2.0 version

  • Mandarin now includes a mandarin.gen.auth AuthContext User users modelled on the phx.gen.auth generator but with support for “vertical feature folders” and support for prettier forms using forage widgets and a boostrap template by default

Things that haven’t changes:

  • Forage is still coupled to bootstrap; that’s pretty much unavoidable for a library of frontend widgets. The widgets must be styled with something, and bootstrap is still one of the easiest frameworks to use and customize, particularly for something like CRUD pages

  • Mandarin still dumps a bunch of files in your application. I’ve decided that mandarin will continue to focus on code generators instead of macros or other use of compile/runtime metaprogramming. No mandarin code runs at runtime.

  • No integration with LiveView. Simple CRUD pages are easy to do with normal controllers + minimal AJAX and more complex CRUD pages are outside of Mandarin’s scope. I’ve had some succes in integrating AlpineJS into mandarin pages (although neither Mandarin nor Forage contain any direct support for it)

The main problems I’ve had in getting to this stage are the fact that Phoenix continues to change the generators (for the better, of course!) on each minor version, and I took som etime to catch up to it. Although my generators are mostly copied from Phoenix, they have some important customizations, so thet job of updating them is not exactly simple. Note that this is not a case of Phoenix changing in backward-incompatible ways; it’s just a case of generators being different.

dimitarvp

dimitarvp

I’d use something like bureacratic or frontdesk.

Well, this is open source. Assuming a car doesn’t kill you on the spot one morning, you can always hand ownership over to somebody else. I see nothing wrong with a bit more official-looking name.

peerreynders

peerreynders

Go for an “adjacent” name then, for example:

The Bennu is an ancient Egyptian deity linked with the sun, creation, and rebirth. It may have been the inspiration for the phoenix in Greek mythology.

just as an example.

tmbb

tmbb

So I’ve made some progress. I can generate automatically the Controllers, the Views and the code to access the resources from the database. The only thing I haven’t solved yet how to handle templates. I’m trying to make this work without generating any files to the disk, but I want users to be able to supply their own templates for maximum customization.

Currently the following code generates a fully fledged admin interface that works with the templates generated by the Mandarin generators (only the templates, no need for views, controllers, etc.). I’m actually re-implementing a demo application with the new system by deleting the files I don’t need as I replace them with macros.

First, the code that creates an admin interface (aptly called Mandarin.Magik, but I’m open to new naming suggestions):

defmodule MandarinOfficeWeb.ExampleAdmin do
  alias MandarinOffice.Admin

  use Mandarin.Magik,
    app_web: MandarinOfficeWeb,
    scope: MandarinOfficeWeb.Admin,
    repo: MandarinOffice.Repo

  add_resource(Admin.Employee)
  add_resource(Admin.Department)
  add_resource(Admin.Function)
  add_resource(Admin.Benefit)
end

Now, the router (the magik part is at the end):

defmodule MandarinOfficeWeb.Router do
  use MandarinOfficeWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", MandarinOfficeWeb do
    pipe_through :browser

    get "/", PageController, :index
  end

  require MandarinOfficeWeb.ExampleAdmin, as: ExampleAdmin

  ExampleAdmin.scope "/back_office" do
    # You can (and should!) reuse your normal pipelines
    # inside the admin interface scope.
    pipe_through(:browser)
  end
end

The Admin.Employee, Admin.Department, Admin.Function and Admin.Benefit are completely ordinary Ecto modules which Mandarin.Magik introspects to generate the code (plus some small decorations).

I still haven’t published these changes because I’m still not sure on how to solve the template problem (I’ll probably just copy what Phoenix does when searching for the templates, and use the “default templates” if it doesn’t find any. I’ll post a video soon.

Where Next?

Popular in Libraries Top

pkrawat1
Hey guyz We at @aviabird are working on a payment library in elixir/phoenix. We are targeting March 2018 to add 56 Gateways to it. Have...
New
tompave
Hello there, I would like to share a feature toggles library (AKA feature flags) I’ve been working on. The main package is FunWithFlags...
New
blatyo
https://www.conduitframework.com/ The best overview for how things are tied together is this presentation. Modules and functions are pre...
New
asiniy
Hey there! I wrote a download elixir package which does exactly what its name about - an easy way to download files. I saw solutions ...
New
benlime
I created a new library GitHub - benvp/ex_cva: Class Variance Authority for Elixir which aims to make it very easy to define different va...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
bryanjos
Hi, I just published version 0.23.0 of Elixirscript. Most of the changes are around JavaScript interop now that Elixirscript uses the ...
New
tmbb
I’ve decided to create this topic to discuss optimization possibilities for something like Phoenix LiveView. I’ve created this topic unde...
New
Qqwy
While not as prevalent as in imperative languages, arrays (collections with efficient random element access) are still very useful in Eli...
New
versilov
Could not wait for the missing Elixir ML libraries to appear, so, I wrote one myself, taking https://github.com/sdwolfz/exlearn as a foun...
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Sub Categories:

We're in Beta

About us Mission Statement