{"id":271231,"date":"2015-12-27T19:12:02","date_gmt":"2015-12-27T16:12:02","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=271231"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=271231","title":{"rendered":"\u042d\u0440\u043b\u0430\u043d\u0433 \u0434\u043b\u044f \u0432\u0435\u0431-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 (2) -&gt; \u0411\u0414 \u0438 \u0434\u0435\u043f\u043b\u043e\u0439;"},"content":{"rendered":"<p>       <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/ab9\/08a\/eb3\/ab908aeb3c9c4422ab0de8abe4b4a361.png\" align=\"right\"\/><br \/>  \u0412 <a href=\"http:\/\/habrahabr.ru\/post\/273979\/\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a> \u043c\u044b \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u043b\u0438\u0441\u044c \u0441 \u042d\u0440\u043b\u0430\u043d\u0433\u043e\u043c \u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u043c n2o. \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0430\u0448 \u0431\u043b\u043e\u0433:  <\/p>\n<ul>\n<li>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u0444\u0435\u0439\u0441\u0431\u0443\u043a, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u043c \u0438\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435;<\/li>\n<li>\u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u0438 \u043f\u043e\u0441\u0442\u044b \u0432 NoSQL \u0431\u0430\u0437\u0435;<\/li>\n<li>\u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0435\u043c \u043d\u0430\u0448 \u0431\u043b\u043e\u0433 \u043d\u0430 DigitalOcean \u0438 \u0437\u0430\u043c\u0435\u0440\u0438\u043c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c (<i>\u0441\u043f\u043e\u0439\u043b\u0435\u0440 \u2014 1300 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443<\/i>).<\/li>\n<\/ul>\n<p>  \u041a\u043e\u0434 \u0438\u0437 \u0441\u0442\u0430\u0442\u0435\u0439 <a href=\"https:\/\/github.com\/denys-potapov\/n2o-blog-example\">https:\/\/github.com\/denys-potapov\/n2o-blog-example<\/a>, \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <a href=\"http:\/\/46.101.118.21:8001\/\">http:\/\/46.101.118.21:8001\/<\/a>. <\/p>\n<p>  <a name=\"habracut\"><\/a><\/p>\n<h2>\u0424\u0430\u0439\u043b\u044b \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438<\/h2>\n<p>  \u0414\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0433\u0434\u0435-\u0442\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c facebook_app_id, \u0432 \u042d\u0440\u043b\u0430\u043d\u0433 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u0432 sys.config, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0443\u0434\u0430 \u043d\u0430\u0448 facebook_app_id  <\/p>\n<pre><code class=\"erlang\">[{n2o, [     {port,8001},     {route,routes},     {log_modules,sample} ]}, {sample, [      {facebook_app_id, &quot;631083680327759&quot;} ]} ]. <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 application:get_env(sample, facebook_app_id, &quot;&quot;)<\/p>\n<h2>\u0412\u044b\u0437\u043e\u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430<\/h2>\n<p>  \u0414\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0442\u0438 \u0432 n2o \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0435\u0441\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/github.com\/synrc\/avz\">avz<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 Twitter, Google, Facebook, Github \u0438 Microsoft \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e. \u041d\u043e, avz \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u0443\u044e \u0441\u0445\u0435\u043c\u0443 \u0432 \u0411\u0414, \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0443 \u043d\u0430\u0441 \u043f\u043e\u043a\u0430 \u043d\u0435\u0442, \u0430 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0440\u0435\u043b\u0438\u0437\u0443\u0435\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e.<\/p>\n<p>  \u0424\u0443\u043d\u043a\u0446\u0438\u044f <b> wf:wire(#api{name=login})<\/b> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u0432\u044b\u0437\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 login \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u043a \u0441\u043e\u0431\u044b\u0442\u0438\u044e <br \/>  api_event(login, Response, Term) \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435.<\/p>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u0439\u043b login.erl:  <\/p>\n<pre><code class=\"erlang\">-module(login). -compile(export_all). -include_lib(&quot;n2o\/include\/wf.hrl&quot;). -include_lib(&quot;nitro\/include\/nitro.hrl&quot;). -include_lib(&quot;records.hrl&quot;).  main() -&gt;      wf:wire(#api{name=login}),     #dtl{file=&quot;login&quot;, bindings=[{app_id, application:get_env(sample, facebook_app_id, &quot;&quot;)}]}.  api_event(login, Response, Term) -&gt;     {Props} = jsone:decode(list_to_binary(Response)),     User = binary_to_list(proplists:get_value(&lt;&lt;&quot;name&quot;&gt;&gt;, Props)),     wf:user(User),     wf:redirect(&quot;\/&quot;). <\/code><\/pre>\n<p>  \u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 main\/0 \u043c\u044b \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u0435\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u0435 login, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u0442\u043e\u043c \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0432 api_event. \u041c\u044b \u0434\u0435\u043a\u043e\u0434\u0438\u0440\u0443\u0435\u043c json \u0441\u0442\u0440\u043e\u043a\u0443, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443. \u0412 priv\/templates\/login.html \u043a\u043e\u0434 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d \u0441 \u043e\u0431\u0440\u0430\u0437\u0446\u0430 \u043d\u0430 facebook, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0433\u043b\u0430\u0432\u043d\u0430\u044f \u043c\u0430\u0433\u0438\u044f \u0432 \u0432\u044b\u0437\u043e\u0432\u0435 <b>login(response)<\/b>.  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">priv\/templates\/login.html<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"html\">{% extends &quot;base.html&quot; %} {% block title %}Login{% endblock %} {% block content %}  &lt;h1&gt;Login&lt;\/h1&gt; &lt;p id=&quot;status&quot;&gt;&lt;\/p&gt; &lt;button id=&quot;login&quot; class=&quot;btn btn-primary&quot; onclick=&quot;onLoginClick();&quot;&gt;     Login with facebook &lt;\/button&gt;  &lt;script&gt;     \/\/ This is called with the results from from FB.getLoginStatus().   function statusChangeCallback(response) {     console.log('statusChangeCallback');     if (response.status === 'connected') {       \/\/ Logged into your app and Facebook.       FB.api('\/me', function(response) {         login(response);       });     } else if (response.status === 'not_authorized') {       document.getElementById('status').innerHTML = 'Please log ' +         'into this app.';     } else {       document.getElementById('status').innerHTML = 'Please log ' +         'into Facebook.';     }   }    window.fbAsyncInit = function() {     FB.init({       appId      : '{{ app_id }}',       cookie     : true,       version    : 'v2.2' \/\/ use version 2.2     });     FB.getLoginStatus(function(response) {       statusChangeCallback(response);     });   };    \/\/ Load the SDK asynchronously   (function(d, s, id) {     var js, fjs = d.getElementsByTagName(s)[0];     if (d.getElementById(id)) return;     js = d.createElement(s); js.id = id;     js.src = &quot;\/\/connect.facebook.net\/en_US\/sdk.js&quot;;     fjs.parentNode.insertBefore(js, fjs);   }(document, 'script', 'facebook-jssdk'));    function onLoginClick() {     FB.login(function(response) {       statusChangeCallback(response);     }, {scope: 'public_profile,email'});&lt;source lang=&quot;html&quot;&gt; {% extends &quot;base.html&quot; %} {% block title %}Login{% endblock %} {% block content %}  &lt;h1&gt;Login&lt;\/h1&gt; &lt;p id=&quot;status&quot;&gt;&lt;\/p&gt; &lt;button id=&quot;login&quot; class=&quot;btn btn-primary&quot; onclick=&quot;onLoginClick();&quot;&gt;     Login with facebook &lt;\/button&gt;  &lt;script&gt;     \/\/ This is called with the results from from FB.getLoginStatus().   function statusChangeCallback(response) {     console.log('statusChangeCallback');     if (response.status === 'connected') {       \/\/ Logged into your app and Facebook.       FB.api('\/me', function(response) {         login(response);       });     } else if (response.status === 'not_authorized') {       document.getElementById('status').innerHTML = 'Please log ' +         'into this app.';     } else {       document.getElementById('status').innerHTML = 'Please log ' +         'into Facebook.';     }   }    window.fbAsyncInit = function() {     FB.init({       appId      : '{{ app_id }}',       cookie     : true,       version    : 'v2.2' \/\/ use version 2.2     });     FB.getLoginStatus(function(response) {       statusChangeCallback(response);     });   };    \/\/ Load the SDK asynchronously   (function(d, s, id) {     var js, fjs = d.getElementsByTagName(s)[0];     if (d.getElementById(id)) return;     js = d.createElement(s); js.id = id;     js.src = &quot;\/\/connect.facebook.net\/en_US\/sdk.js&quot;;     fjs.parentNode.insertBefore(js, fjs);   }(document, 'script', 'facebook-jssdk'));    function onLoginClick() {     FB.login(function(response) {       statusChangeCallback(response);     }, {scope: 'public_profile,email'});   }; &lt;\/script&gt; {% endblock %} <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<h2>\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435<\/h2>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 (index.erl) \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0445\u0435\u0430\u0434\u0435\u0440, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u043a\u043d\u043e\u043f\u043a\u0430 \u0432\u044b\u0445\u043e\u0434\u0430. \u0425\u0435\u0430\u0434\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u043e\u0447\u0438\u0449\u0435\u043d\u044b:  <\/p>\n<pre><code class=\"erlang\">buttons() -&gt;     case wf:user() of         undefined -&gt; #li{body=#link{body = &quot;Login&quot;, url=&quot;\/login&quot;}};         _ -&gt; [                 #p{class=[&quot;navbar-text&quot;], body=&quot;Hello, &quot; ++ wf:user()},                 #li{body=#link{body = &quot;New post&quot;, url=&quot;\/new&quot;}},                 #li{body=#link{body = &quot;Logout&quot;, postback=logout}}         ] end.  header() -&gt;     #ul{id=header, class=[&quot;nav&quot;, &quot;navbar-nav&quot;, &quot;navbar-right&quot;], body = buttons()}.           main() -&gt; #dtl{file=&quot;index&quot;, bindings=[{posts, posts()}, {header, header()}]}.  event(logout) -&gt;     wf:user(undefined),     wf:update(header, header()). <\/code><\/pre>\n<p>  \u0412 \u0441\u043e\u0431\u044b\u0442\u0438\u0438 event(logout) \u043c\u044b \u043e\u0447\u0438\u0449\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442.<\/p>\n<h2>\u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438<\/h2>\n<p>  \u0414\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0431\u0430\u0437\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/github.com\/synrc\/kvs\">kvs<\/a>. kvs \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441\u043f\u0438\u0441\u043a\u0438 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0430\u0437\u043d\u044b\u0435 \u0431\u0435\u043a\u0435\u043d\u0434\u044b (Mnesia, Riak, KAI, Redis, MongoDB). \u0414\u0430\u043b\u044c\u0448\u0435 \u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"http:\/\/learnyousomeerlang.com\/mnesia\">mnesia<\/a>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d\u0430 \u0438\u0434\u0435\u0442 \u0432 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u0435 \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0438 \u0435\u0435 \u043d\u0435 \u043d\u0430\u0434\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c.<\/p>\n<p>  \u0417\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0432 \u042d\u0440\u043b\u0430\u043d\u0433 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u043b\u0435\u0436\u0430\u0442 \u0432 \u0444\u0430\u0439\u043b\u0435 rebar.config, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u0443\u0434\u0430 kvs:  <\/p>\n<pre><code class=\"erlang\">{kvs,    &quot;.*&quot;, {git, &quot;git:\/\/github.com\/synrc\/kvs&quot;,          {tag, &quot;2.9&quot;}   }} <\/code><\/pre>\n<p>  \u0412 sys.config \u0443\u043a\u0430\u0436\u0435\u043c \u043a\u0430\u043a\u043e\u0439 \u0431\u0435\u043a\u0435\u043d\u0434 \u0438 \u043a\u0430\u043a\u0443\u044e \u0441\u0445\u0435\u043c\u0443 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c. \u0421\u0445\u0435\u043c\u0430 \u043d\u0443\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f mnesia, \u0434\u043b\u044f \u0434\u0440\u0443\u0433\u0438\u0445 \u0431\u0435\u043a\u0435\u043d\u0434\u043e\u0432 \u043e\u043d\u0430 \u043d\u0435 \u043d\u0443\u0436\u043d\u0430.  <\/p>\n<pre><code class=\"erlang\">{kvs,      {dba,store_mnesia},     {schema,[sample]} ]} <\/code><\/pre>\n<p>  \u0421\u0445\u0435\u043c\u0443 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 metainfo\/0 \u0432 sample.erl:  <\/p>\n<pre><code class=\"erlang\">metainfo() -&gt;     #schema{name=sample,tables=[         #table{name=id_seq,fields=record_info(fields,id_seq),keys=[thing]},         #table{name=post,fields=record_info(fields,post)}     ]}. <\/code><\/pre>\n<p>  \u041c\u044b \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0434\u0432\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b: post, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u0430\u043f\u0438\u0441\u0438 \u0442\u0438\u043f\u0430 post, \u0438 id_seq, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 kvs \u0445\u0440\u0430\u043d\u0438\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u0430.<\/p>\n<p>  \u0422\u0443\u0442 \u0436\u0435 \u0432 sample.erl \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 init\/1 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a kvs.  <\/p>\n<pre><code class=\"erlang\">init([])   -&gt;  \tcase cowboy:start_http(http,3,port(),env()) of         {ok, _}   -&gt; ok;         {error,_} -&gt; halt(abort,[]) end, sup(),     kvs:join(). <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0435\u0441\u043b\u0438 \u043c\u044b \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u043d\u0430\u0448\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b.  <\/p>\n<pre><code class=\"erlang_repl\">2&gt; kvs:dir(). [{table,post},{table,id_seq},{table,schema}] <\/code><\/pre>\n<h2>\u0427\u0442\u0435\u043d\u0438\u0435 \u0438 \u0437\u0430\u043f\u0438\u0441\u044c<\/h2>\n<p>  \u0412 \u043c\u043e\u0434\u0443\u043b\u0435 \/src\/new.erl \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043e\u0434\u043d\u043e \u0441\u043e\u0431\u044b\u0442\u0438\u0435 event(post), \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u0441\u0442 \u0432 \u0411\u0414 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 kvs:put\/1:  <\/p>\n<pre><code class=\"erlang\">-module(new). -compile(export_all). -include_lib(&quot;n2o\/include\/wf.hrl&quot;). -include_lib(&quot;nitro\/include\/nitro.hrl&quot;). -include_lib(&quot;records.hrl&quot;).  main() -&gt;     case wf:user() of         undefined -&gt; wf:header(&lt;&lt;&quot;Location&quot;&gt;&gt;, wf:to_binary(&quot;\/login&quot;)), wf:state(status,302), [];         _ -&gt; #dtl{file=&quot;new&quot;, bindings=[{button, #button{id=send, class=[&quot;btn&quot;, &quot;btn-primary&quot;], body=&quot;Add post&quot;,postback=post,source=[title,text]} }]} end.  event(post) -&gt;     Id = kvs:next_id(&quot;post&quot;,1),     Post = #post{id=Id,author=wf:user(),title=wf:q(title),text=wf:q(text)},     kvs:put(Post),     wf:redirect(&quot;\/post?id=&quot; ++ wf:to_list(Id)). <\/code><\/pre>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\/priv\/templates\/new.html<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"html\">{% extends &quot;base.html&quot; %} {% block title %}New Post{% endblock %} {% block content %} &lt;h1&gt;Add new post&lt;\/h1&gt; &lt;h3&gt;Title&lt;\/h3&gt; &lt;input id=&quot;title&quot; class=&quot;form-control&quot;&gt; &lt;h3&gt;Body&lt;\/h3&gt; &lt;textarea id=&quot;text&quot; maxlength=&quot;1000&quot; class=&quot;form-control&quot; rows=10&gt;      &lt;\/textarea&gt; {{ button }} {% endblock %} <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u0444\u0430\u0439\u043b\u0435 post.erl \u0437\u0430\u043c\u0435\u043d\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u0441\u0442\u0430, \u0435\u0441\u043b\u0438 \u043f\u043e\u0441\u0442 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u0432\u044b\u0434\u0430\u0435\u043c 404 \u043e\u0448\u0438\u0431\u043a\u0443.  <\/p>\n<pre><code class=\"erlang\">main() -&gt;     case kvs:get(post, post_id()) of         {ok, Post} -&gt; #dtl{file=&quot;post&quot;, bindings=[             {title, wf:html_encode(Post#post.title)},             {text, wf:html_encode(Post#post.text)},             {author, wf:html_encode(Post#post.author)},             {comments, comments()}]};         _ -&gt; wf:state(status,404), &quot;Post not found&quot; end. <\/code><\/pre>\n<p>  \u0412 \u043c\u043e\u0434\u0443\u043b\u0435 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b index.erl \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u0441\u0435 \u043f\u043e\u0441\u0442\u044b \u0432\u044b\u0437\u043e\u0432\u043e\u043c kvs:all(post):  <\/p>\n<pre><code class=\"erlang\">posts() -&gt; [     #panel{body=[         #h2{body = #link{body = wf:html_encode(P#post.title), url = &quot;\/post?id=&quot; ++ wf:to_list(P#post.id)}},         #p{body = wf:html_encode(P#post.text)}       ]} || P &lt;- kvs:all(post)]. <\/code><\/pre>\n<h2>\u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u0438 \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440\u044b<\/h2>\n<p>  \u0414\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0432 kvs \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u0438 \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432. \u0418\u0442\u0435\u0440\u0430\u0442\u043e\u0440 \u0445\u0440\u0430\u043d\u0438\u0442 \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 \u0434\u0432\u0443\u0441\u0432\u044f\u0437\u043d\u044b\u0445 \u0441\u043f\u0438\u0441\u043a\u043e\u0432, \u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0445\u0440\u0430\u043d\u0438\u0442 \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0438 \u043d\u0430 \u0433\u043e\u043b\u043e\u0432\u0443 \u0438 \u0445\u0432\u043e\u0441\u0442 \u0441\u043f\u0438\u0441\u043a\u0430. <\/p>\n<p>  \u041e\u0431\u043d\u043e\u0432\u0438\u043c \u043d\u0430\u0448\u0438 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 records.hrl \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439 \u0438 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u043f\u043e\u0441\u0442:  <\/p>\n<pre><code class=\"erlang\">-record(post, {?CONTAINER, title, text, author}). -record(comment, {?ITERATOR(post), text, author}). <\/code><\/pre>\n<p>  \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0445\u0435\u043c\u0443:  <\/p>\n<pre><code class=\"erlang\">metainfo() -&gt;     #schema{name=sample,tables=[         #table{name=id_seq,fields=record_info(fields,id_seq),keys=[thing]},         #table{name=post,container=true,fields=record_info(fields,post)},         #table{name=comment,container=post,fields=record_info(fields,comment)}     ]}. <\/code><\/pre>\n<p>  \u041f\u0435\u0440\u0435\u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u0445\u0435\u043c\u0443 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445:  <\/p>\n<pre><code class=\"erlang_repl\">2&gt; kvs:destroy(). ok 3&gt; kvs:join(). ok <\/code><\/pre>\n<p>  \u0412 \u043c\u043e\u0434\u0443\u043b\u0435 post.erl \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043b\u043e\u0433\u0438\u043a\u0443 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432:  <\/p>\n<pre><code class=\"erlang\">comments() -&gt;     case wf:user() of         undefined -&gt; #link{body = &quot;Login to add comment&quot;, url=&quot;\/login&quot;};         _ -&gt; [                 #textarea{id=comment, class=[&quot;form-control&quot;], rows=3},                   #button{id=send, class=[&quot;btn&quot;, &quot;btn-default&quot;], body=&quot;Post comment&quot;,postback=comment,source=[comment]}         ] end.  event(init) -&gt;     [event({client,Comment}) || Comment &lt;- kvs:entries(kvs:get(post, post_id()),comment,undefined) ],     wf:reg({post, post_id()});  event(comment) -&gt;     Comment = #comment{id=kvs:next_id(&quot;comment&quot;,1),author=wf:user(),feed_id=post_id(),text=wf:q(comment)},     kvs:add(Comment),     wf:send({post, post_id()}, {client, Comment});  event({client, Comment}) -&gt; \twf:insert_bottom(comments, \t\t#blockquote{body = [ \t\t\t#p{body = wf:html_encode(Comment#comment.text)}, \t\t\t#footer{body = wf:html_encode(Comment#comment.author)} \t\t]}). <\/code><\/pre>\n<p>  \u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 comments(), \u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0430\u0432\u0442\u0440\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c. \u0412 event(init) \u043c\u044b \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u044c\u0441\u044f \u043a \u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u043e\u0441\u0442\u0443 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0438\u0445 \u0432 \u0441\u043e\u0431\u044b\u0442\u0438\u0438 event({client, Comment}), \u0442\u043e \u0435\u0441\u0442\u044c \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u0443 \u043d\u0430\u0441 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\n<p>  \u0412 \u0441\u043e\u0431\u044b\u0442\u0438\u0438 event(comment) \u043c\u044b \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u043c \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439, \u043d\u043e \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0435\u0433\u043e \u0432 \u0411\u0414.<\/p>\n<h2>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0432\u043e\u0438\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432<\/h2>\n<p>  \u0414\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0439 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 DSL \u0441\u0432\u043e\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 pagination. \u0412 \u0444\u0430\u0439\u043b\u0435 \/apps\/sample\/include\/elements.hrl \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0437\u0430\u043f\u0438\u0441\u044c, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0443\u043a\u0430\u0436\u0435\u043c \u043a\u0430\u043a\u043e\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430:  <\/p>\n<pre><code class=\"erlang\">-include_lib(&quot;nitro\/include\/nitro.hrl&quot;).  -record(pagination, {?ELEMENT_BASE(element_pagination), active, count, url}). <\/code><\/pre>\n<p>  \u0421\u0430\u043c \u043c\u043e\u0434\u0443\u043b\u044c \u0432\u044b\u0432\u043e\u0434\u0430 element_pagination.erl \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442:  <\/p>\n<pre><code class=\"erlang\">-module(element_pagination). -compile(export_all). -include_lib(&quot;nitro\/include\/nitro.hrl&quot;). -include_lib(&quot;elements.hrl&quot;).  link(Class, Body, Url) -&gt; #li{class=[Class], body=#link{body=Body, url=Url}}. disabled(Body) -&gt; link(&quot;disabled&quot;, Body, &quot;#&quot;).  left_arrow(#pagination{active = 1}) -&gt; disabled(&quot;&laquo;&quot;); left_arrow(#pagination{active = Active, url = Url}) -&gt;     link(&quot;&quot;, &quot;&laquo;&quot;, Url ++ wf:to_list(Active - 1)).  right_arrow(#pagination{active = Count, count = Count}) -&gt; disabled(&quot;&raquo;&quot;); right_arrow(#pagination{active = Active, url = Url}) -&gt;     link(&quot;&quot;, &quot;&raquo;&quot;,  Url ++ wf:to_list(Active + 1)).  left(0, P) -&gt; [left_arrow(P)]; left(I, P) -&gt;     S = wf:to_list(I),     left(I - 1, P) ++ [link(&quot;&quot;, S, P#pagination.url ++ S)].  right(I, P = #pagination{count = Count}) when I &gt; Count -&gt; [right_arrow(P)]; right(I, P) -&gt;     S = wf:to_list(I),     [link(&quot;&quot;, S, P#pagination.url ++ S) | right(I + 1, P)].  render_element(P = #pagination{}) -&gt;     wf:render(#nav{body=#ul{class=[&quot;pagination&quot;], body=[         left(P#pagination.active - 1, P),         link(&quot;active&quot;, wf:to_list(P#pagination.active), &quot;#&quot;),         right(P#pagination.active + 1, P)     ]}}). <\/code><\/pre>\n<h2>\u041a\u0430\u043a \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f<\/h2>\n<p>  Kvs \u0441\u043e\u0437\u0434\u0430\u043d \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441\u043f\u0438\u0441\u043a\u043e\u0432, \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043b\u043e\u0445\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0439 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438. <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0420\u0435\u0437\u043a\u0438\u0439 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439 \u0430\u0432\u0442\u043e\u0440\u0430 kvs \u043e \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0439 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u0432 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u0432\u0435\u0431\u0435<\/b><\/p>\n<div class=\"spoiler_text\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/d3c\/9fe\/7c0\/d3c9fe7c0bf34193b65077e02e5b4495.png\"\/>  <\/div>\n<\/div>\n<p>  \u041d\u043e, \u0434\u043b\u044f \u0447\u0438\u0441\u0442\u043e\u0442\u044b \u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u0430, \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u0443\u044e \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 feed, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u043e\u0441\u0442\u044b  <\/p>\n<pre><code class=\"erlang\">-record(feed, {?CONTAINER}). -record(post, {?ITERATOR(feed), title, text, author}). -record(comment, {?ITERATOR(feed), text, author}). <\/code><\/pre>\n<p>  \u0418 \u043e\u0431\u043d\u043e\u0432\u0438\u043c \u0441\u0445\u0435\u043c\u0443:  <\/p>\n<pre><code class=\"erlang\">metainfo() -&gt;     #schema{name=sample,tables=[         #table{name=id_seq,fields=record_info(fields,id_seq),keys=[thing]},         #table{name=feed,container=true,fields=record_info(fields,feed)},         #table{name=post,container=feed,fields=record_info(fields,post)},         #table{name=comment,container=feed,fields=record_info(fields,comment)}     ]}. <\/code><\/pre>\n<p>  \u041a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435 feed \u0432\u0438\u0434\u0430 {post, post_id()}:  <\/p>\n<pre><code class=\"erlang\">Comment = #comment{id=kvs:next_id(&quot;comment&quot;,1),author=wf:user(),feed_id={post, post_id()},text=wf:q(comment)}, <\/code><\/pre>\n<p>  \u0418 \u0431\u0443\u0434\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430:  <\/p>\n<pre><code class=\"erlang\">[event({client,Comment}) || Comment &lt;- kvs:entries(kvs:get(feed, {post, post_id()}),comment,undefined) ]; <\/code><\/pre>\n<p>  \u041e\u0433\u0440\u0430\u043d\u0438\u0437\u0443\u0435\u043c \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u043d\u0430 \u0433\u043b\u0430\u0432\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435. \u0415\u0449\u0435 \u0440\u0430\u0437 \u043e\u0442\u043c\u0435\u0447\u0443, \u0447\u0442\u043e kvs \u043f\u043b\u043e\u0445\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0439 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438, \u0438 \u044d\u0442\u043e\u0442 \u043a\u043e\u0434 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0435\u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0437\u0430\u043f\u0443\u0442\u044b\u0432\u0430\u043d\u0438\u044e \u043a\u043e\u0434\u0430:  <\/p>\n<pre><code class=\"erlang\">-define(POST_PER_PAGE, 3).  page() -&gt;     case wf:q(&lt;&lt;&quot;page&quot;&gt;&gt;) of         undefined -&gt; 1;         Page      -&gt; wf:to_integer(Page)     end.  pages() -&gt;     Pages = kvs:count(post) div ?POST_PER_PAGE,     case kvs:count(post) rem ?POST_PER_PAGE of         0 -&gt; Pages;         _ -&gt; Pages + 1     end.  posts() -&gt; [     #panel{body=[         #h2{body = #link{body = wf:html_encode(P#post.title), url = &quot;\/post?id=&quot; ++ wf:to_list(P#post.id)}},         #p{body = wf:html_encode(P#post.author)}       ]} || P &lt;- lists:reverse(kvs:traversal(post, kvs:count(post) - (page() - 1) * ?POST_PER_PAGE, ?POST_PER_PAGE, #iterator.prev))]. <\/code><\/pre>\n<h2>\u0414\u0435\u043f\u043b\u043e\u0439 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c<\/h2>\n<p>  Mad \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0431\u0430\u043d\u0434\u043b \u2014 \u043e\u0434\u0438\u043d \u0444\u0430\u0439\u043b, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u043a\u043e\u0434 \u0438 \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u044b (\u0448\u0430\u0431\u043b\u043e\u043d\u044b, \u0441\u0442\u0430\u0442\u0438\u043a\u0430). \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0438 \u0437\u0430\u043b\u044c\u0435\u043c \u043d\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440:  <\/p>\n<pre><code class=\"bash\">mad deps compile plan bundle sample scp sample root@46.101.117.36:\/var\/www\/sample\/ <\/code><\/pre>\n<p>  \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u043d\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u043d\u043e\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u042d\u0440\u043b\u0430\u043d\u0433 \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435:  <\/p>\n<pre><code class=\"bash\">wget https:\/\/packages.erlang-solutions.com\/erlang\/esl-erlang\/FLAVOUR_1_general\/esl-erlang_18.0-1~ubuntu~trusty_amd64.deb dpkg -i esl-erlang_18.0-1~ubuntu~trusty_amd64.deb escript sample <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u044f \u0441\u043e\u0437\u0434\u0430\u043b \u0441\u0430\u043c\u044b\u0439 \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 \u0434\u0440\u043e\u043f\u043b\u0435\u0442 \u043d\u0430 DigitalOcean (512 MB Memory \/ 20 GB Disk). \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0430 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c 20 \u0442\u044b\u0441\u044f\u0447 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043f\u043e 50 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e:<\/p>\n<pre> root@ubuntu-1gb-fra1-01:~# ab -l -n 20000 -c 50 -g gnuplot.dat http:\/\/46.101.118.21:8001\/ ... Concurrency Level:      50 Time taken for tests:   15.131 seconds Complete requests:      20000 Failed requests:        0 Total transferred:      78279988 bytes HTML transferred:       76399988 bytes Requests per second:    1321.80 [#\/sec] (mean) Time per request:       37.827 [ms] (mean) Time per request:       0.757 [ms] (mean, across all concurrent requests) Transfer rate:          5052.26 [Kbytes\/sec] received  Connection Times (ms)               min  mean[+\/-sd] median   max Connect:        0    0   0.3      0       9 Processing:     9   37   4.9     37      65 Waiting:        9   37   4.9     37      65 Total:         11   38   4.9     37      65  Percentage of the requests served within a certain time (ms)   50%     37   66%     38   75%     39   80%     40   90%     44   95%     47   98%     53   99%     56  100%     65 (longest request) <\/pre>\n<p>  \u0421\u0435\u0440\u0432\u0435\u0440 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b \u043e\u043a\u043e\u043b\u043e 1300 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443, 95% \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u0437\u0430 \u043c\u0435\u043d\u044c\u0448\u0435 \u0447\u0435\u043c 50 \u043c\u0441, \u0447\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0434\u043b\u044f \u0445\u043e\u0441\u0442\u0438\u043d\u0433\u0430 \u0437\u0430 5$ \u0432 \u043c\u0435\u0441\u044f\u0446. \u0422\u043e\u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0432 \u0432\u0438\u0434\u0435 \u0433\u0440\u0430\u0444\u0438\u043a\u0430:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/988\/ca7\/585\/988ca7585026483a93066e8b832925d6.png\"\/>               <\/p>\n<div class=\"clear\"><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"http:\/\/habrahabr.ru\/post\/274107\/\"> http:\/\/habrahabr.ru\/post\/274107\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>       <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/ab9\/08a\/eb3\/ab908aeb3c9c4422ab0de8abe4b4a361.png\" align=\"right\"\/><br \/>  \u0412 <a href=\"http:\/\/habrahabr.ru\/post\/273979\/\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a> \u043c\u044b \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u043b\u0438\u0441\u044c \u0441 \u042d\u0440\u043b\u0430\u043d\u0433\u043e\u043c \u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u043c n2o. \u0412 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0430\u0448 \u0431\u043b\u043e\u0433:  <\/p>\n<ul>\n<li>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u0444\u0435\u0439\u0441\u0431\u0443\u043a, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u043c \u0438\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435;<\/li>\n<li>\u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u0438 \u043f\u043e\u0441\u0442\u044b \u0432 NoSQL \u0431\u0430\u0437\u0435;<\/li>\n<li>\u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0435\u043c \u043d\u0430\u0448 \u0431\u043b\u043e\u0433 \u043d\u0430 DigitalOcean \u0438 \u0437\u0430\u043c\u0435\u0440\u0438\u043c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c (<i>\u0441\u043f\u043e\u0439\u043b\u0435\u0440 \u2014 1300 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443<\/i>).<\/li>\n<\/ul>\n<p>  \u041a\u043e\u0434 \u0438\u0437 \u0441\u0442\u0430\u0442\u0435\u0439 <a href=\"https:\/\/github.com\/denys-potapov\/n2o-blog-example\">https:\/\/github.com\/denys-potapov\/n2o-blog-example<\/a>, \u0433\u043e\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <a href=\"http:\/\/46.101.118.21:8001\/\">http:\/\/46.101.118.21:8001\/<\/a>. <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-271231","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/271231","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=271231"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/271231\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=271231"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=271231"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=271231"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}