Page MenuHomePhorge

No OneTemporary

Size
20 KB
Referenced Files
None
Subscribers
None
diff --git a/README.md b/README.md
index f9243f3..5bf0245 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,39 @@
-# ❄ FlakeID
+# ❄ FlakeId
> Decentralized, k-ordered ID generation service
## Installation
Add `flake_id` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:flake_id, "~> 0.1.0"}
]
end
```
## Usage
```elixir
-iex> FlakeID.get()
+iex> FlakeId.get()
"9n3171dJZpdD77K3DU"
```
See [https://hexdocs.pm/flake_id](https://hexdocs.pm/flake_id) for the complete documentation.
### With Ecto
-It is possible to use `FlakeID` with [`Ecto`](https://github.com/elixir-ecto/ecto/). See [`FlakeID.Ecto.Type`](https://hexdocs.pm/flake_id/FlakeID.Ecto.Type.html) documentation for detailed instructions.
+It is possible to use `FlakeId` with [`Ecto`](https://github.com/elixir-ecto/ecto/). See [`FlakeId.Ecto.Type`](https://hexdocs.pm/flake_id/FlakeId.Ecto.Type.html) documentation for detailed instructions.
## Prior Art
* [flaky](https://github.com/nirvana/flaky), released under the terms of the Truly Free License,
* [Flake](https://github.com/boundary/flake), Copyright 2012, Boundary, Apache License, Version 2.0
## Copyright and License
Copyright © 2017-2019 [Pleroma Authors](https://pleroma.social/)
-FlakeID source code is licensed under the GNU LGPLv3 License.
+FlakeId source code is licensed under the GNU LGPLv3 License.
diff --git a/lib/flake_id.ex b/lib/flake_id.ex
index 997fcee..8253ee7 100644
--- a/lib/flake_id.ex
+++ b/lib/flake_id.ex
@@ -1,100 +1,100 @@
-# FlakeID: Decentralized, k-ordered ID generation service
+# FlakeId: Decentralized, k-ordered ID generation service
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: LGPL-3.0-only
-defmodule FlakeID do
+defmodule FlakeId do
@moduledoc """
Decentralized, k-ordered ID generation service.
"""
import Kernel, except: [to_string: 1]
@doc """
Converts a binary Flake to a String
## Examples
- iex> FlakeID.to_string(<<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>)
+ iex> FlakeId.to_string(<<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>)
"9n2ciuz1wdesFnrGJU"
"""
def to_string(<<_::integer-size(64), _::integer-size(48), _::integer-size(16)>> = binary_flake) do
<<integer::integer-size(128)>> = binary_flake
Base62.encode(integer)
end
def to_string(string), do: string
@doc """
Converts a String to a binary Flake
## Examples
- iex> FlakeID.from_string("9n2ciuz1wdesFnrGJU")
+ iex> FlakeId.from_string("9n2ciuz1wdesFnrGJU")
<<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>
"""
@spec from_string(binary()) :: nil | <<_::128>>
def from_string(string)
def from_string(<<_::integer-size(128)>> = flake), do: flake
def from_string(string) when is_binary(string) and byte_size(string) < 18, do: nil
def from_string(string), do: string |> Base62.decode!() |> from_integer
@doc """
Converts a binary Flake to an integer
## Examples
- iex> FlakeID.to_integer(<<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>)
+ iex> FlakeId.to_integer(<<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>)
28939165907792829150732718047232
"""
@spec to_integer(<<_::128>>) :: non_neg_integer
def to_integer(binary_flake)
def to_integer(<<integer::integer-size(128)>>), do: integer
@doc """
Converts an integer to a binary Flake
## Examples
- iex> FlakeID.from_integer(28939165907792829150732718047232)
+ iex> FlakeId.from_integer(28939165907792829150732718047232)
<<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>
"""
@spec from_integer(integer) :: <<_::128>>
def from_integer(integer) do
<<_time::integer-size(64), _node::integer-size(48), _seq::integer-size(16)>> =
<<integer::integer-size(128)>>
end
@doc """
Generates a string with Flake
"""
@spec get :: String.t()
- def get, do: FlakeID.Worker.get() |> to_string()
+ def get, do: FlakeId.Worker.get() |> to_string()
@doc """
- Checks that ID is a valid FlakeID
+ Checks that ID is a valid FlakeId
## Examples
- iex> FlakeID.flake_id?("9n2ciuz1wdesFnrGJU")
+ iex> FlakeId.flake_id?("9n2ciuz1wdesFnrGJU")
true
- iex> FlakeID.flake_id?("#cofe")
+ iex> FlakeId.flake_id?("#cofe")
false
- iex> FlakeID.flake_id?("pleroma.social")
+ iex> FlakeId.flake_id?("pleroma.social")
false
"""
@spec flake_id?(String.t()) :: boolean
def flake_id?(id), do: flake_id?(String.to_charlist(id), true)
defp flake_id?([c | cs], true) when c >= ?0 and c <= ?9, do: flake_id?(cs, true)
defp flake_id?([c | cs], true) when c >= ?A and c <= ?Z, do: flake_id?(cs, true)
defp flake_id?([c | cs], true) when c >= ?a and c <= ?z, do: flake_id?(cs, true)
defp flake_id?([], true), do: true
defp flake_id?(_, _), do: false
end
diff --git a/lib/flake_id/application.ex b/lib/flake_id/application.ex
index 1b8e223..c87f562 100644
--- a/lib/flake_id/application.ex
+++ b/lib/flake_id/application.ex
@@ -1,22 +1,22 @@
-# FlakeID: Decentralized, k-ordered ID generation service
+# FlakeId: Decentralized, k-ordered ID generation service
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: LGPL-3.0-only
-defmodule FlakeID.Application do
+defmodule FlakeId.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
children = [
- FlakeID.Worker
+ FlakeId.Worker
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
- opts = [strategy: :one_for_one, name: FlakeID.Supervisor]
+ opts = [strategy: :one_for_one, name: FlakeId.Supervisor]
Supervisor.start_link(children, opts)
end
end
diff --git a/lib/flake_id/ecto/compat_type.ex b/lib/flake_id/ecto/compat_type.ex
index 1f3fdb1..8e6d8a6 100644
--- a/lib/flake_id/ecto/compat_type.ex
+++ b/lib/flake_id/ecto/compat_type.ex
@@ -1,68 +1,68 @@
-# FlakeID: Decentralized, k-ordered ID generation service
+# FlakeId: Decentralized, k-ordered ID generation service
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: LGPL-3.0-only
-defmodule FlakeID.Ecto.CompatType do
+defmodule FlakeId.Ecto.CompatType do
@moduledoc """
Provides a compatibility Ecto type for someone wishes to migrate from integer ids to flakes.
## Schema Example
- @primary_key {:id, FlakeID.Ecto.CompatType, autogenerate: true}
+ @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
schema "posts" do
add :body, :string
end
"""
import Kernel, except: [to_string: 1]
@behaviour Ecto.Type
def embed_as(_), do: :self
def equal?(term1, term2), do: term1 == term2
def type, do: :uuid
- defdelegate autogenerate, to: FlakeID, as: :get
+ defdelegate autogenerate, to: FlakeId, as: :get
def cast(value) do
{:ok, to_string(value)}
end
def load(value) do
{:ok, to_string(value)}
end
def dump(value) do
{:ok, from_string(value)}
end
defp to_string(<<0::integer-size(64), id::integer-size(64)>>), do: Kernel.to_string(id)
- defp to_string(binary_flake), do: FlakeID.to_string(binary_flake)
+ defp to_string(binary_flake), do: FlakeId.to_string(binary_flake)
# zero or -1 is a null flake
for i <- [-1, 0] do
defp from_string(unquote(i)), do: <<0::integer-size(128)>>
defp from_string(unquote(Kernel.to_string(i))), do: <<0::integer-size(128)>>
end
defp from_string(int) when is_integer(int) do
int
|> Kernel.to_string()
|> from_string()
end
defp from_string(<<_::integer-size(128)>> = flake), do: flake
defp from_string(string) when is_binary(string) and byte_size(string) < 18 do
case Integer.parse(string) do
{id, ""} -> <<0::integer-size(64), id::integer-size(64)>>
_ -> nil
end
end
- defp from_string(string), do: FlakeID.from_string(string)
+ defp from_string(string), do: FlakeId.from_string(string)
end
diff --git a/lib/flake_id/ecto/type.ex b/lib/flake_id/ecto/type.ex
index 42c3bab..914c864 100644
--- a/lib/flake_id/ecto/type.ex
+++ b/lib/flake_id/ecto/type.ex
@@ -1,46 +1,46 @@
-# FlakeID: Decentralized, k-ordered ID generation service
+# FlakeId: Decentralized, k-ordered ID generation service
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: LGPL-3.0-only
-defmodule FlakeID.Ecto.Type do
+defmodule FlakeId.Ecto.Type do
@moduledoc """
Provides a type for Ecto usage.
The underlying data type should be an `:uuid`.
## Migration Example
create table(:posts, primary_key: false) do
add :id, :uuid, primary_key: true
add :body, :text
end
## Schema Example
- @primary_key {:id, FlakeID.Ecto.Type, autogenerate: true}
+ @primary_key {:id, FlakeId.Ecto.Type, autogenerate: true}
schema "posts" do
add :body, :string
end
"""
@behaviour Ecto.Type
def embed_as(_), do: :self
def equal?(term1, term2), do: term1 == term2
def type, do: :uuid
- defdelegate autogenerate, to: FlakeID, as: :get
+ defdelegate autogenerate, to: FlakeId, as: :get
def cast(value) do
- {:ok, FlakeID.to_string(value)}
+ {:ok, FlakeId.to_string(value)}
end
def load(value) do
- {:ok, FlakeID.to_string(value)}
+ {:ok, FlakeId.to_string(value)}
end
def dump(value) do
- {:ok, FlakeID.from_string(value)}
+ {:ok, FlakeId.from_string(value)}
end
end
diff --git a/lib/flake_id/worker.ex b/lib/flake_id/worker.ex
index b502389..c675255 100644
--- a/lib/flake_id/worker.ex
+++ b/lib/flake_id/worker.ex
@@ -1,70 +1,70 @@
-# FlakeID: Decentralized, k-ordered ID generation service
+# FlakeId: Decentralized, k-ordered ID generation service
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: LGPL-3.0-only
-defmodule FlakeID.Worker do
+defmodule FlakeId.Worker do
@moduledoc false
use GenServer
defstruct node: nil, time: 0, sq: 0
@type state :: %__MODULE__{
node: non_neg_integer,
time: non_neg_integer,
sq: non_neg_integer
}
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@impl true
@spec init([]) :: {:ok, state}
def init([]) do
{:ok, %__MODULE__{node: worker_id(), time: time()}}
end
@impl true
def handle_call(:get, _from, state) do
{flake, new_state} = get(time(), state)
{:reply, flake, new_state}
end
@spec get :: binary
def get, do: GenServer.call(__MODULE__, :get)
# Matches when the calling time is the same as the state time. Incr. sq
@spec get(non_neg_integer, state) ::
{<<_::128>>, state} | {:error, :clock_running_backwards}
def get(time, %__MODULE__{time: time} = state) do
new_state = %__MODULE__{state | sq: state.sq + 1}
{gen_flake(new_state), new_state}
end
# Matches when the times are different, reset sq
def get(newtime, %__MODULE__{time: time} = state) when newtime > time do
new_state = %__MODULE__{state | time: newtime, sq: 0}
{gen_flake(new_state), new_state}
end
# Error when clock is running backwards
def get(newtime, %__MODULE__{time: time}) when newtime < time do
{:error, :clock_running_backwards}
end
@spec gen_flake(state) :: <<_::128>>
def gen_flake(%__MODULE__{time: time, node: node, sq: seq}) do
<<time::integer-size(64), node::integer-size(48), seq::integer-size(16)>>
end
def time do
{mega_seconds, seconds, micro_seconds} = :erlang.timestamp()
1_000_000_000 * mega_seconds + seconds * 1000 + :erlang.trunc(micro_seconds / 1000)
end
def worker_id do
<<worker::integer-size(48)>> = :crypto.strong_rand_bytes(6)
worker
end
end
diff --git a/mix.exs b/mix.exs
index d8c9784..3438a20 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,51 +1,51 @@
-defmodule FlakeID.MixProject do
+defmodule FlakeId.MixProject do
use Mix.Project
def project do
[
app: :flake_id,
version: "0.1.0",
description: "Decentralized, k-ordered ID generation service",
elixir: "~> 1.8",
start_permanent: Mix.env() == :prod,
deps: deps(),
package: package(),
# Docs
- name: "FlakeID",
+ name: "FlakeId",
source_url: "https://git.pleroma.social/pleroma/flake_id",
homepage_url: "https://git.pleroma.social/pleroma/flake_id",
docs: [
main: "readme",
extras: ["README.md"],
source_url_pattern:
"https://git.pleroma.social/pleroma/flake_id/blob/master/%{path}#L%{line}"
]
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger],
- mod: {FlakeID.Application, []}
+ mod: {FlakeId.Application, []}
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:credo, "~> 1.1.0", only: [:dev, :test], runtime: false},
{:ecto, ">= 2.0.0", optional: true},
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
{:base62, "~> 1.2"}
]
end
defp package do
[
licenses: ["LGPLv3"],
links: %{"GitLab" => "https://git.pleroma.social/pleroma/flake_id"}
]
end
end
diff --git a/test/flake_id/ecto/compat_type_test.exs b/test/flake_id/ecto/compat_type_test.exs
index c5ea4ce..2261c14 100644
--- a/test/flake_id/ecto/compat_type_test.exs
+++ b/test/flake_id/ecto/compat_type_test.exs
@@ -1,57 +1,57 @@
-defmodule FlakeID.Ecto.CompatTypeTest do
+defmodule FlakeId.Ecto.CompatTypeTest do
use ExUnit.Case, async: true
- alias FlakeID.Ecto.CompatType
+ alias FlakeId.Ecto.CompatType
@flake_string "9n2ciuz1wdesFnrGJU"
@flake_binary <<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>
@fake_flake <<0::integer-size(64), 42::integer-size(64)>>
test "cast/1" do
assert CompatType.cast(@flake_binary) == {:ok, @flake_string}
assert CompatType.cast(@flake_string) == {:ok, @flake_string}
end
test "load/1" do
assert CompatType.load(@flake_binary) == {:ok, @flake_string}
assert CompatType.load(@flake_string) == {:ok, @flake_string}
end
test "dump/1" do
assert CompatType.dump(@flake_string) == {:ok, @flake_binary}
assert CompatType.dump(@flake_binary) == {:ok, @flake_binary}
end
test "equal?/2" do
assert CompatType.equal?(@flake_binary, @flake_binary) == true
end
test "autogenerate/0" do
flake = CompatType.autogenerate()
assert String.valid?(flake)
- assert FlakeID.flake_id?(flake)
+ assert FlakeId.flake_id?(flake)
end
describe "fake flakes (compatibility with older serial integers)" do
test "with an integer" do
assert CompatType.dump(42) == {:ok, @fake_flake}
end
test "with an integer as string" do
assert CompatType.dump("42") == {:ok, @fake_flake}
end
test "zero or -1 is a null flake" do
null_flake = <<0::integer-size(128)>>
assert CompatType.dump(0) == {:ok, null_flake}
assert CompatType.dump(-1) == {:ok, null_flake}
assert CompatType.dump("0") == {:ok, null_flake}
assert CompatType.dump("-1") == {:ok, null_flake}
end
test "cast" do
assert CompatType.cast(@fake_flake) == {:ok, "42"}
end
end
end
diff --git a/test/flake_id/ecto/type_test.exs b/test/flake_id/ecto/type_test.exs
index d73792c..8ea21a7 100644
--- a/test/flake_id/ecto/type_test.exs
+++ b/test/flake_id/ecto/type_test.exs
@@ -1,34 +1,34 @@
-defmodule FlakeID.Ecto.TypeTest do
+defmodule FlakeId.Ecto.TypeTest do
use ExUnit.Case, async: true
- alias FlakeID.Ecto.Type
+ alias FlakeId.Ecto.Type
@flake_string "9n2ciuz1wdesFnrGJU"
@flake_binary <<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>
test "cast/1" do
assert Type.cast(@flake_binary) == {:ok, @flake_string}
assert Type.cast(@flake_string) == {:ok, @flake_string}
end
test "load/1" do
assert Type.load(@flake_binary) == {:ok, @flake_string}
assert Type.load(@flake_string) == {:ok, @flake_string}
end
test "dump/1" do
assert Type.dump(@flake_string) == {:ok, @flake_binary}
assert Type.dump(@flake_binary) == {:ok, @flake_binary}
end
test "equal?/2" do
assert Type.equal?(@flake_binary, @flake_binary) == true
end
test "autogenerate/0" do
flake = Type.autogenerate()
assert String.valid?(flake)
- assert FlakeID.flake_id?(flake)
+ assert FlakeId.flake_id?(flake)
end
end
diff --git a/test/flake_id/worker_test.exs b/test/flake_id/worker_test.exs
index 046288c..e4d8b6b 100644
--- a/test/flake_id/worker_test.exs
+++ b/test/flake_id/worker_test.exs
@@ -1,38 +1,38 @@
-defmodule FlakeID.WorkerTest do
+defmodule FlakeId.WorkerTest do
use ExUnit.Case, async: true
- alias FlakeID.Worker
+ alias FlakeId.Worker
test "get/1" do
flake = Worker.get()
assert is_binary(flake)
assert <<_::integer-size(128)>> = flake
end
describe "get/2" do
test "increment `:sq` when the calling time is the same as the state time" do
time = Worker.time()
node = Worker.worker_id()
state = %Worker{time: time, node: node, sq: 0}
assert {<<_::integer-size(128)>>, %Worker{node: ^node, time: ^time, sq: 1}} =
Worker.get(time, state)
end
test "reset `:sq` when the times are different" do
time = Worker.time()
node = Worker.worker_id()
state = %Worker{time: time - 1, node: node, sq: 42}
assert {<<_::integer-size(128)>>, %Worker{time: ^time, sq: 0}} = Worker.get(time, state)
end
test "wrror when clock is running backwards" do
time = Worker.time()
state = %Worker{time: time + 1}
assert Worker.get(time, state) == {:error, :clock_running_backwards}
end
end
end
diff --git a/test/flake_id_test.exs b/test/flake_id_test.exs
index 927088d..c796377 100644
--- a/test/flake_id_test.exs
+++ b/test/flake_id_test.exs
@@ -1,57 +1,57 @@
-defmodule FlakeIDTest do
+defmodule FlakeIdTest do
use ExUnit.Case, async: true
- doctest FlakeID
+ doctest FlakeId
@flake_string "9n2ciuz1wdesFnrGJU"
@flake_binary <<0, 0, 1, 109, 67, 124, 251, 125, 95, 28, 30, 59, 36, 42, 0, 0>>
@flake_integer 28_939_165_907_792_829_150_732_718_047_232
test "flake_id?/1" do
- assert FlakeID.flake_id?(@flake_string)
- refute FlakeID.flake_id?("http://example.com/activities/3ebbadd1-eb14-4e20-8118-b6f79c0c7b0b")
- refute FlakeID.flake_id?("#cofe")
+ assert FlakeId.flake_id?(@flake_string)
+ refute FlakeId.flake_id?("http://example.com/activities/3ebbadd1-eb14-4e20-8118-b6f79c0c7b0b")
+ refute FlakeId.flake_id?("#cofe")
end
test "get/0" do
- flake = FlakeID.get()
+ flake = FlakeId.get()
assert String.valid?(flake)
- assert FlakeID.flake_id?(flake)
+ assert FlakeId.flake_id?(flake)
end
describe "to_string/1" do
test "with binary" do
- assert FlakeID.to_string(@flake_binary) == @flake_string
+ assert FlakeId.to_string(@flake_binary) == @flake_string
bin = <<1::integer-size(64), 2::integer-size(48), 3::integer-size(16)>>
- assert FlakeID.to_string(bin) == "LygHa16ApeN"
+ assert FlakeId.to_string(bin) == "LygHa16ApeN"
end
test "does nothing with other types" do
- assert FlakeID.to_string("cofe") == "cofe"
- assert FlakeID.to_string(42) == 42
+ assert FlakeId.to_string("cofe") == "cofe"
+ assert FlakeId.to_string(42) == 42
end
end
describe "from_string/1" do
test "with a flake string" do
- assert FlakeID.from_string(@flake_string) == @flake_binary
+ assert FlakeId.from_string(@flake_string) == @flake_binary
end
test "with a flake binary" do
- assert FlakeID.from_string(@flake_binary) == @flake_binary
+ assert FlakeId.from_string(@flake_binary) == @flake_binary
end
test "with a non flake string" do
- assert FlakeID.from_string("cofe") == nil
+ assert FlakeId.from_string("cofe") == nil
end
end
test "to_integer/1" do
- assert FlakeID.to_integer(@flake_binary) == @flake_integer
+ assert FlakeId.to_integer(@flake_binary) == @flake_integer
end
test "from_integer/1" do
- assert FlakeID.from_integer(@flake_integer) == @flake_binary
+ assert FlakeId.from_integer(@flake_integer) == @flake_binary
end
end

File Metadata

Mime Type
text/x-diff
Expires
Mon, Oct 12, 9:34 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1786118
Default Alt Text
(20 KB)

Event Timeline