Packages
riptide
0.5.0-beta4
0.5.2
0.5.1
0.5.0-beta9
0.5.0-beta8
0.5.0-beta7
0.5.0-beta6
0.5.0-beta5
0.5.0-beta4
0.5.0-beta3
0.5.0-beta2
0.5.0-beta11
0.5.0-beta10
0.5.0-beta
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.3.0-bd63a38
0.2.79
0.2.78
0.2.74
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A data first framework for building realtime applications
Current section
Files
Jump to
Current section
Files
lib/riptide.ex
defmodule Riptide do
@moduledoc """
Riptide is a data first framework for building realtime applications. Riptide makes building snappy, realtime applications a breeze by letting you think purely in terms of your data and functionally about what should happen when it changes.
"""
@internal %{internal: true}
use Supervisor
@doc """
Starts a Riptide process.
Probably should not called this directly and instead should be placed
inside your application's root supervisor.
## Options
* `:port` - Optional, will override default port of `12_000`
"""
def start_link(opts \\ []) do
Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
end
@doc false
def init(opts) do
Riptide.Store.init()
Riptide.Migration.run()
Supervisor.init(
[
if Riptide.Config.riptide_scheduler() do
{Riptide.Scheduler, []}
end,
{Riptide.Websocket.Server,
Keyword.merge(
[handlers: Riptide.Config.riptide_handlers()],
opts
)}
]
|> Enum.filter(& &1),
strategy: :one_for_one
)
end
@doc """
Pass in a query and get the results.
Read more about query structure [here](https://riptide.ironbay.co/docs/queries). State parameter is optional and is passed to interceptors.
## Options
* `:min` - Starting range of query, optional
* `:max` - End range of query, optional
* `:limit` - Max number of results, optional
## Examples
iex> Riptide.query(%{ "todo:info" => %{} })
%{
"todo:info" => %{
"todo1" => %{
"text" => "Document riptide"
}
}
}
"""
def query(query, state \\ @internal) do
with :ok <- Riptide.Interceptor.query_before(query, state) do
resolved = Riptide.Interceptor.query_resolve(query, state)
store =
resolved
|> Enum.reduce(query, fn {path, _value}, collect ->
Dynamic.delete(collect, path)
end)
|> case do
result when result === %{} ->
%{}
remaining ->
Riptide.Store.query(remaining)
end
{:ok,
Enum.reduce(resolved, store, fn {path, value}, collect ->
Dynamic.put(collect, path, value)
end)}
end
end
@doc """
Return a stream of values underneath a path
## Options
* `:min` - Starting range of query, optional
* `:max` - End range of query, optional
* `:limit` - Max number of results, optional
## Examples
iex> Riptide.stream(["todo:info"]) |> Enum.take(1)
[
%{"todo1", %{ "text" => "Document riptide" }}
]
"""
def stream(path, opts \\ %{}, state \\ @internal) do
query = Dynamic.put(%{}, path, opts)
with :ok <- Riptide.Interceptor.query_before(query, state) do
Riptide.Store.stream(path, opts)
end
end
@doc """
The same as `query_path/3` but raises an exception if it fails
"""
def query_path!(path, opts \\ %{}, state \\ @internal) do
{:ok, result} = query_path(path, opts, state)
result
end
@doc """
Return data under a specific path
## Options
* `:min` - Starting range of query, optional
* `:max` - End range of query, optional
* `:limit` - Max number of results, optional
"""
def query_path(path, opts \\ %{}, state \\ @internal) do
case query(Dynamic.put(%{}, path, opts), state) do
{:ok, result} -> {:ok, Dynamic.get(result, path)}
result -> result
end
end
@doc """
The same as `mutation/2` but raises an exception if it fails
"""
def mutation!(mut, state \\ @internal) do
case mutation(mut, state) do
{:ok, result} -> result
end
end
@doc """
Apply a mutation.
This will do following steps in order
1. Trigger `c:Riptide.Interceptor.mutation_before/4`
2. Trigger `c:Riptide.Interceptor.mutation_effect/4`
3. Broadcast mutation to interested processes
4. Write mutation to stores
5. Trigger `c:Riptide.Interceptor.mutation_after/4`
## Examples
iex> mut = Riptide.Mutation.put_merge(["foo", "bar"], "hello")
iex> Riptide.mutation(mut)
{:ok, %{
merge: %{
"foo" => %{
"bar" => "hello
}
},
delete: %{}
}}
"""
@spec mutation(Riptide.Mutation.t(), any()) :: {:ok, Riptide.Mutation.t()} | {:error, any()}
def mutation(mut, state \\ @internal) do
with {:ok, prepared} <- Riptide.Interceptor.mutation_before(mut, state),
prepared <- Riptide.Interceptor.mutation_effect(prepared, state),
:ok <- Riptide.Subscribe.broadcast_mutation(prepared),
:ok <- Riptide.Store.mutation(prepared),
:ok <- Riptide.Interceptor.mutation_after(prepared, state) do
{:ok, prepared}
end
end
@doc """
Convience method to apply a mutation that merges a single value
## Examples
iex> Riptide.merge(["foo", "bar"], "hello")
{:ok, %{
merge: %{
"foo" => %{
"bar" => "hello
}
},
delete: %{}
}}
"""
def merge(path, value, state \\ @internal),
do:
path
|> Riptide.Mutation.put_merge(value)
|> mutation(state)
@doc """
The same as `merge/3` but raises an exception if it fails
"""
def merge!(path, value, state \\ @internal) do
{:ok, result} = merge(path, value, state)
result
end
@doc """
Convience method to apply a mutation that deletes a single path
## Examples
iex> Riptide.delete(["foo", "bar"])
{:ok, %{
delete: %{
"foo" => %{
"bar" => 1
}
},
delete: %{}
}}
"""
def delete(path, state \\ @internal),
do:
path
|> Riptide.Mutation.put_delete()
|> mutation(state)
@doc """
The same as `delete/2` but raises an exception if it fails
"""
def delete!(path, state \\ @internal) do
{:ok, result} = delete(path, state)
result
end
end