{"id":416039,"date":"2024-06-30T00:42:53","date_gmt":"2024-06-30T00:42:53","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=416039"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=416039","title":{"rendered":"<span>\u0424\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0430 \u0440\u0435\u043b\u044c\u0441\u0430\u0445 (\u043f\u043e\u0447\u0442\u0438) \u0431\u0435\u0437 JS<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u043e\u043f\u0440\u0435\u043a\u0438 \u0441\u043b\u0443\u0445\u0430\u043c \u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0430\u0445 \u0434\u0435\u0432\u0435\u043b\u043e\u043f\u0435\u0440\u0441\u043a\u0438\u0445 \u043a\u043e\u043c\u044c\u044e\u043d\u0438\u0442\u0438, Rails \u043d\u0435 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0435\u0439 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0435\u0439, \u043e\u043d \u043d\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u0443\u043c\u0438\u0440\u0430\u0442\u044c, \u0438 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432\u0430\u0448\u0435\u0433\u043e \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0418 \u043e\u0434\u043d\u0430 \u0438\u0437 \u043f\u0440\u0438\u0447\u0438\u043d \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0443 Rails \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043a\u0440\u044b\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0442\u0438\u043f\u0438\u0447\u043d\u043e\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0412\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0434\u0443\u043c\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u041d\u0422\u0422\u0420 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0432\u0432\u043e\u0434\u0430 \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0431\u0430\u0437\u044b, \u043a\u0430\u043a \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c HTML, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0443\u0432\u0438\u0434\u044f\u0442 \u0432 \u0441\u0432\u043e\u0438\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445, \u0438 \u0434\u0430\u0436\u0435 \u043a\u0430\u043a &#171;\u0432\u0434\u043e\u0445\u043d\u0443\u0442\u044c \u0436\u0438\u0437\u043d\u044c&#187; \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. <\/p>\n<p>\u042f \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0432 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432\u043e\u0439\u00a0\u0441\u0442\u0443\u0434\u0438\u0438 \u0438 \u043a \u043d\u0430\u043c \u0447\u0430\u0441\u0442\u043e \u043e\u0431\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0437\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 MVP \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432. \u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u044b \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b Rails \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u043d\u0438\u0445 \u0438 \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<\/p>\n<h2>\u0422\u043e, \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438: rails-ujs, turbolinks <\/h2>\n<h4>Rails UJS<\/h4>\n<p>\u0414\u0430\u0432\u043d\u044b\u043c \u0434\u0430\u0432\u043d\u043e, \u043a\u043e\u0433\u0434\u0430 \u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0441\u0432\u0435\u0440\u0441\u0442\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u043f\u0435\u0440\u0432\u0443\u044e HTML \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043a\u0443, \u0443 Rails \u0443\u0436\u0435 \u0431\u044b\u043b \u043a\u0440\u0443\u0442\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 jquery-ujs (unobtrusive javascript), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f rails-ujs. \u041e\u043d \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u0440\u0435\u043b\u044c\u0441\u043e\u0432\u044b\u043c \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0430\u0440\u043e\u0447\u043a\u0443 AJAX \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043c\u0430\u043b\u043e\u0439 \u0446\u0435\u043d\u043e\u0439.<\/p>\n<p>\u041c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435 \u044d\u0442\u043e\u0433\u043e:<\/p>\n<p><strong>app\/controllers\/money_controller.rb<\/strong> <\/p>\n<pre><code class=\"ruby\">class MoneyController &lt; ApplicationController   def show     @money = GetAllMoney.call   end    def destroy     SpendAllMoney.call   end end<\/code><\/pre>\n<p><strong>views\/money\/show.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;div class=\"money\">   &lt;h3>Your money&lt;\/h3>   &lt;span id=\"money-amount\">&lt;%= @money %>&lt;\/span>   &lt;span>$&lt;\/span>    &lt;%= link_to 'Spend all money',               money_path,               method: 'delete',               remote: true,               data: { confirm: 'Do you want to spend all money?' },               class: 'spend-money-button' %> &lt;\/div><\/code><\/pre>\n<p><strong>views\/money\/destroy.js<\/strong><\/p>\n<pre><code class=\"javascript\">document.querySelector('#money-amount').innerHTML = 0<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/90f\/4fb\/7dc\/90f4fb7dc211443885191c750c05f4e9.gif\" alt=\"\u041d\u043e\u0432\u044b\u0439 \u0441\u0430\u0439\u0442 \u0441\u043e \u0441\u0442\u0430\u0432\u043a\u0430\u043c\u0438 \u043d\u0430 \u0441\u043f\u043e\u0440\u0442. \u041b\u0443\u0447\u0448\u0438\u0435 \u043a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442\u044b xD \" title=\"\u041d\u043e\u0432\u044b\u0439 \u0441\u0430\u0439\u0442 \u0441\u043e \u0441\u0442\u0430\u0432\u043a\u0430\u043c\u0438 \u043d\u0430 \u0441\u043f\u043e\u0440\u0442. \u041b\u0443\u0447\u0448\u0438\u0435 \u043a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442\u044b xD \" width=\"817\" height=\"638\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/90f\/4fb\/7dc\/90f4fb7dc211443885191c750c05f4e9.gif\"\/><figcaption>\u041d\u043e\u0432\u044b\u0439 \u0441\u0430\u0439\u0442 \u0441\u043e \u0441\u0442\u0430\u0432\u043a\u0430\u043c\u0438 \u043d\u0430 \u0441\u043f\u043e\u0440\u0442. \u041b\u0443\u0447\u0448\u0438\u0435 \u043a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442\u044b xD <\/figcaption><\/figure>\n<p>\u0418\u0442\u0430\u043a, \u0432\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 AJAX \u0437\u0430\u043f\u0440\u043e\u0441, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0432\u0441\u0435\u0433\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e HTML \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0438 \u043e\u0434\u0438\u043d JS \u0444\u0430\u0439\u043b \u0441 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u0447\u043a\u043e\u0439 \u043a\u043e\u0434\u0430. \u041a\u0440\u0443\u0442\u043e, \u043f\u0440\u0430\u0432\u0434\u0430?<\/p>\n<h4>Turbolinks<\/h4>\n<p>\u0415\u0449\u0435 \u043e\u0434\u0438\u043d \u0441\u0442\u0430\u0440\u043e\u0436\u0438\u043b \u0432 \u043c\u0438\u0440\u0435 Rails &#8212; Turbolinks. \u042d\u0442\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0441\u0442\u0430\u0434\u0438\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043d\u043e \u043e \u0435\u0435 \u043f\u0440\u0435\u0435\u043c\u043d\u0438\u043a\u0435 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0437\u0436\u0435. \u0412 \u0434\u0432\u0443\u0445 \u0441\u043b\u043e\u0432\u0430\u0445, Turbolinks \u043f\u0440\u0438\u043d\u043e\u0441\u0438\u0442 \u0432\u0430\u043c SPA \u043e\u043f\u044b\u0442 \u043f\u043e\u0447\u0442\u0438 \u0431\u0435\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0433\u043e \u043a\u043e\u0434\u0430. \u0415\u0441\u043b\u0438 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e, \u0442\u043e \u044d\u0442\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430:<\/p>\n<ul>\n<li>\n<p>\u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043d\u043e\u0432\u044b\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e JS \u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 \u0435\u0433\u043e \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430;\u00a0<\/p>\n<\/li>\n<li>\n<p>\u043e\u043d\u0430 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0435 \u043f\u043e\u0441\u0435\u0449\u0435\u043d\u0438\u044f \u043a\u0430\u0437\u0430\u043b\u0438\u0441\u044c \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u044b\u043c\u0438; <\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043d\u0435\u0438\u0437\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0435\u0440\u0432\u044b\u0435 \u0434\u0432\u0435 \u0444\u0438\u0447\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438, \u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u044f\u0432\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0430 \u0432 \u0432\u0430\u0448\u0435\u043c \u043a\u043e\u0434\u0435. \u042f \u043f\u043e\u043a\u0430\u0436\u0443 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0438 \u043d\u0430\u0434\u0443\u043c\u0430\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0441\u0442\u0438\u0447\u044c \u0441 \u0435\u0435 \u043f\u043e\u043c\u043e\u0449\u044c\u044e. \u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0433\u0434\u0435-\u0442\u043e \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0435\u0441\u0442\u044c \u0441\u0447\u0435\u0442\u0447\u0438\u043a \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439.<\/p>\n<p><strong>app\/helpers\/application_helper.rb<\/strong><\/p>\n<pre><code class=\"ruby\">module ApplicationHelper   def notifications_count     sleep 3 # emulate some calculations      10   end    def articles     Article.last(5)   end end<\/code><\/pre>\n<p><strong>app\/views\/layouts\/application.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;!DOCTYPE html> &lt;html> &lt;head>   &lt;title>Turbolinks&lt;\/title>   &lt;meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">   &lt;%= csrf_meta_tags %>   &lt;%= csp_meta_tag %>    &lt;%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>   &lt;%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> &lt;\/head>  &lt;body> &lt;div class=\"container\">   &lt;nav class=\"navigation\">     &lt;ul>       &lt;%- articles.each do |article| %>         &lt;li>           &lt;%= link_to article.title, article_path(article.id) %>         &lt;\/li>       &lt;% end %>     &lt;\/ul>     &lt;div class=\"notifications\">       &lt;div class=\"notifications-badge\">         &lt;%= notifications_count %>       &lt;\/div>     &lt;\/div>   &lt;\/nav>   &lt;section class=\"content\">     &lt;%= yield %>   &lt;\/section> &lt;\/div> &lt;\/body> &lt;\/html> <\/code><\/pre>\n<p>\u041f\u043e\u0434\u0441\u0447\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u043d\u043e \u044d\u0442\u043e \u0446\u0435\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432\u044b \u043f\u043b\u0430\u0442\u0438\u0442\u0435 \u0437\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0<\/p>\n<p>\u041f\u043e\u0437\u0436\u0435, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u044b \u0442\u0430\u043a\u0436\u0435 \u0437\u0430\u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439, \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0432\u0448\u0438\u0441\u044c \u043d\u0430 \u0442\u0430\u043a\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u0423 Rails \u0434\u0430\u0436\u0435 \u0435\u0441\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 Action Cable \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e.<\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044d\u0442\u0430 \u0440\u0430\u0431\u043e\u0442\u0430 \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u043d\u0430 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0435, \u0432\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u043c\u0435\u0436\u0434\u0443 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430\u043c\u0438, \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u043c\u0438 Turbolinks. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0432\u0441\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043d\u043e \u0437\u043d\u0430\u0435\u0442\u0435\u2026 \u0435\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0432\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0432\u0435\u0449\u0438 \u0432 CS\u2026 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043a\u044d\u0448\u0430\u2026 \u0438 \u043c\u044b \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e Turbolinks.<\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043a\u043e\u0434, \u0435\u0441\u043b\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u043b\u0430\u0441\u044c Turbolinks \u0438 \u0438 \u0437\u0430\u043f\u0440\u0435\u0442\u0438\u0442\u044c Turbolinks \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0447\u0430\u0441\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u0412\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442:<\/p>\n<p><strong>app\/helpers\/application_helper.rb<\/strong><\/p>\n<pre><code class=\"diff\">module ApplicationHelper   def notifications_count +   return nil if request.headers['Turbolinks-Referrer'].present? +     sleep 3 # emulate some calculations      10   end    def articles     Article.last(5)   end end<\/code><\/pre>\n<p><strong>app\/views\/layouts\/application.html.erb<\/strong><\/p>\n<pre><code class=\"diff\">&lt;div class=\"notifications\"> -   &lt;div class=\"notifications-badge\" id=\"notifications-badge\"> +   &lt;div class=\"notifications-badge\" id=\"notifications-badge\" data-turbolinks-permanent>     &lt;%= notifications_count %>   &lt;\/div> &lt;\/div><\/code><\/pre>\n<h4>\u0427\u0435\u0433\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u043d\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e<\/h4>\n<p>\u0421\u0442\u0430\u0440\u0435\u043d\u044c\u043a\u0438\u0435 \u0444\u0438\u0447\u0438 Rails \u0434\u0435\u043b\u0430\u044e\u0442 \u0441\u0432\u043e\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u0445\u043e\u0440\u043e\u0448\u043e \u0438 \u043c\u043d\u043e\u0433\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u043d\u0430 \u043d\u0438\u0445 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u044e\u0437\u0435\u0440\u0441\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0431\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e JS \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430. \u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u044d\u0442\u043e, \u043d\u0430\u043c \u0432\u0441\u0435 \u0435\u0449\u0435 \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0444\u0438\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0434\u0435\u043b\u0430\u044e\u0442 \u043d\u0430\u0448\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0438 \u0443\u043f\u0440\u043e\u0441\u0442\u044f\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430.<\/p>\n<h3>\u041d\u043e\u0432\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b Rails<\/h3>\n<p>\u0412 \u043d\u0430\u0447\u0430\u043b\u0435 2021 \u0433\u043e\u0434\u0430 DHH \u043e\u0431\u044a\u044f\u0432\u0438\u043b \u043e \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u0438 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 Hotwire, \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0430 Rails \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432. \u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e Hotwire \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0434\u043b\u044f \u0441\u0435\u043c\u0435\u0439\u0441\u0442\u0432\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u044d\u0442\u0430 \u0441\u0435\u043c\u044c\u044f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u0430\u043b\u0430. \u041f\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u043d\u0430 \u043e\u043a\u0442\u044f\u0431\u0440\u044c 2021 \u0433\u043e\u0434\u0430 \u0431\u044b\u043b\u043e \u0432\u0441\u0435\u0433\u043e \u0434\u0432\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438: <a href=\"https:\/\/turbo.hotwire.dev\/\" rel=\"noopener noreferrer nofollow\"><u>Turbo<\/u><\/a> \u0438 <a href=\"https:\/\/stimulus.hotwire.dev\/\" rel=\"noopener noreferrer nofollow\"><u>Stimulus<\/u><\/a>.<\/p>\n<p>\u041e\u043d\u0438 \u043e\u0431\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u044b \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 Rails \u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u0435\u0437 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0432\u0430\u0448 \u0432\u0435\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043c\u043e\u043d\u043e\u043b\u0438\u0442. \u042f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e Turbo, \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u043e\u0432\u0430\u044f \u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u0442 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e Turbolinks.<\/p>\n<h4>Turbo<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0434\u0443\u043c\u0430\u043b\u0438, \u0447\u0442\u043e Turbolinks \u043f\u043e\u0442\u0435\u0440\u044f\u043b\u0438 \u0441\u0432\u043e\u044e \u0447\u0430\u0441\u0442\u044c &#171;links&#187;, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u0447\u0435\u043c \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f, \u0432\u044b \u043d\u0430 100% \u043f\u0440\u0430\u0432\u044b. \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 Turbo \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0430 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u0435\u0439, \u0433\u0434\u0435 \u043a\u0430\u0436\u0434\u0430\u044f \u0441\u043b\u0443\u0436\u0438\u0442 \u0435\u0434\u0438\u043d\u043e\u0439 \u0446\u0435\u043b\u0438 &#8212; \u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 \u0432\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 HTML, \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0441 \u0440\u0430\u0437\u043d\u0438\u0446\u0435\u0439 \u0432 \u0442\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u0438 \u043a\u0430\u043a \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p>Turbo Drive &#8212; \u0442\u043e\u0442 \u0441\u0442\u0430\u0440\u044b\u0439 \u0434\u043e\u0431\u0440\u044b\u0439 Turbolinks, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u044b \u0437\u043d\u0430\u043a\u043e\u043c\u044b.\u00a0<\/p>\n<\/li>\n<li>\n<p>Turbo Frames &#8212; \u201c\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435\u201d \u0444\u0440\u0435\u0439\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b, \u043a\u043e\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0444\u0440\u0435\u0439\u043c \u0441 \u0442\u0435\u043c \u0436\u0435 id.\u00a0<\/p>\n<\/li>\n<li>\n<p>Turbo Streams &#8212; \u0434\u0440\u0443\u0433\u043e\u0439 \u0442\u0438\u043f \u0444\u0440\u0435\u0439\u043c\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438\u043b\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 Websocket.\u00a0<\/p>\n<\/li>\n<li>\n<p>Turbo Native &#8212; \u043e\u0431\u0451\u0440\u0442\u043a\u0430 \u0432\u0430\u0448\u0435\u0433\u043e \u201c\u0442\u0443\u0440\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e\u201d \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0435\u0442 \u0435\u0433\u043e \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0418\u0442\u0430\u043a, \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u0431\u043e \u0432\u0441\u0435\u043c \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443.<\/p>\n<h4>Turbo Drive<\/h4>\n<p>\u041a\u0430\u043a \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b\u043e\u0441\u044c \u0440\u0430\u043d\u0435\u0435, Turbo Drive \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 Turbolinks \u0438 \u0431\u0435\u0440\u0435\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e \u043c\u0435\u0436\u0434\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u043c\u0438. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043f\u043e\u0447\u0442\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c, \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u0430.<\/p>\n<p>\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0430\u043a\u0435\u0442 npm <\/p>\n<pre><code class=\"bash\">yarn add @hotwired\/turbo<\/code><\/pre>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u044c Turbolinks \u043d\u0430 Turbo \u0432 \u0432\u0430\u0448\u0435\u043c javascript \u043a\u043e\u0434\u0435<\/p>\n<p><strong>app\/javascript\/packs\/application.js<\/strong><\/p>\n<pre><code class=\"diff\">  import Rails from \"@rails\/ujs\" - import Turbolinks from \"turbolinks\" + import * as Turbo from \"@hotwired\/turbo\"     Rails.start() - Turbolinks.start()<\/code><\/pre>\n<p>\u0417\u043c\u0435\u043d\u0438\u0442\u044c <code>data-turbolinks...<\/code> \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0441 <code>data-turbo\u2026<\/code><\/p>\n<p><strong>app\/views\/layouts\/application.html.erb<\/strong><\/p>\n<pre><code class=\"diff\"> -    &lt;%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> -    &lt;%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> +    &lt;%= stylesheet_link_tag 'application', media: 'all', 'data-turbo-track': 'reload' %> +    &lt;%= javascript_pack_tag 'application', 'data-turbo-track': 'reload' %><\/code><\/pre>\n<p>\u0412\u0430\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 &#8212;\u00a0 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u044b. Turbo drive \u0431\u0435\u0440\u0435\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u0438 \u044d\u0442\u043e. \u041f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e, \u043e\u043d \u043e\u0436\u0438\u0434\u0430\u0435\u0442, \u0447\u0442\u043e redirect \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b \u0431\u0443\u0434\u0435\u0442 \u0441\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u043c 303, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c Fetch API \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0437\u0430 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u043e\u043c. \u042d\u0442\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 \u041d\u0422\u0422\u0420 \u0434\u043b\u044f <em>\u043d\u0435\u0438\u043d\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u044b\u0445<\/em> (\u0443\u043c\u043d\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f HTTP \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u043e\u043c\u0438\u043c\u043e GET \u0438 HEAD ?) \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0435\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044f \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u043b\u0430\u0441\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 GET. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b \u0431\u0443\u0434\u0443\u0442 \u043b\u0438\u0448\u044c POST \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u043d\u0438 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u044e\u0442 \u0441\u0442\u0430\u0442\u0443\u0441\u044b 301 \u0438 302. \u0422\u0430\u043a \u0447\u0442\u043e \u0432\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u044f\u0432\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u043e\u0434 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0434\u043b\u044f \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0430. \u0418 \u0432\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c:<\/p>\n<p><strong>app\/controllers\/any_controller.rb<\/strong><\/p>\n<pre><code class=\"diff\">-    redirect_to money_path +    redirect_to money_path, status: :see_other<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0438\u043b\u0438 \u0438\u043d\u0430\u0447\u0435 \u0432 \u0440\u0435\u043b\u044c\u0441\u043e\u0432\u044b\u0445 \u0444\u043e\u0440\u043c\u0430\u0445 \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043c\u0435\u0442\u043e\u0434 POST \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>&lt;input type=\"hidden\" name=\"_method\" value=\"patch\"><\/code>, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043a\u0430\u043a\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0432\u0430\u0448\u0438 \u0444\u043e\u0440\u043c\u044b \u0432\u0441\u0435 \u0435\u0449\u0435 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0430 \u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0443\u0436\u0435 \u0432\u0435\u043b\u0438\u0441\u044c <a href=\"https:\/\/github.com\/hotwired\/turbo\/issues\/84\" rel=\"noopener noreferrer nofollow\"><u>\u0431\u0443\u0440\u043d\u044b\u0435 \u0434\u0438\u0441\u043a\u0443\u0441\u0441\u0438\u0438<\/u><\/a>.\u00a0<\/p>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435, \u043d\u0430 \u0447\u0442\u043e \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e Turbo \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>local: true<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f JS-\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u043d\u0430\u0434 \u0444\u043e\u0440\u043c\u043e\u0439. \u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u0432\u0430\u0448 \u0441\u043b\u0443\u0447\u0430\u0439, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u043d\u0435\u0441\u0442\u0438 \u0435\u0449\u0435 \u043e\u0434\u043d\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435: <\/p>\n<p><strong>app\/views\/_any_form.html.erb<\/strong><\/p>\n<pre><code class=\"diff\">- &lt;%= form_with(url: money_path, local: true) do |f| %> + &lt;%= form_with(url: money_path, data: { turbo: false }) do |f| %><\/code><\/pre>\n<h4>Turbo Frames<\/h4>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446 \u043c\u044b \u043f\u043e\u0434\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u043a \u0447\u0435\u043c\u0443-\u0442\u043e \u043d\u043e\u0432\u0435\u043d\u044c\u043a\u043e\u043c\u0443 \u0432 \u0440\u0435\u043b\u044c\u0441\u0435. Turbo Frame &#8212; \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0441 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e. \u0422\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u0432 \u0433\u0435\u043c\u0435 <a href=\"https:\/\/github.com\/renderedtext\/render_async\" rel=\"noopener noreferrer nofollow\"><u>render_async<\/u><\/a> \u0438\u043b\u0438 <code>.ejs<\/code> \u043e\u0442 Rails, \u043d\u043e \u0441 \u043c\u0435\u043d\u044c\u0448\u0438\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u043a\u043e\u0434\u0430.\u00a0<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u043f\u043e\u0434\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438\u0445 \u0432\u0438\u0434\u0438\u0442. \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0441 \u043e\u0431\u0449\u0435\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439, \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0438 \u043e\u0442\u0437\u044b\u0432\u0430\u043c\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432. \u041d\u0435\u0442 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0438, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u0441\u0435\u0442\u0438\u0442 \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u043e\u0432, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0438\u0445 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u043e\u043d \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u0431\u0440\u0430\u043b\u0441\u044f \u0434\u043e \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f24\/691\/7b1\/f246917b1a5b8252d925d04e83749a42.gif\" alt=\"\u0412\u0430\u0448 \u043d\u043e\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\" title=\"\u0412\u0430\u0448 \u043d\u043e\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\" width=\"1460\" height=\"939\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f24\/691\/7b1\/f246917b1a5b8252d925d04e83749a42.gif\"\/><figcaption>\u0412\u0430\u0448 \u043d\u043e\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d<\/figcaption><\/figure>\n<p>\u042f \u043f\u0440\u043e\u043f\u0443\u0449\u0443 \u0447\u0430\u0441\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u0430, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0443\u044e\u0441\u044f \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 Bootstrap \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044e CSS. \u041f\u043e\u043b\u0430\u0433\u0430\u044e, \u0432\u0430\u0441 \u043d\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442 \u0442\u0430\u043a\u0438\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0432\u0435\u0449\u0438.<\/p>\n<p>\u0412\u043e\u0442 \u0442\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043d\u0430\u0448 <code>app\/views\/products\/show.html.erb<\/code><\/p>\n<pre><code class=\"xml\">&lt;div class=\"product\">   &lt;ul class=\"nav nav-tabs\" id=\"product-tab\" role=\"tablist\">     &lt;li class=\"nav-item\" role=\"presentation\">       &lt;button class=\"nav-link active\" id=\"home-tab\" data-bs-toggle=\"tab\" data-bs-target=\"#general\" type=\"button\" role=\"tab\" aria-controls=\"general\" aria-selected=\"true\">General&lt;\/button>     &lt;\/li>     &lt;li class=\"nav-item\" role=\"presentation\">       &lt;button class=\"nav-link\" id=\"properties-tab\" data-bs-toggle=\"tab\" data-bs-target=\"#properties\" type=\"button\" role=\"tab\" aria-controls=\"properties\" aria-selected=\"false\">Properties&lt;\/button>     &lt;\/li>     &lt;li class=\"nav-item\" role=\"presentation\">       &lt;button class=\"nav-link\" id=\"reviews-tab\" data-bs-toggle=\"tab\" data-bs-target=\"#reviews\" type=\"button\" role=\"tab\" aria-controls=\"reviews\" aria-selected=\"false\">Reviews&lt;\/button>     &lt;\/li>   &lt;\/ul>    &lt;div class=\"tab-content\">     &lt;div class=\"tab-pane active p-3\" id=\"general\" role=\"tabpanel\" aria-labelledby=\"general-tab\">       &lt;turbo-frame id=\"&lt;%= dom_id(@product, 'general') %>\" loading=\"lazy\" src=\"&lt;%= general_product_path %>\">         &lt;%= render('common\/spinner') %>       &lt;\/turbo-frame>     &lt;\/div>     &lt;div class=\"tab-pane p-3\" id=\"properties\" role=\"tabpanel\" aria-labelledby=\"properties-tab\">       &lt;turbo-frame id=\"&lt;%= dom_id(@product, 'properties') %>\" loading=\"lazy\" src=\"&lt;%= properties_product_path %>\">         &lt;%= render('common\/spinner') %>       &lt;\/turbo-frame>     &lt;\/div>     &lt;div class=\"tab-pane p-3\" id=\"reviews\" role=\"tabpanel\" aria-labelledby=\"reviews-tab\">       &lt;turbo-frame id=\"&lt;%= dom_id(@product, 'reviews') %>\" loading=\"lazy\" src=\"&lt;%= reviews_product_path %>\">         &lt;%= render('common\/spinner') %>       &lt;\/turbo-frame>     &lt;\/div>   &lt;\/div> &lt;\/div> <\/code><\/pre>\n<p>\u042d\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u0442\u0430\u0431\u044b \u0438\u0437 Bootstrap. \u041d\u043e \u0441\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u0432 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0445 <code>.tab-page<\/code>. \u041c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0442\u0435\u0433 <code>turbo-frame<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430\u0448\u0438\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u043c \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f. \u0423 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0430 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0441\u0432\u043e\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0430\u0442\u0440\u0438\u0431\u0443\u0442 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0430 (<code>id<\/code>), \u0430 \u0445\u0435\u043b\u043f\u0435\u0440 <code>dom_id<\/code> \u0431\u0443\u0434\u0435\u0442 \u0445\u043e\u0440\u043e\u0448\u0438\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043e\u0441\u0432\u043e\u0431\u043e\u0434\u0438\u0442\u044c \u043d\u0430\u0441 \u043e\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u0443\u043c\u0430\u0442\u044c \u043d\u0430\u0434 \u0438\u043c\u0435\u043d\u0430\u043c\u0438. \u0414\u043b\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0440\u0435\u0439\u043c\u0430 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 <code>src<\/code>, \u0438 \u043e\u0442\u0432\u0435\u0442 \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0444\u0440\u0435\u0439\u043c \u0441 \u0442\u0430\u043a\u0438\u043c \u0436\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c (<code>id<\/code>).<\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0438\u0434\u0438\u043c\u0443\u044e \u0447\u0430\u0441\u0442\u044c, \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <code>loading=\"lazy\"<\/code> \u0438 \u0444\u0440\u0435\u0439\u043c \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u043e\u0433\u0434\u0430, \u043a\u043e\u0433\u0434\u0430 \u044d\u0442\u043e\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043d\u0435 \u0432\u0430\u0436\u043d\u043e, \u043a\u0430\u043a \u044d\u0442\u043e\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0441\u0442\u0430\u043b \u0432\u0438\u0434\u0438\u043c\u044b\u043c. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0440\u043e\u0441\u043a\u0440\u043e\u043b\u0438\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043a \u044d\u0442\u043e\u043c\u0443 \u0442\u0435\u0433\u0443 \u0438 \u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0441\u044f, \u0441\u0442\u0438\u043b\u0438 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u043c\u043e\u0433\u0443\u0442 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c\u0441\u044f \u0441 <code>display: none<\/code> \u043d\u0430 <code>display: block<\/code>, \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u0442\u0435\u0433 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Javascript \u0438\u043b\u0438 \u0432\u044b \u0434\u0430\u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u043e\u0434\u0438\u043d \u0444\u0440\u0435\u0439\u043c \u0438\u0437 \u0434\u0440\u0443\u0433\u043e\u0433\u043e (\u043d\u043e \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043a\u0430\u043a-\u043d\u0438\u0431\u0443\u0434\u044c \u0432\u044b\u0439\u0442\u0438 \u0438\u0437 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0438).<\/p>\n<p>\u0421\u043f\u0438\u043d\u043d\u0435\u0440 \u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 &#8212; \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e div \u0441 CSS \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0435\u0439. \u0412\u0430\u043c \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0441 \u044d\u0442\u0438\u043c \u0434\u0435\u043b\u0430\u0442\u044c. \u041e\u043d \u043f\u0440\u043e\u0441\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f, \u043f\u043e\u043a\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0440\u0435\u0439\u043c\u0430 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0441\u044f \u0438 \u043f\u043e\u044f\u0432\u0438\u0442\u0441\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435.<\/p>\n<p><strong>app\/views\/common\/_spinner.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;div class=\"text-center mt-5\">   &lt;div class=\"spinner-grow text-secondary\" role=\"status\">     &lt;span class=\"visually-hidden\">Loading...&lt;\/span>   &lt;\/div> &lt;\/div><\/code><\/pre>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u044b \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 <code>busy<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e \u0444\u0440\u0435\u0439\u043c \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435, \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0432\u043e\u0439 CSS, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438.\u00a0<\/p>\n<p>\u041d\u0430\u0448 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439:<\/p>\n<p><strong>app\/controllers\/products_controller.rb<\/strong><\/p>\n<pre><code class=\"ruby\">class ProductsController &lt; ApplicationController   def show     @product = Product.find(params[:id])   end    def general     @product = Product.find(params[:id])      render partial: 'products\/general'   end    def properties     @product = Product.find(params[:id])      render partial: 'products\/properties'   end    def reviews     @product = Product.find(params[:id])      render partial: 'products\/reviews'   end end<\/code><\/pre>\n<p><strong>app\/views\/products\/_general.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;turbo-frame id=\"&lt;%= dom_id(@product, 'general') %>\" loading=\"lazy\" src=\"&lt;%= general_product_path(product_id: @product) %>\">   &lt;div class=\"product--general\">     &lt;h1>       &lt;%= @product.title %>     &lt;\/h1>     &lt;div class=\"row mt-4\">       &lt;div class=\"col\">         &lt;div class=\"product--image\">           &lt;%= image_tag @product.image %>         &lt;\/div>        &lt;\/div>       &lt;div class=\"col\">         &lt;h3>           &lt;%= @product.price %>         &lt;\/h3>          &lt;%= @product.content %>       &lt;\/div>     &lt;\/div>   &lt;\/div> &lt;\/turbo-frame> <\/code><\/pre>\n<p><strong>app\/views\/products\/_properties.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;turbo-frame id=\"&lt;%= dom_id(@product, 'properties') %>\">   &lt;h1>     &lt;%= @product.title %> properties   &lt;\/h1>   &lt;dl class=\"row mt-4\">     &lt;%- @product.properties.each do |name, value| %>       &lt;dt class=\"col-sm-3\">&lt;%= name.to_s.titleize %>&lt;\/dt>       &lt;dd class=\"col-sm-9\">&lt;%= value %>&lt;\/dd>     &lt;% end %>   &lt;\/dl> &lt;\/turbo-frame> <\/code><\/pre>\n<p><strong>app\/views\/products\/_review.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;turbo-frame id=\"&lt;%= dom_id(@product, 'reviews') %>\">   &lt;%- @product.reviews.each do |review| %>     &lt;div class=\"card mb-3\">       &lt;div class=\"card-body\">         &lt;div class=\"card-title\">           &lt;%= review.author %>         &lt;\/div>         &lt;div class=\"card-text\">           &lt;%= review.content %>         &lt;\/div>       &lt;\/div>     &lt;\/div>   &lt;% end %> &lt;\/turbo-frame><\/code><\/pre>\n<p>\u0422\u0443\u0442 \u043c\u044b \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b, \u043d\u043e \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0438 \u0432\u0441\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441 \u043c\u0430\u043a\u0435\u0442\u043e\u043c. \u0413\u043b\u0430\u0432\u043d\u043e\u0435 &#8212; \u043e\u0442\u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u0442\u0435\u0433 <code>turbo-frame<\/code> \u0441 \u0442\u0435\u043c \u0436\u0435 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c, \u0447\u0442\u043e \u0438 \u0443 \u0442\u0435\u0433\u0430 \u043a\u0443\u0434\u0430 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0431\u0443\u0434\u0435\u0442 \u0432\u0441\u0442\u0430\u0432\u043b\u0435\u043d.<\/p>\n<p>\u0412 \u0446\u0435\u043b\u043e\u043c, \u044d\u0442\u043e \u0432\u0441\u0435, \u0447\u0442\u043e \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u201c\u043b\u0435\u043d\u0438\u0432\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443\u201d. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u044f \u043d\u0435 \u0441\u043c\u043e\u0433 \u043d\u0430\u0439\u0442\u0438 \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043e\u0448\u0438\u0431\u043e\u043a \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 turbo frames, \u043d\u043e \u043d\u0430\u0431\u0440\u043e\u0441\u0430\u043b \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u0430\u043c \u0432 \u043f\u043e\u043c\u043e\u0449\u044c:<\/p>\n<p><strong>app\/controllers\/any_controller.rb<\/strong><\/p>\n<pre><code class=\"ruby\">def general   @product = Product.find(params[:id])    raise StandardError, 'Some error'    render partial: 'products\/general', locals: { in_cart: product_in_cart?(@product) } rescue StandardError   render partial: 'common\/turbo_error',          locals: { id: dom_id(@product, 'general'), error_message: 'Oops. Something went wrong' } end<\/code><\/pre>\n<p><strong>app\/views\/common\/_turbo_error.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;turbo-frame id=\"&lt;%= id %>\">   &lt;%= error_message %> &lt;\/turbo-frame><\/code><\/pre>\n<p>\u0415\u0449\u0451 \u043e\u0434\u043d\u0430 \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0449\u044c, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441 turbo frames, \u044d\u0442\u043e \u0437\u0430\u043c\u0435\u043d\u044f\u0442\u044c \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0432 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u044b. \u0418\u0434\u0435\u044f \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0430\u044f. \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0434\u043e\u043b\u0436\u043d\u043e \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0442\u0435\u0433 <code>turbo-frame<\/code> \u0438 Turbo \u0437\u0430\u043c\u0435\u043d\u0438\u0442 \u0435\u0433\u043e \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u043c \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0442\u043e\u0432\u0430\u0440\u044b \u0432 \u043a\u043e\u0440\u0437\u0438\u043d\u0435.<\/p>\n<p><strong>app\/controllers\/products_controller.rb<\/strong><\/p>\n<pre><code class=\"diff\">class ProductsController &lt; ApplicationController   include ActionView::RecordIdentifier    def show     @product = Product.find(params[:id])   end    def general     @product = Product.find(params[:id])  -   render partial: 'products\/general' +   render partial: 'products\/general', locals: { in_cart: product_in_cart?(@product) }   end    def properties     @product = Product.find(params[:id])      render partial: 'products\/properties'   end    def reviews     @product = Product.find(params[:id])      render partial: 'products\/reviews'   end  +  def add_to_cart +    @product = Product.find(params[:id]) + +    session[:cart] = (session[:cart] || []) &lt;&lt; @product.id + +    render partial: 'products\/general', locals: { in_cart: product_in_cart?(@product) } +  end + +  def remove_from_cart +    @product = Product.find(params[:id]) + +    session[:cart] = (session[:cart] || []).reject { |id| @product.id == id } + +    render partial: 'products\/general', locals: { in_cart: product_in_cart?(@product) } +  end + +  private + +  def product_in_cart?(product) +    return false unless product &amp;&amp; session[:cart] + +    session[:cart].include?(product.id) +  end end<\/code><\/pre>\n<p><strong>app\/views\/products\/_general.html.erb<\/strong><\/p>\n<pre><code class=\"diff\">&lt;turbo-frame id=\"&lt;%= dom_id(@product, 'general') %>\" loading=\"lazy\" src=\"&lt;%= general_product_path(product_id: @product) %>\">   &lt;div class=\"product--general\">     &lt;h1>       &lt;%= @product.title %>     &lt;\/h1>     &lt;div class=\"row mt-4\">       &lt;div class=\"col\">         &lt;div class=\"product--image\">           &lt;%= image_tag @product.image %>         &lt;\/div>        &lt;\/div>       &lt;div class=\"col\">         &lt;h3>           &lt;%= @product.price %>         &lt;\/h3>  +        &lt;%- if in_cart %> +          &lt;%= form_with(url: remove_from_cart_product_path) do |f| %> +            &lt;%= f.submit 'Remove from cart', class: 'my-3 btn btn-danger' %> +          &lt;%- end %> +        &lt;%- else %> +          &lt;%= form_with(url: add_to_cart_product_path) do |f| %> +            &lt;%= f.submit 'Add to cart', class: 'my-3 btn btn-success' %> +          &lt;%- end %> +        &lt;% end %>         &lt;%= @product.content %>       &lt;\/div>     &lt;\/div>   &lt;\/div> &lt;\/turbo-frame> <\/code><\/pre>\n<p>\u0412\u044b \u0432\u0438\u0434\u0438\u0442\u0435, \u0447\u0442\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0443 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0435\u0441\u0442\u044c \u044d\u043a\u0448\u0435\u043d\u044b \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0432\u0430\u0440\u043e\u0432. \u041e\u0431\u0430 \u044d\u0442\u0438\u0445 \u043c\u0435\u0442\u043e\u0434\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u044f\u0442 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 <code>general<\/code> \u0438 \u043e\u043d \u0432\u043e\u043b\u0448\u0435\u0431\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435. \u042d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u0445 <code>.js.erb<\/code>. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, turbo &#8212; \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e JS \u043a\u043e\u0434\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u043a \u0442\u043e\u043c\u0443 \u0436\u0435, \u043b\u0435\u0436\u0438\u0442 \u0432 \u043f\u0430\u043f\u043a\u0435 <code>views<\/code>.<\/p>\n<h4>Turbo Streams<\/h4>\n<p>Turbo \u0434\u0430\u043b \u043d\u0430\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f HTML \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 &#8212; Turbo Streams. \u041e\u043d \u0434\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 DOM \u0438 \u0432\u044b \u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u044b \u0437\u0430\u043c\u0435\u043d\u043e\u0439 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0430, \u043a\u0430\u043a \u044d\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441 Turbo frames. \u042d\u0442a \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u044f \u0441 DOM \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442\u0441\u044f <code>action<\/code> \u0438 \u043e\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043d\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0445 <code>targets<\/code>, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0438\u0437 \u043a\u0430\u043a\u043e\u0433\u043e-\u043b\u0438\u0431\u043e \u0441\u0435\u043b\u0435\u043a\u0442\u043e\u0440\u0430. Turbo streams \u0434\u0430\u0435\u0442 \u0432\u0430\u043c 7 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f:<\/p>\n<ul>\n<li>\n<p>append &#8212; \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c html \u0432 \u043d\u0430\u0447\u0430\u043b\u043e \u0446\u0435\u043b\u0438.\u00a0<\/p>\n<\/li>\n<li>\n<p>prepend &#8212; \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c html \u0432 \u043a\u043e\u043d\u0435\u0446 \u0446\u0435\u043b\u0438.\u00a0<\/p>\n<\/li>\n<li>\n<p>replace &#8212; \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u0441\u044e \u0446\u0435\u043b\u044c \u043d\u0430 html.\u00a0<\/p>\n<\/li>\n<li>\n<p>update &#8212; \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c html \u0432\u043d\u0443\u0442\u0440\u0438 \u0446\u0435\u043b\u0438.\u00a0<\/p>\n<\/li>\n<li>\n<p>remove &#8212; \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u044e \u0446\u0435\u043b\u044c.\u00a0<\/p>\n<\/li>\n<li>\n<p>before &#8212; \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c html \u043f\u043e\u0441\u043b\u0435 \u0446\u0435\u043b\u0438.\u00a0<\/p>\n<\/li>\n<li>\n<p>after &#8212; \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c html \u043f\u0435\u0440\u0435\u0434 \u0446\u0435\u043b\u044c\u044e<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u044b \u043e\u0431\u044b\u0447\u043d\u043e \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0441\u043b\u044b\u0448\u0430\u0442\u044c\/\u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043e Turbo Streams, \u043a\u043e\u0433\u0434\u0430 \u0440\u0435\u0447\u044c \u0437\u0430\u0445\u043e\u0434\u0438\u0442 \u043f\u0440\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0435\u0449\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0447\u0430\u0442\u0430. \u041d\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043f\u043e\u043f\u0440\u043e\u0449\u0435 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a Turbo Streams \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u044b \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c \u0441 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u043c: \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u043e\u0442\u0437\u044b\u0432 \u0438 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0442\u0437\u044b\u0432\u043e\u0432. \u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u044f \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0442\u0437\u044b\u0432\u043e\u0432 \u0438 \u0444\u043e\u0440\u043c\u0443 \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043e\u0442\u0437\u044b\u0432\u0430: <\/p>\n<p><strong>app\/views\/products\/show.html.erb<\/strong><\/p>\n<pre><code class=\"diff\">&lt;ul class=\"nav nav-tabs\" id=\"product-tab\" role=\"tablist\">     &lt;li class=\"nav-item\" role=\"presentation\">       &lt;button class=\"nav-link active\" id=\"home-tab\" data-bs-toggle=\"tab\" data-bs-target=\"#general\" type=\"button\" role=\"tab\" aria-controls=\"general\" aria-selected=\"true\">General&lt;\/button>     &lt;\/li>     &lt;li class=\"nav-item\" role=\"presentation\">       &lt;button class=\"nav-link\" id=\"properties-tab\" data-bs-toggle=\"tab\" data-bs-target=\"#properties\" type=\"button\" role=\"tab\" aria-controls=\"properties\" aria-selected=\"false\">Properties&lt;\/button>     &lt;\/li>     &lt;li class=\"nav-item\" role=\"presentation\"> -      &lt;button class=\"nav-link\" id=\"reviews-tab\" data-bs-toggle=\"tab\" data-bs-target=\"#reviews\" type=\"button\" role=\"tab\" aria-controls=\"reviews\" aria-selected=\"false\">Reviews&lt;\/button> +      &lt;button +        class=\"nav-link\" +        id=\"reviews-tab\" +        data-bs-toggle=\"tab\" +        data-bs-target=\"#reviews\" +        type=\"button\" +        role=\"tab\" +        aria-controls=\"reviews\" +        aria-selected=\"false\" +      > +        Reviews +        &lt;span id=&lt;%= dom_id(@product, 'reviews_count') %>> +          &lt;%= render(partial: 'products\/reviews\/count_badge', locals: { count: @product.reviews.count }) %> +        &lt;\/span> +      &lt;\/button>     &lt;\/li>   &lt;\/ul><\/code><\/pre>\n<p><strong>app\/views\/products\/_reviews.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;turbo-frame id=\"&lt;%= dom_id(@product, 'reviews') %>\">   &lt;%= render(partial: 'products\/reviews\/form', locals: { product: @product }) %>    &lt;div id=\"&lt;%= dom_id(@product, 'reviews_list') %>\">     &lt;%- @product.reviews.each do |review| %>       &lt;%= render(partial: 'products\/reviews\/card', locals: { review: review }) %>     &lt;% end %>   &lt;\/div> &lt;\/turbo-frame> <\/code><\/pre>\n<p><strong>app\/views\/products\/reviews\/_count_badge.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;span class=\"badge bg-primary\">   &lt;%= count %> &lt;\/span><\/code><\/pre>\n<p><strong>app\/views\/products\/reviews\/_form.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;%= form_with(url: add_review_product_path(id: product.id), class: 'mb-4', id: dom_id(product, 'reviews_form')) do |f| %>   &lt;%= f.text_area :review, class: \"form-control mb-1\" %>   &lt;%= f.submit 'Add a review', class: 'btn btn-primary' %> &lt;% end %> <\/code><\/pre>\n<p><strong>app\/views\/products\/reviews\/_card.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;div class=\"card mb-3\">   &lt;div class=\"card-body\">     &lt;div class=\"card-title\">       &lt;%= review.author %>     &lt;\/div>     &lt;div class=\"card-text\">       &lt;%= review.content %>     &lt;\/div>   &lt;\/div> &lt;\/div><\/code><\/pre>\n<p>\u0418 \u0432\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/454\/79a\/aad\/45479aaad413d52106705dab6e8db7d2.jpg\" alt=\"\u041b\u044e\u0434\u044f\u043c \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f\" title=\"\u041b\u044e\u0434\u044f\u043c \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f\" width=\"2944\" height=\"1360\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/454\/79a\/aad\/45479aaad413d52106705dab6e8db7d2.jpg\" data-blurred=\"true\"\/><figcaption>\u041b\u044e\u0434\u044f\u043c \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f<\/figcaption><\/figure>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f Turbo Streams: <\/p>\n<p><strong>app\/controllers\/products_controller.rb<\/strong><\/p>\n<pre><code class=\"diff\">+ def add_review +    @product = Product.find(params[:id]) +    @review = @product.add_review(author: 'You', content: params[:review]) + end<\/code><\/pre>\n<p><strong>app\/views\/products\/add_review.turbo_stream.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;%= turbo_stream.update(dom_id(@product, 'reviews_count')) do %>   &lt;%= render(partial: 'products\/reviews\/count_badge', locals: { count: @product.reviews.count }) %> &lt;% end %>  &lt;%= turbo_stream.replace(dom_id(@product, 'reviews_form')) do %>   &lt;%= render(partial: 'products\/reviews\/form', locals: { product: @product }) %> &lt;% end %>  &lt;%= turbo_stream.prepend(dom_id(@product, 'reviews_list')) do %>   &lt;%= render(partial: 'products\/reviews\/card', locals: { review: @review }) %> &lt;% end %> <\/code><\/pre>\n<p>\u0421\u0430\u043c\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u0432\u043e\u0442 \u0437\u0434\u0435\u0441\u044c <code>_add_review.turbo_stream.erb<\/code>. \u0424\u043e\u0440\u043c\u0430\u0442 <code>turbo_stream<\/code>\u00a0\u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u043e\u0432\u044b\u043c \u0434\u043b\u044f \u0432\u0430\u0441, \u0435\u0441\u043b\u0438 \u0432\u044b \u0432\u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u0442\u0435\u0441\u044c \u0441 Turbo Streams. Turbo \u0442\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b HTTP \u043e\u0442\u0432\u0435\u0442 \u0438\u043c\u0435\u043b \u043a\u043e\u043d\u0442\u0435\u043d\u0442-\u0442\u0438\u043f <code>text\/vnd.turbo-stream.html<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043b\u0438\u0431\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c <code>content_type: \"text\/vnd.turbo-stream.html\"<\/code> \u0432 \u043c\u0435\u0442\u043e\u0434 render \u00a0\u0432 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430, \u043b\u0438\u0431\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f <code>.turbo_stream.erb<\/code> \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u0448\u0430\u0431\u043b\u043e\u043d\u0430. \u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043c\u043d\u0435 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u043d\u044b\u043c. \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0441\u0443\u0431\u044a\u0435\u043a\u0442 \u0432 <code>_add_review.turbo_stream.erb<\/code> \u044d\u0442\u043e \u0445\u044d\u043b\u043f\u0435\u0440 <code>turbo_stream<\/code>. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0435\u0433\u043e \u0434\u043b\u044f \u0432\u044b\u0437\u043e\u0432\u0430 \u0440\u0430\u043d\u0435\u0435 \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439. \u0410 \u0435\u0441\u043b\u0438 \u0442\u043e\u0447\u043d\u0435\u0435, \u043e\u043d \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 XML \u0442\u0435\u0433\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442, \u043a\u0430\u043a\u0438\u0435 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0438 DOM \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u043d\u044b. \u042d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b \u0434\u0435\u043b\u0430\u0435\u0442 \u0442\u0440\u0438 \u0432\u0435\u0449\u0438:<\/p>\n<ul>\n<li>\n<p>\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441\u0447\u0435\u0442\u0447\u0438\u043a \u043e\u0442\u0437\u044b\u0432\u043e\u0432 &#8212; \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0442\u0435\u0433\u0430 \u0441 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c <code>dom_id(@product, 'reviews_count')<\/code>\u00a0<\/p>\n<\/li>\n<li>\n<p>\u0421\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u0444\u043e\u0440\u043c\u0443 \u043e\u0431\u0437\u043e\u0440\u0430 &#8212; \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 \u0432\u0435\u0441\u044c \u0442\u0435\u0433 \u043d\u0430 id <code>dom_id(@product, 'reviews_count').<\/code>\u00a0<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 \u043e\u0431\u0437\u043e\u0440 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 &#8212; \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0432 \u043d\u0430\u0447\u0430\u043b\u043e \u0442\u0435\u0433\u0430 \u0441 id <code>dom_id(@product, 'reviews_list')<\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6aa\/ba1\/054\/6aaba1054de904c6e2ad5accbb1c7f12.gif\" alt=\"\u0415\u0449\u0435 \u043e\u0434\u0438\u043d \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\" title=\"\u0415\u0449\u0435 \u043e\u0434\u0438\u043d \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\" width=\"1214\" height=\"801\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6aa\/ba1\/054\/6aaba1054de904c6e2ad5accbb1c7f12.gif\"\/><figcaption>\u0415\u0449\u0435 \u043e\u0434\u0438\u043d \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c<\/figcaption><\/figure>\n<p>\u042d\u0442\u043e \u0432\u0441\u0435, \u0447\u0442\u043e \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0411\u0435\u0437 \u0435\u0434\u0438\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 JS \u043a\u043e\u0434\u0430! \u0418 \u044d\u0442\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439.\u00a0<\/p>\n<p>\u0421 Turbo Streams \u0432\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e WebSocket. \u042d\u0442\u043e \u043d\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0441 \u043d\u0430\u0448\u0435\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b. \u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043f\u0440\u0438\u043c\u0435\u0440\u0443 \u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043e\u0442\u0437\u044b\u0432\u044b \u0432\u043e \u0432\u0441\u0435\u0445 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445, \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u043d\u043e\u0432\u044b\u0439 \u043e\u0442\u0437\u044b\u0432.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u043c\u044b \u043d\u0430\u0447\u043d\u0435\u043c, \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0441\u0432\u043e\u0439 Gemfile \u0433\u0435\u043c <code>turbo-rails<\/code> \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u044d\u0442\u0443 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>bundle exec rails turbo:install<\/code><\/p>\n<p>\u041e\u043d \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442 <code>@hotwired\/turbo-rails<\/code> \u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u0442 \u0430\u0434\u0430\u043f\u0442\u0435\u0440 Action Cable \u0441 <code>async<\/code>(\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e) \u043d\u0430 redis. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0433\u043e\u0442\u043e\u0432\u044b \u043a \u0440\u0430\u0431\u043e\u0442\u0435 \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438.<\/p>\n<p>\u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u044d\u0442\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430. \u042d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0445\u044d\u043b\u043f\u0435\u0440\u0443\u00a0 <code>turbo_stream_from<\/code>. \u0412\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442:<\/p>\n<p><strong>app\/views\/products\/show.html.erb<\/strong><\/p>\n<pre><code class=\"diff\">&lt;div class=\"product\"> +  &lt;%= turbo_stream_from @product %>    &lt;ul class=\"nav nav-tabs\" id=\"product-tab\" role=\"tablist\"><\/code><\/pre>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c, \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0442\u0435\u0433\u0438 <code>turbo-frame<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442, \u043a\u0430\u043a\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b \u043d\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435, \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u043c \u044d\u0442\u0438 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432\u0441\u0435\u043c \u0441\u043b\u0443\u0448\u0430\u0442\u0435\u043b\u044f\u043c (\u0432\u0441\u0435\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u043c \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430)<\/p>\n<p><strong>app\/controllers\/products_controller.rb<\/strong><\/p>\n<pre><code class=\"diff\">def add_review   @product = Product.find(params[:id])   @review = @product.add_review(author: 'You', content: params[:review])  +  Turbo::StreamsChannel.broadcast_update_to( +    @product, +    target: ActionView::RecordIdentifier.dom_id(@product, 'reviews_count'), +    partial: 'products\/reviews\/count_badge', +    locals: { count: @product.reviews.count } +  ) + +  Turbo::StreamsChannel.broadcast_prepend_to( +    @product, +    target: ActionView::RecordIdentifier.dom_id(@product, 'reviews_list'), +    partial: 'products\/reviews\/card', +    locals: { review: @review } +  ) end<\/code><\/pre>\n<p>\u0418 \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0434\u0432\u0430\u0436\u0434\u044b, \u0443\u0434\u0430\u043b\u0438\u043c \u0438\u0445 \u0438\u0437 \u041d\u0422\u0422\u0420 \u043e\u0442\u0432\u0435\u0442\u0430<\/p>\n<p><strong>app\/views\/products\/add_review.turbo_stream.erb<\/strong><\/p>\n<pre><code class=\"diff\">- &lt;%= turbo_stream.update(dom_id(@product, 'reviews_count')) do %> -   &lt;%= render(partial: 'products\/reviews\/count_badge', locals: { count: @product.reviews.count }) %> - &lt;% end %>  &lt;%= turbo_stream.replace(dom_id(@product, 'reviews_form')) do %>   &lt;%= render(partial: 'products\/reviews\/form', locals: { product: @product }) %> &lt;% end %>  - &lt;%= turbo_stream.prepend(dom_id(@product, 'reviews_list')) do %> -   &lt;%= render(partial: 'products\/reviews\/card', locals: { review: @review }) %> - &lt;% end %><\/code><\/pre>\n<p>\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u044b \u0432\u0441\u0435 \u0435\u0449\u0435 \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u0432 HTTP \u043e\u0442\u0432\u0435\u0442\u0435, \u0442\u0430\u043a \u043a\u0430\u043a \u0444\u043e\u0440\u043c\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u043e\u0447\u0438\u0449\u0435\u043d\u0430 \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438, \u0438 \u043c\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u043e\u0447\u0438\u0449\u0430\u0442\u044c \u0435\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/eca\/b49\/19b\/ecab4919bbe7bf7a20cceebd799ac217.gif\" width=\"1792\" height=\"1012\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/eca\/b49\/19b\/ecab4919bbe7bf7a20cceebd799ac217.gif\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0451, \u0447\u0442\u043e \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e, \u0435\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 Rails \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043a\u043e\u043c\u043c\u0443\u043d\u0438\u043a\u0430\u0446\u0438\u0438 \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u041c\u0430\u0433\u0438\u044f \u0440\u0435\u043b\u044c\u0441\u043e\u0432 \u0432\u043e \u0432\u0441\u0435\u0439 \u0435\u0451 \u043a\u0440\u0430\u0441\u0435!<\/p>\n<p>\u041a\u043e\u043c\u0430\u043d\u0434\u0430 Rails \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u043b\u0430 \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443, \u0447\u0442\u043e\u0431\u044b \u0441\u0432\u0435\u0441\u0442\u0438 \u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c\u0443 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441\u043e \u0432\u0441\u0435\u043c \u044d\u0442\u0438\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043c \u0438 \u0441\u0442\u0440\u0430\u0448\u043d\u044b\u043c \u043c\u0438\u0440\u043e\u043c JS, \u043e\u0441\u0442\u0430\u0432\u0438\u0432 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u0438\u0440 \u043c\u043e\u0436\u0435\u0442 (\u0438, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u0431\u0443\u0434\u0435\u0442) \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c \u043c\u043e\u0436\u0435\u0442 \u0434\u0430\u0442\u044c Turbo. \u0418 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 Rails \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0430 <a href=\"https:\/\/stimulus.hotwired.dev\/\" rel=\"noopener noreferrer nofollow\"><u>Stimulus<\/u><\/a> \u0438 <a href=\"https:\/\/github.com\/rails\/request.js\" rel=\"noopener noreferrer nofollow\"><u>request.js<\/u><\/a>, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0430\u0448\u0443 \u0436\u0438\u0437\u043d\u044c \u043b\u0435\u0433\u0447\u0435, \u043a\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u0432\u0441\u0435-\u0442\u0430\u043a\u0438 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c JS \u043a\u043e\u0434 \u0432 Rails \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438. \u0412\u043f\u0440\u043e\u0447\u0435\u043c, \u044d\u0442\u043e \u0443\u0436\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0434\u0440\u0443\u0433\u0430\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \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:\/\/habr.com\/ru\/articles\/590381\/\"> https:\/\/habr.com\/ru\/articles\/590381\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u043e\u043f\u0440\u0435\u043a\u0438 \u0441\u043b\u0443\u0445\u0430\u043c \u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0430\u0445 \u0434\u0435\u0432\u0435\u043b\u043e\u043f\u0435\u0440\u0441\u043a\u0438\u0445 \u043a\u043e\u043c\u044c\u044e\u043d\u0438\u0442\u0438, Rails \u043d\u0435 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0435\u0439 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0435\u0439, \u043e\u043d \u043d\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044f \u0443\u043c\u0438\u0440\u0430\u0442\u044c, \u0438 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432\u0430\u0448\u0435\u0433\u043e \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0418 \u043e\u0434\u043d\u0430 \u0438\u0437 \u043f\u0440\u0438\u0447\u0438\u043d \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0443 Rails \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043a\u0440\u044b\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0442\u0438\u043f\u0438\u0447\u043d\u043e\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0412\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0434\u0443\u043c\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u041d\u0422\u0422\u0420 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0432\u0432\u043e\u0434\u0430 \u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u0431\u0430\u0437\u044b, \u043a\u0430\u043a \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c HTML, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0443\u0432\u0438\u0434\u044f\u0442 \u0432 \u0441\u0432\u043e\u0438\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445, \u0438 \u0434\u0430\u0436\u0435 \u043a\u0430\u043a &#171;\u0432\u0434\u043e\u0445\u043d\u0443\u0442\u044c \u0436\u0438\u0437\u043d\u044c&#187; \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. <\/p>\n<p>\u042f \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0432 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432\u043e\u0439\u00a0\u0441\u0442\u0443\u0434\u0438\u0438 \u0438 \u043a \u043d\u0430\u043c \u0447\u0430\u0441\u0442\u043e \u043e\u0431\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0437\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 MVP \u0434\u043b\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u0432. \u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u044b \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b Rails \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0442 \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u043d\u0438\u0445 \u0438 \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<\/p>\n<h2>\u0422\u043e, \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438: rails-ujs, turbolinks <\/h2>\n<h4>Rails UJS<\/h4>\n<p>\u0414\u0430\u0432\u043d\u044b\u043c \u0434\u0430\u0432\u043d\u043e, \u043a\u043e\u0433\u0434\u0430 \u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0441\u0432\u0435\u0440\u0441\u0442\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u043f\u0435\u0440\u0432\u0443\u044e HTML \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043a\u0443, \u0443 Rails \u0443\u0436\u0435 \u0431\u044b\u043b \u043a\u0440\u0443\u0442\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 jquery-ujs (unobtrusive javascript), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f rails-ujs. \u041e\u043d \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441 \u0440\u0435\u043b\u044c\u0441\u043e\u0432\u044b\u043c \u0431\u044d\u043a\u0435\u043d\u0434\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0430\u0440\u043e\u0447\u043a\u0443 AJAX \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043c\u0430\u043b\u043e\u0439 \u0446\u0435\u043d\u043e\u0439.<\/p>\n<p>\u041c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0432\u0440\u043e\u0434\u0435 \u044d\u0442\u043e\u0433\u043e:<\/p>\n<p><strong>app\/controllers\/money_controller.rb<\/strong> <\/p>\n<pre><code class=\"ruby\">class MoneyController &lt; ApplicationController   def show     @money = GetAllMoney.call   end    def destroy     SpendAllMoney.call   end end<\/code><\/pre>\n<p><strong>views\/money\/show.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;div class=\"money\">   &lt;h3>Your money&lt;\/h3>   &lt;span id=\"money-amount\">&lt;%= @money %>&lt;\/span>   &lt;span>$&lt;\/span>    &lt;%= link_to 'Spend all money',               money_path,               method: 'delete',               remote: true,               data: { confirm: 'Do you want to spend all money?' },               class: 'spend-money-button' %> &lt;\/div><\/code><\/pre>\n<p><strong>views\/money\/destroy.js<\/strong><\/p>\n<pre><code class=\"javascript\">document.querySelector('#money-amount').innerHTML = 0<\/code><\/pre>\n<figure class=\"full-width\"><figcaption>\u041d\u043e\u0432\u044b\u0439 \u0441\u0430\u0439\u0442 \u0441\u043e \u0441\u0442\u0430\u0432\u043a\u0430\u043c\u0438 \u043d\u0430 \u0441\u043f\u043e\u0440\u0442. \u041b\u0443\u0447\u0448\u0438\u0435 \u043a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442\u044b xD <\/figcaption><\/figure>\n<p>\u0418\u0442\u0430\u043a, \u0432\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 AJAX \u0437\u0430\u043f\u0440\u043e\u0441, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0432\u0441\u0435\u0433\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e HTML \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0438 \u043e\u0434\u0438\u043d JS \u0444\u0430\u0439\u043b \u0441 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u0447\u043a\u043e\u0439 \u043a\u043e\u0434\u0430. \u041a\u0440\u0443\u0442\u043e, \u043f\u0440\u0430\u0432\u0434\u0430?<\/p>\n<h4>Turbolinks<\/h4>\n<p>\u0415\u0449\u0435 \u043e\u0434\u0438\u043d \u0441\u0442\u0430\u0440\u043e\u0436\u0438\u043b \u0432 \u043c\u0438\u0440\u0435 Rails &#8212; Turbolinks. \u042d\u0442\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0441\u0442\u0430\u0434\u0438\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043d\u043e \u043e \u0435\u0435 \u043f\u0440\u0435\u0435\u043c\u043d\u0438\u043a\u0435 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0437\u0436\u0435. \u0412 \u0434\u0432\u0443\u0445 \u0441\u043b\u043e\u0432\u0430\u0445, Turbolinks \u043f\u0440\u0438\u043d\u043e\u0441\u0438\u0442 \u0432\u0430\u043c SPA \u043e\u043f\u044b\u0442 \u043f\u043e\u0447\u0442\u0438 \u0431\u0435\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0433\u043e \u043a\u043e\u0434\u0430. \u0415\u0441\u043b\u0438 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e, \u0442\u043e \u044d\u0442\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430:<\/p>\n<ul>\n<li>\n<p>\u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043d\u043e\u0432\u044b\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e JS \u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 \u0435\u0433\u043e \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430;\u00a0<\/p>\n<\/li>\n<li>\n<p>\u043e\u043d\u0430 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0435 \u043f\u043e\u0441\u0435\u0449\u0435\u043d\u0438\u044f \u043a\u0430\u0437\u0430\u043b\u0438\u0441\u044c \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u044b\u043c\u0438; <\/p>\n<\/li>\n<li>\n<p>\u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043d\u0435\u0438\u0437\u043c\u0435\u043d\u043d\u044b\u043c\u0438 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0435\u0440\u0432\u044b\u0435 \u0434\u0432\u0435 \u0444\u0438\u0447\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438, \u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u044f\u0432\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0430 \u0432 \u0432\u0430\u0448\u0435\u043c \u043a\u043e\u0434\u0435. \u042f \u043f\u043e\u043a\u0430\u0436\u0443 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0438 \u043d\u0430\u0434\u0443\u043c\u0430\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0441\u0442\u0438\u0447\u044c \u0441 \u0435\u0435 \u043f\u043e\u043c\u043e\u0449\u044c\u044e. \u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0433\u0434\u0435-\u0442\u043e \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0435\u0441\u0442\u044c \u0441\u0447\u0435\u0442\u0447\u0438\u043a \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439.<\/p>\n<p><strong>app\/helpers\/application_helper.rb<\/strong><\/p>\n<pre><code class=\"ruby\">module ApplicationHelper   def notifications_count     sleep 3 # emulate some calculations      10   end    def articles     Article.last(5)   end end<\/code><\/pre>\n<p><strong>app\/views\/layouts\/application.html.erb<\/strong><\/p>\n<pre><code class=\"xml\">&lt;!DOCTYPE html> &lt;html> &lt;head>   &lt;title>Turbolinks&lt;\/title>   &lt;meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">   &lt;%= csrf_meta_tags %>   &lt;%= csp_meta_tag %>    &lt;%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>   &lt;%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> &lt;\/head>  &lt;body> &lt;div class=\"container\">   &lt;nav class=\"navigation\">     &lt;ul>       &lt;%- articles.each do |article| %>         &lt;li>           &lt;%= link_to article.title, article_path(article.id) %>         &lt;\/li>       &lt;% end %>     &lt;\/ul>     &lt;div class=\"notifications\">       &lt;div class=\"notifications-badge\">         &lt;%= notifications_count %>       &lt;\/div>     &lt;\/div>   &lt;\/nav>   &lt;section class=\"content\">     &lt;%= yield %>   &lt;\/section> &lt;\/div> &lt;\/body> &lt;\/html> <\/code><\/pre>\n<p>\u041f\u043e\u0434\u0441\u0447\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043d\u044f\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0432\u0440\u0435\u043c\u044f, \u043d\u043e \u044d\u0442\u043e \u0446\u0435\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432\u044b \u043f\u043b\u0430\u0442\u0438\u0442\u0435 \u0437\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0<\/p>\n<p>\u041f\u043e\u0437\u0436\u0435, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u044b \u0442\u0430\u043a\u0436\u0435 \u0437\u0430\u0445\u043e\u0442\u0438\u0442\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439, \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0432\u0448\u0438\u0441\u044c \u043d\u0430 \u0442\u0430\u043a\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. \u0423 Rails \u0434\u0430\u0436\u0435 \u0435\u0441\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 Action Cable \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e.<\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044d\u0442\u0430 \u0440\u0430\u0431\u043e\u0442\u0430 \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u043d\u0430 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0435, \u0432\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u043c\u0435\u0436\u0434\u0443 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430\u043c\u0438, \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u043c\u0438 Turbolinks. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0432\u0441\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043d\u043e \u0437\u043d\u0430\u0435\u0442\u0435\u2026 \u0435\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0432\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0432\u0435\u0449\u0438 \u0432 CS\u2026 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u043a\u044d\u0448\u0430\u2026 \u0438 \u043c\u044b \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e Turbolinks.<\/p>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043a\u043e\u0434, \u0435\u0441\u043b\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u043b\u0430\u0441\u044c Turbolinks \u0438 \u0438 \u0437\u0430\u043f\u0440\u0435\u0442\u0438\u0442\u044c Turbolinks \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0447\u0430\u0441\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b. \u0412\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442:<\/p>\n<p><strong>app\/helpers\/application_helper.rb<\/strong><\/p>\n<pre><code class=\"diff\">module ApplicationHelper   def notifications_count +   return nil if request.headers['Turbolinks-Referrer'].present? +     sleep 3 # emulate some calculations      10   end    def articles     Article.last(5)   end end<\/code><\/pre>\n<p><strong>app\/views\/layouts\/application.html.erb<\/strong><\/p>\n<pre><code class=\"diff\">&lt;div class=\"notifications\"> -   &lt;div class=\"notifications-badge\" id=\"notifications-badge\"> +   &lt;div class=\"notifications-badge\" id=\"notifications-badge\" data-turbolinks-permanent>     &lt;%= notifications_count %>   &lt;\/div> &lt;\/div><\/code><\/pre>\n<h4>\u0427\u0435\u0433\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u043d\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e<\/h4>\n<p>\u0421\u0442\u0430\u0440\u0435\u043d\u044c\u043a\u0438\u0435 \u0444\u0438\u0447\u0438 Rails \u0434\u0435\u043b\u0430\u044e\u0442 \u0441\u0432\u043e\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u0445\u043e\u0440\u043e\u0448\u043e \u0438 \u043c\u043d\u043e\u0433\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0442\u0440\u043e\u0438\u043b\u0438 \u043d\u0430 \u043d\u0438\u0445 \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u044e\u0437\u0435\u0440\u0441\u043a\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0431\u0435\u0437 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043b\u043e\u0436\u043d\u043e\u0433\u043e JS \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430. \u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u044d\u0442\u043e, \u043d\u0430\u043c \u0432\u0441\u0435 \u0435\u0449\u0435 \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u0444\u0438\u0447, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0434\u0435\u043b\u0430\u044e\u0442 \u043d\u0430\u0448\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0438 \u0443\u043f\u0440\u043e\u0441\u0442\u044f\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430.<\/p>\n<h3>\u041d\u043e\u0432\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u043e\u0442 \u043a\u043e\u043c\u0430\u043d\u0434\u044b Rails<\/h3>\n<p>\u0412 \u043d\u0430\u0447\u0430\u043b\u0435 2021 \u0433\u043e\u0434\u0430 DHH \u043e\u0431\u044a\u044f\u0432\u0438\u043b \u043e \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u0438 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430 Hotwire, \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u0430 Rails \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432. \u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e Hotwire \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0434\u043b\u044f \u0441\u0435\u043c\u0435\u0439\u0441\u0442\u0432\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u044d\u0442\u0430 \u0441\u0435\u043c\u044c\u044f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u0430\u043b\u0430. \u041f\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u043d\u0430 \u043e\u043a\u0442\u044f\u0431\u0440\u044c 2021 \u0433\u043e\u0434\u0430 \u0431\u044b\u043b\u043e \u0432\u0441\u0435\u0433\u043e \u0434\u0432\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438: <a href=\"https:\/\/turbo.hotwire.dev\/\" rel=\"noopener noreferrer nofollow\"><u>Turbo<\/u><\/a> \u0438 <a href=\"https:\/\/stimulus.hotwire.dev\/\" rel=\"noopener noreferrer nofollow\"><u>Stimulus<\/u><\/a>.<\/p>\n<p>\u041e\u043d\u0438 \u043e\u0431\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u044b \u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0439 Rails \u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u0435\u0437 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0432\u0430\u0448 \u0432\u0435\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043c\u043e\u043d\u043e\u043b\u0438\u0442. \u042f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e Turbo, \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u043e\u0432\u0430\u044f \u0438 \u0437\u0430\u043c\u0435\u043d\u0438\u0442 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e Turbolinks.<\/p>\n<h4>Turbo<\/h4>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0434\u0443\u043c\u0430\u043b\u0438, \u0447\u0442\u043e Turbolinks \u043f\u043e\u0442\u0435\u0440\u044f\u043b\u0438 \u0441\u0432\u043e\u044e \u0447\u0430\u0441\u0442\u044c &#171;links&#187;, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u0447\u0435\u043c \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f, \u0432\u044b \u043d\u0430 100% \u043f\u0440\u0430\u0432\u044b. \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 Turbo \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0430 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u0435\u0439, \u0433\u0434\u0435 \u043a\u0430\u0436\u0434\u0430\u044f \u0441\u043b\u0443\u0436\u0438\u0442 \u0435\u0434\u0438\u043d\u043e\u0439 \u0446\u0435\u043b\u0438 &#8212; \u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 \u0432\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 HTML, \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0441 \u0440\u0430\u0437\u043d\u0438\u0446\u0435\u0439 \u0432 \u0442\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u0438 \u043a\u0430\u043a \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f:<\/p>\n<ul>\n<li>\n<p>Turbo Drive &#8212; \u0442\u043e\u0442 \u0441\u0442\u0430\u0440\u044b\u0439 \u0434\u043e\u0431\u0440\u044b\u0439 Turbolinks, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u044b \u0437\u043d\u0430\u043a\u043e\u043c\u044b.\u00a0<\/p>\n<\/li>\n<li>\n<p>Turbo Frames &#8212; \u201c\u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435\u201d \u0444\u0440\u0435\u0439\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b, \u043a\u043e\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0444\u0440\u0435\u0439\u043c \u0441 \u0442\u0435\u043c \u0436\u0435 id.\u00a0<\/p>\n<\/li>\n<li>\n<p>Turbo Streams &#8212; \u0434\u0440\u0443\u0433\u043e\u0439 \u0442\u0438\u043f \u0444\u0440\u0435\u0439\u043c\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 HTTP \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438\u043b\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 Websocket.\u00a0<\/p>\n<\/li>\n<li>\n<p>Turbo Native &#8212; \u043e\u0431\u0451\u0440\u0442\u043a\u0430 \u0432\u0430\u0448\u0435\u0433\u043e \u201c\u0442\u0443\u0440\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e\u201d \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0435\u0442 \u0435\u0433\u043e \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0418\u0442\u0430\u043a, \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u0431\u043e \u0432\u0441\u0435\u043c \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443.<\/p>\n<h4>Turbo Drive<\/h4>\n<p>\u041a\u0430\u043a \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b\u043e\u0441\u044c \u0440\u0430\u043d\u0435\u0435, Turbo Drive \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 Turbolinks \u0438 \u0431\u0435\u0440\u0435\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e \u043c\u0435\u0436\u0434\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u043c\u0438. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043f\u043e\u0447\u0442\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c, \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u0430.<\/p>\n<p>\u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0430\u043a\u0435\u0442 npm <\/p>\n<pre><code class=\"bash\">yarn add @hotwired\/turbo<\/code><\/pre>\n<p>\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u044c Turbolinks \u043d\u0430 Turbo \u0432 \u0432\u0430\u0448\u0435\u043c javascript \u043a\u043e\u0434\u0435<\/p>\n<p><strong>app\/javascript\/packs\/application.js<\/strong><\/p>\n<pre><code class=\"diff\">  import Rails from \"@rails\/ujs\" - import Turbolinks from \"turbolinks\" + import * as Turbo from \"@hotwired\/turbo\"     Rails.start() - Turbolinks.start()<\/code><\/pre>\n<p>\u0417\u043c\u0435\u043d\u0438\u0442\u044c <code>data-turbolinks...<\/code> \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u0441 <code>data-turbo\u2026<\/code><\/p>\n<p><strong>app\/views\/layouts\/application.html.erb<\/strong><\/p>\n<pre><code class=\"diff\"> -    &lt;%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> -    &lt;%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> +    &lt;%= stylesheet_link_tag 'application', media: 'all', 'data-turbo-track': 'reload' %> +    &lt;%= javascript_pack_tag 'application', 'data-turbo-track': 'reload' %><\/code><\/pre>\n<p>\u0412\u0430\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 &#8212;\u00a0 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u044b. Turbo drive \u0431\u0435\u0440\u0435\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u0438 \u044d\u0442\u043e. \u041f\u0440\u0435\u0436\u0434\u0435 \u0432\u0441\u0435\u0433\u043e, \u043e\u043d \u043e\u0436\u0438\u0434\u0430\u0435\u0442, \u0447\u0442\u043e redirect \u043f\u043e\u0441\u043b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u043e\u0440\u043c\u044b \u0431\u0443\u0434\u0435\u0442 \u0441\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u043c 303, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c Fetch API \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0437\u0430 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u043e\u043c. \u042d\u0442\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 \u041d\u0422\u0422\u0420 \u0434\u043b\u044f <em>\u043d\u0435\u0438\u043d\u0434\u0435\u043c\u043f\u043e\u0442\u0435\u043d\u0442\u043d\u044b\u0445<\/em> (\u0443\u043c\u043d\u043e\u0435 \u0441\u043b\u043e\u0432\u043e \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f HTTP \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u043e\u043c\u0438\u043c\u043e GET \u0438 HEAD ?) \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0435\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044f \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u043b\u0430\u0441\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 GET. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b \u0431\u0443\u0434\u0443\u0442 \u043b\u0438\u0448\u044c POST \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u043d\u0438 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u044e\u0442 \u0441\u0442\u0430\u0442\u0443\u0441\u044b 301 \u0438 302. \u0422\u0430\u043a \u0447\u0442\u043e \u0432\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u044f\u0432\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u043e\u0434 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0434\u043b\u044f \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0430. \u0418 \u0432\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c:<\/p>\n<p><strong>app\/controllers\/any_controller.rb<\/strong><\/p>\n<pre><code class=\"diff\">-    redirect_to money_path +    redirect_to money_path, status: :see_other<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0438\u043b\u0438 \u0438\u043d\u0430\u0447\u0435 \u0432 \u0440\u0435\u043b\u044c\u0441\u043e\u0432\u044b\u0445 \u0444\u043e\u0440\u043c\u0430\u0445 \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043c\u0435\u0442\u043e\u0434 POST \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f <code>&lt;input type=\"hidden\" name=\"_method\" value=\"patch\"><\/code>, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043a\u0430\u043a\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0432\u0430\u0448\u0438 \u0444\u043e\u0440\u043c\u044b \u0432\u0441\u0435 \u0435\u0449\u0435 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0430 \u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0443\u0436\u0435 \u0432\u0435\u043b\u0438\u0441\u044c <a href=\"https:\/\/github.com\/hotwired\/turbo\/issues\/84\" rel=\"noopener noreferrer nofollow\"><u>\u0431\u0443\u0440\u043d\u044b\u0435 \u0434\u0438\u0441\u043a\u0443\u0441\u0441\u0438\u0438<\/u><\/a>.\u00a0<\/p>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435, \u043d\u0430 \u0447\u0442\u043e \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e Turbo \u043d\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>local: true<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f JS-\u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u043d\u0430\u0434 \u0444\u043e\u0440\u043c\u043e\u0439. \u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u0432\u0430\u0448 \u0441\u043b\u0443\u0447\u0430\u0439, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432\u043d\u0435\u0441\u0442\u0438 \u0435\u0449\u0435 \u043e\u0434\u043d\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435: <\/p>\n<p><strong>app\/views\/_any_form.html.erb<\/strong><\/p>\n<pre><code class=\"diff\">- &lt;%= form_with(url: money_path, local: true) do |f| %> + &lt;%= form_with(url: money_path, data: { turbo: false }) do |f| %><\/code><\/pre>\n<h4>Turbo Frames<\/h4>\n<p>\u041d\u0430\u043a\u043e\u043d\u0435\u0446 \u043c\u044b \u043f\u043e\u0434\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u043a \u0447\u0435\u043c\u0443-\u0442\u043e \u043d\u043e\u0432\u0435\u043d\u044c\u043a\u043e\u043c\u0443 \u0432 \u0440\u0435\u043b\u044c\u0441\u0435. Turbo Frame &#8212; \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430 \u0441 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c\u0441\u044f \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e. \u0422\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u0432 \u0433\u0435\u043c\u0435 <a href=\"https:\/\/github.com\/renderedtext\/render_async\" rel=\"noopener noreferrer nofollow\"><u>render_async<\/u><\/a> \u0438\u043b\u0438 <code>.ejs<\/code> \u043e\u0442 Rails, \u043d\u043e \u0441 \u043c\u0435\u043d\u044c\u0448\u0438\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u043a\u043e\u0434\u0430.\u00a0<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0447\u0430\u0441\u0442\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u043f\u043e\u0434\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438\u0445 \u0432\u0438\u0434\u0438\u0442. \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0441 \u043e\u0431\u0449\u0435\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439, \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0438 \u043e\u0442\u0437\u044b\u0432\u0430\u043c\u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432. \u041d\u0435\u0442 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0438, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u043e\u0441\u0435\u0442\u0438\u0442 \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u043e\u0432, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0438\u0445 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u043e\u043d \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u0431\u0440\u0430\u043b\u0441\u044f \u0434\u043e \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\n<figure class=\"full-width\"><figcaption>\u0412\u0430\u0448 \u043d\u043e\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d<\/figcaption><\/figure>\n<p>\u042f \u043f\u0440\u043e\u043f\u0443\u0449\u0443 \u0447\u0430\u0441\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u0430, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0443\u044e\u0441\u044f \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 \u043c\u043e\u0434\u0435\u043b\u0435\u0439,<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\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-416039","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/416039","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=416039"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/416039\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=416039"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=416039"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=416039"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}