GithubHelp home page GithubHelp logo

bitwalker / uniq Goto Github PK

View Code? Open in Web Editor NEW
88.0 5.0 15.0 110 KB

Provides UUID generation, parsing, and formatting. Supports RFC 4122, and the v6 draft extension

License: Apache License 2.0

Elixir 100.00%

uniq's People

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  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  avatar  avatar

uniq's Issues

Ecto is not really an optional dependency

Including the library without having ecto in the mix.exs, compiling the dependency fails with the following error:

== Compilation error in file lib/uuid.ex ==
** (CompileError) lib/uuid.ex:911: module Ecto.ParameterizedType is not loaded and could not be found
    (elixir 1.13.4) expanding macro: Kernel.use/1
    lib/uuid.ex:911: Uniq.UUID (module)
    (elixir 1.13.4) expanding macro: Kernel.if/2
    lib/uuid.ex:910: Uniq.UUID (module)

`uniq` causes constant recompilations and causes tooling issues

Not sure whether to report this here or in other repositories, but I made an issue in rrrene/credo#1052 that causes tooling in editors using e.g. credo to break. I'm a newbie Elixir programmer, so not sure whether the issue is something I can resolve or something uniq can do about it, but I wanted to raise this issue here just in case.

Invalid typespec

While using dialyzer and this library, it appears that the typespec is incorrect, submitted #16 to try to fix it. Let me know what you think

Parsing versions 1, 3, 4, 5 fails on OTP 26

I'm trying to update my application that uses Uniq to Erlang/OTP 26, but Uniq.UUID.valid?/1 is incorrectly returning false for valid UUIDs. I ran the Uniq tests (after updating the locked Ecto to v3.9.5), and I'm seeing 16 failures, all of which are related to parsing but none of which specifically parse versions 6 or 7.

