Introducing Enumex: an advanced enum library

Yes, one more library for working with enums and also my 2nd enum library!

Introduction

My first library was really simple and was mainly intended for personal usage. I did not even expected that other people would use it. This one is completely different as I found that there is still something to do in this place. First of all this library was intended to be ecto-postgres-enum ~> 2.0, but way too many things changed.

The first thing to note is that it’s not intended to work only for PostgreSQL database and it even does not require any database. There are lots of helpful features which could be used as same as ecto without ecto_sql and that’s mainly why it’s not just another version of ecto-postgres-enum.

Try it

For now this library depends on master branch of ecto_sql, so I’m unable to release it as a hex package. For now please give it a try by cloning a GitLab repository:

This is only a release candidate and gives only preview on what I would like to provide in final release. Based on contribution it may change at any time before 1.0.0 stable release. For each release candidate I would add a git tag.

Migrations

However the true power of this library comes with ecto_sql support. The one of main features are migrations … fully reversible migrations! It’s not only about create and drop enum, but also add and drop value. I have written test to ensure that every migration does not cause problem with existing databases, but saying that is like don’t saying anything … You may wonder what happens if we would like to drop value which is already used, but this use case is also covered and tested!

My code for migrations is based on few articles which describes a safely work on database enum and here is one of them:

How does it work in migration file? It’s as simple as calling just one function!

Enumex.Ecto.Migration.drop_value(adapter, prefix, name, repo, value, default)

At this point you may be confused … How could it be reversible? Of course it’s not possible to turn back value for each row, but this is normal and same result you would have working in raw PostgreSQL console. This library focused on what database is able to do and offer such features in as simple as possible solutions.

This one feature took for me lots of time especially that ecto_sql previously blocked me in this specific scenario. Fortunately thanks to @josevalim this has been solved in master branch of ecto_sql.

Tasks

Of course there is more interesting feature … What would you say if you could simply diff updated your enum in Elixir just now with values which database is storing? Yes, it’s possible! Take a look at enumex.gen.migration mix task. It’s not only automatically detecting value add and drop, but also rename!

Other features

I could describe here all of features, but it could turn into a series of blog posts, so I simply list them all:

  1. Constants-like macros
    In response to: https://github.com/gjaldon/ecto_enum/issues/87

  2. Built-in absinthe support! Everything you need is to import type from YourEnum.Absinthe module.
    In response to: https://github.com/gjaldon/ecto_enum/issues/78

  3. Migrations - except those already described there are few more helpful functions.

  4. Ecto support

  5. Lots of other debug functions, guards, work with indexes and more …

Database support

For now I’m only supporting PostgreSQL, but I have a plans to change it in next release candidates. However my knowledge about other databases is limited and it could take for me some time. I have take a look at MySQL and … do I need to comment it? :077:

This library is intended for database like PostgreSQL which provides a reasonable API. Ok, but what if other databases? I have an idea here. I can implement a Enumex.Adapters.SQLTable which would provide a __using__/1 macro for final adapters. In this way I would be able to add support not only for MySQL, but SQLite also does not looks so problematic with such solution.

Anyway as said I have not much experience with other databases (at least in enum topic), so I it would be really nice if someone could help with adding support for other adapters.

Future plans

As already said I’m planning to add at least one helper module called Enumex.Adapters.SQLTable and I would write (just for an example) an implementation for PostgreSQL so other developer may see how to deal with it. This mean that there would be at least one release candidate. What will come later I’m not sure, but I would really like to release it on hex as soon as possible even if it would be only for release candidate test purposes.

Have a nice day! :smiley:

Related

I would like to provide a list of issues and pull requests less or more related to enumex library.

This one I noticed when working with enumex.gen.enum mix task as ecto.gen.enum was a really good source of inspiration:


The blocking flush() bug and new execute usage:



A confusing error of wrongly emitted Erlang AST:

Once again I would like to thank @josevalim for all his help as without him really important parts of this library would not work. Also without it this library would would not be released so fast and most probably it could have a different form.

Feel free in case of any question. I would try to reply to all of them as soon as possible. Hope my work would help in future also in production environments.

15 Likes

Is this still being maintained?

1 Like

Yes, I have even plans for next release candidate (which most probably would be final version) when 2 things would happen:

  1. ecto would release a new version with this PR merged
    https://github.com/elixir-ecto/ecto/pull/3339
  2. Temperature here would be lower :wink:

Lots of things happen in last 7 months and I was not focused on next release candidate.I have started working on it and already made 1/3 of things on my TODO list locally.

I can say already that final version would have 4 scenarios:

  1. Advanced (based on Enumex.Advanced.Adapter behaviour) - that is for optimizations on db side - like support for PostgreSQL custom type (i.e. create type … syntax)

  2. Custom - case when enum values are stored in database table - that’s my concept of custom enum il.e. Advanced scenario, but similarly to Simple useful when Enumex.Advanced.Adapter cannot be implemented. You may want to save more information in database or add for example constraints.

  3. Simple - similar to Advanced, but for cases when implementing Enumex.Advanced.Adapter is not possible (MySQL) - you would be able to store plain integer (index in enum) or string (value in enum)

  4. Extra support for Ecto.ParameterizedType and :enum (see PR) - due to how ecto would solve things it would work as same as Simple, but would have a bit different API (you would need to pass schema and field names instead of working on each enum module)

Each scenario is completely different and depends on few things:

  1. If you have legacy codebase with any of such scenario
  2. If you need to use specific database i.e. project requirements
  3. If for some reason you need to use older versions of Ecto
  4. If you want to use Ecto.ParameterizedType and :enum

Once next release candidate would be released I would like to know:

  1. Do you have any other scenario (especially in prod)?
  2. Would it work with ecto version 2.x? Originally there were no plans for it, but I will check how much work I would need to do in order to add backwards compatibility.
  3. Do you have some other suggestions? Improvements? Bugs?
1 Like