{"id":329776,"date":"2022-02-17T21:01:31","date_gmt":"2022-02-17T21:01:31","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=329776"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=329776","title":{"rendered":"<span>\u041f\u0438\u0448\u0435\u043c \u0441\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u0441\u0435\u0442\u044c \u043d\u0430 Ruby on Rails. \u0427\u0430\u0441\u0442\u044c 1<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/c3c\/087\/484\/c3c0874840cd54bb465caf566878132b.jpg\" alt=\"\u041a\u0430\u0434\u0440 \u0438\u0437 \u0444\u0438\u043b\u044c\u043c\u0430 &quot;\u0421\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0421\u0435\u0442\u044c&quot;\" title=\"\u041a\u0430\u0434\u0440 \u0438\u0437 \u0444\u0438\u043b\u044c\u043c\u0430 &quot;\u0421\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0421\u0435\u0442\u044c&quot;\" width=\"900\" height=\"506\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c3c\/087\/484\/c3c0874840cd54bb465caf566878132b.jpg\" data-blurred=\"true\"\/><figcaption>\u041a\u0430\u0434\u0440 \u0438\u0437 \u0444\u0438\u043b\u044c\u043c\u0430 &#171;\u0421\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0421\u0435\u0442\u044c&#187;<\/figcaption><\/figure>\n<h2>\u0417\u0430\u0447\u0435\u043c \u0438 \u0434\u043b\u044f \u043a\u043e\u0433\u043e \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f?<\/h2>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u042f Ruby on Rails Developer \u0438 \u0435\u0449\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u0434\u0430\u0432\u043d\u043e \u044f \u043d\u0430\u0447\u0438\u043d\u0430\u043b \u0441\u0432\u043e\u0439 \u043f\u0443\u0442\u044c \u0432 \u044d\u0442\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438. \u042f \u0443\u0436\u0435 \u043f\u0440\u043e\u0448\u0435\u043b \u043f\u0435\u0440\u0432\u044b\u0435 \u0448\u0430\u0433\u0438 (\u043e \u043d\u0438\u0445 \u044f \u043f\u0438\u0441\u0430\u043b \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 <a href=\"https:\/\/habr.com\/ru\/post\/591971\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0442\u044c\u0435<\/a>), \u043a\u0430\u043a \u0432\u044b\u0431\u043e\u0440 \u044f\u0437\u044b\u043a\u0430, \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u0435 \u0435\u0433\u043e \u043e\u0441\u043d\u043e\u0432, \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u043e \u0441 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u043c, \u043f\u0435\u0440\u0432\u044b\u0435 pet-\u043f\u0440\u043e\u0435\u043a\u0442\u044b, \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u043e\u0431\u0435\u0441\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f, \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0444\u0444\u0435\u0440, \u043f\u0435\u0440\u0432\u0430\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f. \u041d\u043e \u043c\u043d\u043e\u0433\u0438\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0430\u043b\u0438 \u0438\u0434\u0442\u0438 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043f\u0443\u0442\u0438 \u0438 \u0438\u043c\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u043d\u0438\u0445 \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f. \u041f\u043e \u0441\u0432\u043e\u0435\u043c\u0443 \u043e\u043f\u044b\u0442\u0443 \u043f\u043e\u043c\u043d\u044e, \u043a\u0430\u043a \u0441\u043b\u043e\u0436\u043d\u043e \u0438\u0441\u043a\u0430\u0442\u044c \u0433\u0430\u0439\u0434\u044b (\u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0438\u0437 \u043d\u0438\u0445 \u043f\u0440\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043d\u0438\u0436\u043d\u044b\u0445 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u043e\u0432, \u043b\u0438\u0447\u043d\u044b\u0445 \u0431\u043b\u043e\u0433\u043e\u0432 \u0438 \u0442.\u0434.), \u043f\u043e\u044d\u0442\u043e\u043c\u0443, \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u043c\u043d\u043e\u0433\u0438\u043c \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0446 \u0441\u0435\u0442\u0438. <\/p>\n<h2>\u041f\u043e\u0447\u0435\u043c\u0443 \u0441\u043e\u0446 \u0441\u0435\u0442\u044c \u0438 \u043a\u0430\u043a \u0431\u0443\u0434\u0435\u0442 \u0438\u0434\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441?<\/h2>\n<p>\u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u043c\u043d\u0435 \u0441\u0430\u043c\u043e\u043c\u0443 \u0431\u044b\u043b\u043e \u0431\u044b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, \u044f \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0438\u043b\u0441\u044f \u043a\u043d\u0438\u0433\u043e\u0439 <strong>Practical Rails Social Networking Sites by Alan Bradburne<\/strong>. \u041c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e \u043a\u043d\u0438\u0433\u0435, \u0441\u043a\u0430\u0436\u0435\u0442\u0435 \u0432\u044b. \u041d\u043e \u0437\u0430\u0447\u0435\u043c \u0442\u043e\u0433\u0434\u0430 \u044f \u0438 \u043c\u043e\u0438 \u0441\u0442\u0430\u0442\u044c\u0438? \u041a\u043d\u0438\u0433\u0430 2007 \u0433\u043e\u0434\u0430 \u0438 \u0432\u0435\u0440\u0441\u0438\u044f ruby \u0442\u0430\u043c 1.8, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u0432\u043e\u0435\u043c \u0431\u0443\u0434\u0443\u0442 \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b \u0432 \u043d\u0430\u0448\u0435 \u0432\u0440\u0435\u043c\u044f. \u041a \u0442\u043e\u043c\u0443 \u0436\u0435, \u044f \u043d\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0441\u044c \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e \u043a\u043d\u0438\u0433\u0435, \u0430 \u043b\u0438\u0448\u044c \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0435\u0439 (\u0434\u0438\u0437\u0430\u0439\u043d \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0437 \u043d\u0435\u0435 \u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c <code>bootsrap<\/code>). \u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u0438\u0435 \u0433\u0435\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u044b \u0434\u043b\u044f \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432. \u041d\u043e \u0441\u043a\u0430\u0436\u0443 \u0441\u0440\u0430\u0437\u0443: \u044d\u0442\u0430 \u0441\u0435\u0440\u0438\u044f \u0441\u0442\u0430\u0442\u0435\u0439 \u043d\u0435 \u0434\u043b\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u0442\u0430. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0432\u0435\u0449\u0438 (\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 <code>ruby<\/code>, <code>rails<\/code>, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <code>MVC<\/code>, <code>git<\/code> \u0438 \u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435) \u044f \u0431\u0443\u0434\u0443 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0442\u044c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0442\u043b\u043e\u0436\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0441\u0442\u0430\u0442\u0435\u0439 \u0438 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u043d\u0435\u043c\u0443 \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435. \u0415\u0441\u043b\u0438 \u0432\u044b \u043e\u043f\u044b\u0442\u043d\u044b\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0438 \u0447\u0438\u0442\u0430\u0435\u0442\u0435 \u044d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e, \u0431\u0443\u0434\u0443 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u0438\u0437\u043d\u0430\u0442\u0435\u043b\u0435\u043d \u0443\u0441\u043b\u044b\u0448\u0430\u0442\u044c \u0432\u0430\u0448\u0435 \u043c\u043d\u0435\u043d\u0438\u0435. \u041a\u0430\u0441\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u0445\u043e\u0434\u0430 \u0441\u0442\u0430\u0442\u0435\u0439 \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0445 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435 \u043c\u043e\u0433\u0443 \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u044e \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0438 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u0438. \u041d\u043e \u0431\u0443\u0434\u0443 \u0441\u0442\u0430\u0440\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u043e\u0442\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u0432 \u0434\u043e\u043b\u0433\u0438\u0439 \u044f\u0449\u0438\u043a \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u0441 \u0445\u043e\u0440\u043e\u0448\u0438\u043c \u0442\u0435\u043c\u043f\u043e\u043c.<\/p>\n<h2>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0438 \u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u043c\u044b \u043d\u0430\u0447\u043d\u0435\u043c, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0438:<\/p>\n<ul>\n<li>\n<p><code>Ruby 3.0.3<\/code><\/p>\n<\/li>\n<li>\n<p><code>Rails 6.1.4.6<\/code><\/p>\n<\/li>\n<li>\n<p><code>MySQL 8.0<\/code><\/p>\n<\/li>\n<li>\n<p><code>Node 10.19<\/code><\/p>\n<\/li>\n<li>\n<p><code>Yarn 1.22.17<\/code><\/p>\n<\/li>\n<\/ul>\n<p>\u041e\u0447\u0435\u043d\u044c \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0432\u0430\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>github<\/code> \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0427\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435 \u043c\u044b \u0441 \u0432\u0430\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c <code>CI\/CD<\/code> \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043a\u0440\u0430\u0439\u043d\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0434\u043b\u044f \u0432\u0430\u0441 \u043e\u043f\u044b\u0442\u043e\u043c. \u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442. \u042f \u043d\u0430\u0437\u043e\u0432\u0443 \u0435\u0433\u043e <code>g_connect<\/code>, \u043d\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u043e\u0435 \u0434\u0440\u0443\u0433\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 (\u0435\u0441\u043b\u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0440\u0443\u0433\u043e\u0435, \u0432\u0435\u0437\u0434\u0435, \u0433\u0434\u0435 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>g_connect<\/code>, \u043f\u0438\u0448\u0438\u0442\u0435 \u0441\u0432\u043e\u0435).<\/p>\n<pre><code class=\"bash\">rails new g_connect -d mysql<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043f\u0430\u043f\u043a\u0443 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c \u0438 \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438. \u042f \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044e \u0441 <code>Gemfile<\/code> \u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0433\u0435\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u043e\u0447\u043d\u043e \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<pre><code class=\"ruby\">#Gemfile  source 'https:\/\/rubygems.org' git_source(:github) { |repo| \"https:\/\/github.com\/#{repo}.git\" }  ruby '3.0.3'  gem 'aasm' gem 'bootsnap', '>= 1.4.4', require: false gem 'bootstrap' gem 'devise' gem 'jbuilder', '~> 2.7' gem 'mysql2', '~> 0.5.2' gem 'puma', '~> 5.0' gem 'rails', '~> 6.1.4.6' gem 'sass-rails', '>= 6' gem 'slim' gem 'turbolinks', '~> 5' gem 'webpacker', '~> 5.0'  group :development, :test do   gem 'better_errors'   gem 'binding_of_caller'   gem 'byebug', platforms: %i[mri mingw x64_mingw]   gem 'factory_bot_rails'   gem 'faker'   gem 'rails-controller-testing'   gem 'rspec-rails'   gem 'rubocop'   gem 'rubocop-rails'   gem 'rubocop-rspec' end  group :development do   gem 'annotate'   gem 'listen', '~> 3.3'   gem 'rack-mini-profiler', '~> 2.0'   gem 'spring'   gem 'web-console', '>= 4.1.0' end  group :test do   gem 'capybara', '>= 3.26'   gem 'rspec_junit_formatter'   gem 'selenium-webdriver'   gem 'simplecov', require: false   gem 'webdrivers' end  gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] <\/code><\/pre>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u044f \u043f\u043e\u044f\u0441\u043d\u044e, \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0433\u0435\u043c\u043e\u0432 (\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043f\u0435\u0440\u0435\u0434 \u0441\u0442\u0430\u0440\u0442\u043e\u043c \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439):<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/aasm\/aasm\" rel=\"noopener noreferrer nofollow\">gem &#8216;aasm&#8217;<\/a> &#8212; \u0435\u0433\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0439 \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f\u043c\u0438<\/p>\n<\/li>\n<li>\n<p> <a href=\"https:\/\/github.com\/twbs\/bootstrap-rubygem\" rel=\"noopener noreferrer nofollow\">gem &#8216;bootstrap&#8217;<\/a> &#8212; \u044d\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u0430 (\u044f \u0434\u0435\u043b\u0430\u044e \u0443\u043f\u043e\u0440 \u043d\u0430 \u0431\u044d\u043a \u0438 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u0443\u0434\u0443 \u0443\u0434\u0435\u043b\u044f\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0444\u0440\u043e\u043d\u0442\u0443, \u043d\u043e \u0432 \u0441\u0430\u043c\u043e\u043c \u043a\u043e\u043d\u0446\u0435 \u043c\u043e\u0436\u0435\u0442 \u0438 \u043f\u0440\u0438\u0434\u0435\u0442 \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0435\u043d\u0438\u0435 \u043d\u0430 \u043d\u0430\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043a\u0440\u0430\u0441\u043e\u0442\u044b)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/heartcombo\/devise\" rel=\"noopener noreferrer nofollow\">gem &#8216;devise&#8217;<\/a> &#8212; \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f (\u043a\u043e\u0440\u043e\u0442\u043a\u043e \u0438 \u044f\u0441\u043d\u043e, \u0431\u0443\u0434\u0435\u0442 \u0435\u0449\u0435 \u0433\u0435\u043c \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043d\u043e \u044f \u0435\u0449\u0435 \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043b, \u043a\u0430\u043a\u043e\u0439)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/slim-template\/slim\" rel=\"noopener noreferrer nofollow\">gem &#8216;slim&#8217;<\/a> &#8212; \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u043c \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 html-\u0442\u0435\u0433\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/BetterErrors\/better_errors\" rel=\"noopener noreferrer nofollow\">gem &#8216;better_errors&#8217;<\/a> &#8212; \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u043e\u0448\u0438\u0431\u043e\u043a \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0440\u043e\u0443\u0442 \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/thoughtbot\/factory_bot_rails\" rel=\"noopener noreferrer nofollow\">gem &#8216;factory_bot_rails&#8217;<\/a> &#8212; \u0448\u0430\u0431\u043b\u043e\u043d, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043d\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u0430\u0445<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/faker-ruby\/faker\" rel=\"noopener noreferrer nofollow\">gem &#8216;faker&#8217; <\/a>&#8212; \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/rspec\/rspec-rails\" rel=\"noopener noreferrer nofollow\">gem &#8216;rspec-rails <\/a>&#8212; \u044d\u0442\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u0440\u0435\u0434\u0443 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f <code>RSpec<\/code> \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/rubocop\/rubocop\" rel=\"noopener noreferrer nofollow\">gem &#8216;rubocop&#8217;<\/a> &#8212; \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430\u0448 \u043a\u043e\u0434<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/ctran\/annotate_models\" rel=\"noopener noreferrer nofollow\">gem &#8216;annotate&#8217;<\/a> &#8212; \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u043d\u0430\u0448\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 (\u0437\u0430\u0447\u0435\u043c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0435\u0439, \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u044d\u0442\u043e\u0442 \u0433\u0435\u043c)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/simplecov-ruby\/simplecov\" rel=\"noopener noreferrer nofollow\">gem &#8216;simplecov&#8217; <\/a>&#8212; \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430, \u0432\u0441\u0435 \u043b\u0438 \u043c\u044b \u043f\u043e\u043a\u0440\u044b\u043b\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u0433\u0435\u043c\u044b \u0438 \u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <code>bundle<\/code> \u0432 \u043d\u0430\u0448\u0435\u043c \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435 (\u043a\u0441\u0442\u0430\u0442\u0438 \u0434\u0430, \u0437\u0430\u0431\u044b\u043b \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>Ubuntu<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f <code>MacOS\/Windows<\/code>(\u0432\u043e\u0442 \u0432\u0438\u043d\u0434\u0443 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u0439\u0442\u0435 \u043b\u0443\u0447\u0448\u0435 \u043f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043d\u0430 <code>ruby<\/code>, \u043d\u043e \u0443\u0436 \u0435\u0441\u043b\u0438 \u043e\u0447 \u0445\u043e\u0447\u0435\u0442\u0441\u044f) \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e). \u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u0430\u043f\u043a\u0443 <code>test<\/code>, \u043e\u043d\u0430 \u043d\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f (\u0432\u0435\u0434\u044c \u0431\u0443\u0434\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c <code>rspec<\/code>-\u0438).<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043d\u0430\u0448\u0443 \u0411\u0414. \u0412 \u0444\u0430\u0439\u043b\u0435 <code>config\/database.yml<\/code> \u0443\u043a\u0430\u0436\u0438\u0442\u0435 \u0441\u0432\u043e\u0438 username \u0438 password (\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e \u0434\u0435\u043b\u0430\u043b <code>root\/root<\/code>). \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 (\u0435\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442, \u0442\u043e \u044d\u0442\u0430 \u043e\u0434\u043d\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0441\u0440\u0430\u0437\u0443 \u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 <code>db:create<\/code>, <code>db:schema:load<\/code> \u0438 <code>db:seed<\/code>):<\/p>\n<pre><code class=\"bash\">rails db:setup<\/code><\/pre>\n<p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u043d\u0430\u0448\u0438\u0445 \u0433\u0435\u043c\u043e\u0432 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \u0418\u043c\u0438 \u043c\u044b \u0441\u0435\u0439\u0447\u0430\u0441 \u0438 \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f (<code>devise<\/code> \u0442\u0430\u043a\u0436\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u043d\u043e \u0438\u043c \u043c\u044b \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f \u043f\u043e\u0437\u0436\u0435, \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e). \u041d\u0430\u0447\u043d\u0435\u043c \u0441 <code>bootstrap<\/code>. \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u0444\u0430\u0439\u043b <code>app\/assets\/stylesheets\/application.scss<\/code> (\u0444\u0430\u0439\u043b \u043c\u043e\u0436\u0435\u0442 \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u0438\u043c\u0435\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 <code>.css<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0441\u043f\u0440\u0430\u0432\u044c\u0442\u0435 \u0435\u0433\u043e \u043d\u0430 <code>.scss<\/code>) \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u043e\u0447\u043a\u0443:<\/p>\n<pre><code class=\"css\">\/*app\/assets\/stylesheets\/application.scss*\/  @import \"bootstrap\"; <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c <code>annotate<\/code>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u0443:<\/p>\n<pre><code class=\"bash\">rails g annotate:install<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c <code>rspec<\/code>:<\/p>\n<pre><code>rails g rspec:install<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 <code>factory_bot_rails<\/code> \u0438 <code>simplecov<\/code>. \u0412 \u043d\u0430\u0448\u0435\u0439 \u043f\u0430\u043f\u043a\u0435 <code>spec<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u043f\u043a\u0443 <code>support<\/code>, \u0430 \u0432 \u043d\u0435\u0439 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>factory_bot.rb<\/code> \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043a\u043e\u0434\u043e\u043c:<\/p>\n<pre><code class=\"ruby\">#spec\/support\/factory_bot.rb  RSpec.configure do |config|   config.include FactoryBot::Syntax::Methods end <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043d\u0430\u0448 <code>spec\/rails_helper.rb<\/code>. \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043d\u0430\u0448 \u0444\u0430\u0439\u043b \u0434\u043b\u044f <code>factory_bot<\/code>, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c <code>simplecov<\/code> (\u0434\u0432\u0435 \u0441\u0442\u0440\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f <code>simplecov<\/code> \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432 \u0441\u0430\u043c\u043e\u043c \u043d\u0430\u0447\u0430\u043b\u0435 \u0444\u0430\u0439\u043b\u0430).<\/p>\n<pre><code class=\"ruby\">#spec\/rails_helper.rb  require 'simplecov' SimpleCov.start 'rails' require_relative '.\/support\/factory_bot'<\/code><\/pre>\n<p>\u0421 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u0433\u0435\u043c\u043e\u0432 \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0438. \u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u043d\u0442 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u0442\u043e \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c <code>xdg-open coverage\/index.html<\/code> \u0438 \u0432\u043e\u0442 \u043e\u043d\u0430 \u043c\u0430\u0433\u0438\u044f. \u041d\u043e \u044f \u0431\u044b \u0445\u043e\u0442\u0435\u043b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0449\u0435 \u043f\u0430\u0440\u0443 \u043c\u043e\u043c\u0435\u043d\u0442\u043e\u0432. \u041f\u0435\u0440\u0432\u044b\u0439 &#8212; <code>shared_context.rb<\/code> \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u0448\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439. \u0412 \u043f\u0430\u043f\u043a\u0435 <code>spec\/support<\/code> \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 <code>shared_context.rb<\/code><\/p>\n<pre><code class=\"ruby\">#spec\/support\/shared_context.rb  RSpec.shared_examples 'creates_object_for' do |model_name|   subject { FactoryBot.build(model_name) }    it 'creates object' do     expect { subject.save }.to change { described_class.count }.by(1)   end end  RSpec.shared_examples 'not_create_object_for' do |model_name, parameter|   subject { described_class.create(attributes) }    let(:attributes) { FactoryBot.attributes_for(model_name, parameter) }    it 'does not create object' do     expect { subject.save }.to change { described_class.count }.by(0)   end    it 'raise RecordInvalid error' do     expect { subject.save! }.to raise_error(ActiveRecord::RecordInvalid)   end end <\/code><\/pre>\n<p>\u041d\u0430\u0448 <code>shared_context<\/code> \u0431\u0443\u0434\u0435\u0442 \u0438\u0433\u0440\u0430\u0442\u044c \u0440\u043e\u043b\u044c \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439. \u0412 \u043d\u0435\u043c \u043c\u044b \u043e\u043f\u0438\u0448\u0435\u043c, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c, \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u043d\u044b \u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f, \u0438 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c, \u0435\u0441\u043b\u0438 \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b \u043b\u0438\u0431\u043e \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f. \u042d\u0442\u043e \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u043f\u043e\u0442\u043e\u043c \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0432\u044b \u0432 \u044d\u0442\u043e\u043c \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c. \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0435\u0433\u043e \u0432 \u043d\u0430\u0448 <code>spec_helper.rb<\/code><\/p>\n<pre><code class=\"ruby\">#spec\/spec_helper.rb  require_relative '.\/support\/shared_context' <\/code><\/pre>\n<p>\u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c \u043f\u0435\u0440\u0435\u0434 \u0441\u0442\u0430\u0440\u0442\u043e\u043c: \u0447\u0443\u0442\u043a\u0430 \u0434\u0438\u0437\u0430\u0439\u043d\u0430. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 <code>app\/views\/layouts\/application.html.erb<\/code>. \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 <code>.erb<\/code> \u043d\u0430 <code>.slim<\/code> \u0438 \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"ruby\">#app\/views\/layouts\/application.html.slim  doctype html html   head     meta content='text\/html; charset=UTF-8' http-equiv='Content-Type'     title G-Connect     = csrf_meta_tags     = csp_meta_tag     = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'     = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload'   body     #container       #header                #sidemenu         = render 'application\/sidemenu'       #content         = yield <\/code><\/pre>\n<p>\u041a\u0430\u043a \u043f\u043e \u043c\u043d\u0435, \u044d\u0442\u043e \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u043b\u0443\u0447\u0448\u0435 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0441\u044f, \u0447\u0435\u043c <code>.html.erb<\/code>. \u0415\u0441\u043b\u0438 \u0432\u044b \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 <code>.slim<\/code>, \u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0432\u043e\u0442 <a href=\"https:\/\/erb2slim.com\/\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u0442<\/a> \u0440\u0435\u0441\u0443\u0440\u0441 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0438\u0437 <code>.html.erb<\/code> \u0432 <code>.html.slim<\/code>. \u0414\u0430\u043b\u0435\u0435 \u0432 \u043f\u0430\u043f\u043a\u0435 <code>app\/views<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u043f\u043a\u0443 application, \u0430 \u0432 \u043d\u0435\u0439 \u0444\u0430\u0439\u043b <code>_sidemenu.html.slim<\/code> \u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0433\u043e \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u0447\u043a\u0438:<\/p>\n<pre><code class=\"ruby\">#app\/views\/layouts\/_sidemenu.html.slim  ul   li     = link_to 'Home', '\/', class: 'btn btn-sm btn-light' <\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 <code>app\/assets\/stylesheets\/application.scss<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<pre><code class=\"css\">\/*app\/assets\/stylesheets\/application.scss*\/  body {    margin: 0;    padding: 0;    background-color: #f0ffff;    font-family: Arial, Helvetica, sans-serif;  }   #header {    background-color: #f0ffff;    height: 60px;    margin-top: 10px;    text-align: left;    padding-top: 1px;  }   #container {    width: 760px;    min-width: 760px;    margin: 0 auto;    padding: 0px;  }   #sidemenu {    font-size: 80%;    float: left;    width: 100px;    padding: 0px;  }   #sidemenu ul {    list-style: none;    margin-left: 0px;    padding: 0px;  }   a {    color: #b00;  }   a:hover {    background-color: #b00; color: #f0ffff;  }   #content {    float: right;    width: 650px;  }   th {    background-color: #933;    color: #f0ffff;  }   tr.odd {    background-color: #fcc;  }   tr.even {    background-color: #ecc;  } <\/code><\/pre>\n<p>\u041f\u043e\u043a\u0430 \u0441\u043e\u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u043e (<code>css<\/code> \u0432\u0437\u044f\u0442 \u0438\u0437 \u043a\u043d\u0438\u0433\u0438), \u043d\u043e, \u043a\u0430\u043a \u044f \u0438 \u0433\u043e\u0432\u043e\u0440\u0438\u043b \u0440\u0430\u043d\u0435\u0435, \u044f \u0434\u0435\u043b\u0430\u044e \u0443\u043f\u043e\u0440 \u043d\u0430 \u0431\u044d\u043a. \u041a\u043e\u0433\u0434\u0430 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u043e\u043a\u043e\u043d\u0447\u0435\u043d\u0430, \u043c\u043e\u0436\u0435\u043c \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435. \u041c\u043e\u0436\u0435\u0442 \u0437\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435, \u0447\u0442\u043e \u043c\u044b \u0441 \u0432\u0430\u043c\u0438 \u0441\u0434\u0435\u043b\u0430\u043b\u0438, \u043d\u0430 <code>github<\/code> \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0432\u0435\u0449\u0438 \u0443\u0436\u0435 \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u0432\u0435\u0442\u043a\u0435. \u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043f\u0435\u0440\u0435\u0434 \u044d\u0442\u0438\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 <code>rubocop<\/code>-\u043e\u043c (\u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0439 <code>IDE<\/code> \u0438 \u043e\u043d \u0441\u0440\u0430\u0437\u0443 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0441\u0432\u0435\u0447\u0438\u0432\u0430\u0442\u044c \u0432\u0430\u043c \u0444\u0430\u0439\u043b\u044b, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0435\u0441\u0442\u044c \u043d\u0435\u0434\u043e\u0447\u0435\u0442\u044b).<\/p>\n<h2>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446<\/h2>\n<p>\u041f\u0435\u0440\u0432\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0434\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446. \u0423 \u043d\u0435\u0435 \u0431\u0443\u0434\u0443\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043f\u043e\u043b\u044f<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<td>\n<p align=\"center\">\u0418\u043c\u044f \u043f\u043e\u043b\u044f<\/p>\n<\/td>\n<td>\n<p align=\"center\">\u0422\u0438\u043f<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"center\"><code>id<\/code><\/p>\n<\/td>\n<td>\n<p align=\"center\"><code>integer<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"center\"><code>title<\/code><\/p>\n<\/td>\n<td>\n<p align=\"center\"><code>string<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"center\"><code>permalink<\/code><\/p>\n<\/td>\n<td>\n<p align=\"center\"><code>string<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"center\"><code>body<\/code><\/p>\n<\/td>\n<td>\n<p align=\"center\"><code>text<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p><code>Permalink<\/code> \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0438\u0437 \u043d\u0430\u0448\u0435\u0433\u043e <code>title<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0432\u0438\u0434, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 <code>url<\/code>. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u044c.<\/p>\n<pre><code>rails g model Page<\/code><\/pre>\n<p>\u041f\u043e\u043c\u0438\u043c\u043e \u043c\u043e\u0434\u0435\u043b\u0438 \u0443 \u043d\u0430\u0441 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b\u0430\u0441\u044c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u0438 \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432, \u043a \u043d\u0438\u043c \u043c\u044b \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435. \u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0435 <code>db\/migrate<\/code>. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u043d\u0435\u0435:<\/p>\n<pre><code class=\"ruby\">#db\/migrate\/date_time_create_pages.rb  class CreatePages &lt; ActiveRecord::Migration[6.1]   def change     create_table :pages do |t|       t.string :title,          null: false       t.string :permalink       t.text :body,             null: false     end   end end <\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <code>rails db:migrate<\/code> (\u043d\u0430\u0448 <code>annotate<\/code> \u0441\u0440\u0430\u0437\u0443 \u0436\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a\u0438\u0435 \u0444\u0430\u0439\u043b\u044b \u0431\u044b\u043b\u0438 \u0430\u043d\u043d\u043e\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b) \u0438 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043d\u0430\u0448\u0443 \u043c\u043e\u0434\u0435\u043b\u044c. \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u043d\u0430\u0448\u0438 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e <code>permalink<\/code> (\u0435\u0433\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043b\u043b\u0431\u044d\u043a\u0430 <code>after_create<\/code>). \u0412 \u044d\u0442\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u0434\u043b\u044f \u043f\u043e\u043c\u043e\u0449\u0438 \u0441 \u0438\u0445 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u044f \u0432\u0441\u0435\u0433\u0434\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <a href=\"https:\/\/rubular.com\/\" rel=\"noopener noreferrer nofollow\">Rubular<\/a>.<\/p>\n<pre><code class=\"ruby\">#app\/model\/page.rb  class Page &lt; ApplicationRecord   after_create :clean_url    validates_presence_of :title, :body   validates :title, length: { in: 3..250 }   validates :body, length: { in: 3..100_00 }    private    def clean_url     return unless self.permalink.nil?     url = title.downcase.gsub(\/\\s+\/, '_').gsub(\/[^a-zA-Z0-9_]+\/, '')     self.permalink = url     save   end end <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0437\u0430\u043d\u044f\u0442\u044c\u0441\u044f \u043d\u0430\u0448\u0438\u043c\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438. \u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0444\u0430\u0439\u043b\u0430 <code>spec\/factories\/pages.rb<\/code><\/p>\n<pre><code class=\"ruby\">#spec\/factories\/pages.rb  FactoryBot.define do   factory :page do     title { 'Test' }     body { 'Test' }   end end <\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043d\u044f\u0442\u044c\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u0442\u0435\u0441\u0442\u043e\u0432. \u0418\u043c\u0435\u043d\u043d\u043e \u0432 \u044d\u0442\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u043d\u0430\u043c \u0438 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430\u0448 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0440\u0430\u043d\u0435\u0435 <code>spec\/supprot\/shared_context.rb<\/code>.<\/p>\n<pre><code class=\"ruby\">#spec\/models\/page_spec.rb  require 'rails_helper'  RSpec.describe Page, type: :model do   describe '.create' do     context 'with valid attributes' do       include_examples 'creates_object_for', :page     end      context 'with invalid attributes' do       context 'with short title' do         include_examples 'not_create_object_for', :page, title: 'te'       end        context 'with too long title' do         include_examples 'not_create_object_for', :page, title: Faker::String.random(length: 253)       end        context 'with short body' do         include_examples 'not_create_object_for', :page, body: 'te'       end        context 'with too long body' do         include_examples 'not_create_object_for', :page, body: Faker::String.random(length: 100_02)       end     end      context 'with missing attributes' do       context 'with missing title' do         include_examples 'not_create_object_for', :page, title: nil       end        context 'with missing body' do         include_examples 'not_create_object_for', :page, body: nil       end     end   end end <\/code><\/pre>\n<p> \u0414\u0443\u043c\u0430\u044e, \u0437\u0434\u0435\u0441\u044c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u0439, \u043b\u0438\u0448\u044c \u043e\u0442\u043c\u0435\u0447\u0443, \u0447\u0442\u043e private \u043c\u0435\u0442\u043e\u0434\u044b \u043d\u0435 \u043d\u0443\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0434\u0435\u0441\u044c \u0438 \u043d\u0435\u0442 \u0442\u0435\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e <code>clean_url<\/code>. \u041c\u043e\u0436\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c <code>rspec<\/code> \u0432 \u043d\u0430\u0448\u0435\u043c \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435 \u0438 \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u0442\u0435\u0441\u0442\u044b \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0442 \u0431\u0435\u0437 \u043e\u0448\u0438\u0431\u043e\u043a. \u041a\u043e\u0433\u0434\u0430 \u0441 \u043c\u043e\u0434\u0435\u043b\u044c\u044e \u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0434\u043b\u044f \u043d\u0438\u0445 \u043c\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0437\u0430\u043d\u044f\u0442\u044c\u0441\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u043c. \u042f \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>pages_controller.rb<\/code> \u0432 \u043d\u0430\u0448\u0435\u0439 \u043f\u0430\u043f\u043a\u0435 <code>app\/controllers<\/code>. \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u0440\u043e\u043f\u0438\u0448\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<pre><code class=\"ruby\">#app\/controllers\/pages_controller.rb  class PagesController &lt; ApplicationController   before_action :find_page, only: %i[show edit update destroy]    def index     @pages = Page.all   end    def show; end    def new     @page = Page.new   end    def create     @page = Page.create(page_params)      if @page.save       redirect_to pages_path, notice: 'Page created'     else       render :new     end   end    def edit; end    def update     if @page.update(page_params)       redirect_to page_path(@page), notice: 'Page updated'     else       render :edit     end   end    def destroy     @page.destroy     redirect_to pages_path, notice: 'Page deleted'   end    private    def find_page     @page = Page.find(params[:id])   end    def page_params     params.require(:page).permit(:title, :permalink, :body)   end end <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0435 \u0441 \u0431\u043e\u043b\u044c\u0448\u0435 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0430\u043a\u0436\u0435 \u043d\u0435 \u0431\u0443\u0434\u0443 \u0437\u0430\u043e\u0441\u0442\u0440\u044f\u0442\u044c \u043d\u0430 \u044d\u0442\u043e\u043c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0440\u043e\u0443\u0442\u044b \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430:<\/p>\n<pre><code class=\"ruby\">#config\/routes.rb  Rails.application.routes.draw do   root 'pages#index'    resources :pages end<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043a\u0440\u043e\u0438\u043c \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u043d\u0430\u0448 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440. \u0414\u043b\u044f \u044d\u0442\u043e \u0432 \u043f\u0430\u043f\u043a\u0435 <code>spec<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u043f\u043a\u0443 <code>controllers<\/code> \u0438 \u0442\u0430\u043c \u0436\u0435 \u0441\u0440\u0430\u0437\u0443 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>pages_controller_spec.rb<\/code><\/p>\n<pre><code class=\"ruby\">#spec\/controllers\/pages_controller_spec.rb  require 'rails_helper'  RSpec.describe PagesController, type: :controller do   describe 'GET #index' do     let(:pages) { [FactoryBot.create(:page)] }      it 'returns all pages' do       get :index        expect(response).to render_template('index')       expect(response).to have_http_status(:ok)       expect(assigns(:pages)).to eq(pages)     end   end    describe 'GET #show' do     let(:page) { FactoryBot.create(:page) }      it 'assigns page' do       get :show, params: { id: page.id }        expect(response).to render_template('show')       expect(response).to have_http_status(:ok)       expect(assigns(:page)).to eq(page)     end   end    describe 'GET #new' do     it 'returns render form for creating new page' do       get :new        expect(response).to render_template('new')       expect(response).to have_http_status(:success)     end   end    describe 'POST #create' do     let(:page_params) { FactoryBot.attributes_for(:page) }      it 'creates new page' do       get :create, params: { page: page_params }        expect(response).to redirect_to('\/pages')       expect(response).to have_http_status(:found)     end      it 'doesn`t create new page' do       get :create, params: { page: page_params.except(:title) }        expect(response).to render_template('new')     end   end    describe 'PUT #update' do     let(:page) { FactoryBot.create(:page) }      it 'updates the requested page' do       put :update, params: { id: page.id, page: { title: 'brbrbr' } }        expect(response).to redirect_to(\"\/pages\/#{page.id}\")       expect(response).to have_http_status(:found)     end      it 'doesn`t update page' do       put :update, params: { id: page.id, page: { title: '' } }        expect(response).to render_template('edit')     end   end    describe 'DELETE #destroy' do     let(:page) { FactoryBot.create(:page) }      it 'destroys page' do       delete :destroy, params: { id: page.id }        expect(response).to redirect_to('\/pages')     end   end end <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0436\u0435 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0438\u0437\u0443\u0430\u043b \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430. \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 <code>app\/views<\/code> \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u0430\u043c \u043f\u0430\u043f\u043a\u0443 <code>pages<\/code>, \u0430 \u0432 \u043d\u0435\u0439 5 \u0444\u0430\u0439\u043b\u043e\u0432: <code>_form.htm.slim<\/code>, <code>new.html.slim<\/code>, <code>edit.html.slim<\/code>, <code>show.html.slim<\/code> \u0438 <code>index.html.slim<\/code>. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0438\u0437 \u043d\u0438\u0445. \u0412 \u043d\u0430\u0448\u0435\u043c <code>_form.htm.slim<\/code> \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0444\u043e\u0440\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0438\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0438\u0445 <code>Pages<\/code>. \u042d\u0442\u0443 \u0444\u043e\u0440\u043c\u0443 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u0432 \u043d\u0430\u0448\u0438\u0445 <code>new<\/code> \u0438 <code>edit<\/code> \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e.<\/p>\n<pre><code class=\"ruby\">#app\/views\/pages\/_form.html.slim  = form_with(model: page, local: true) do |f|   .form-group     = f.label :title     = f.text_field :title   .form-group     = f.label :body     = f.text_area :body   .form-group     = f.submit 'Submit', class: 'btn btn-success' <\/code><\/pre>\n<pre><code class=\"ruby\">#app\/views\/pages\/new.html.slim  h1 New Page  = render 'form', page: @page  = link_to 'Back', :back, class: 'btn btn-sm btn-primary' <\/code><\/pre>\n<pre><code class=\"ruby\">#app\/views\/pages\/edit.html.slim  h1 Edit Page  = render 'form', page: @page  = link_to 'Back', :back, class: 'btn btn-sm btn-primary' <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f <code>show<\/code> \u0438 <code>index<\/code>:<\/p>\n<pre><code class=\"ruby\">#app\/views\/pages\/show.html.slim  p   strong Title:    = @page.title p   strong Body:    = @page.body  = link_to 'Edit', edit_page_path(@page), class: 'btn btn-sm btn-success' ' = link_to 'Delete', page_path(@page), method: :delete, class: 'btn btn-sm btn-danger', data: { confirm: 'Are you sure?' } ' = link_to 'Back', :back, class: 'btn btn-sm btn-primary' <\/code><\/pre>\n<pre><code class=\"ruby\">#app\/views\/pages\/index.html.slim  h2 Pages ul   - @pages.each do |page|     li       = page.permalink       |:        = page.title       |       p           = link_to 'Show', page_path(page), class: 'btn btn-sm btn-info'  p   = link_to 'Create new page', new_page_url, class: 'btn btn-sm btn-primary' <\/code><\/pre>\n<p>\u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u0432\u0435\u0449\u044c \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f &#8212; \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u043f\u0440\u0430\u0432\u0438\u043c <code>_sidemenu.html.slim<\/code><\/p>\n<pre><code class=\"ruby\">#app\/views\/application\/_sidemenu.html.slim   ul   li     = link_to 'Home', root_path, class: 'btn btn-sm btn-light' <\/code><\/pre>\n<p>\u0414\u0443\u043c\u0430\u044e, \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u0443\u0436\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e. \u0418\u0442\u0430\u043a \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043e\u0431\u044a\u0435\u043c\u043d\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0430\u0441\u044c. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0432\u0441\u0435 <code>rubocop<\/code>-\u043e\u043c, \u0438\u0441\u043f\u0440\u0430\u0432\u044c\u0442\u0435 \u043d\u0435\u0434\u043e\u0447\u0435\u0442\u044b, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e, \u0438 \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u043c\u0435\u043b\u043e \u0437\u0430\u043b\u0438\u0432\u0430\u0442\u044c \u043d\u0430 \u0432\u0430\u0448 <code>github<\/code>. \u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, <code>devise<\/code> \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c <code>CI\/CD<\/code>. \u041d\u0430\u0434\u0435\u044e\u0441\u044c \u0432\u0430\u043c \u0432\u0441\u0435\u043c \u0431\u044b\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0447\u0438\u0442\u0430\u0442\u044c \u044d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e. \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u044f \u043b\u0438\u0431\u043e \u0436\u0435 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f &#8212; \u0441\u043c\u0435\u043b\u043e \u043f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445. \u0416\u0435\u043b\u0430\u044e \u0432\u0441\u0435\u043c \u043f\u043e\u043c\u0435\u043d\u044c\u0448\u0435 \u043e\u0448\u0438\u0431\u043e\u043a \u0432 \u043a\u043e\u0434\u0435 \u0438 \u043f\u043e\u0431\u043e\u043b\u044c\u0448\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/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\/post\/652035\/\"> https:\/\/habr.com\/ru\/post\/652035\/<\/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_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption>\u041a\u0430\u0434\u0440 \u0438\u0437 \u0444\u0438\u043b\u044c\u043c\u0430 &#171;\u0421\u043e\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0421\u0435\u0442\u044c&#187;<\/figcaption><\/figure>\n<h2>\u0417\u0430\u0447\u0435\u043c \u0438 \u0434\u043b\u044f \u043a\u043e\u0433\u043e \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f?<\/h2>\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u042f Ruby on Rails Developer \u0438 \u0435\u0449\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u0434\u0430\u0432\u043d\u043e \u044f \u043d\u0430\u0447\u0438\u043d\u0430\u043b \u0441\u0432\u043e\u0439 \u043f\u0443\u0442\u044c \u0432 \u044d\u0442\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438. \u042f \u0443\u0436\u0435 \u043f\u0440\u043e\u0448\u0435\u043b \u043f\u0435\u0440\u0432\u044b\u0435 \u0448\u0430\u0433\u0438 (\u043e \u043d\u0438\u0445 \u044f \u043f\u0438\u0441\u0430\u043b \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 <a href=\"https:\/\/habr.com\/ru\/post\/591971\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0442\u044c\u0435<\/a>), \u043a\u0430\u043a \u0432\u044b\u0431\u043e\u0440 \u044f\u0437\u044b\u043a\u0430, \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u0435 \u0435\u0433\u043e \u043e\u0441\u043d\u043e\u0432, \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u043e \u0441 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u043c, \u043f\u0435\u0440\u0432\u044b\u0435 pet-\u043f\u0440\u043e\u0435\u043a\u0442\u044b, \u043f\u0435\u0440\u0432\u044b\u0435 \u0441\u043e\u0431\u0435\u0441\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f, \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0444\u0444\u0435\u0440, \u043f\u0435\u0440\u0432\u0430\u044f \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u044f. \u041d\u043e \u043c\u043d\u043e\u0433\u0438\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0447\u0430\u043b\u0438 \u0438\u0434\u0442\u0438 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u043f\u0443\u0442\u0438 \u0438 \u0438\u043c\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u043d\u0438\u0445 \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f. \u041f\u043e \u0441\u0432\u043e\u0435\u043c\u0443 \u043e\u043f\u044b\u0442\u0443 \u043f\u043e\u043c\u043d\u044e, \u043a\u0430\u043a \u0441\u043b\u043e\u0436\u043d\u043e \u0438\u0441\u043a\u0430\u0442\u044c \u0433\u0430\u0439\u0434\u044b (\u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0438\u0437 \u043d\u0438\u0445 \u043f\u0440\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043d\u0438\u0436\u043d\u044b\u0445 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u043e\u0432, \u043b\u0438\u0447\u043d\u044b\u0445 \u0431\u043b\u043e\u0433\u043e\u0432 \u0438 \u0442.\u0434.), \u043f\u043e\u044d\u0442\u043e\u043c\u0443, \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u043c\u043d\u043e\u0433\u0438\u043c \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u0446 \u0441\u0435\u0442\u0438. <\/p>\n<h2>\u041f\u043e\u0447\u0435\u043c\u0443 \u0441\u043e\u0446 \u0441\u0435\u0442\u044c \u0438 \u043a\u0430\u043a \u0431\u0443\u0434\u0435\u0442 \u0438\u0434\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441?<\/h2>\n<p>\u0412\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u043c\u043d\u0435 \u0441\u0430\u043c\u043e\u043c\u0443 \u0431\u044b\u043b\u043e \u0431\u044b \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0412\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, \u044f \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0438\u043b\u0441\u044f \u043a\u043d\u0438\u0433\u043e\u0439 <strong>Practical Rails Social Networking Sites by Alan Bradburne<\/strong>. \u041c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e \u043a\u043d\u0438\u0433\u0435, \u0441\u043a\u0430\u0436\u0435\u0442\u0435 \u0432\u044b. \u041d\u043e \u0437\u0430\u0447\u0435\u043c \u0442\u043e\u0433\u0434\u0430 \u044f \u0438 \u043c\u043e\u0438 \u0441\u0442\u0430\u0442\u044c\u0438? \u041a\u043d\u0438\u0433\u0430 2007 \u0433\u043e\u0434\u0430 \u0438 \u0432\u0435\u0440\u0441\u0438\u044f ruby \u0442\u0430\u043c 1.8, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u0432\u043e\u0435\u043c \u0431\u0443\u0434\u0443\u0442 \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b \u0432 \u043d\u0430\u0448\u0435 \u0432\u0440\u0435\u043c\u044f. \u041a \u0442\u043e\u043c\u0443 \u0436\u0435, \u044f \u043d\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u044e\u0441\u044c \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u043e \u043a\u043d\u0438\u0433\u0435, \u0430 \u043b\u0438\u0448\u044c \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0435\u0439 (\u0434\u0438\u0437\u0430\u0439\u043d \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0437 \u043d\u0435\u0435 \u0441 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c <code>bootsrap<\/code>). \u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u0438\u0435 \u0433\u0435\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u044b \u0434\u043b\u044f \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0449\u0438\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432. \u041d\u043e \u0441\u043a\u0430\u0436\u0443 \u0441\u0440\u0430\u0437\u0443: \u044d\u0442\u0430 \u0441\u0435\u0440\u0438\u044f \u0441\u0442\u0430\u0442\u0435\u0439 \u043d\u0435 \u0434\u043b\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u0442\u0430. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u0432\u0435\u0449\u0438 (\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 <code>ruby<\/code>, <code>rails<\/code>, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 <code>MVC<\/code>, <code>git<\/code> \u0438 \u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435) \u044f \u0431\u0443\u0434\u0443 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0442\u044c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0442\u043b\u043e\u0436\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0441\u0442\u0430\u0442\u0435\u0439 \u0438 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u043d\u0435\u043c\u0443 \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435. \u0415\u0441\u043b\u0438 \u0432\u044b \u043e\u043f\u044b\u0442\u043d\u044b\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0438 \u0447\u0438\u0442\u0430\u0435\u0442\u0435 \u044d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e, \u0431\u0443\u0434\u0443 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u0438\u0437\u043d\u0430\u0442\u0435\u043b\u0435\u043d \u0443\u0441\u043b\u044b\u0448\u0430\u0442\u044c \u0432\u0430\u0448\u0435 \u043c\u043d\u0435\u043d\u0438\u0435. \u041a\u0430\u0441\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u043d\u043e\u0441\u0442\u0438 \u0432\u044b\u0445\u043e\u0434\u0430 \u0441\u0442\u0430\u0442\u0435\u0439 \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0445 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435 \u043c\u043e\u0433\u0443 \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u044e \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442 \u0432 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0438 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u0438. \u041d\u043e \u0431\u0443\u0434\u0443 \u0441\u0442\u0430\u0440\u0430\u0442\u044c\u0441\u044f \u043d\u0435 \u043e\u0442\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u0432 \u0434\u043e\u043b\u0433\u0438\u0439 \u044f\u0449\u0438\u043a \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u0441 \u0445\u043e\u0440\u043e\u0448\u0438\u043c \u0442\u0435\u043c\u043f\u043e\u043c.<\/p>\n<h2>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442 \u0438 \u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438<\/h2>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u043c\u044b \u043d\u0430\u0447\u043d\u0435\u043c, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0438:<\/p>\n<ul>\n<li>\n<p><code>Ruby 3.0.3<\/code><\/p>\n<\/li>\n<li>\n<p><code>Rails 6.1.4.6<\/code><\/p>\n<\/li>\n<li>\n<p><code>MySQL 8.0<\/code><\/p>\n<\/li>\n<li>\n<p><code>Node 10.19<\/code><\/p>\n<\/li>\n<li>\n<p><code>Yarn 1.22.17<\/code><\/p>\n<\/li>\n<\/ul>\n<p>\u041e\u0447\u0435\u043d\u044c \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0432\u0430\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>github<\/code> \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430. \u0427\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435 \u043c\u044b \u0441 \u0432\u0430\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c <code>CI\/CD<\/code> \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043a\u0440\u0430\u0439\u043d\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u0434\u043b\u044f \u0432\u0430\u0441 \u043e\u043f\u044b\u0442\u043e\u043c. \u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442. \u042f \u043d\u0430\u0437\u043e\u0432\u0443 \u0435\u0433\u043e <code>g_connect<\/code>, \u043d\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u043e\u0435 \u0434\u0440\u0443\u0433\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 (\u0435\u0441\u043b\u0438 \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0440\u0443\u0433\u043e\u0435, \u0432\u0435\u0437\u0434\u0435, \u0433\u0434\u0435 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>g_connect<\/code>, \u043f\u0438\u0448\u0438\u0442\u0435 \u0441\u0432\u043e\u0435).<\/p>\n<pre><code class=\"bash\">rails new g_connect -d mysql<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043f\u0430\u043f\u043a\u0443 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c \u0438 \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438. \u042f \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044e \u0441 <code>Gemfile<\/code> \u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0433\u0435\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u043e\u0447\u043d\u043e \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<pre><code class=\"ruby\">#Gemfile  source 'https:\/\/rubygems.org' git_source(:github) { |repo| \"https:\/\/github.com\/#{repo}.git\" }  ruby '3.0.3'  gem 'aasm' gem 'bootsnap', '>= 1.4.4', require: false gem 'bootstrap' gem 'devise' gem 'jbuilder', '~> 2.7' gem 'mysql2', '~> 0.5.2' gem 'puma', '~> 5.0' gem 'rails', '~> 6.1.4.6' gem 'sass-rails', '>= 6' gem 'slim' gem 'turbolinks', '~> 5' gem 'webpacker', '~> 5.0'  group :development, :test do   gem 'better_errors'   gem 'binding_of_caller'   gem 'byebug', platforms: %i[mri mingw x64_mingw]   gem 'factory_bot_rails'   gem 'faker'   gem 'rails-controller-testing'   gem 'rspec-rails'   gem 'rubocop'   gem 'rubocop-rails'   gem 'rubocop-rspec' end  group :development do   gem 'annotate'   gem 'listen', '~> 3.3'   gem 'rack-mini-profiler', '~> 2.0'   gem 'spring'   gem 'web-console', '>= 4.1.0' end  group :test do   gem 'capybara', '>= 3.26'   gem 'rspec_junit_formatter'   gem 'selenium-webdriver'   gem 'simplecov', require: false   gem 'webdrivers' end  gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] <\/code><\/pre>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u044f \u043f\u043e\u044f\u0441\u043d\u044e, \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0433\u0435\u043c\u043e\u0432 (\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043f\u0435\u0440\u0435\u0434 \u0441\u0442\u0430\u0440\u0442\u043e\u043c \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439):<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/aasm\/aasm\" rel=\"noopener noreferrer nofollow\">gem &#8216;aasm&#8217;<\/a> &#8212; \u0435\u0433\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0439 \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f\u043c\u0438<\/p>\n<\/li>\n<li>\n<p> <a href=\"https:\/\/github.com\/twbs\/bootstrap-rubygem\" rel=\"noopener noreferrer nofollow\">gem &#8216;bootstrap&#8217;<\/a> &#8212; \u044d\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u0430 (\u044f \u0434\u0435\u043b\u0430\u044e \u0443\u043f\u043e\u0440 \u043d\u0430 \u0431\u044d\u043a \u0438 \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0431\u0443\u0434\u0443 \u0443\u0434\u0435\u043b\u044f\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0444\u0440\u043e\u043d\u0442\u0443, \u043d\u043e \u0432 \u0441\u0430\u043c\u043e\u043c \u043a\u043e\u043d\u0446\u0435 \u043c\u043e\u0436\u0435\u0442 \u0438 \u043f\u0440\u0438\u0434\u0435\u0442 \u0432\u0434\u043e\u0445\u043d\u043e\u0432\u0435\u043d\u0438\u0435 \u043d\u0430 \u043d\u0430\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043a\u0440\u0430\u0441\u043e\u0442\u044b)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/heartcombo\/devise\" rel=\"noopener noreferrer nofollow\">gem &#8216;devise&#8217;<\/a> &#8212; \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f (\u043a\u043e\u0440\u043e\u0442\u043a\u043e \u0438 \u044f\u0441\u043d\u043e, \u0431\u0443\u0434\u0435\u0442 \u0435\u0449\u0435 \u0433\u0435\u043c \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043d\u043e \u044f \u0435\u0449\u0435 \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043b, \u043a\u0430\u043a\u043e\u0439)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/slim-template\/slim\" rel=\"noopener noreferrer nofollow\">gem &#8216;slim&#8217;<\/a> &#8212; \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u043c \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0438 html-\u0442\u0435\u0433\u043e\u0432<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/BetterErrors\/better_errors\" rel=\"noopener noreferrer nofollow\">gem &#8216;better_errors&#8217;<\/a> &#8212; \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u043e\u0448\u0438\u0431\u043e\u043a \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0440\u043e\u0443\u0442 \u0443\u043a\u0430\u0437\u0430\u043b\u0438 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/thoughtbot\/factory_bot_rails\" rel=\"noopener noreferrer nofollow\">gem &#8216;factory_bot_rails&#8217;<\/a> &#8212; \u0448\u0430\u0431\u043b\u043e\u043d, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043d\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u0430\u0445<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/faker-ruby\/faker\" rel=\"noopener noreferrer nofollow\">gem &#8216;faker&#8217; <\/a>&#8212; \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0444\u0435\u0439\u043a\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/rspec\/rspec-rails\" rel=\"noopener noreferrer nofollow\">gem &#8216;rspec-rails <\/a>&#8212; \u044d\u0442\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u0440\u0435\u0434\u0443 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f <code>RSpec<\/code> \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/rubocop\/rubocop\" rel=\"noopener noreferrer nofollow\">gem &#8216;rubocop&#8217;<\/a> &#8212; \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430\u0448 \u043a\u043e\u0434<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/ctran\/annotate_models\" rel=\"noopener noreferrer nofollow\">gem &#8216;annotate&#8217;<\/a> &#8212; \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u043d\u0430\u0448\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 (\u0437\u0430\u0447\u0435\u043c \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043c\u0435\u0436\u0434\u0443 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0435\u0439, \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u044d\u0442\u043e\u0442 \u0433\u0435\u043c)<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/simplecov-ruby\/simplecov\" rel=\"noopener noreferrer nofollow\">gem &#8216;simplecov&#8217; <\/a>&#8212; \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430, \u0432\u0441\u0435 \u043b\u0438 \u043c\u044b \u043f\u043e\u043a\u0440\u044b\u043b\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u0433\u0435\u043c\u044b \u0438 \u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <code>bundle<\/code> \u0432 \u043d\u0430\u0448\u0435\u043c \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435 (\u043a\u0441\u0442\u0430\u0442\u0438 \u0434\u0430, \u0437\u0430\u0431\u044b\u043b \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>Ubuntu<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f <code>MacOS\/Windows<\/code>(\u0432\u043e\u0442 \u0432\u0438\u043d\u0434\u0443 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u0439\u0442\u0435 \u043b\u0443\u0447\u0448\u0435 \u043f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043d\u0430 <code>ruby<\/code>, \u043d\u043e \u0443\u0436 \u0435\u0441\u043b\u0438 \u043e\u0447 \u0445\u043e\u0447\u0435\u0442\u0441\u044f) \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e). \u0422\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u0430\u043f\u043a\u0443 <code>test<\/code>, \u043e\u043d\u0430 \u043d\u0435 \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f (\u0432\u0435\u0434\u044c \u0431\u0443\u0434\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c <code>rspec<\/code>-\u0438).<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c \u043d\u0430\u0448\u0443 \u0411\u0414. \u0412 \u0444\u0430\u0439\u043b\u0435 <code>config\/database.yml<\/code> \u0443\u043a\u0430\u0436\u0438\u0442\u0435 \u0441\u0432\u043e\u0438 username \u0438 password (\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e \u0434\u0435\u043b\u0430\u043b <code>root\/root<\/code>). \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 (\u0435\u0441\u043b\u0438 \u043a\u0442\u043e-\u0442\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442, \u0442\u043e \u044d\u0442\u0430 \u043e\u0434\u043d\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0441\u0440\u0430\u0437\u0443 \u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 <code>db:create<\/code>, <code>db:schema:load<\/code> \u0438 <code>db:seed<\/code>):<\/p>\n<pre><code class=\"bash\">rails db:setup<\/code><\/pre>\n<p>\u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0437 \u043d\u0430\u0448\u0438\u0445 \u0433\u0435\u043c\u043e\u0432 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. \u0418\u043c\u0438 \u043c\u044b \u0441\u0435\u0439\u0447\u0430\u0441 \u0438 \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f (<code>devise<\/code> \u0442\u0430\u043a\u0436\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u043d\u043e \u0438\u043c \u043c\u044b \u0437\u0430\u0439\u043c\u0435\u043c\u0441\u044f \u043f\u043e\u0437\u0436\u0435, \u043a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e). \u041d\u0430\u0447\u043d\u0435\u043c \u0441 <code>bootstrap<\/code>. \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u0444\u0430\u0439\u043b <code>app\/assets\/stylesheets\/application.scss<\/code> (\u0444\u0430\u0439\u043b \u043c\u043e\u0436\u0435\u0442 \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u0438\u043c\u0435\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 <code>.css<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0441\u043f\u0440\u0430\u0432\u044c\u0442\u0435 \u0435\u0433\u043e \u043d\u0430 <code>.scss<\/code>) \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u043e\u0447\u043a\u0443:<\/p>\n<pre><code class=\"css\">\/*app\/assets\/stylesheets\/application.scss*\/  @import \"bootstrap\"; <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c <code>annotate<\/code>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u0443:<\/p>\n<pre><code class=\"bash\">rails g annotate:install<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c <code>rspec<\/code>:<\/p>\n<pre><code>rails g rspec:install<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 <code>factory_bot_rails<\/code> \u0438 <code>simplecov<\/code>. \u0412 \u043d\u0430\u0448\u0435\u0439 \u043f\u0430\u043f\u043a\u0435 <code>spec<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u043f\u043a\u0443 <code>support<\/code>, \u0430 \u0432 \u043d\u0435\u0439 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <code>factory_bot.rb<\/code> \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043a\u043e\u0434\u043e\u043c:<\/p>\n<pre><code class=\"ruby\">#spec\/support\/factory_bot.rb  RSpec.configure do |config|   config.include FactoryBot::Syntax::Methods end <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 \u043d\u0430\u0448 <code>spec\/rails_helper.rb<\/code>. \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043d\u0430\u0448 \u0444\u0430\u0439\u043b \u0434\u043b\u044f <code>factory_bot<\/code>, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c <code>simplecov<\/code> (\u0434\u0432\u0435 \u0441\u0442\u0440\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f <code>simplecov<\/code> \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432 \u0441\u0430\u043c\u043e\u043c \u043d\u0430\u0447\u0430\u043b\u0435 \u0444\u0430\u0439\u043b\u0430).<\/p>\n<pre><code class=\"ruby\">#spec\/rails_helper.rb  require 'simplecov' SimpleCov.start 'rails' require_relative '.\/support\/factory_bot'<\/code><\/pre>\n<p>\u0421 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u0433\u0435\u043c\u043e\u0432 \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0438. \u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u043d\u0442 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u0442\u043e \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c <code>xdg-open coverage\/index.html<\/code> \u0438 \u0432\u043e\u0442 \u043e\u043d\u0430 \u043c\u0430\u0433\u0438\u044f. \u041d\u043e \u044f \u0431\u044b \u0445\u043e\u0442\u0435\u043b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0449\u0435 \u043f\u0430\u0440\u0443 \u043c\u043e\u043c\u0435\u043d\u0442\u043e\u0432. \u041f\u0435\u0440\u0432\u044b\u0439 &#8212; <code>shared_context.rb<\/code> \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u043e\u0432 \u043d\u0430\u0448\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439. \u0412 \u043f\u0430\u043f\u043a\u0435 <code>spec\/support<\/code> \u0441\u043e\u0437\u0434\u0430\u0439\u0442\u0435 <code>shared_context.rb<\/code><\/p>\n<pre><code class=\"ruby\">#spec\/support\/shared_context.rb  RSpec.shared_examples 'creates_object_for' do |model_name|   subject { FactoryBot.build(model_name) }    it 'creates object' do     expect { subject.save }.to change { described_class.count }.by(1)   end end  RSpec.shared_examples 'not_create_object_for' do |model_name, parameter|   subject { described_class.create(attributes) }    let(:attributes) { FactoryBot.attributes_for(model_name, parameter) }    it 'does not create object' do     expect { subject.save }.to change { described_class.count }.by(0)   end    it 'raise RecordInvalid error' do     expect { subject.save! }.to raise_error(ActiveRecord::RecordInvalid)   end end <\/code><\/pre>\n<p>\u041d\u0430\u0448 <code>shared_context<\/code> \u0431\u0443\u0434\u0435\u0442 \u0438\u0433\u0440\u0430\u0442\u044c \u0440\u043e\u043b\u044c \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439. \u0412 \u043d\u0435\u043c \u043c\u044b \u043e\u043f\u0438\u0448\u0435\u043c, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c, \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u0430\u043b\u0438\u0434\u043d\u044b \u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f, \u0438 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c, \u0435\u0441\u043b\u0438 \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b \u043b\u0438\u0431\u043e \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f. \u042d\u0442\u043e \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0442\u0435\u0441\u0442\u043e\u0432 \u0434\u043b\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u043f\u043e\u0442\u043e\u043c \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0432\u044b \u0432 \u044d\u0442\u043e\u043c \u0443\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c. \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u0435\u0433\u043e \u0432 \u043d\u0430\u0448 <code>spec_helper.rb<\/code><\/p>\n<pre><code class=\"ruby\">#spec\/spec_helper.rb  require_relative '.\/support\/shared_context' <\/code><\/pre>\n<p>\u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c \u043f\u0435\u0440\u0435\u0434 \u0441\u0442\u0430\u0440\u0442\u043e\u043c: \u0447\u0443\u0442\u043a\u0430 \u0434\u0438\u0437\u0430\u0439\u043d\u0430. \u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u0432 <code>app\/views\/layouts\/application.html.erb<\/code>. \u0418\u0437\u043c\u0435\u043d\u0438\u0442\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 <code>.erb<\/code> \u043d\u0430 <code>.slim<\/code> \u0438 \u0441\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"ruby\">#app\/views\/layouts\/application.html.slim  doctype html html   head     meta content='text\/html; charset=UTF-8' http-equiv='Content-Type'     title G-Connect     = csrf_meta_tags     = csp_meta_tag     = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'     = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload'   body     #container       #header                #sidemenu         = render 'application\/sidemenu'       #content         = yield <\/code><\/pre>\n<p>\u041a\u0430\u043a \u043f\u043e \u043c\u043d\u0435, \u044d\u0442\u043e \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u043b\u0443\u0447\u0448\u0435 \u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0441\u044f, \u0447\u0435\u043c <code>.html.erb<\/code>. \u0415\u0441\u043b\u0438 \u0432\u044b \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 <code>.slim<\/code>, \u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0432\u043e\u0442 <a href=\"https:\/\/erb2slim.com\/\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u0442<\/a> \u0440\u0435\u0441\u0443\u0440\u0441 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0438\u0437 <code>.html.erb<\/code> \u0432 <code>.html.slim<\/code>. \u0414\u0430\u043b\u0435\u0435 \u0432 \u043f\u0430\u043f\u043a\u0435 <code>app\/views<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u043f\u043a\u0443 application, \u0430 \u0432 \u043d\u0435\u0439 \u0444\u0430\u0439\u043b <code>_sidemenu.html.slim<\/code> \u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0435\u0433\u043e \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u0447\u043a\u0438:<\/p>\n<pre><code class=\"ruby\">#app\/views\/layouts\/_sidemenu.html.slim  ul   li     = link_to 'Home', '\/', class: 'btn btn-sm btn-light' <\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u0432 <code>app\/assets\/stylesheets\/application.scss<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<pre><code class=\"css\">\/*app\/assets\/stylesheets\/application.scss*\/  body {    margin: 0;    padding: 0;    background-color: #f0ffff;    font-family: Arial, Helvetica, sans-serif;  }   #header {    background-color: #f0ffff;    height: 60px;    margin-top: 10px;    text-align: left;    padding-top: 1px;  }   #container {    width: 760px;    min-width: 760px;    margin: 0 auto;    padding: 0px;  }   #sidemenu {    font-size: 80%;    float: left;    width: 100px;    padding: 0px;  }   #sidemenu ul {    list-style: none;    margin-left: 0px;    padding: 0px;  }   a {    color: #b00;  }   a:hover {    background-color: #b00; color: #f0ffff;  }   #content {    float: right;    width: 650px;  }   th {    background-color: #933;    color: #f0ffff;  }   tr.odd {    background-color: #fcc;  }   tr.even {    background-color: #ecc;  } <\/code><\/pre>\n<p>\u041f\u043e\u043a\u0430 \u0441\u043e\u0432\u0441\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u043e (<code>css<\/code> \u0432\u0437\u044f\u0442 \u0438\u0437 \u043a\u043d\u0438\u0433\u0438), \u043d\u043e, \u043a\u0430\u043a \u044f \u0438 \u0433\u043e\u0432\u043e\u0440\u0438\u043b \u0440\u0430\u043d\u0435\u0435, \u044f \u0434\u0435\u043b\u0430\u044e \u0443\u043f\u043e\u0440 \u043d\u0430 \u0431\u044d\u043a. \u041a\u043e\u0433\u0434\u0430 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u0430\u044f<\/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-329776","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/329776","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=329776"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/329776\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=329776"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=329776"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=329776"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}