Fluid - a library to create meaningful IDs

Hello people,

This is my first library and it is still a proof of concept. It is taking me more time than I expected, for this very reason, I just published something functional with some documentation to see if it is interesting for the community:

I’m learning a lot of metaprogramming with this project. It is nice, but hard error messages sometimes.

I just want to know if you find it interesting, if you’d use it, if you understand it, what about ID definitions, what about the code, etc. In fact, any kind of comment (good or bad) would be very useful and appreciated.

I have other ideas in mind, so if I’m working in a no great idea, it’s better to know as soon as possible! :slight_smile:

Thanks in advance.

P.D. Some examples:

defmodule MyApp.ID do
  use Fluid,
    fields: [
      inserted_at: %Fluid.Field.NaiveDateTime{
        size: 41,
        epoch: ~N[2018-01-01 00:00:00],
        time_unit: :millisecond
      },
      node_id: %Fluid.Field.Integer{size: 13},
      local_id: %Fluid.Field.Integer{size: 10, unsigned: false}
    ],
    formats: [
      hex: %Fluid.Format.Hexadecimal{
        separator: ?-,
        groups: 4
      }
    ],
    ecto: [type: :integer, format: %Fluid.Formatter.Hexadecimal{}]
end

iex(3)> MyApp.ID.new(inserted_at: ~N[2018-01-01 00:00:00], node_id: 0, local_id: 0)
{:ok, "0000-0000-0000-0000"}

iex(4)> MyApp.ID.new(inserted_at: ~N[2043-07-31 12:34:56.654], node_id: 1976, local_id: 432)
{:ok, "5df8-423a-071e-e1b0"}

iex(8)> MyApp.ID.get("5df8-423a-071e-e1b0", :inserted_at)
{:ok, ~N[2043-07-31 12:34:56]}

iex(9)> MyApp.ID.get("5df8-423a-071e-e1b0", :node_id)
{:ok, 1976}

iex(10)> MyApp.ID.get("5df8-423a-071e-e1b0", :local_id)
{:ok, 432}

iex(3)> MyApp.ID.__fluid__(:max, :inserted_at)
~N[2087-09-07 15:47:35]

defmodule MyApp.Post do
  use Ecto.Schema

  @primary_key {:id, MyApp.ID, autogenerate: true, read_after_writes: true}
  @foreign_key MyApp.ID
  @timestamps_opts [inserted_at: false, type: :utc_datetime, usec: false]

  schema "posts" do
    belongs_to :user, MyApp.User
    field :body, :string
  end

  def inserted_at(%__MODULE__{id: id}), do: MyApp.ID.get(id, :inserted_at)
end

iex> init_date = MyApp.ID.new(inserted_at: ~N[2018-03-01 00:00:00], local_id: 0, node_id: 0)
{:ok, "0097-eb9a-0000-0000"}
iex> final_date = MyApp.ID.new(inserted_at: ~N[2018-04-01 00:00:00], local_id: 0, node_id: 0)
{:ok, "00e7-be2c-0000-0000"}
iex> from p in Post, where: p.id > ^init_date and p.id < ^final_date
4 Likes

Very interesting! Cool
What problems led you to solve with this?

Well, the example I gave tries to generate ID in a distributed way, with no communication, with no collisions and time orderable.

The library itself tries to make this “kind” of ID easier to code. What kind of ID can be made? Well I don’t know all but here another example:

defmodule ID do
  use Fluid,
    fields: [type: %Fluid.Field.Enum{
      types: [User, Post, Comment, Image, Song...]
      size: 5
    }, local_id: %Fluid.Field.Integer{size: 59}]
   ...
end

type = ID.get("aaaaaa-bbbbb-cccc", :type)
local_id = ID.get("aaaaaa-bbbbbb-cccc", :local_id)
Repo.get(type, local_id)

It allows to embed the table and the id in just one “token”.

Great example. I’ve used https://github.com/rails/globalid before, and Fluid looks like I could do something similar for helping dynamic lookups.

I realized that there was a typo in Ecto dependency, and it wasn’t optional because of this. Now it fixed :slight_smile:

1 Like