Skip to content

Commit

Permalink
feat: implement observe/observe_deep
Browse files Browse the repository at this point in the history
Unlike the original observe/observe_deep, it is not called in the middle of a transaction.
It is called in the form of a process message after the transaction is completed.
  • Loading branch information
satoren committed Nov 15, 2024
1 parent 765f1cc commit 24cf968
Show file tree
Hide file tree
Showing 25 changed files with 1,804 additions and 21 deletions.
34 changes: 34 additions & 0 deletions lib/nif.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ defmodule Yex.Nif do
def text_to_string(_text, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)
def text_length(_text, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)

def text_observe(_text, _cur_txn, _pid, _ref, _metadata), do: :erlang.nif_error(:nif_not_loaded)

def text_observe_deep(_text, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def array_insert(_array, _cur_txn, _index, _value), do: :erlang.nif_error(:nif_not_loaded)
def array_insert_list(_array, _cur_txn, _index, _values), do: :erlang.nif_error(:nif_not_loaded)
def array_length(_array, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)
Expand All @@ -51,13 +56,24 @@ defmodule Yex.Nif do

def array_to_json(_array, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)

def array_observe(_array, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def array_observe_deep(_array, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def map_set(_map, _cur_txn, _key, _value), do: :erlang.nif_error(:nif_not_loaded)
def map_size(_map, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)
def map_get(_map, _cur_txn, _key), do: :erlang.nif_error(:nif_not_loaded)
def map_delete(_map, _cur_txn, _key), do: :erlang.nif_error(:nif_not_loaded)
def map_to_map(_map, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)
def map_to_json(_map, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)

def map_observe(_map, _cur_txn, _pid, _ref, _metadata), do: :erlang.nif_error(:nif_not_loaded)

def map_observe_deep(_map, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def xml_fragment_insert(_xml_fragment, _cur_txn, _index, _content),
do: :erlang.nif_error(:nif_not_loaded)

Expand All @@ -71,6 +87,12 @@ defmodule Yex.Nif do

def xml_fragment_parent(_xml_fragment, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)

def xml_fragment_observe(_map, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def xml_fragment_observe_deep(_map, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def xml_element_insert(_xml_element, _cur_txn, _index, _content),
do: :erlang.nif_error(:nif_not_loaded)

Expand All @@ -97,6 +119,12 @@ defmodule Yex.Nif do

def xml_element_parent(_xml_element, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)

def xml_element_observe(_map, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def xml_element_observe_deep(_map, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def xml_text_insert(_xml_text, _cur_txn, _index, _content),
do: :erlang.nif_error(:nif_not_loaded)

Expand All @@ -119,6 +147,12 @@ defmodule Yex.Nif do

def xml_text_parent(_xml_text, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)

def xml_text_observe(_map, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def xml_text_observe_deep(_map, _cur_txn, _pid, _ref, _metadata),
do: :erlang.nif_error(:nif_not_loaded)

def encode_state_vector_v1(_doc, _cur_txn), do: :erlang.nif_error(:nif_not_loaded)
def encode_state_as_update_v1(_doc, _cur_txn, _diff), do: :erlang.nif_error(:nif_not_loaded)
def apply_update_v1(_doc, _cur_txn, _update), do: :erlang.nif_error(:nif_not_loaded)
Expand Down
5 changes: 1 addition & 4 deletions lib/protocols/awareness.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
defmodule Yex.Awareness do
@moduledoc """
Awareness is an optional feature that works well together with Yjs.
## Examples
iex> doc = Yex.Doc.new()
iex> {:ok, _awareness} = Yex.Awareness.new(doc)
"""

defstruct [
Expand Down
34 changes: 34 additions & 0 deletions lib/shared_type/array.ex
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ defmodule Yex.Array do
Yex.Nif.array_delete_range(array, cur_txn(array), index, length)
end

@doc """
Get content at the specified index.
## Examples
iex> doc = Yex.Doc.new()
iex> array = Yex.Doc.get_array(doc, "array")
iex> Yex.Array.push(array, "Hello")
iex> Yex.Array.get(array, 0)
{:ok, "Hello"}
"""
@deprecated "Rename to `fetch/2`"
@spec get(t, integer()) :: {:ok, term()} | :error
def get(array, index) do
Expand Down Expand Up @@ -142,6 +151,31 @@ defmodule Yex.Array do
Yex.Nif.array_to_json(array, cur_txn(array))
end

@doc """
see `Yex.SharedType.observe/2`
"""
@spec observe(t, keyword()) :: reference()
def observe(%__MODULE__{} = array, opt \\ []) do
ref = make_ref()
sub = Yex.Nif.array_observe(array, cur_txn(array), self(), ref, Keyword.get(opt, :metadata))
Process.put(ref, sub)
ref
end

@doc """
see `Yex.SharedType.observe_deep/2`
"""
@spec observe_deep(t, keyword()) :: reference()
def observe_deep(%__MODULE__{} = array, opt \\ []) do
ref = make_ref()

sub =
Yex.Nif.array_observe_deep(array, cur_txn(array), self(), ref, Keyword.get(opt, :metadata))

Process.put(ref, sub)
ref
end

defp cur_txn(%__MODULE__{doc: doc_ref}) do
Process.get(doc_ref, nil)
end
Expand Down
116 changes: 116 additions & 0 deletions lib/shared_type/event.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
defmodule Yex.ArrayEvent do
@moduledoc """
Event when Array type changes
@see Yex.Array.observe/1
@see Yex.Array.observe_deep/1
@see Yex.Map.observe_deep/1
"""
defstruct [
:path,
:target,
:change
]

@type t :: %__MODULE__{
path: list(number() | String.t()),
target: Yex.Array.t(),
change: %{insert: list()} | %{delete: number()} | %{}
}
end

defmodule Yex.MapEvent do
@moduledoc """
Event when Map type changes
@see Yex.Map.observe/1
@see Yex.Array.observe_deep/1
@see Yex.Map.observe_deep/1
"""
defstruct [
:path,
:target,
:keys
]

@type change ::
%{action: :add, new_value: term()}
| %{action: :delete, old_value: term()}
| %{action: :update, old_value: term(), new_value: term()}
@type keys :: %{String.t() => %{}}

@type t :: %__MODULE__{
path: list(number() | String.t()),
target: Yex.Map.t(),
keys: keys
}
end

defmodule Yex.TextEvent do
@moduledoc """
Event when Text type changes
@see Yex.Text.observe/1
@see Yex.Array.observe_deep/1
@see Yex.Map.observe_deep/1
"""
defstruct [
:path,
:target,
:delta
]

@type t :: %__MODULE__{
path: list(number() | String.t()),
target: Yex.Map.t(),
delta: Yex.Text.delta()
}
end

defmodule Yex.XmlEvent do
@moduledoc """
Event when XMLFragment/Element type changes
@see Yex.Text.observe/1
@see Yex.Array.observe_deep/1
@see Yex.Map.observe_deep/1
"""
defstruct [
:path,
:target,
:delta,
:keys
]

@type t :: %__MODULE__{
path: list(number() | String.t()),
target: Yex.Map.t(),
delta: Yex.Text.delta(),
keys: %{insert: list()} | %{delete: number()} | %{}
}
end

defmodule Yex.XmlTextEvent do
@moduledoc """
Event when Text type changes
@see Yex.Text.observe/1
@see Yex.Array.observe_deep/1
@see Yex.Map.observe_deep/1
"""
defstruct [
:path,
:target,
:delta
]

@type t :: %__MODULE__{
path: list(number() | String.t()),
target: Yex.Map.t(),
delta: Yex.Text.delta()
}
end
24 changes: 23 additions & 1 deletion lib/shared_type/map.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ defmodule Yex.Map do

@doc """
get a key from the map.
## Examples
## Examples
iex> doc = Yex.Doc.new()
iex> map = Yex.Doc.get_map(doc, "map")
iex> Yex.Map.set(map, "plane", ["Hello", "World"])
Expand Down Expand Up @@ -115,6 +115,28 @@ defmodule Yex.Map do
Yex.Nif.map_to_json(map, cur_txn(map))
end

@doc """
see `Yex.SharedType.observe/2`
"""
@spec observe(t, keyword()) :: reference()
def observe(%__MODULE__{} = map, opt \\ []) do
ref = make_ref()
sub = Yex.Nif.map_observe(map, cur_txn(map), self(), ref, Keyword.get(opt, :metadata))
Process.put(ref, sub)
ref
end

@doc """
see `Yex.SharedType.observe_deep/2`
"""
@spec observe_deep(t, keyword()) :: reference()
def observe_deep(%__MODULE__{} = map, opt \\ []) do
ref = make_ref()
sub = Yex.Nif.map_observe_deep(map, cur_txn(map), self(), ref, Keyword.get(opt, :metadata))
Process.put(ref, sub)
ref
end

defp cur_txn(%__MODULE__{doc: doc_ref}) do
Process.get(doc_ref, nil)
end
Expand Down
Loading

0 comments on commit 24cf968

Please sign in to comment.