Exact output
โฏ mix test                       


  1) test can autogenerate primary keys (Uniq.Ecto.Test)
     test/ecto_test.exs:47
     match (=) failed
     code:  assert {:ok, %UUID{version: 4}} = UUID.parse(uuid)
     left:  {:ok, %Uniq.UUID{version: 4}}
     right: {:error, {:invalid_format, <<240>>, 2, 6, 14}}
     stacktrace:
       test/ecto_test.exs:52: (test)



  2) test generating can generate version 3 (Uniq.Test)
     test/uniq_test.exs:147
     match (=) failed
     code:  assert {:ok, %UUID{format: :default, version: 3}} = UUID.parse(default)
     left:  {:ok, %Uniq.UUID{format: :default, version: 3}}
     right: {:error, {:invalid_format, "W", 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:154: (test)



  3) property parsing can parse any 22-byte base64-encoded string which represents a valid uuid (Uniq.Test)
     test/uniq_test.exs:95
     Failed with generated values (after 0 successful runs):
     
         * Clause:    {version, variant, uuid} <- valid_uuid(:slug)
           Generated: {3, <<2::size(2)>>, "AAAAAAAAMACAAAAAAAAAAA"}
     
     match (=) failed
     The following variables were pinned:
       version = 3
       variant = <<2::size(2)>>
     code:  assert {:ok, %UUID{version: ^version, variant: ^variant}} = UUID.parse(uuid)
     left:  {:ok, %Uniq.UUID{version: ^version, variant: ^variant}}
     right: {:error, {:invalid_format, <<0>>, 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:97: anonymous fn/4 in Uniq.Test."property parsing can parse any 22-byte base64-encoded string which represents a valid uuid"/1
       (stream_data 0.5.0) lib/stream_data.ex:2148: StreamData.shrink_failure/6
       (stream_data 0.5.0) lib/stream_data.ex:2108: StreamData.check_all/7
       test/uniq_test.exs:96: (test)

.

  4) test generating can generate version 1 (Uniq.Test)
     test/uniq_test.exs:139
     match (=) failed
     code:  assert {:ok, %UUID{format: :default, version: 1}} = UUID.parse(default)
     left:  {:ok, %Uniq.UUID{format: :default, version: 1}}
     right: {:error, {:invalid_format, <<218>>, 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:143: (test)

.

  5) test generating can generate version 4 (Uniq.Test)
     test/uniq_test.exs:164
     match (=) failed
     code:  assert {:ok, %UUID{format: :default, version: 4}} = UUID.parse(default)
     left:  {:ok, %Uniq.UUID{format: :default, version: 4}}
     right: {:error, {:invalid_format, "3", 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:168: (test)



  6) doctest Uniq.UUID.info/2 (1) (Uniq.Test)
     test/uniq_test.exs:6
     Doctest failed
     doctest:
       iex> Uniq.UUID.info("870df8e8-3107-4487-8316-81e089b8c2cf", :keyword)
       {:ok, [uuid: "870df8e8-3107-4487-8316-81e089b8c2cf",
        binary: <<135, 13, 248, 232, 49, 7, 68, 135, 131, 22, 129, 224, 137, 184, 194, 207>>,
        type: :default,
        version: 4,
        variant: :rfc4122]}
     code:  Uniq.UUID.info("870df8e8-3107-4487-8316-81e089b8c2cf", :keyword) === {:ok, [uuid: "870df8e8-3107-4487-8316-81e089b8c2cf",
             binary: <<135, 13, 248, 232, 49, 7, 68, 135, 131, 22, 129, 224, 137, 184, 194, 207>>,
             type: :default,
             version: 4,
             variant: :rfc4122]}
     left:  {:error, {:invalid_format, "\f", 2, 6, 14}}
     right: {
              :ok,
              [uuid: "870df8e8-3107-4487-8316-81e089b8c2cf", binary: <<135, 13, 248, 232, 49, 7, 68, 135, 131, 22, 129, 224, 137, 184, 194, 207>>, type: :default, version: 4, variant: :rfc4122]
            }
     stacktrace:
       lib/uuid.ex:364: Uniq.UUID (module)



  7) doctest Uniq.UUID.info/2 (2) (Uniq.Test)
     test/uniq_test.exs:6
     Doctest failed
     doctest:
       iex> Uniq.UUID.info("870df8e8-3107-4487-8316-81e089b8c2cf")
       {:ok, %Uniq.UUID{
        format: :default,
        version: 4,
        variant: <<2::2>>,
        time: 326283406408022248,
        seq: 790,
        node: <<129, 224, 137, 184, 194, 207>>,
        bytes: <<135, 13, 248, 232, 49, 7, 68, 135, 131, 22, 129, 224, 137, 184, 194, 207>>,
       }}
     code:  Uniq.UUID.info("870df8e8-3107-4487-8316-81e089b8c2cf") === {:ok, %Uniq.UUID{
             format: :default,
             version: 4,
             variant: <<2::2>>,
             time: 326283406408022248,
             seq: 790,
             node: <<129, 224, 137, 184, 194, 207>>,
             bytes: <<135, 13, 248, 232, 49, 7, 68, 135, 131, 22, 129, 224, 137, 184, 194, 207>>,
            }}
     left:  {:error, {:invalid_format, "\f", 2, 6, 14}}
     right: {:ok, #UUIDv4<870df8e8-3107-4487-8316-81e089b8c2cf>}
     stacktrace:
       lib/uuid.ex:371: Uniq.UUID (module)

..

  8) property parsing can parse any 128-bit binary with valid version/variant values (Uniq.Test)
     test/uniq_test.exs:71
     Failed with generated values (after 0 successful runs):
     
         * Clause:    {version, variant, uuid} <- valid_uuid()
           Generated: {3, <<2::size(2)>>, <<0, 0, 0, 0, 0, 0, 48, 0, 128, 0, 0, 0, 0, 0, 0, 0>>}
     
     match (=) failed
     The following variables were pinned:
       version = 3
       variant = <<2::size(2)>>
     code:  assert {:ok, %UUID{version: ^version, variant: ^variant}} = UUID.parse(uuid)
     left:  {:ok, %Uniq.UUID{version: ^version, variant: ^variant}}
     right: {:error, {:invalid_format, <<0>>, 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:73: anonymous fn/4 in Uniq.Test."property parsing can parse any 128-bit binary with valid version/variant values"/1
       (stream_data 0.5.0) lib/stream_data.ex:2148: StreamData.shrink_failure/6
       (stream_data 0.5.0) lib/stream_data.ex:2108: StreamData.check_all/7
       test/uniq_test.exs:72: (test)



  9) test generating can generate version 5 (Uniq.Test)
     test/uniq_test.exs:172
     match (=) failed
     code:  assert {:ok, %UUID{format: :default, version: 5}} = UUID.parse(default)
     left:  {:ok, %Uniq.UUID{format: :default, version: 5}}
     right: {:error, {:invalid_format, <<243>>, 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:179: (test)

..

 10) test parsing can parse version 5 (Uniq.Test)
     test/uniq_test.exs:59
     match (=) failed
     The following variables were pinned:
       version = 5
     code:  assert {:ok, %UUID{format: :raw, version: ^version}} = UUID.parse(uuids[:raw][version])
     left:  {:ok, %Uniq.UUID{format: :raw, version: ^version}}
     right: {:error, {:invalid_format, "_", 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:207: Uniq.Test.parse/2
       test/uniq_test.exs:60: (test)



 11) property parsing can parse any 32-byte hex string which represents a valid uuid (Uniq.Test)
     test/uniq_test.exs:83
     Failed with generated values (after 0 successful runs):
     
         * Clause:    {version, variant, uuid} <- valid_uuid(:hex)
           Generated: {3, <<2::size(2)>>, "00000000000030008000000000000000"}
     
     match (=) failed
     The following variables were pinned:
       version = 3
       variant = <<2::size(2)>>
     code:  assert {:ok, %UUID{version: ^version, variant: ^variant}} = UUID.parse(uuid)
     left:  {:ok, %Uniq.UUID{version: ^version, variant: ^variant}}
     right: {:error, {:invalid_format, <<0>>, 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:85: anonymous fn/4 in Uniq.Test."property parsing can parse any 32-byte hex string which represents a valid uuid"/1
       (stream_data 0.5.0) lib/stream_data.ex:2148: StreamData.shrink_failure/6
       (stream_data 0.5.0) lib/stream_data.ex:2108: StreamData.check_all/7
       test/uniq_test.exs:84: (test)

......

 12) doctest Uniq.UUID.parse/1 (3) (Uniq.Test)
     test/uniq_test.exs:6
     match (=) failed
     code:  {:ok, uuid} = Uniq.UUID.parse("f81d4fae-7dec-11d0-a765-00a0c91e6bf6")
     left:  {:ok, uuid}
     right: {:error, {:invalid_format, <<157>>, 2, 6, 14}}
     stacktrace:
       (for doctest at) lib/uuid.ex:443: (test)

.

 13) test parsing can parse version 1 (Uniq.Test)
     test/uniq_test.exs:47
     match (=) failed
     The following variables were pinned:
       version = 1
     code:  assert {:ok, %UUID{format: :raw, version: ^version}} = UUID.parse(uuids[:raw][version])
     left:  {:ok, %Uniq.UUID{format: :raw, version: ^version}}
     right: {:error, {:invalid_format, <<226>>, 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:207: Uniq.Test.parse/2
       test/uniq_test.exs:48: (test)

.

 14) test parsing can parse version 4 (Uniq.Test)
     test/uniq_test.exs:55
     match (=) failed
     The following variables were pinned:
       version = 4
     code:  assert {:ok, %UUID{format: :raw, version: ^version}} = UUID.parse(uuids[:raw][version])
     left:  {:ok, %Uniq.UUID{format: :raw, version: ^version}}
     right: {:error, {:invalid_format, "`", 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:207: Uniq.Test.parse/2
       test/uniq_test.exs:56: (test)



 15) test parsing can parse version 3 (Uniq.Test)
     test/uniq_test.exs:51
     match (=) failed
     The following variables were pinned:
       version = 3
     code:  assert {:ok, %UUID{format: :raw, version: ^version}} = UUID.parse(uuids[:raw][version])
     left:  {:ok, %Uniq.UUID{format: :raw, version: ^version}}
     right: {:error, {:invalid_format, "B", 2, 6, 14}}
     stacktrace:
       test/uniq_test.exs:207: Uniq.Test.parse/2
       test/uniq_test.exs:52: (test)

.

 16) doctest Uniq.UUID.parse/1 (4) (Uniq.Test)
     test/uniq_test.exs:6
     Doctest failed
     doctest:
       iex> match?({:ok, %Uniq.UUID{format: :default, version: 1}}, Uniq.UUID.uuid1() |> Uniq.UUID.parse())
       true
     code:  match?({:ok, %Uniq.UUID{format: :default, version: 1}}, Uniq.UUID.uuid1() |> Uniq.UUID.parse()) === true
     left:  false
     right: true
     stacktrace:
       lib/uuid.ex:456: Uniq.UUID (module)

.
Finished in 15.1 seconds (0.1s async, 15.0s sync)
4 doctests, 6 properties, 22 tests, 16 failures

Randomized with seed 100798

I looked at the code and the Erlang/OTP 26 release notes to see if I could spot the problem, but I can't.

False positive on `Uniq.UUID.valid?/1`?

I'm not sure if this is an actual problem as I'm not well versed in UUID, but this looks kinda weird.

iex()> Uniq.UUID.valid?("aaaaaa:aa:aaaaaa") 
true

To me knowledge this string is not a valid UUID (also checked with some online validator) but I'm happy to be educated on this subject.

If we attempt to parse it:

{:ok, uuid} = Uniq.UUID.parse("aaaaaa:aa:aaaaaa")

uuid |> IO.inspect(structs: false)       
%{      
  __struct__: Uniq.UUID,
  bytes: "aaaaaa:aa:aaaaaa",
  format: :raw,
  node: "aaaaaa",
  seq: 24890,
  time: 747986083993706849,
  variant: <<0::size(1)>>,
  version: 3
}

Any idea here? ๐Ÿค”

Compilation Issue due to Elixir compiled_env usage

Hey, since I upgraded the package I am getting the following error:

https://github.com/straw-hat-team/beam-monorepo/actions/runs/3123794947

ERROR! the application :uniq has a different value set for key :otp_version during runtime compared to compile time. Since this application environment entry was marked as compile time, this difference can lead to different behaviour than expected:

  * Compile time value was set to: #Version<24.0.0>
  * Runtime value was not set

To fix this error, you might:

  * Make the runtime value match the compile time one

  * Recompile your project. If the misconfigured application is a dependency, you may need to run "mix deps.compile uniq --force"

  * Alternatively, you can disable this check. If you are using releases, you can set :validate_compile_env to false in your release configuration. If you are using Mix to start your system, you can pass the --no-validate-compile-env flag



21:45:14.798 [error] Task #PID<0.320.0> started from #PID<0.93.0> terminating
** (stop) "aborting boot"
    (elixir 1.13.2) Config.Provider.boot/2
Function: &:erlang.apply/2
    Args: [#Function<1.45581530/1 in Mix.Tasks.Compile.All.load_apps/3>, [uniq: "/Users/ubi/Developer/github.com/straw-hat-team/beam-monorepo/_build/dev/lib"]]
** (EXIT from #PID<0.93.0>) an exception was raised:
    ** (ErlangError) Erlang error: "aborting boot"
        (elixir 1.13.2) Config.Provider.boot/2

Confusion on many_to_many (uuid::bytea coercion)

I have

config :my_app, MyApp.Repo,
  migration_primary_key: [type: :binary_id],
  migration_foreign_key: [type: :binary_id]

(which sets it to uuid type in DB)

  @primary_key {:id, Uniq.UUID, version: 7, autogenerate: true}
  @foreign_key_type Uniq.UUID
      
  schema "users" do
    # ...
    many_to_many :locations, Location, join_through: "user_locations"
  end

And if I call Repo.preload(user, [:locations]) I get the following error:

[error] Task #PID<0.730.0> started from #PID<0.723.0> terminating
** (Postgrex.Error) ERROR 42846 (cannot_coerce) cannot cast type uuid to bytea

   query: SELECT l0."id", l0."email", l0."name", l0."inserted_at", l0."updated_at", u1."user_id"::bytea
           FROM "locations" AS l0 INNER JOIN "user_locations" AS u1 ON l0."id" = u1."location_id"
           WHERE (u1."user_id" = ANY($1)) ORDER BY u1."user_id"::bytea

   (ecto_sql 3.9.1) lib/ecto/adapters/sql.ex:913: Ecto.Adapters.SQL.raise_sql_call_error/1
   (ecto_sql 3.9.1) lib/ecto/adapters/sql.ex:828: Ecto.Adapters.SQL.execute/6
   (ecto 3.9.2) lib/ecto/repo/queryable.ex:229: Ecto.Repo.Queryable.execute/4
   (ecto 3.9.2) lib/ecto/repo/queryable.ex:19: Ecto.Repo.Queryable.all/3
   (ecto 3.9.2) lib/ecto/repo/preloader.ex:272: Ecto.Repo.Preloader.fetch_query/8
   (elixir 1.14.2) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
   (elixir 1.14.2) lib/task/supervised.ex:34: Task.Supervised.reply/4
   (stdlib 4.1.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Function: &:erlang.apply/2
   Args: [#Function<8.114411606/1 in Ecto.Repo.Preloader.maybe_pmap/3>,
         [#Function<20.114411606/1 in Ecto.Repo.Preloader.prepare_queries/6>]]

What is the best way to make this work?

Comparing UUID7 doesn't seem to produce the expected results

I am attempting to sort a list of UUIDs and when I use Uniq.UUID.compare to do that I am not getting the list of UUIDs not sorted properly.

Here is the code used to produce and sort the UUIDs and debugging output.

Produce the UUIDs:

uuids_with_index =
  1..10
  |> Enum.to_list()
  |> Enum.map(fn i ->
    {i, Uniq.UUID.uuid7()}
  end)

Output:

uuids_with_index #=> [
  {1, "018dd112-d7d4-79b7-9c3f-cd8f5a268bad"},
  {2, "018dd112-d7d4-7963-ac79-96d0ba13f0e3"},
   {3, "018dd112-d7d4-7348-8dd5-e7a8fed42a22"},
   {4, "018dd112-d7d4-7a48-a433-fcb3294b9fb3"},
   {5, "018dd112-d7d4-72fb-9650-558ff08be388"},
   {6, "018dd112-d7d4-7f7a-80fd-7bf8daeab8ce"},
   {7, "018dd112-d7d4-7b67-8c66-f0b665a19e4f"},
   {8, "018dd112-d7d4-7a80-bad6-2d0378fc51c5"},
   {9, "018dd112-d7d4-74f9-a64f-2ab48c5f1ee8"},
   {10, "018dd112-d7d4-74a4-804b-686439c83abc"}
]

Create the list and reverse it:

uuids = Enum.map(uuids_with_index, fn {_i, uuid} -> uuid end) |> Enum.reverse()

Output:

uuids #=> ["018dd112-d7d4-74a4-804b-686439c83abc", "018dd112-d7d4-74f9-a64f-2ab48c5f1ee8",
 "018dd112-d7d4-7a80-bad6-2d0378fc51c5", "018dd112-d7d4-7b67-8c66-f0b665a19e4f",
 "018dd112-d7d4-7f7a-80fd-7bf8daeab8ce", "018dd112-d7d4-72fb-9650-558ff08be388",
 "018dd112-d7d4-7a48-a433-fcb3294b9fb3", "018dd112-d7d4-7348-8dd5-e7a8fed42a22",
 "018dd112-d7d4-7963-ac79-96d0ba13f0e3", "018dd112-d7d4-79b7-9c3f-cd8f5a268bad"]

Attempt to sort them:

sorted =
  Enum.sort(uuids, fn a, b ->
    case Uniq.UUID.compare(a, b) do
      :eq -> true
      :lt -> true
      :gt -> false
    end
  end)

Sorted output:

sorted #=> ["018dd112-d7d4-7f7a-80fd-7bf8daeab8ce", "018dd112-d7d4-7b67-8c66-f0b665a19e4f",
 "018dd112-d7d4-7a80-bad6-2d0378fc51c5", "018dd112-d7d4-7a48-a433-fcb3294b9fb3",
 "018dd112-d7d4-79b7-9c3f-cd8f5a268bad", "018dd112-d7d4-7963-ac79-96d0ba13f0e3",
 "018dd112-d7d4-74f9-a64f-2ab48c5f1ee8", "018dd112-d7d4-74a4-804b-686439c83abc",
 "018dd112-d7d4-7348-8dd5-e7a8fed42a22", "018dd112-d7d4-72fb-9650-558ff08be388"]

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.