Namdak Tonpa 7 years ago
parent
commit
fdfbc9e4ee
2 changed files with 337 additions and 2 deletions
  1. 335 0
      man/kvs.htm
  2. 2 2
      src/kvs_stream.erl

+ 335 - 0
man/kvs.htm

@@ -0,0 +1,335 @@
+<html>
+
+<head>
+    <meta charset="utf-8" />
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta name="description" content="" />
+    <meta name="author" content="Maxim Sokhatsky" />
+    <title>N2O</title>
+    <link rel="stylesheet" href="https://n2o.space/n2o.css" />
+</head>
+
+<body>
+
+<header>
+    <a href="../index.html"><img src="https://n2o.space/img/Synrc Neo.svg"></a>
+    <h1>N2O API</h1>
+</header>
+
+<main>
+    <section>
+
+<h3>INTRO</h3>
+
+<p>The <b>n2o</b> defines the way you create, configure and run
+arbitrary applications and protocols inside some hosts, into
+which N2O can be injected, such as <b>cowboy</b> and <b>emqttd</b>.
+Each application can spawn its instance in its own way, like
+web pages spawn WebSocket connections, workflow engines
+spawn business proceseses and chat applications spawns roster
+and chatroom processes. With N2O everything is managed by protocols.</p>
+
+<p>The N2O itself a an enbeddable protocol loop. However besides that
+it handles cache and session ETS tables along with flexible async
+processes with no ownership restriction. It also introduces logging
+approach, AES/CBC&mdash;128 pickling and with BERT/JSON formatter.</p>
+
+    </section>
+    <section>
+
+<h3>RECORDS</h3>
+
+<figure><figcaption>Listing 1. Erlang/OTP records</figcaption><code>
+      #ok { data  = [] :: term() }.
+   #error { data  = [] :: term() }.
+
+</code></figure>
+
+<figure><figcaption>Listing 2. N2O Protocol</figcaption><code>
+   #reply { data  = [] :: [] | binary(),
+            req   = [] :: [] | term(),
+            state = [] :: [] | term() }.
+
+ #unknown { data  = [] :: [] | binary(),
+            req   = [] :: [] | term(),
+            state = [] :: [] | term() }.
+
+</code></figure>
+
+<figure><figcaption>Listing 3. N2O State</figcaption><code>
+      #cx { session   = [] :: [] | binary(),
+            formatter = bert :: bert | json,
+            actions   = [] :: list(tuple()),
+            state     = [] :: [] | term(),
+            module    = [] :: [] | atom(),
+            lang      = [] :: [] | atom(),
+            path      = [] :: [] | binary(),
+            node      = [] :: [] | atom(),
+            pid       = [] :: [] | pid(),
+            vsn       = [] :: [] | integer() }).
+
+</code></figure>
+
+    </section>
+    <section>
+
+<h3>PROTOCOLS</h3>
+
+<p>While all application protocols in the system
+are desired to be placed in the single effectful
+environment or same error handling path, <b>n2o</b> defines
+single protocol loop for all applications in its federation of protocols.</p>
+
+<p>In core bundle <b>n2o</b> is shipped with NITRO and FTP protocols which
+allows you to create real-time web applications with binary-based protocols,
+priving also robust and performant upload client and file transfer protocol.
+For bulding web based NITRO applications you need to include
+<b>nitro</b> dependency.</p>
+
+<h4>info(term(),term(),#cx{}) -> #reply{} | #unknown{}.</h4>
+
+<p>The <b>info/2</b> is a N2O protocol callback that will be called on each
+incoming request. N2O code should be embedded into applications host:
+MQTT (as ring of MQTT clients), or HTTP server or WebSocket, or raw TCP.</p>
+
+    </section>
+    <section>
+
+<h3>RPC MQTT</h3>
+
+<p>N2O provides RPC over MQ mechanism for MQTT devices. N2O spawn a set of
+<a href="n2o_vnode.htm">n2o_vnode</a> workers as <a href="n2o_async.htm">n2o_async</a>
+processes that listen to <b>events</b> topic. Response are sent to <b>actions</b> topic,
+which is subscribed automatically on MQTT session init.</p>
+
+<figure><figcaption>Listing 4. MQTT RPC Topics</figcaption><code>
+ actions/:vsn/:module/:client
+  events/:vsn/:node/:module/:client
+
+</code></figure>
+
+    </section>
+    <section>
+
+<h3>RPC WebSocket</h3>
+
+<p>In pure WebSocket case N2O implements <a href="n2o_proto.htm">n2o_proto</a> as cowboy module supporting binary and text messages.</p>
+
+<figure><figcaption>Listing 5. Cowboy stream protocol</figcaption><code>
+ #binary { data :: binary() }.
+   #text { data :: binary() }.
+
+</code></figure>
+
+    </section>
+    <section>
+
+<h3>EXAMPLE</h3>
+
+<p>Here is little example of overriding INIT protocol message from NITRO protocol
+and generate standart token stored in KVS.</p>
+
+<figure><figcaption>Listing 6. Custom INIT Protocol</figcaption><code>
+ -module(custom_init).
+ -compile(export_all).
+
+ info({init, <<>>}, Req, State = #cx{session = Session}) ->
+      {'Token', Token} = n2o_auth:gen_token([], Session),
+      #cx{params = Client} = get(context),
+      kvs:put(#'Token'{token = Token, client = Client}),
+      n2o_nitro:info({init, n2o:format(Token)}, Req, State);
+
+ info(Message,Req,State) -> {unknown,Message,Req,State}.
+
+</code></figure>
+
+    </section>
+    <section>
+
+<h3>CONFIG</h3>
+
+<p>Just put protocol implementation module name to <b>protocol</b> option in sys.config.</p>
+
+<figure><code>
+  [{n2o,[{cache,n2o},
+         {mq,n2o},
+         {formatter,n2o_format},
+         {logging,n2o_io},
+         {log_modules,n2o},
+         {log_level,n2o},
+         {session,n2o_session},
+         {pickler,n2o_secret},
+         {protocols,[custom_init,n2o_ftp,n2o_nitro]},
+         {timer,{0,10,0}}]}].
+
+</code></figure>
+
+<p>N2O is the facade of the following services: cache, mq, message formating, loging,
+   sessions, pickling and protocol loops. The other part of N2O is <a href="n2o_async.htm">n2o_async</a> module
+   for spawning supervised application processes tha use N2O API. In this simple
+   configuration you may set any implementation to any service.</p>
+
+    </section>
+    <section>
+
+<h3>CACHE</h3>
+
+<p>Cache is fast expirable memory store. Just put values onto keys using these
+functions and system timer will clear expired entries eventually.
+You can select caching module implementation by seting <b>cache</b> n2o parameter to module name.
+Default n2o cache implementation just turns each ets store into expirable.</p>
+
+<h4>cache(Tab, Key, Value, Till) -> term().</h4>
+
+<p>Sets a Value with a given TTL.</p>
+
+<h4>cache(Tab, Key) -> term().</h4>
+
+<p>Gets a Value.</p>
+
+    </section>
+    <section>
+
+<h3>PUBSUB</h3>
+
+<p>The minimal requirement for any framework is to pub/sub API.
+   N2O provides selectable API through <b>mq</b> environment parameter.</p>
+
+<h4>subscribe(Client, Topic, Options) -> term().</h4>
+
+<p>Subscribe an absctract client to a transient topic. In particular
+   implementation the semantics could differ. In MQTT you can subscribe
+   offline/online clients to any persistent topics.</p>
+
+<h4>unsubscribe(Client, Topic, Options) -> term().</h4>
+
+<p>Unsubscribe an abstract client from a transient topic. In MQTT
+   we remove the subscription from persistent database.</p>
+
+<h4>publish(Topic, Message, Options) -> term().</h4>
+
+<p>Publish a message to a topic. In MQTT if clients are offline
+they will receive offline messages from the
+inflight srotarge once they become online.</p>
+
+    </section>
+    <section>
+
+<h3>FORMAT</h3>
+
+<p>Call this function for changeable at runtime socket binary formatting.
+   Used to format data field of the returned values in protocol implementations.
+   N2O provides selectable API through <b>formatter</b> environment parameter.</p>
+
+<h4>format(Message, bert | json) -> binary().</h4>
+
+<figure><code>
+ > io:format("~ts~n",[n2o:format(#io{},json)]).
+ {"t":104,"v":[{"t":100,"v":"io"},{"t":109,"v":""},{"t":109,"v":[131,106]}]}
+ ok
+
+ > n2o:format(#ok{code=undefined},bert).
+ {binary,<<131,104,2,100,0,2,111,107,100,0,9,117,110,100,
+           101,102,105,110,101,100>>}
+
+</code></figure>
+
+    </section>
+    <section>
+
+<h3>LOG</h3>
+
+<p>First you need specify global module in sys.config,
+where functions <b>log_level</b> and <b>log_modules</b> are placed.
+See options in config with with same names as functions.</p>
+
+<p>Then implement these function in way of returning the list
+modules you want to trace, and global log level for them.</p>
+
+<figure><figcaption>Listing 7. Log Framework</figcaption>
+<code>
+ log_modules() -> [n2o,n2o_async,n2o_proto].
+ log_level() -> info.
+
+</code></figure>
+
+<p>In your code you should use following trace functions
+   which are the same as callback API for n2o logging
+   environment variable.</p>
+
+<h4>error(Module, Format, Args) -> ok | skip.</h4>
+
+<h4>info(Module, Format, Args) -> ok | skip.</h4>
+
+<h4>warning(Module, Format, Args) -> ok | skip.</h4>
+
+    </section>
+    <section>
+
+<h3>SESSION</h3>
+
+<p>Sessions are stored in <b>cookies</b> table and indexed by security token which
+is usually a password based token. All session variables from all users are stored in this table.
+each user see only its variables indexed by his token. Sessions like a cache are expirable.
+Technically, N2O sessions are the server controlling mechanism of JavaScript cookies.</p>
+
+<h4>session(Key, Value) -> term().</h4>
+
+<p>Sets a Value into ETS table <b>cookies</b> for a token from #cx.session which
+is set there earlier from INIT message or MQTT headers,
+before entering the top level N2O loop.</p>
+
+<figure><figcaption>Listing 8. Sessions</figcaption>
+<code>
+ 1> rr(n2o).
+ [bin,client,cx,direct,ev,flush,ftp,ftpack,handler,
+  mqtt_client,mqtt_message,pickle,server]
+ 2> put(context,#cx{session=10}).
+ undefined
+ 3> n2o:session(user,maxim).
+ maxim
+ 4> ets:tab2list(cookies).
+ [{{10,user},
+   <<"/">>,
+   {1504,977449,476430},
+   {{2017,9,9},{20,32,29}},
+   maxim}]
+
+</code></figure>
+
+<h4>session(Key) -> term().</h4>
+
+<p>Gets a Value by any Key.</p>
+
+    </section>
+    <section>
+
+<h3>PICKLE</h3>
+
+<p>Call this function for changeable at runtime term pickling.</p>
+
+<h4>pickle(term()) -> binary().</h4>
+
+<h4>depickle(binary()) -> term().</h4>
+
+    </section>
+    <section>
+
+<p>This module may refer to:
+<a href="http://erlang.org/doc/man/io.html">io</a></b>,
+<a href="http://erlang.org/doc/man/ets.html">ets</a></b>,
+<a href="n2o_async.htm">n2o_asynrc</a></b>,
+<a href="n2o_vnode.htm">n2o_vnode</a></b>,
+<a href="n2o_proto.htm"><b>n2o_proto</b></a>.
+</p>
+
+    </section>
+</main>
+
+<footer>
+    2005&mdash;2017 &copy; Synrc Research Center
+</footer>
+
+</body>
+</html>

+ 2 - 2
src/kvs_stream.erl

@@ -63,7 +63,7 @@ take(A,N,#cur{val=B}=C,R) -> take(A,N-1,?MODULE:A(C),[B|R]).
 
 % TESTS
 
-check() -> test(), test2(), test3(), ok.
+check() -> test1(), test2(), test3(), ok.
 
 test2() ->
     Cur = new(user),
@@ -87,7 +87,7 @@ test3() ->
     Y = remove(B,remove(D,remove(A,remove(C,S)))),
     [] = take(-1,down(top(Y))).
 
-test() ->
+test1() ->
     Cur = new(user),
     take(-1,down(top(Cur))),
     [A,B,C,D] = [ kvs:next_id(user,1) || _ <- lists:seq(1,4) ],