Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion lib/paginator/cursor.ex
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
defmodule Paginator.Cursor do
@moduledoc false

def decode(nil), do: nil

def decode(encoded_cursor) do
encoded_cursor
|> Base.url_decode64!()
|> :erlang.binary_to_term([:safe])
|> Enum.map(&Paginator.Cursor.Decode.convert/1)
end

def encode(values) when is_list(values) do
values
|> Enum.map(&Paginator.Cursor.Encode.convert/1)
|> :erlang.term_to_binary()
|> Base.url_encode64()
end
Expand All @@ -19,3 +20,23 @@ defmodule Paginator.Cursor do
encode([value])
end
end

defprotocol Paginator.Cursor.Encode do
@fallback_to_any true

def convert(term)
end

defprotocol Paginator.Cursor.Decode do
@fallback_to_any true

def convert(term)
end

defimpl Paginator.Cursor.Encode, for: Any do
def convert(term), do: term
end

defimpl Paginator.Cursor.Decode, for: Any do
def convert(term), do: term
end
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ defmodule Paginator.Mixfile do
elixirc_paths: elixirc_paths(Mix.env()),
build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
consolidate_protocols: Mix.env() != :test,
deps: deps(),

# Hex
Expand Down
33 changes: 33 additions & 0 deletions test/paginator/cursor_test.exs
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
defmodule Paginator.CursorTest do
use ExUnit.Case, async: true

defmodule MYTEST1 do
defstruct id: nil
end

defmodule MYTEST2 do
defstruct id: nil
end

defimpl Paginator.Cursor.Encode, for: MYTEST1 do
def convert(term), do: {:m1, term.id}
end

defimpl Paginator.Cursor.Decode, for: Tuple do
def convert({:m1, id}), do: %MYTEST1{id: id}
end

alias Paginator.Cursor

describe "encoding and decoding terms" do
test "cursor for struct with custom implementation is shorter" do
cursor1 = Cursor.encode([%MYTEST1{id: 1}])

assert Cursor.decode(cursor1) == [%MYTEST1{id: 1}]

cursor2 = Cursor.encode([%MYTEST2{id: 1}])

assert Cursor.decode(cursor2) == [%MYTEST2{id: 1}]
assert bit_size(cursor1) < bit_size(cursor2)
end

test "list of lists " do
cursor = Cursor.encode([[1]])

assert Cursor.decode(cursor) == [[1]]
end

test "it wraps terms into lists" do
cursor = Cursor.encode(1)

Expand Down