Giter Club home page Giter Club logo

sqids-elixir's Introduction

Hex downloads License Elixir Versions Erlang Versions CI status

Sqids (pronounced "squids") for Elixir is a library for generating YouTube-looking IDs from numbers. These IDs are short, can be generated with a custom alphabet and are collision-free. Read more.

This is what they look like in URLs:

https://example.com/LchsyE
https://example.com/Uxmq8Y
https://example.com/3CwlG7

Why use them?

The main purpose is visual: you can use Sqids if you'd like to expose integer identifiers in your software as alphanumeric strings.

✅ Use Cases

  • Link shortening: default alphabet is safe to use in URLs, and common profanity is avoided
  • Event IDs: collision-free ID generation
  • Database lookups: by decoding IDs back into numbers

❌ Not Good For

  • Sensitive data: this it not an encryption library
  • User IDs generated in sequence, or equivalents, which can be decoded, revealing user count and/or business growth

Features

  • 🆔 Generate short IDs from non-negative integers
  • 🤬 Avoid common profanity in generated IDs
  • 🎲 IDs appear randomized when encoding incremental numbers
  • 🧰 Decode IDs back into numbers
  • ↔️ Generate IDs with a minimum length, making them more uniform
  • 🔤 Generate IDs with a custom alphabet
  • 👩‍💻 Available in multiple programming languages
  • 👯‍♀️ Equally configured implementations produce the same IDs
  • 🍻 Small library with a permissive license

🚀 Getting started

Latest version API reference Last commit

The package can be installed by adding sqids to your list of dependencies in mix.exs:

def deps do
  [
    {:sqids, "~> 0.1.0"}
  ]
end

👩‍💻 Examples

Default configuration

iex> {:ok, sqids} = Sqids.new()
iex> numbers = [1, 2, 3]
iex> id = Sqids.encode!(sqids, numbers)
iex> ^id = "86Rf07"
iex> ^numbers = Sqids.decode!(sqids, id)

Note 🚧 Because of the algorithm's design, multiple IDs can decode back into the same sequence of numbers. If it's important to your design that IDs are canonical, you have to re-encode decoded numbers and check that the generated ID matches.

Convenience: create context at compile time

Having to pass sqids context on every encode and decode call can be cumbersome.

To work around this, you can create context with new!/0 or new!/1 at compile time if all options are either default or known at that moment:

iex> defmodule MyApp.CompileTimeSqids do
iex>   import Sqids.Hacks, only: [dialyzed_ctx: 1]
iex>   @context Sqids.new!()
iex>
iex>   def encode!(numbers), do: Sqids.encode!(dialyzed_ctx(@context), numbers)
iex>   def decode!(id), do: Sqids.decode!(dialyzed_ctx(@context), id)
iex> end
iex>
iex> numbers = [1, 2, 3]
iex> id = MyApp.CompileTimeSqids.encode!(numbers)
iex> ^id = "86Rf07"
iex> ^numbers = MyApp.CompileTimeSqids.decode!(id)

Convenience: place context under your supervision tree

This also allows you to encode and decode IDs without managing context.

If not all options are known at compile time but you'd still like to not pass context on every encode and decode call, you can use Sqids, which will generate functions that retrieve the underlying context transparently and call Sqids for you.

The context is stored in a uniquely named persistent_term, managed by a uniquely named process, which is to be placed under your application's supervision tree. Both names are derived from your module's.

iex> defmodule MyApp.SupervisedSqids do
iex>   use Sqids
iex>   # Functions encode/1, encode!/1, decode/1, decode!/1, etc
iex>   # will be generated.
iex>
iex>   @impl true
iex>   def child_spec() do
iex>       child_spec([
iex>           # alphabet: alphabet,     # Custom alphabet
iex>           # min_length: min_length, # Padding
iex>           # blocklist: blocklist    # Custom blocklist
iex>       ])
iex>   end
iex> end
iex>
iex>
iex> defmodule MyApp.Application do
iex>   # ...
iex>   def start(_type, _args) do
iex>      children = [
iex>        MyApp.SupervisedSqids,
iex>        # ...
iex>      ]
iex>
iex>      opts = [strategy: :one_for_one, name: MyApp.Supervisor]
iex>      Supervisor.start_link(children, opts)
iex>   end
iex> end
iex>
iex>
iex> {:ok, _} = MyApp.Application.start(:normal, [])
iex> numbers = [1, 2, 3]
iex> id = MyApp.SupervisedSqids.encode!(numbers)
iex> ^id = "86Rf07"
iex> ^numbers = MyApp.SupervisedSqids.decode!(id)

Custom configuration

Examples of custom configuration follow. All options are applicable to the two convenient ways of creating context shown above.

Note that different options can be used together for further customization. Check the API reference for details.

Padding: generated IDs have a minimum length

iex> {:ok, sqids} = Sqids.new(min_length: 10)
iex> numbers = [1, 2, 3]
iex> id = Sqids.encode!(sqids, numbers)
iex> ^id = "86Rf07xd4z" # instead of "86Rf07"
iex> ^numbers = Sqids.decode!(sqids, id)

(Older IDs, for ex. generated with a previous configuration in which padding was not yet enforced or a different length was configured, can still be decoded.)

Using a custom alphabet

Generated IDs will only contain characters from the chosen alphabet, which is sensitive to both case and order.

iex> {:ok, sqids} = Sqids.new(alphabet: "cdefhjkmnprtvwxy2345689")
iex> numbers = [1, 2, 3]
iex> id = Sqids.encode!(sqids, numbers)
iex> ^id = "wc9xdr"
iex> ^numbers = Sqids.decode!(sqids, id)

In order to decode IDs back, they need to be in the same alphabet.

For practical reasons, the standard limits custom alphabets to ASCII characters.

(Thanks to @benjiwheeler for his suggestion for a set of unambiguous looking characters on Stack Overflow.)

Profanity: excluding specific words from the IDs

As an example, set the ID generated with default options as the blocklist.

iex> {:ok, sqids} = Sqids.new(blocklist: ["86Rf07"])
iex> numbers = [1, 2, 3]
iex> id = Sqids.encode!(sqids, numbers)
iex> ^id = "se8ojk" # see how "86Rf07" was censored
iex> ^numbers = Sqids.decode!(sqids, id)

📚 API Reference

The API reference can be found on HexDocs.

📝 License

MIT

sqids-elixir's People

Contributors

4kimov avatar dependabot[bot] avatar g-andrade avatar hanifanazka avatar tshakah avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

sqids-elixir's Issues

Assertion raises because ExUnit is missing

I've just tried integrating this into an app as I like the ID format, but it crashes in production due to the assert you are using from ExUnit. It would be great to have something in the docs about this, although I'd argue you shouldn't be using it in non-test code

This was the error:

    ** (UndefinedFunctionError) function ExUnit.Assertions.__equal__?/2 is undefined (module ExUnit.Assertions is not available)
        ExUnit.Assertions.__equal__?(22, nil)
        (sqids 0.1.2) lib/sqids/alphabet.ex:113: Sqids.Alphabet.index_of!/2
        (sqids 0.1.2) lib/sqids.ex:426: Sqids.decode_valid_id/2
        (sqids 0.1.2) lib/sqids.ex:165: Sqids.decode!/2

I'm using the compile time config

Allow for compile time instantiation with ‘new!/1’

Like in Elixir’s hashids lib mentioned in the website. Params will rarely change, so this makes sense as a simpler alternative to generating code with use Sqids, when we wish to ease management of context.

In other words: support @sqids Sqids.new!(…) -like usage.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.