{"id":281534,"date":"2016-11-25T11:20:04","date_gmt":"2016-11-25T08:20:04","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=281534"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=281534","title":{"rendered":"\u041a\u043b\u043e\u043d Trello \u043d\u0430 Phoenix \u0438 React. \u0427\u0430\u0441\u0442\u0438 10-12. \u0424\u0438\u043d\u0438\u0448 \u0434\u043e\u043b\u0433\u043e\u0441\u0442\u0440\u043e\u044f"},"content":{"rendered":"<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/86e\/e8d\/293\/86ee8d2930024c60a834695276f5de15.jpg\"\/>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041e\u0433\u043b\u0430\u0432\u043b\u0435\u043d\u0438\u0435 (\u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0432\u044b\u0434\u0435\u043b\u0435\u043d)<\/b><\/p>\n<div class=\"spoiler_text\">\n<ol>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308056\/#1\">\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0438 \u0432\u044b\u0431\u043e\u0440 \u0441\u0442\u0435\u043a\u0430 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308056\/#2\">\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Phoenix Framework<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308056\/#3\">\u041c\u043e\u0434\u0435\u043b\u044c User \u0438 JWT-\u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308100\/#4\">Front-end \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 React \u0438 Redux<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308100\/#5\">\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308248\/#6\">\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 front-end \u043d\u0430 React \u0438 Redux<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308248\/#7\">\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0441\u043e\u043a\u0435\u0442\u044b \u0438 \u043a\u0430\u043d\u0430\u043b\u044b<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308382\/#8\">\u0412\u044b\u0432\u043e\u0434\u0438\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u043e\u0432\u044b\u0435 \u0434\u043e\u0441\u043a\u0438<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308382\/#9\">\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u043e\u0432\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043e\u0441\u043e\u043a<\/a><\/li>\n<li><strong>\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043e\u0441\u043e\u043a<\/strong><\/li>\n<li><strong>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043f\u0438\u0441\u043a\u0438 \u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438<\/strong><\/li>\n<li><strong>\u0412\u044b\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 Heroku<\/strong><\/li>\n<\/ol>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p><em>\u042d\u0442\u0430 \u0447\u0430\u0441\u0442\u044c \u2014 \u0437\u0430\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f, \u0438 \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0434\u043b\u0438\u043d\u043d\u043e\u0439, \u043d\u043e \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0443\u0436\u0435 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c \u0441 \u0446\u0438\u043a\u043b\u043e\u043c \u0438 \u043f\u043e\u0439\u0442\u0438 \u0434\u0430\u043b\u044c\u0448\u0435. \u0422\u0430\u043a \u0436\u0435 \u043f\u0440\u043e\u0448\u0443 \u043f\u0440\u043e\u0449\u0435\u043d\u0438\u044f \u0437\u0430 \u0441\u0442\u043e\u043b\u044c \u043e\u0433\u0440\u043e\u043c\u043d\u0443\u044e \u043f\u0430\u0443\u0437\u0443 \u043f\u0440\u0438 \u0435\u0451 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0435 \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438. \u041e\u0434\u043d\u0430\u043a\u043e \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0435 \u043f\u0440\u043e\u0448\u043b\u043e \u0434\u0430\u0440\u043e\u043c \u0438 \u0434\u0430\u043b\u043e \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0434\u043b\u044f \u043d\u043e\u0432\u044b\u0445, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0445, \u0441\u0442\u0430\u0442\u0435\u0439 \u2014 \u043f\u0440\u0438\u043c. \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\u0430<\/em><\/p>\n<p><a name=\"habracut\"><\/a><br \/>  <a name=\"10\"><\/a>  <\/p>\n<h2 id=\"otslezhivaem-podklyuchenie-uchastnikov-dosok\">\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0434\u043e\u0441\u043e\u043a<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/blog.diacode.com\/trello-clone-with-phoenix-and-react-pt-10\">\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b<\/a><\/p>\n<p>  <\/p>\n<p><strong>\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435 \u043e\u0442 \u0430\u0432\u0442\u043e\u0440\u0430: \u044d\u0442\u0430 \u0447\u0430\u0441\u0442\u044c \u0431\u044b\u043b\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u0434\u043e \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 Presence \u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u043c \u0432\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u0432 \u043e\u0441\u043d\u043e\u0432\u044b \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f GenServer.<\/strong><\/p>\n<p>  <\/p>\n<p>\u0412\u0441\u043f\u043e\u043c\u043d\u0438\u043c <a href=\"https:\/\/habrahabr.ru\/post\/308382\/\">\u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0443\u044e \u0447\u0430\u0441\u0442\u044c<\/a>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u043b\u0438 \u043d\u0430\u0448\u0438\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0445 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u043d\u0430 \u0441\u0432\u043e\u0438 \u0434\u043e\u0441\u043a\u0438. \u041f\u0440\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0438 e-mail \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u043b\u0430\u0441\u044c \u043d\u043e\u0432\u0430\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0441\u0432\u044f\u0437\u044c \u043c\u0435\u0436\u0434\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \u0438 \u0434\u043e\u0441\u043a\u0430\u043c\u0438, \u0430 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u043b\u0438\u0441\u044c \u0447\u0435\u0440\u0435\u0437 \u043a\u0430\u043d\u0430\u043b (channel), \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0447\u0435\u0433\u043e \u0435\u0433\u043e \u0430\u0432\u0430\u0442\u0430\u0440 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u043b\u0441\u044f \u0432\u0441\u0435\u043c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c \u0434\u043e\u0441\u043a\u0438, \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u043c\u0441\u044f \u043e\u043d\u043b\u0430\u0439\u043d. \u041d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u044d\u0442\u043e \u043a\u0440\u0443\u0442\u043e, \u043d\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043b\u0443\u0447\u0448\u0435 \u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u0435\u0435, \u0435\u0441\u043b\u0438 \u0441\u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f online \u0438 \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u044e\u0442 \u0434\u043e\u0441\u043a\u0443. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0451\u043c!<\/p>\n<p><cut\/>  <\/p>\n<h3 id=\"problema\">\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430<\/h3>\n<p>  <\/p>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435, \u0447\u0435\u043c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0437\u0430\u0434\u0443\u043c\u0430\u0435\u043c\u0441\u044f \u043e \u0442\u043e\u043c, \u0447\u0435\u0433\u043e \u0445\u043e\u0442\u0438\u043c \u0434\u043e\u0441\u0442\u0438\u0447\u044c. \u0422\u0430\u043a, \u043f\u043e \u0441\u0443\u0442\u0438, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0434\u043e\u0441\u043a\u0430 \u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u043e \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u044c \u0435\u0451 url, \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044f\u0441\u044c \u043a \u043a\u0430\u043d\u0430\u043b\u0443 \u0434\u043e\u0441\u043a\u0438. \u041a\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0441\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0430\u0432\u0430\u0442\u0430\u0440 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u043e\u043a\u0430\u0437\u0430\u043d \u0431\u0435\u0437 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u0438, \u0432 \u043f\u0440\u043e\u0442\u0438\u0432\u043e\u043f\u043e\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c, \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u043c\u0441\u044f \u043e\u0444\u043b\u0430\u0439\u043d, \u0430\u0432\u0430\u0442\u0430\u0440\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u043e\u043b\u0443\u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u044b\u043c\u0438.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/3e4\/f09\/096\/3e4f090964204d388fdd00f5a2310da2.jpg\"\/>  <\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043f\u043e\u043a\u0438\u0434\u0430\u0435\u0442 url \u0434\u043e\u0441\u043a\u0438, \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0438\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u0434\u0430\u0436\u0435 \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u043e\u043a\u043d\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u043f\u043e\u0432\u0435\u0441\u0442\u0438\u0442\u044c \u043e\u0431 \u044d\u0442\u043e\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u0438 \u0432\u0441\u0435\u0445 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0445 \u043a \u043a\u0430\u043d\u0430\u043b\u0443 \u0434\u043e\u0441\u043a\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u0435\u0433\u043e \u0430\u0432\u0430\u0442\u0430\u0440 \u0441\u043d\u043e\u0432\u0430 \u0441\u0442\u0430\u043b \u043f\u043e\u043b\u0443\u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u044b\u043c, \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u044f, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u043e\u043b\u0435\u0435 \u043d\u0435 \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u0442 \u0434\u043e\u0441\u043a\u0443. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u044d\u0442\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0438\u0447\u044c \u0438 \u0438\u0445 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u0438:<\/p>\n<p>  <\/p>\n<ol>\n<li>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u043e\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0445 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u043d\u0430 front-end \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 Redux. \u041d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c, \u043d\u043e \u043e\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432, \u0443\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u043a \u043a\u0430\u043d\u0430\u043b\u0443 \u0434\u043e\u0441\u043a\u0438. \u041d\u0435\u0434\u0430\u0432\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0438\u0435\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u044d\u0442\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.<\/li>\n<li>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432. \u042d\u0442\u043e \u0442\u043e\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c, \u043d\u043e \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442 \u043d\u0430\u0441 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u0434\u0451\u0440\u0433\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u0441\u043f\u0438\u0441\u043a\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0438 \u0435\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u0440\u0438 \u043b\u044e\u0431\u043e\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u0438\u043b\u0438 \u0432\u044b\u0445\u043e\u0434\u0435 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430, \u043d\u0435 \u0433\u043e\u0432\u043e\u0440\u044f \u0443\u0436\u0435 \u043e \u0441\u043c\u0435\u0448\u0435\u0432\u0430\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u0432\u0435\u0441\u044c\u043c\u0430 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/li>\n<\/ol>\n<p>  <\/p>\n<p>\u0422\u0430\u043a \u0433\u0434\u0435 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u044d\u0442\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0434\u0430\u0442\u044c \u043a \u043d\u0435\u0439 \u0434\u043e\u0441\u0442\u0443\u043f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e? \u041b\u0435\u0433\u043a\u043e. \u0412\u2026 \u043d\u0430\u0431\u0435\u0440\u0438\u0442\u0435\u0441\u044c \u0442\u0435\u0440\u043f\u0435\u043d\u0438\u044f\u2026 \u043f\u0435\u0440\u043c\u0430\u043d\u0435\u043d\u0442\u043d\u043e\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0449\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435.<\/p>\n<p>  <\/p>\n<h3 id=\"principy-genserver\">\u041f\u0440\u0438\u043d\u0446\u0438\u043f\u044b GenServer<\/h3>\n<p>  <\/p>\n<p>\u0425\u043e\u0442\u044f \u0444\u0440\u0430\u0437\u0430 <em>\u043f\u0435\u0440\u043c\u0430\u043d\u0435\u043d\u0442\u043d\u044b\u0439 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0449\u0438\u0439 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441<\/em> \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043d\u0430\u0447\u0430\u043b\u0443 \u0437\u0432\u0443\u0447\u0430\u0442\u044c \u0443\u0441\u0442\u0440\u0430\u0448\u0430\u044e\u0449\u0435, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043f\u0440\u043e\u0449\u0435, \u0447\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043e\u0436\u0438\u0434\u0430\u0442\u044c, \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f <strong>Elixir<\/strong> \u0438 \u0435\u0433\u043e <a href=\"http:\/\/elixir-lang.org\/docs\/stable\/elixir\/GenServer.html\">GenServer<\/a>.<\/p>\n<p>  <\/p>\n<blockquote><p> GenServer \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u043b\u044e\u0431\u043e\u043c\u0443 \u0434\u0440\u0443\u0433\u043e\u043c\u0443 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0443 Elixir, \u0438 \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u0438 \u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0433\u043e.  <\/p><\/blockquote>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0441\u0435\u0431\u0435 \u044d\u0442\u043e \u043a\u0430\u043a \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0439\u0441\u044f \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u0438\u043c\u0435\u044e\u0449\u0438\u0439 \u0430\u0441\u0441\u043e\u0446\u0438\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 (map), \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0434\u043e\u0441\u043a\u0438 \u0441\u043f\u0438\u0441\u043e\u043a id \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0427\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435 \u0442\u0430\u043a\u043e\u0433\u043e:<\/p>\n<p>  <\/p>\n<pre><code>%{   &quot;1&quot; =&gt; [1, 2, 3],   &quot;2&quot; =&gt; [4, 5] }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435, \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0438\u043c\u0435\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0430\u0441\u0441\u043e\u0446\u0438\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u043e\u043a \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0427\u0442\u043e\u0436, \u044d\u0442\u043e, \u0432 \u0446\u0435\u043b\u043e\u043c, \u043f\u0440\u043e\u0446\u0435\u0441\u0441 <strong>GenServer<\/strong>, \u0438 \u044f \u0433\u043e\u0432\u043e\u0440\u044e &quot;\u0432 \u0446\u0435\u043b\u043e\u043c&quot; \u043f\u043e\u0441\u0442\u043e\u043b\u044c\u043a\u0443, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u043d \u0442\u0430\u043a \u0436\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u0432\u0440\u043e\u0434\u0435 \u0442\u0440\u0430\u0441\u0438\u0440\u043e\u0432\u043a\u0438, \u043e\u0442\u0447\u0451\u0442\u0430 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445 \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f (supervision).<\/p>\n<p>  <\/p>\n<h3 id=\"monitor-boardchannel\">\u041c\u043e\u043d\u0438\u0442\u043e\u0440 BoardChannel<\/h3>\n<p>  <\/p>\n<p>\u0418\u0442\u0430\u043a, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0430\u043c\u0443\u044e \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0434\u043e\u0441\u043a\u0438:<\/p>\n<p>  <\/p>\n<pre><code># \/lib\/phoenix_trello\/board_channel\/monitor.ex  defmodule PhoenixTrello.BoardChannel.Monitor do   use GenServer    #####   # Client API    def start_link(initial_state) do    GenServer.start_link(__MODULE__, initial_state, name: __MODULE__)   end end<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u044f \u0441 <strong>GenServer<\/strong> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0440\u043e\u0434\u0443\u043c\u0430\u0442\u044c \u043a\u0430\u043a \u0444\u0443\u043d\u043a\u0446\u0438\u0438 API \u0434\u043b\u044f \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432, \u0442\u0430\u043a \u0438 \u0438\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e. \u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>start_link<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e-\u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u043c\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c GenServer, \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u0432 \u043d\u0435\u0433\u043e \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u2014 \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0443\u0441\u0442\u043e\u0439 \u0430\u0441\u0441\u043e\u0446\u0438\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432, \u2014 \u043c\u0435\u0436\u0434\u0443 \u0438\u043c\u0435\u043d\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044f \u0438 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u041c\u044b \u0445\u043e\u0442\u0438\u043c \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0442\u0430\u043a \u0447\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u0432 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0442\u043e\u043c\u043a\u043e\u0432 \u0432 \u043d\u0430\u0448\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u0435 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f (supervision tree):<\/p>\n<p>  <\/p>\n<pre><code># \/lib\/phoenix_trello.ex  defmodule PhoenixTrello do   use Application    def start(_type, _args) do     import Supervisor.Spec, warn: fals e     children = [       # ...       worker(PhoenixTrello.BoardChannel.Monitor, [%{}]),       # ...     ]      # ...   end end<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>start_link<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043b\u0438, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u0435\u0439 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043f\u0443\u0441\u0442\u043e\u0439 \u0430\u0441\u0441\u043e\u0446\u0438\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 <code>%{}<\/code>. \u0415\u0441\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 <code>Monitor<\/code> \u043f\u0440\u0435\u0440\u0432\u0451\u0442\u0441\u044f \u043f\u043e \u043b\u044e\u0431\u043e\u0439 \u043f\u0440\u0438\u0447\u0438\u043d\u0435, \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442 \u0435\u0433\u043e \u0437\u0430\u043d\u043e\u0432\u043e \u0441 \u043d\u043e\u0432\u044b\u043c \u043f\u0443\u0441\u0442\u044b\u043c \u0430\u0441\u0441\u043e\u0446\u0438\u0430\u0442\u0438\u0432\u043d\u044b\u043c \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u043c. \u0417\u0434\u043e\u0440\u043e\u0432\u043e, \u043d\u0435 \u043f\u0440\u0430\u0432\u0434\u0430 \u043b\u0438? \u0422\u0435\u043f\u0435\u0440\u044c, \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0432 \u0432\u0441\u0451 \u044d\u0442\u043e, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0451\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0432 \u043c\u0430\u0441\u0441\u0438\u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 <code>Monitor<\/code>&#8216;\u0430.<\/p>\n<p>  <\/p>\n<h3 id=\"obrabotka-podklyucheniy-uchastnikov\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432<\/h3>\n<p>  <\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0435\u0439 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0439 \u0441\u0432\u044f\u0437\u0438 (\u0434\u0430\u043b\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u043e callback-\u0444\u0443\u043d\u043a\u0446\u0438\u0438):<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\/lib\/phoenix_trello\/board_channel\/monitor.ex<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code># \/lib\/phoenix_trello\/board_channel\/monitor.ex  defmodule PhoenixTrello.BoardChannel.Monitor do   use GenServer    #####   # Client API    # ...    def member_joined(board, member) do    GenServer.call(__MODULE__, {:member_joined, board, member})   end    #####  # Server callbacks   def handle_call({:member_joined, board, member}, _from, state) do    state = case Map.get(state, board) do      nil -&gt;        state = state        |&gt; Map.put(board, [member])         {:reply, [member], state}      members -&gt;        state = state        |&gt; Map.put(board, Enum.uniq([member | members]))         {:reply, Map.get(state, board), state}    end  end end<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u041f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>member_joined\/2<\/code>, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u0435\u0439 \u0434\u043e\u0441\u043a\u0443 \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u043e\u0432\u0435\u0440\u0448\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0443 <strong>GenServer<\/strong> \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c <code>{:member_joined, board, member}<\/code>. \u041f\u043e \u044d\u0442\u043e\u0439 \u043f\u0440\u0438\u0447\u0438\u043d\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a callback-\u0444\u0443\u043d\u043a\u0446\u0438\u0438. Callback-\u0444\u0443\u043d\u043a\u0446\u0438\u044f <a href=\"http:\/\/elixir-lang.org\/docs\/stable\/elixir\/GenServer.html#c:handle_call\/3\"><code>handle_call\/3<\/code><\/a> \u0438\u0437 <code>GenServer<\/code> \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435-\u0437\u0430\u043f\u0440\u043e\u0441, \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435. \u0422\u0430\u043a \u0447\u0442\u043e \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u043a\u0443 \u0438\u0437 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0435\u0451 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u043a\u0438 \u0435\u0449\u0451 \u043d\u0435\u0442, \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0451 \u0441 \u043d\u043e\u0432\u044b\u043c \u0441\u043f\u0438\u0441\u043a\u043e\u043c, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432\u0448\u0435\u0433\u043e\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0442\u0432\u0435\u0442\u0430 \u043c\u044b \u0432\u0435\u0440\u043d\u0451\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043f\u0440\u0438\u043d\u0430\u0434\u043b\u0435\u0436\u0430\u0449\u0438\u0439 \u044d\u0442\u043e\u0439 \u0434\u043e\u0441\u043a\u0435.<\/p>\n<p>  <\/p>\n<p>\u041e\u0442\u043a\u0443\u0434\u0430 \u0441\u0442\u043e\u0438\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <code>member_joined<\/code>? \u0418\u0437 <strong>BoardChannel<\/strong> \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\/web\/channels\/board_channel.ex<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code># \/web\/channels\/board_channel.ex  defmodule PhoenixTrello.BoardChannel do   use PhoenixTrello.Web, :channel    alias PhoenixTrello.{User, Board, UserBoard, List, Card, Comment, CardMember}   alias PhoenixTrello.BoardChannel.Monitor    def join(&quot;boards:&quot; &lt;&gt; board_id, _params, socket) do     current_user = socket.assigns.current_user     board = get_current_board(socket, board_id)      connected_users = Monitor.user_joined(board_id, current_user.id)      send(self, {:after_join, connected_users})      {:ok, %{board: board}, assign(socket, :board, board)}   end    def handle_info({:after_join, connected_users}, socket) do     broadcast! socket, &quot;user:joined&quot;, %{users: connected_users}      {:noreply, socket}   end    # ... end<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u043e\u043d \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f, \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>Monitor<\/code> \u0434\u043b\u044f \u0435\u0433\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f, \u0438 \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0435\u043c \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u043a\u0435\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043e\u0441\u043a\u0438. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u044d\u0442\u0443 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442-\u044d\u043d\u0434\u0435, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u044b\u043c \u0441\u043f\u0438\u0441\u043a\u043e\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439:<\/p>\n<p>  <\/p>\n<pre><code>\/\/ \/web\/static\/js\/actions\/current_board.js  import Constants  from '..\/constants';  const Actions = {    \/\/ ...   connectToChannel: (socket, boardId) =&gt; {     return dispatch =&gt; {       const channel = socket.channel(`boards:${boardId}`);       \/\/ ...        channel.on('user:joined', (msg) =&gt; {         dispatch({           type: Constants.CURRENT_BOARD_CONNECTED_USERS,           users: msg.users,         });       });     };   } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435, \u0447\u0442\u043e \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u2014 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0430\u0432\u0430\u0442\u0430\u0440\u0430 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0442\u043e\u0433\u043e, \u0443\u043a\u0430\u0437\u0430\u043d \u043b\u0438 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u0434\u043e\u0441\u043a\u0438 \u0432 \u044d\u0442\u043e\u043c \u0441\u043f\u0438\u0441\u043a\u0435 \u0438\u043b\u0438 \u043d\u0435\u0442:<\/p>\n<p>  <\/p>\n<pre><code>\/\/ \/web\/static\/js\/components\/boards\/users.js  export default class BoardUsers extends React.Component {   _renderUsers() {     return this.props.users.map((user) =&gt; {       const index = this.props.connectedUsers.findIndex((cu) =&gt; {         return cu.id === user.id;       });        const classes = classnames({ connected: index != -1 });        return (         &lt;li className={classes} key={user.id}&gt;           &lt;ReactGravatar className=&quot;react-gravatar&quot; email={user.email} https\/&gt;         &lt;\/li&gt;       );     });   }    \/\/ ... }<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"obrabotka-otklyucheniy-polzovateley\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/h3>\n<p>  <\/p>\n<p>\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e\u0442 \u043a\u0430\u043d\u0430\u043b\u0430 \u0434\u043e\u0441\u043a\u0438 \u043f\u043e\u0447\u0442\u0438 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435. \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u043c <code>Monitor<\/code>, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0443\u044e \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0435\u0439 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u0443\u044e callback-\u0444\u0443\u043d\u043a\u0446\u0438\u044e:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\/lib\/phoenix_trello\/board_channel\/monitor.ex<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code># \/lib\/phoenix_trello\/board_channel\/monitor.ex  defmodule PhoenixTrello.BoardChannel.Monitor do   use GenServer    #####   # Client API    # ...    def member_left(board, member) do     GenServer.call(__MODULE__, {:member_left, board, member})   end    #####   # Server callbacks    # ...    def handle_call({:member_left, board, member}, _from, state) do     new_members = state       |&gt; Map.get(board)       |&gt; List.delete(member)      state = state       |&gt; Map.update!(board, fn(_) -&gt; new_members end)      {:reply, new_members, state}   end end<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u041a\u0430\u043a \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u044d\u0442\u043e \u043f\u043e\u0447\u0442\u0438 \u0442\u0430 \u0436\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c, \u0447\u0442\u043e \u0438 \u0443 <code>member_join<\/code>, \u043d\u043e \u0440\u0430\u0437\u0432\u0451\u0440\u043d\u0443\u0442\u0430\u044f \u0432 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435. \u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u043e\u0438\u0441\u043a \u0434\u043e\u0441\u043a\u0438 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430, \u0430 \u0437\u0430\u0442\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0434\u043e\u0441\u043a\u0438 \u043d\u043e\u0432\u044b\u043c \u0438 \u0435\u0433\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0442 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435. \u0422\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0438\u0437 <strong>BoardChannel<\/strong>, \u0442\u0430\u043a \u0447\u0442\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0435\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u043c:<\/p>\n<p>  <\/p>\n<pre><code># \/web\/channels\/board_channel.ex  defmodule PhoenixTrello.BoardChannel do   use PhoenixTrello.Web, :channel   # ...    def terminate(_reason, socket) do     board_id = Board.slug_id(socket.assigns.board)     user_id = socket.assigns.current_user.id      broadcast! socket, &quot;user:left&quot;, %{users: Monitor.user_left(board_id, user_id)}      :ok   end end<\/code><\/pre>\n<p>  <\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u043a\u0430\u043d\u0430\u043b\u0443 \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f, \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0440\u0430\u0437\u043e\u0448\u043b\u0451\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0447\u0435\u0440\u0435\u0437 \u0441\u043e\u043a\u0435\u0442, \u043a\u0430\u043a \u043c\u044b \u0434\u0435\u043b\u0430\u043b\u0438 \u0438 \u0434\u043e \u044d\u0442\u043e\u0433\u043e. \u0414\u043b\u044f \u043f\u0440\u0435\u0440\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u043a\u0430\u043d\u0430\u043b\u0443 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f (action creator), \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043f\u0440\u0438 \u043e\u0442\u043c\u043e\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u043e\u0441\u043a\u0438; \u0442\u0430\u043a \u0436\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0434\u043b\u044f \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0438 <code>user:left<\/code>:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\/web\/static\/js\/actions\/current_board.js<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>\/\/ \/web\/static\/js\/actions\/current_board.js  import Constants  from '..\/constants';  const Actions = {    \/\/ ...    connectToChannel: (socket, boardId) =&gt; {     return dispatch =&gt; {       const channel = socket.channel(`boards:${boardId}`);       \/\/ ...        channel.on('user:left', (msg) =&gt; {         dispatch({           type: Constants.CURRENT_BOARD_CONNECTED_USERS,           users: msg.users,         });       });     };   },    leaveChannel: (channel) =&gt; {     return dispatch =&gt; {       channel.leave();     };   }, }<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>BoardShowView<\/code>, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0435\u0433\u043e \u043e\u0442\u043c\u043e\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f <code>leaveChannel<\/code>:<\/p>\n<p>  <\/p>\n<pre><code>\/\/ \/web\/static\/js\/views\/boards\/show.js  import Actions              from '..\/..\/actions\/current_board'; \/\/ ...  class BoardsShowView extends React.Component {   \/\/ ...    componentWillUnmount() {     const { dispatch,  currentBoard} = this.props;      dispatch(Actions.leaveChannel(currentBoard.channel));   }  }  \/\/ ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u043d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451! \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0432\u0448\u0435\u0435\u0441\u044f, \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u043a\u0440\u043e\u0439\u0442\u0435 \u0434\u0432\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u0438 \u0432\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u0434 \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438. \u0417\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 \u043e\u0434\u043d\u0443 \u0438 \u0442\u0443 \u0436\u0435 \u0434\u043e\u0441\u043a\u0443 \u0432 \u043e\u0431\u043e\u0438\u0445 \u0438 \u043f\u043e\u0438\u0433\u0440\u0430\u0439\u0442\u0435\u0441\u044c, \u0432\u0445\u043e\u0434\u044f \u0438 \u0432\u044b\u0445\u043e\u0434\u044f \u0438\u0437 \u0434\u043e\u0441\u043a\u0438 \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0412\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435, \u043a\u0430\u043a \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0435\u0433\u043e \u0430\u0432\u0430\u0442\u0430\u0440\u0430 \u0431\u0443\u0434\u0435\u0442 \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u0442\u0443\u0434\u0430 \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e, \u0447\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043a\u043b\u0451\u0432\u043e.<\/p>\n<p>  <\/p>\n<p>\u042f \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u0432\u044b \u043d\u0430\u0441\u043b\u0430\u0434\u0438\u043b\u0438\u0441\u044c \u0440\u0430\u0431\u043e\u0442\u043e\u0439 \u0441 <strong>GenServer<\/strong> \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u044f \u0432 \u043f\u0435\u0440\u0432\u044b\u0439 \u0440\u0430\u0437. \u041d\u043e \u043c\u044b \u0437\u0430\u0442\u0440\u043e\u043d\u0443\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u0430\u043b\u0443\u044e \u0447\u0430\u0441\u0442\u044c. <strong>GenServer<\/strong> \u0438 <strong>Supervisor<\/strong> \u2014 \u043e\u0447\u0435\u043d\u044c \u0431\u043e\u0433\u0430\u0442\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0438\u0437 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u043c\u044b\u0445 <strong>Elixir<\/strong>, \u043f\u0440\u0438\u0447\u0451\u043c \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0438 \u043f\u0443\u043b\u0435\u043d\u0435\u043f\u0440\u043e\u0431\u0438\u0432\u0430\u0435\u043c\u044b\u0435 <em>(\u0432 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u0435 \u0430\u0432\u0442\u043e\u0440 \u0443\u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u0442 \u0442\u0435\u0440\u043c\u0438\u043d bullet proof, \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u044f, \u0432\u0438\u0434\u0438\u043c\u043e, \u0438\u043c\u0435\u044e\u0449\u0443\u044e\u0441\u044f \u0432 Erlang\/Elixir \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044e \u0446\u0438\u043a\u043b\u0430 \u0436\u0438\u0437\u043d\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432 \u0438 \u0438\u0445 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0443 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u2014 \u043f\u0440\u0438\u043c. \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\u0430)<\/em>, \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0435 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439 \u2014 \u0432 \u043f\u0440\u043e\u0442\u0438\u0432\u043e\u043f\u043e\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <strong>Redis<\/strong>. \u0412 \u0441\u043b\u0435\u0449\u0443\u044e\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0441\u043e\u043a\u0435\u0442\u043e\u0432 \u0438 \u043a\u0430\u043d\u0430\u043b\u043e\u0432.<\/p>\n<p>  <a name=\"11\"><\/a>  <\/p>\n<h2 id=\"dobavlyaem-spiski-i-kartochki\">\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043f\u0438\u0441\u043a\u0438 \u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/blog.diacode.com\/trello-clone-with-phoenix-and-react-pt-11\">\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b<\/a><\/p>\n<p>  <\/p>\n<p>\u0412 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0439, \u043d\u043e \u0443\u0436\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0445 \u043a \u043a\u0430\u043d\u0430\u043b\u0443 \u0434\u043e\u0441\u043a\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <strong>OTP<\/strong> \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 <strong>GenServer<\/strong>. \u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u043d\u0430\u0443\u0447\u0438\u043b\u0438\u0441\u044c \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0447\u0435\u0440\u0435\u0437 \u043a\u0430\u043d\u0430\u043b, \u0442\u0430\u043a \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u0441\u043c\u043e\u0436\u0435\u0442 \u0432\u0438\u0434\u0435\u0442\u044c, \u043a\u0442\u043e \u0435\u0449\u0451 \u043f\u0440\u043e\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u0442 \u0434\u043e\u0441\u043a\u0443 \u0432 \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0432\u0440\u0435\u043c\u044f. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0438 \u0441\u043f\u0438\u0441\u043a\u043e\u0432, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u044f\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043d\u0430 \u0438\u0445 \u044d\u043a\u0440\u0430\u043d\u0430\u0445 \u043d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u2026 \u0421\u0434\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e!<\/p>\n<p>  <\/p>\n<h3 id=\"migracii-i-modeli\">\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0438 \u043c\u043e\u0434\u0435\u043b\u0438<\/h3>\n<p>  <\/p>\n<p>\u0414\u043e\u0441\u043a\u0430 (<code>Board<\/code>) \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u0432 (<strong>lists<\/strong>), \u043a\u043e\u0442\u043e\u0440\u044b\u0435, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a, \u0442\u0430\u043a \u0447\u0442\u043e \u0434\u0435\u0440\u0436\u0430 \u044d\u0442\u043e \u0432 \u0433\u043e\u043b\u043e\u0432\u0435 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0451\u043c \u0441 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0434\u0435\u043b\u0438 <code>List<\/code>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 <code>mix<\/code>:<\/p>\n<p>  <\/p>\n<pre><code>$ mix phoenix.gen.model List lists board_id:references:board name:string ... ... $ mix ecto.migrate<\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u0442\u0438\u043c \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0442\u0430\u0431\u043b\u0438\u0446\u0443 <code>lists<\/code> \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c:<\/p>\n<p>  <\/p>\n<pre><code># web\/models\/list.ex  defmodule PhoenixTrello.List do   use PhoenixTrello.Web, :model    alias PhoenixTrello.{Board, List}    @derive {Poison.Encoder, only: [:id, :board_id, :name]}    schema &quot;lists&quot; do     field :name, :string     belongs_to :board, Board      timestamps   end    @required_fields ~w(name)   @optional_fields ~w()    def changeset(model, params \\\\ :empty) do     model     |&gt; cast(params, @required_fields, @optional_fields)   end end<\/code><\/pre>\n<p>  <\/p>\n<p>\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043c\u043e\u0434\u0435\u043b\u0438 <code>Card<\/code> \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0435:<\/p>\n<p>  <\/p>\n<pre><code>$ mix phoenix.gen.model Card cards list_id:references:lists name:string ... ... $ mix ecto.migrate<\/code><\/pre>\n<p>  <\/p>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043a\u0430\u043a-\u0442\u043e \u0442\u0430\u043a:<\/p>\n<p>  <\/p>\n<pre><code># web\/models\/card.ex  defmodule PhoenixTrello.Card do   use PhoenixTrello.Web, :model    alias PhoenixTrello.{Repo, List, Card}    @derive {Poison.Encoder, only: [:id, :list_id, :name]}    schema &quot;cards&quot; do     field :name, :string     belongs_to :list, List      timestamps   end    @required_fields ~w(name list_id)   @optional_fields ~w()    def changeset(model, params \\\\ :empty) do     model     |&gt; cast(params, @required_fields, @optional_fields)   end end<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0430\u0431\u043e\u0440 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u043a \u0441\u0445\u0435\u043c\u0435 <code>lists<\/code>:<\/p>\n<p>  <\/p>\n<pre><code># web\/models\/list.ex  defmodule PhoenixTrello.List do   # ...    @derive {Poison.Encoder, only: [:id, :board_id, :name, :cards]}    # ...    schema &quot;lists&quot; do     # ..      has_many :cards, Card   end    # ... end<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u0432\u0438\u043d\u0443\u0442\u044c\u0441\u044f \u0432\u043f\u0435\u0440\u0451\u0434, \u043a \u0444\u0440\u043e\u043d\u0442\u044d\u043d\u0434\u0443, \u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b.<\/p>\n<p>  <\/p>\n<h3 id=\"komponent-formy-spiska\">\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0444\u043e\u0440\u043c\u044b \u0441\u043f\u0438\u0441\u043a\u0430<\/h3>\n<p>  <\/p>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435, \u0447\u0435\u043c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c, \u0432\u0441\u043f\u043e\u043c\u043d\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>render<\/code> \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 <code>BoardsShowView<\/code>:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">web\/static\/js\/views\/boards\/show.js<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>\/\/ web\/static\/js\/views\/boards\/show.js  \/\/... \/\/... _renderLists() {   const { lists, channel, id, addingNewCardInListId } = this.props.currentBoard;    return lists.map((list) =&gt; {     return (       &lt;ListCard         key={list.id}         boardId={id}         dispatch={this.props.dispatch}         channel={channel}         isAddingNewCard={addingNewCardInListId === list.id}         {...list} \/&gt;     );   }); }  render() {     const { fetching, name } = this.props.currentBoard;      if (fetching) return (       &lt;div className=&quot;view-container boards show&quot;&gt;         &lt;i className=&quot;fa fa-spinner fa-spin&quot;\/&gt;       &lt;\/div&gt;     );      return (       &lt;div className=&quot;view-container boards show&quot;&gt;         &lt;header className=&quot;view-header&quot;&gt;           &lt;h3&gt;{name}&lt;\/h3&gt;           {::this._renderMembers()}         &lt;\/header&gt;         &lt;div className=&quot;canvas-wrapper&quot;&gt;           &lt;div className=&quot;canvas&quot;&gt;             &lt;div className=&quot;lists-wrapper&quot;&gt;               {::this._renderLists()}               {::this._renderAddNewList()}             &lt;\/div&gt;           &lt;\/div&gt;         &lt;\/div&gt;         {this.props.children}       &lt;\/div&gt;     );   }<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 <code>BoardMembers<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u043c, \u043d\u0430\u043c \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0441\u043f\u0438\u0441\u043a\u0438, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0435\u0441\u044f \u043a \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u043e\u0441\u043a\u0435. \u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0443 \u043d\u0430\u0441 \u043d\u0435\u0442 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0441\u043f\u0438\u0441\u043a\u043e\u0432, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>_renderAddNewList<\/code>:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">web\/static\/js\/views\/boards\/show.js<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>\/\/ web\/static\/js\/views\/boards\/show.js  \/\/ ...    _renderAddNewList() {     const { dispatch, formErrors, currentBoard } = this.props;      if (!currentBoard.showForm) return this._renderAddButton();      return (       &lt;ListForm         dispatch={dispatch}         errors={formErrors}         channel={currentBoard.channel}         onCancelClick={::this._handleCancelClick} \/&gt;     );   }    _renderAddButton() {     return (       &lt;div className=&quot;list add-new&quot; onClick={::this._handleAddNewClick}&gt;         &lt;div className=&quot;inner&quot;&gt;           Add new list...         &lt;\/div&gt;       &lt;\/div&gt;     );   }    _handleAddNewClick() {     const { dispatch } = this.props;      dispatch(Actions.showForm(true));   }    _handleCancelClick() {     this.props.dispatch(Actions.showForm(false));   }  \/\/ ...<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>_renderAddNewList<\/code> \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u043b\u0438 \u0432 <code>true<\/code> \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>currentBoard.showForm<\/code>, \u0442\u0430\u043a \u0447\u0442\u043e \u043e\u043d\u0430 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u0435\u0442 \u043a\u043d\u043e\u043f\u043a\u0443 <em>\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a&#8230;<\/em> \u0432\u043c\u0435\u0441\u0442\u043e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 <code>ListForm<\/code>.<\/p>\n<p>  <\/p>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u043c\u0451\u0442 \u043a\u043d\u043e\u043f\u043a\u0443, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 (action) \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043e \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>showForm<\/code> \u0432 <code>true<\/code>, \u0447\u0442\u043e \u0432\u044b\u0437\u043e\u0432\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u044b. \u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0444\u043e\u0440\u043c\u044b:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">web\/static\/js\/components\/lists\/form.js<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>\/\/ web\/static\/js\/components\/lists\/form.js  import React, { PropTypes } from 'react'; import Actions              from '..\/..\/actions\/lists';  export default class ListForm extends React.Component {   componentDidMount() {     this.refs.name.focus();   }    _handleSubmit(e) {     e.preventDefault();      const { dispatch, channel } = this.props;     const { name } = this.refs;      const data = {       name: name.value,     };      dispatch(Actions.save(channel, data));   }    _handleCancelClick(e) {     e.preventDefault();      this.props.onCancelClick();   }    render() {     return (       &lt;div className=&quot;list form&quot;&gt;         &lt;div className=&quot;inner&quot;&gt;           &lt;form id=&quot;new_list_form&quot; onSubmit={::this._handleSubmit}&gt;             &lt;input ref=&quot;name&quot; id=&quot;list_name&quot; type=&quot;text&quot; placeholder=&quot;Add a new list...&quot; required=&quot;true&quot;\/&gt;             &lt;button type=&quot;submit&quot;&gt;Save list&lt;\/button&gt; or &lt;a href=&quot;#&quot; onClick={::this._handleCancelClick}&gt;cancel&lt;\/a&gt;           &lt;\/form&gt;         &lt;\/div&gt;       &lt;\/div&gt;     );   } }<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/84b\/6dd\/5b4\/84b6dd5b4c9b4da18d0a31dd033ce6f1.jpg\"\/>  <\/p>\n<p>\u042d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0441 \u0444\u043e\u0440\u043c\u043e\u0439, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0435\u0439 \u0442\u0435\u043a\u0441\u0442\u043e\u0435 \u043f\u043e\u043b\u0435 \u0434\u043b\u044f \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430, \u043a\u043d\u043e\u043f\u043a\u043e\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0438 \u0441\u0441\u044b\u043b\u043a\u043e\u0439 \u043d\u0430 \u043e\u0442\u043c\u0435\u043d\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0442\u043e \u0436\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u043e\u043f\u0438\u0441\u0430\u043b\u0438, \u043d\u043e \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u044f <code>showForm<\/code> \u0432 <code>false<\/code>, \u0447\u0442\u043e\u0431\u044b \u0441\u043f\u0440\u044f\u0442\u0430\u0442\u044c \u0444\u043e\u0440\u043c\u0443. \u041a\u043e\u0433\u0434\u0430 \u0444\u043e\u0440\u043c\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430, \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f <code>save<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442 \u0438\u043c\u044f \u043d\u0430 \u0442\u0435\u043c\u0443 <code>lists:create<\/code> \u043a\u0430\u043d\u0430\u043b\u0430 <code>BoardChannel<\/code>:<\/p>\n<p>  <\/p>\n<pre><code>\/\/ web\/static\/js\/actions\/lists.js  import Constants from '..\/constants';  const Actions = {   save: (channel, data) =&gt; {     return dispatch =&gt; {       channel.push('lists:create', { list: data });     };   }, };  export default Actions;<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"boardchannel\">BoardChannel<\/h3>\n<p>  <\/p>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0448\u0430\u0433\u043e\u043c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0443\u0447\u0438\u0442\u044c <code>BoardChannel<\/code> \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 <code>lists:create<\/code>, \u0442\u0430\u043a \u0447\u0442\u043e \u0437\u0430\u0439\u043c\u0451\u043c\u0441\u044f \u044d\u0442\u0438\u043c:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">web\/channels\/board_channel.ex<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code># web\/channels\/board_channel.ex  defmodule PhoenixTrello.BoardChannel do   # ...    def handle_in(&quot;lists:create&quot;, %{&quot;list&quot; =&gt; list_params}, socket) do     board = socket.assigns.board      changeset = board       |&gt; build_assoc(:lists)       |&gt; List.changeset(list_params)      case Repo.insert(changeset) do       {:ok, list} -&gt;         list = Repo.preload(list, [:cards])          broadcast! socket, &quot;list:created&quot;, %{list: list}          {:noreply, socket}       {:error, _changeset} -&gt;         {:reply, {:error, %{error: &quot;Error creating list&quot;}}, socket}     end   end    # ... end<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0434\u043e\u0441\u043a\u0443, \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u043b\u0451\u043d\u043d\u0443\u044e \u043a \u043a\u0430\u043d\u0430\u043b\u0443, \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u044b\u0441\u0442\u0440\u043e\u0438\u0442 \u043d\u0430\u0431\u043e\u0440 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 (changeset) \u043c\u043e\u0434\u0435\u043b\u0438 <code>List<\/code> \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 (<code>list_params<\/code>) \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442 \u0435\u0433\u043e \u0432 \u0431\u0430\u0437\u0443. \u0415\u0441\u043b\u0438 \u0432\u0441\u0451 \u0431\u0443\u0434\u0435\u0442 <code>:ok<\/code>, \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0434\u0435\u043d\u0430 \u0440\u0430\u0441\u0441\u044b\u043b\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 \u043a\u0430\u043d\u0430\u043b <strong>\u0432\u0441\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c<\/strong>, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044f, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0447\u0442\u043e-\u0442\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c, \u0438 \u043c\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e <code>:noreply<\/code>. \u0415\u0441\u043b\u0438 \u0436\u0435 \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u0447\u0443\u0434\u043e\u043c \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0430, \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043e <strong>\u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044e<\/strong>, \u0442\u0430\u043a \u0447\u0442\u043e \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u0437\u043d\u0430\u0442\u044c, \u0447\u0442\u043e \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0448\u043b\u043e \u043d\u0435 \u0442\u0430\u043a.<\/p>\n<p>  <\/p>\n<h3 id=\"preobrazovatel\">\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c<\/h3>\n<p>  <\/p>\n<p>\u041c\u044b \u043f\u043e\u0447\u0442\u0438 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0438 \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u0430\u043c\u0438. \u041a\u0430\u043d\u0430\u043b \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043b\u0438\u0441\u0442, \u0442\u0430\u043a \u0447\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u044d\u0442\u043e\u0433\u043e \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u044d\u043d\u0434 \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0434\u043e\u0441\u043a\u0438, \u0433\u0434\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u043b\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a \u043a\u0430\u043d\u0430\u043b\u0443:<\/p>\n<p>  <\/p>\n<pre><code>\/\/ web\/static\/js\/actions\/current_board.js  import Constants  from '..\/constants';  const Actions = {   \/\/ ...    connectToChannel: (socket, boardId) =&gt; {     return dispatch =&gt; {       const channel = socket.channel(`boards:${boardId}`);       \/\/ ...        channel.on('list:created', (msg) =&gt; {         dispatch({           type: Constants.CURRENT_BOARD_LIST_CREATED,           list: msg.list,         });       });     };   },   \/\/ ... }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c (reducer) \u0434\u043e\u0441\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u043a \u043d\u043e\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043e\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442:<\/p>\n<p>  <\/p>\n<pre><code>\/\/ web\/static\/js\/reducers\/current_board.js  import Constants  from '..\/constants';  export default function reducer(state = initialState, action = {}) {    switch (action.type) {     \/\/...      case Constants.CURRENT_BOARD_LIST_CREATED:       const lists = [...state.lists];        lists.push(action.list);        return { ...state, lists: lists, showForm: false };      \/\/ ...   } }<\/code><\/pre>\n<p>  <\/p>\n<p>\u041d\u0430\u043c \u0442\u0430\u043a \u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0430\u0442\u0442\u0440\u0438\u0431\u0443\u0442 <code>showForm<\/code> \u0432 <code>false<\/code>, \u0447\u0442\u043e\u0431\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043a\u0440\u044b\u0442\u044c \u0444\u043e\u0440\u043c\u0443 \u0438 \u0432\u043d\u043e\u0432\u044c \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443 <em>\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a&#8230;<\/em> \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u043c \u0441\u043f\u0438\u0441\u043a\u043e\u043c:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/947\/041\/bbf\/947041bbf48b466c98c2329e3111ac03.jpg\"\/>  <\/p>\n<h3 id=\"komponent-list\">\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>List<\/code><\/h3>\n<p>  <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430 \u0434\u043e\u0441\u043a\u0435 \u0435\u0441\u0442\u044c \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u043e\u0434\u0438\u043d \u0441\u043f\u0438\u0441\u043e\u043a, \u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>List<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0438:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\/web\/static\/js\/components\/lists\/card.js<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>\/\/ \/web\/static\/js\/components\/lists\/card.js  import React, {PropTypes}       from 'react'; import Actions                  from '..\/..\/actions\/current_board'; import CardForm                 from '..\/..\/components\/cards\/form'; import Card                     from '..\/..\/components\/cards\/card';  export default class ListCard extends React.Component {   \/\/ ...    _renderForm() {     const { isAddingNewCard } = this.props;     if (!isAddingNewCard) return false;      let { id, dispatch, formErrors, channel } = this.props;      return (       &lt;CardForm         listId={id}         dispatch={dispatch}         errors={formErrors}         channel={channel}         onCancelClick={::this._hideCardForm}         onSubmit={::this._hideCardForm}\/&gt;     );   }    _renderAddNewCard() {     const { isAddingNewCard } = this.props;     if (isAddingNewCard) return false;      return (       &lt;a className=&quot;add-new&quot; href=&quot;#&quot; onClick={::this._handleAddClick}&gt;Add a new card...&lt;\/a&gt;     );   }    _handleAddClick(e) {     e.preventDefault();      const { dispatch, id } = this.props;      dispatch(Actions.showCardForm(id));   }    _hideCardForm() {     const { dispatch } = this.props;      dispatch(Actions.showCardForm(null));   }    render() {     const { id, connectDragSource, connectDropTarget, connectCardDropTarget, isDragging } = this.props;      const styles = {       display: isDragging ? 'none' : 'block',     };      return (       &lt;div id={`list_${id}`} className=&quot;list&quot; style={styles}&gt;         &lt;div className=&quot;inner&quot;&gt;           &lt;header&gt;             &lt;h4&gt;{this.props.name}&lt;\/h4&gt;           &lt;\/header&gt;           &lt;div className=&quot;cards-wrapper&quot;&gt;             {::this._renderCards()}           &lt;\/div&gt;           &lt;footer&gt;             {::this._renderForm()}             {::this._renderAddNewCard()}           &lt;\/footer&gt;         &lt;\/div&gt;       &lt;\/div&gt;     );   } }<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0422\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u043f\u0438\u0441\u043a\u043e\u0432, \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u043c\u0441\u044f \u043d\u0430 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0435 \u0444\u043e\u0440\u043c\u044b \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a. \u0412 \u0446\u0435\u043b\u043e\u043c \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0442\u0435\u043c \u0436\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c \u043a \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0435 \u0438\u043b\u0438 \u0441\u043a\u0440\u044b\u0442\u0438\u044e \u0444\u043e\u0440\u043c\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e (<code>prop<\/code>), \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u043e\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u043c \u0434\u043e\u0441\u043a\u0438, \u0438 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/88a\/f14\/b5b\/88af14b5bcdc476c8412ff424541c24c.jpg\"\/>  <\/p>\n<h3 id=\"komponent-formy-kartochki\">\u041a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0444\u043e\u0440\u043c\u044b \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438<\/h3>\n<p>  <\/p>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>ListForm<\/code>:<\/p>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\/web\/static\/js\/components\/cards\/form.js<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>\/\/ \/web\/static\/js\/components\/cards\/form.js  import React, { PropTypes } from 'react'; import Actions              from '..\/..\/actions\/lists'; import PageClick            from 'react-page-click';  export default class CardForm extends React.Component {   _handleSubmit(e) {     e.preventDefault();      let { dispatch, channel } = this.props;     let { name }              = this.refs;      let data = {       list_id: this.props.listId,       name: name.value,     };      dispatch(Actions.createCard(channel, data));     this.props.onSubmit();   }    componentDidMount() {     this.refs.name.focus();   }    _handleCancelClick(e) {     e.preventDefault();      this.props.onCancelClick();   }    render() {     return (       &lt;PageClick onClick={::this._handleCancelClick}&gt;         &lt;div className=&quot;card form&quot;&gt;           &lt;form id=&quot;new_card_form&quot; onSubmit={::this._handleSubmit}&gt;             &lt;textarea ref=&quot;name&quot; id=&quot;card_name&quot; type=&quot;text&quot; required=&quot;true&quot; rows={5}\/&gt;             &lt;button type=&quot;submit&quot;&gt;Add&lt;\/button&gt; or &lt;a href=&quot;#&quot; onClick={::this._handleCancelClick}&gt;cancel&lt;\/a&gt;           &lt;\/form&gt;         &lt;\/div&gt;       &lt;\/PageClick&gt;     );   } }<\/code><\/pre>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p>\u0422\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u0440\u0430\u043d\u0435\u0435, \u043f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 \u0444\u043e\u0440\u043c\u044b \u043c\u044b \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u0441 \u0438\u043c\u0435\u043d\u0435\u043c, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442 \u043d\u043e\u0432\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 \u043a\u0430\u043d\u0430\u043b:<\/p>\n<p>  <\/p>\n<pre><code>\/\/ \/web\/static\/js\/actions\/lists.js  import Constants from '..\/constants';  const Actions = {   \/\/ ...    createCard: (channel, data) =&gt; {     return dispatch =&gt; {       channel.push('cards:create', { card: data });     };   }, };  \/\/ ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a <code>BoardChannel<\/code>:<\/p>\n<p>  <\/p>\n<pre><code>  # web\/channels\/board_channel.ex    def handle_in(&quot;cards:create&quot;, %{&quot;card&quot; =&gt; card_params}, socket) do     board = socket.assigns.board     changeset = board       |&gt; assoc(:lists)       |&gt; Repo.get!(card_params[&quot;list_id&quot;])       |&gt; build_assoc(:cards)       |&gt; Card.changeset(card_params)      case Repo.insert(changeset) do       {:ok, card} -&gt;         broadcast! socket, &quot;card:created&quot;, %{card: card}          {:noreply, socket}       {:error, _changeset} -&gt;         {:reply, {:error, %{error: &quot;Error creating card&quot;}}, socket}     end   end<\/code><\/pre>\n<p>  <\/p>\n<p>\u0422\u0435\u043c \u0436\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c, \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u044f \u0441\u043f\u0438\u0441\u043e\u043a, \u043d\u043e\u0432\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c <code>Card<\/code> \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0430 \u0430\u0441\u0441\u043e\u0446\u0438\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441 \u0434\u043e\u0441\u043a\u043e\u0439, \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u043b\u0451\u043d\u043d\u043e\u0439 \u043a \u043a\u0430\u043d\u0430\u043b\u0443, \u0438 \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u043c, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u043c \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430. \u0415\u0441\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e, \u0437\u0430\u043f\u0438\u0441\u044c \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u0432\u0441\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u043c \u043a \u043a\u0430\u043d\u0430\u043b\u0443 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430\u043c. \u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c callback-\u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043a <strong>js<\/strong>-\u043a\u0430\u043d\u0430\u043b\u0443:<\/p>\n<p>  <\/p>\n<pre><code>\/\/ web\/static\/js\/actions\/current_board.js   \/\/...    channel.on('card:created', (msg) =&gt; {     dispatch({       type: Constants.CURRENT_BOARD_CARD_CREATED,       card: msg.card,     });   });    \/\/ ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u0443\u044e \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0443 \u043a \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c:<\/p>\n<p>  <\/p>\n<pre><code>  \/\/ web\/static\/js\/reducers\/current_board.js    \/\/ ...    case Constants.CURRENT_BOARD_CARD_CREATED:     lists = [...state.lists];     const { card } = action;      const listIndex = lists.findIndex((list) =&gt; { return list.id == card.list_id; });     lists[listIndex].cards.push(card);      return { ...state, lists: lists };    \/\/ ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u044d\u0442\u043e \u0432\u0441\u0451! \u041a\u0430\u0440\u0442\u043e\u0447\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u044f\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0430.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/4b3\/195\/7cb\/4b31957cb2bd4396b6d139a8c76e01ed.jpg\"\/>  <\/p>\n<h3 id=\"chto-teper\">\u0427\u0442\u043e \u0442\u0435\u043f\u0435\u0440\u044c?<\/h3>\n<p>  <\/p>\n<p>\u042d\u0442\u0438\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u043e\u043c \u043c\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u043e\u0439 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u043e\u0441\u043e\u043a, \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044f \u043d\u0430 \u043d\u0438\u0445 \u0434\u0440\u0443\u0433\u0438\u0445 \u043b\u044e\u0434\u0435\u0439 \u0438 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043f\u0443\u0442\u0451\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a. \u041e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0438\u043c\u0435\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439, \u0442\u0430\u043a\u0438\u0445, \u043a\u0430\u043a \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u043e\u0432, \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0438\u0445 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435\u043c, \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0430\u0445, \u0433\u0434\u0435 \u0432\u044b \u0442\u0430\u043a \u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0438\u0442\u044c \u0438\u043c \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0438 \u0434\u0430\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u0438 \u0446\u0432\u0435\u0442\u043d\u044b\u0435 \u043c\u0435\u0442\u043a\u0438, \u043d\u043e \u043c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043d\u0438 \u043e\u0431 \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u043d\u0438\u0445, \u0438\u043d\u0430\u0447\u0435 \u044d\u0442\u043e \u0441\u0442\u0430\u043b\u043e \u0431\u044b \u0432\u0435\u0447\u043d\u044b\u043c \u0442\u0440\u0435\u043d\u0438\u043d\u0433\u043e\u043c. \ud83d\ude00<\/p>\n<p>  <\/p>\n<p>\u041d\u043e \u043d\u0435 \u0432\u043e\u043b\u043d\u0443\u0439\u0442\u0435\u0441\u044c, \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0435\u0449\u0451 \u043e\u0434\u043d\u0430 \u0447\u0430\u0441\u0442\u044c, \u0433\u0434\u0435 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0441\u043e \u0432\u0441\u0435\u043c \u043c\u0438\u0440\u043e\u043c, \u0432\u044b\u043b\u043e\u0436\u0438\u0432 \u0435\u0433\u043e \u043d\u0430 <strong>Heroku<\/strong>.<\/p>\n<p>  <a name=\"12\"><\/a>  <\/p>\n<h2 id=\"vykladyvaem-proekt-na-heroku\">\u0412\u044b\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 Heroku<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/blog.diacode.com\/trello-clone-with-phoenix-and-react-pt-12\">\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b<\/a><\/p>\n<p>  <\/p>\n<p>\u041c\u044b \u043d\u0430\u043a\u043e\u043d\u0435\u0446-\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u044d\u0442\u043e <em>(\u0438 \u044f \u0442\u043e\u0436\u0435 \u2014 \u043f\u0440\u0438\u043c. \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\u0430)<\/em>. \u041f\u043e\u0441\u043b\u0435 5 <em>(\u0432 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u0435 \u2014 11 \u2014 \u043f\u0440\u0438\u043c. \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\u0430)<\/em> \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0439 \u043c\u044b \u0443\u0437\u043d\u0430\u043b\u0438, \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 <strong>Phoenix<\/strong> \u0441 <strong>Webpack<\/strong>, <strong>React<\/strong> \u0438 <strong>Redux<\/strong>. \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u043d\u0430 <strong>JWT<\/strong>-\u0442\u043e\u043a\u0435\u043d\u0430\u0445, \u0441\u043e\u0437\u0434\u0430\u043b\u0438 <strong>\u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438<\/strong> \u0434\u043b\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u0445 \u043d\u0430\u043c \u0441\u0445\u0435\u043c \u043d\u0430\u0448\u0435\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0437\u0430\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0441\u043e\u043a\u0435\u0442\u044b \u0438 \u043a\u0430\u043d\u0430\u043b\u044b \u0434\u043b\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 <strong>GenServer<\/strong>, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0445 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u0434\u043e\u0441\u043e\u043a. \u041d\u0430\u0441\u0442\u0430\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0432\u0441\u0435\u043c \u044d\u0442\u0438\u043c \u0441 \u043c\u0438\u0440\u043e\u043c, \u0432\u044b\u043b\u043e\u0436\u0438\u0432 \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 <strong>Heroku<\/strong>. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u044d\u0442\u043e!<\/p>\n<p>  <\/p>\n<h3 id=\"nastraivaem-heroku\">\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c Heroku<\/h3>\n<p>  <\/p>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435, \u0447\u0435\u043c \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435, \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0430\u043a\u043a\u0430\u0443\u043d\u0442 <strong>Heroku<\/strong> \u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 <a href=\"https:\/\/toolbelt.heroku.com\/\">Heroku Toolbelt<\/a>. \u0427\u0442\u043e\u0431\u044b \u0432\u044b\u043b\u043e\u0436\u0438\u0442\u044c \u043d\u0430 <strong>Heroku<\/strong> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 <strong>Phoenix<\/strong>, \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u0430 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 <em>buildpacks<\/em> (\u043d\u0430\u0431\u043e\u0440\u0430 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438), \u0442\u0430\u043a \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <a href=\"https:\/\/github.com\/ddollar\/heroku-buildpack-multi\">multi-buildpack<\/a>:<\/p>\n<p>  <\/p>\n<pre><code>$ heroku create phoenix-trello --buildpack https:\/\/github.com\/ddollar\/heroku-buildpack-multi<\/code><\/pre>\n<p>  <\/p>\n<p>\u042d\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u043d\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 <strong>Heroku<\/strong> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0439 git-\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 <code>heroku<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u0434\u043b\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438. \u041a\u0430\u043a \u044f \u0441\u043a\u0430\u0437\u0430\u043b \u0440\u0430\u043d\u0435\u0435, \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f Phoenix \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u0432\u0430 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u043f\u0430\u043a\u0435\u0442\u0430 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438:<\/p>\n<p>  <\/p>\n<ol>\n<li><a href=\"https:\/\/github.com\/HashNuke\/heroku-buildpack-elixir\">heroku-buildpack-elixir<\/a>: \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 Elixir.<\/li>\n<li><a href=\"https:\/\/github.com\/gjaldon\/heroku-buildpack-phoenix-static\">heroku-buildpack-phoenix-static<\/a>: \u0414\u043b\u044f \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432.<\/li>\n<\/ol>\n<p>  <\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b <code>.buildpacks<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0431\u0430 \u043d\u0430\u0431\u043e\u0440\u0430:<\/p>\n<p>  <\/p>\n<pre><code># .buildpacks  https:\/\/github.com\/HashNuke\/heroku-buildpack-elixir https:\/\/github.com\/gjaldon\/phoenix-static-buildpack<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043b\u044e\u0431\u043e\u0439 \u0430\u0441\u043f\u0435\u043a\u0442, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0439\u0441\u044f \u043a \u0440\u0430\u0431\u043e\u0447\u0435\u043c\u0443 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044e Elixir, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0444\u0430\u0439\u043b <code>elixir_buildpack.config<\/code>:<\/p>\n<p>  <\/p>\n<pre><code># elixir_buildpack.config  # Elixir version elixir_version=1.2.3  # Always rebuild from scratch on every deploy? always_rebuild=true<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0432\u0435\u0440\u0441\u0438\u044e Elixir, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u043c \u043e\u0442 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u0432\u0441\u0451, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u0439 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438. \u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0438 \u0434\u043b\u044f \u0441\u0442\u0430\u0442\u0438\u043a\u0438 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>phoenix_static_buildpack.config<\/code>:<\/p>\n<p>  <\/p>\n<pre><code># phoenix_static_buildpack.config  # We can set the version of Node to use for the app here node_version=5.3.0  # We can set the version of NPM to use for the app here npm_version=3.5.2<\/code><\/pre>\n<p>  <\/p>\n<p>\u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u0435 \u0434\u043b\u044f <strong>Webpack<\/strong> \u0432\u0435\u0440\u0441\u0438\u0438 <code>node<\/code> \u0438 <code>npm<\/code>. \u0412 \u043a\u043e\u043d\u0446\u0435 \u043a\u043e\u043d\u0446\u043e\u0432 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0444\u0430\u0439\u043b <code>compile<\/code>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u044b \u0443\u043a\u0430\u0436\u0435\u043c, \u043a\u0430\u043a \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u043f\u043e\u0441\u043b\u0435 \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u043e\u0439 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438:<\/p>\n<p>  <\/p>\n<pre><code># compile  info &quot;Building Phoenix static assets&quot; webpack mix phoenix.digest<\/code><\/pre>\n<p>  <\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <a href=\"https:\/\/hexdocs.pm\/phoenix\/Mix.Tasks.Phoenix.Digest.html\">mix-\u0437\u0430\u0434\u0430\u0447\u0443<\/a> <code>phoenix.digest<\/code> \u043f\u043e\u0441\u043b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438 <code>webpack<\/code>, \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0435\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0435 \u0438 \u0441\u0436\u0430\u0442\u044b\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432.<\/p>\n<p>  <\/p>\n<h3 id=\"nastroyka-rabochego-okruzheniya\">\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0440\u0430\u0431\u043e\u0447\u0435\u0433\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f<\/h3>\n<p>  <\/p>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435, \u0447\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u0443\u044e \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044e, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0444\u0430\u0439\u043b <code>prod.exs<\/code>, \u0441\u0434\u0435\u043b\u0430\u0432 \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438:<\/p>\n<p>  <\/p>\n<pre><code># config\/prod.exs  use Mix.Config # ...  config :phoenix_trello, PhoenixTrello.Endpoint,   # ..   url: [scheme: &quot;https&quot;, host: &quot;phoenix-trello.herokuapp.com&quot;, port: 443],   # ..   secret_key_base: System.get_env(&quot;SECRET_KEY_BASE&quot;)  # ..  # Configure your database config :phoenix_trello, PhoenixTrello.Repo,   # ..   url: System.get_env(&quot;DATABASE_URL&quot;),   pool_size: 20  # Configure guardian config :guardian, Guardian,   secret_key: System.get_env(&quot;GUARDIAN_SECRET_KEY&quot;)<\/code><\/pre>\n<p>  <\/p>\n<p>\u0413\u043b\u0430\u0432\u043d\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u0442\u0443\u0442 \u0434\u0435\u043b\u0430\u0435\u043c: \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c URL \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f Heroku \u0438 SSL-\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435. \u041c\u044b \u0442\u0430\u043a \u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>secret_key_base<\/code>, \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 (<code>url<\/code>) \u0438 <code>secret_key<\/code> \u0434\u043b\u044f guardian. \u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u0430 Heroku \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438, \u043d\u043e \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0432\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0445, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443:<\/p>\n<p>  <\/p>\n<pre><code>$ mix phoenix.gen.secret xxxxxxxxxx $ heroku config:set SECRET_KEY_BASE=&quot;xxxxxxxxxx&quot; ... ...  $ mix phoenix.gen.secret yyyyyyyyyyy $ heroku config:set GUARDIAN_SECRET_KEY=&quot;yyyyyyyyyyy&quot; ... ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u043c\u044b \u0433\u043e\u0442\u043e\u0432\u044b \u043a \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438!<\/p>\n<p>  <\/p>\n<h3 id=\"publikaciya\">\u041f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f<\/h3>\n<p>  <\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0432\u0441\u0435\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0432 \u043d\u0430\u0448 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0432:<\/p>\n<p>  <\/p>\n<pre><code>$ git push heroku master ... ... ...<\/code><\/pre>\n<p>  <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u0432\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u043d\u0430 \u0432\u044b\u0432\u043e\u0434 \u043a\u043e\u043d\u0441\u043e\u043b\u0438, \u0442\u043e \u0443\u0432\u0438\u0434\u0438\u043c, \u043a\u0430\u043a \u043e\u0431\u0430 \u043d\u0430\u0431\u043e\u0440\u0430 \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u0434\u0435\u043b\u0430\u044e\u0442 \u0441\u0432\u043e\u044e \u0440\u0430\u0431\u043e\u0442\u0443, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u044f <strong>Erlang<\/strong> \u0438 <strong>Elixir<\/strong> \u0441 \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u044b\u043c\u0438 \u0438\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044f\u043c\u0438, \u0440\u0430\u0432\u043d\u043e \u043a\u0430\u043a \u0438 <strong>node<\/strong>, <strong>npm<\/strong> \u0438 \u043f\u0440\u043e\u0447\u0438\u0435 \u0432\u0435\u0449\u0438. \u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0442\u0430\u0431\u043b\u0438\u0446 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<p>  <\/p>\n<pre><code>$ heroku run mix ecto.migrate<\/code><\/pre>\n<p>  <\/p>\n<p>\u0418 \u043d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0451, \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d\u043e \u0438 \u0433\u043e\u0442\u043e\u0432\u043e \u043a \u0440\u0430\u0431\u043e\u0442\u0435!<\/p>\n<p>  <\/p>\n<h3 id=\"zaklyuchenie\">\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>  <\/p>\n<p>\u041f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f <strong>Phoenix<\/strong> \u043d\u0430 <strong>Heroku<\/strong> \u0434\u043e\u043f\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u0430 \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u0430. \u042d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u043b\u0443\u0447\u0448\u0438\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c, \u043d\u043e \u0434\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u0440\u043e\u0434\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0435\u043f\u043b\u043e\u0445\u043e. \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0432\u044b \u043d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u043b\u0438\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0435\u0439 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u0436\u0435, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438 \u044f. \u0412\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0432\u0441\u0435\u0433\u043e \u0446\u0438\u043a\u043b\u0430 \u044f \u043f\u0440\u043e\u0438\u0437\u0432\u0451\u043b \u043c\u043e\u0440\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0432 \u043e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434, \u0432\u043d\u043e\u0441\u044f \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0435\u0449\u0451 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439. \u0415\u0441\u043b\u0438 \u0445\u043e\u0442\u0438\u0442\u0435 \u043d\u0430 \u043d\u0438\u0445 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u044c <a href=\"https:\/\/phoenix-trello.herokuapp.com\/\">\u0436\u0438\u0432\u043e\u0435 \u0434\u0435\u043c\u043e<\/a> \u0438 \u0444\u043e\u0440\u043a\u043d\u0443\u0442\u044c <a href=\"https:\/\/github.com\/bigardone\/phoenix-trello\">\u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/a> \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430.<\/p>\n<p>  <\/p>\n<p><strong>P.S. \u041a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430, \u043f\u0440\u043e\u0448\u0443 \u0441\u043e\u043e\u0431\u0449\u0430\u0442\u044c \u043e\u0431 \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430\u0445 \u043b\u044e\u0431\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u043b\u0443\u0447\u0448\u0430\u044f \u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u043a\u0430 \u043a\u0430\u043a\u043e\u0439-\u043d\u0438\u0431\u0443\u0434\u044c \u0444\u0440\u0430\u0437\u044b \u2014 \u043d\u0435 \u043f\u043e\u043b\u0435\u043d\u0438\u0442\u0435\u0441\u044c \u043c\u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c, \u0445\u043e\u0442\u044c \u0432 \u043b\u0438\u0447\u043a\u0443, \u0445\u043e\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438, \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044e. \u041d\u0443, \u0438 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0432\u043e\u043f\u0440\u043e\u0441 \u043e \u0442\u043e\u043c, \u0441\u0442\u043e\u0438\u0442 \u043b\u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438, \u0434\u0430\u0441\u0442 \u0440\u0435\u0439\u0442\u0438\u043d\u0433 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430.<\/strong><\/p>\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=\"https:\/\/habrahabr.ru\/post\/316108\/\"> https:\/\/habrahabr.ru\/post\/316108\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/86e\/e8d\/293\/86ee8d2930024c60a834695276f5de15.jpg\"\/>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041e\u0433\u043b\u0430\u0432\u043b\u0435\u043d\u0438\u0435 (\u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0432\u044b\u0434\u0435\u043b\u0435\u043d)<\/b><\/p>\n<div class=\"spoiler_text\">\n<ol>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308056\/#1\">\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0438 \u0432\u044b\u0431\u043e\u0440 \u0441\u0442\u0435\u043a\u0430 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308056\/#2\">\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Phoenix Framework<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308056\/#3\">\u041c\u043e\u0434\u0435\u043b\u044c User \u0438 JWT-\u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308100\/#4\">Front-end \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 React \u0438 Redux<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308100\/#5\">\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308248\/#6\">\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 front-end \u043d\u0430 React \u0438 Redux<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308248\/#7\">\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0441\u043e\u043a\u0435\u0442\u044b \u0438 \u043a\u0430\u043d\u0430\u043b\u044b<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308382\/#8\">\u0412\u044b\u0432\u043e\u0434\u0438\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0438 \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u043e\u0432\u044b\u0435 \u0434\u043e\u0441\u043a\u0438<\/a><\/li>\n<li><a href=\"https:\/\/habrahabr.ru\/post\/308382\/#9\">\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u043e\u0432\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043e\u0441\u043e\u043a<\/a><\/li>\n<li><strong>\u041e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0434\u043e\u0441\u043e\u043a<\/strong><\/li>\n<li><strong>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043f\u0438\u0441\u043a\u0438 \u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438<\/strong><\/li>\n<li><strong>\u0412\u044b\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430 Heroku<\/strong><\/li>\n<\/ol>\n<\/div>\n<\/div>\n<p>  <\/p>\n<p><em>\u042d\u0442\u0430 \u0447\u0430\u0441\u0442\u044c \u2014 \u0437\u0430\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f, \u0438 \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0434\u043b\u0438\u043d\u043d\u043e\u0439, \u043d\u043e \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0443\u0436\u0435 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u0442\u044c \u0441 \u0446\u0438\u043a\u043b\u043e\u043c \u0438 \u043f\u043e\u0439\u0442\u0438 \u0434\u0430\u043b\u044c\u0448\u0435. \u0422\u0430\u043a \u0436\u0435 \u043f\u0440\u043e\u0448\u0443 \u043f\u0440\u043e\u0449\u0435\u043d\u0438\u044f \u0437\u0430 \u0441\u0442\u043e\u043b\u044c \u043e\u0433\u0440\u043e\u043c\u043d\u0443\u044e \u043f\u0430\u0443\u0437\u0443 \u043f\u0440\u0438 \u0435\u0451 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0435 \u0438 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438. \u041e\u0434\u043d\u0430\u043a\u043e \u044d\u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0435 \u043f\u0440\u043e\u0448\u043b\u043e \u0434\u0430\u0440\u043e\u043c \u0438 \u0434\u0430\u043b\u043e \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0434\u043b\u044f \u043d\u043e\u0432\u044b\u0445, \u043d\u0430 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0445, \u0441\u0442\u0430\u0442\u0435\u0439 \u2014 \u043f\u0440\u0438\u043c. \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\u0430<\/em><\/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-281534","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/281534","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=281534"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/281534\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=281534"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=281534"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=281534"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}