{"id":1478,"date":"2025-03-24T08:50:38","date_gmt":"2025-03-23T23:50:38","guid":{"rendered":"https:\/\/dexall.co.jp\/articles\/?p=1478"},"modified":"2025-03-24T08:50:38","modified_gmt":"2025-03-23T23:50:38","slug":"%e3%80%90%e4%bf%9d%e5%ad%98%e7%89%88%e3%80%91ruby-on-rails%e3%81%ae%e5%ae%8c%e5%85%a8%e3%82%ac%e3%82%a4%e3%83%89%ef%bc%9a5%e3%81%a4%e3%81%ae%e5%ae%9f%e8%a3%85%e3%83%91%e3%82%bf%e3%83%bc%e3%83%b3","status":"publish","type":"post","link":"https:\/\/dexall.co.jp\/articles\/?p=1478","title":{"rendered":"\u3010\u4fdd\u5b58\u7248\u3011Ruby on Rails\u306e\u5b8c\u5168\u30ac\u30a4\u30c9\uff1a5\u3064\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3068\u5931\u6557\u3057\u306a\u3044\u305f\u3081\u306e\u5b9f\u8df5\u30c6\u30af\u30cb\u30c3\u30af"},"content":{"rendered":"\n<div class=\"toc\"><br \/>\n<b>Warning<\/b>:  Undefined array key \"is_admin\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>116<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_category_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>121<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>128<\/b><br \/>\n    <div id=\"toc_container\" class=\"sgb-toc--bullets js-smooth-scroll\" data-dialog-title=\"\u76ee\u6b21\">\n      <p class=\"toc_title\">\u76ee\u6b21 <\/p>\n      <ul class=\"toc_list\">  <li class=\"first\">    <a href=\"#i-0\">\u306f\u3058\u3081\u306b\uff1aRuby on Rails\u306e\u9b45\u529b\u3068\u5b66\u3076\u610f\u7fa9<\/a>  <\/li>  <li>    <a href=\"#i-1\">Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u306b\u304a\u3051\u308bRuby on Rails\u306e\u4f4d\u7f6e\u3065\u3051<\/a>  <\/li>  <li>    <a href=\"#i-2\">\u3053\u306e\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u3067\u8eab\u306b\u3064\u304f\u30b9\u30ad\u30eb\u3068\u5230\u9054\u76ee\u6a19<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-3\">\u7fd2\u5f97\u3067\u304d\u308b\u6280\u8853\u30b9\u30ad\u30eb<\/a>      <\/li>      <li>        <a href=\"#i-4\">\u5b66\u7fd2\u5f8c\u306e\u5230\u9054\u76ee\u6a19<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-5\">\u5b66\u7fd2\u306e\u9032\u3081\u65b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-6\">Ruby on Rails\u306e\u57fa\u790e\u77e5\u8b58<\/a>  <\/li>  <li>    <a href=\"#i-7\">Ruby\u8a00\u8a9e\u306e\u7279\u5fb4\u3068Rails\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u306e\u95a2\u4fc2<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-8\">Ruby\u306e\u4e3b\u8981\u306a\u7279\u5fb4<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-9\">Rails\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3068Ruby\u306e\u76f8\u4e57\u52b9\u679c<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-10\">MVC\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u306e\u6982\u8981\u3068\u91cd\u8981\u6027<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-11\">Model\uff08\u30e2\u30c7\u30eb\uff09<\/a>      <\/li>      <li>        <a href=\"#i-12\">View\uff08\u30d3\u30e5\u30fc\uff09<\/a>      <\/li>      <li>        <a href=\"#i-13\">Controller\uff08\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\uff09<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-14\">MVC\u306e\u5229\u70b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-15\">\u958b\u767a\u74b0\u5883\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/a>  <\/li>  <li>    <a href=\"#i-16\">Ruby\u3068Rails\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u65b9\u6cd5\uff08OS\u5225\u30ac\u30a4\u30c9\uff09<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-17\">macOS\u3067\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/a>      <\/li>      <li>        <a href=\"#i-18\">Windows\u3067\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-19\">Linux\uff08Ubuntu\uff09\u3067\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-20\">\u7d71\u5408\u958b\u767a\u74b0\u5883\uff08IDE\uff09\u306e\u9078\u3073\u65b9\u3068\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-21\">\u63a8\u5968IDE<\/a>      <\/li>      <li>        <a href=\"#i-22\">\u958b\u767a\u74b0\u5883\u306e\u691c\u8a3c<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-23\">\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-24\">\u306f\u3058\u3081\u3066\u306eRails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u4f5c\u6210<\/a>  <\/li>  <li>    <a href=\"#i-25\">rails new\u30b3\u30de\u30f3\u30c9\u306e\u4f7f\u3044\u65b9\u3068\u521d\u671f\u8a2d\u5b9a<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-26\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210<\/a>      <\/li>      <li>        <a href=\"#i-27\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u69cb\u9020\u306e\u7406\u89e3<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-28\">\u521d\u671f\u8a2d\u5b9a\u306e\u5b9f\u65bd<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-29\">\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3001\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3001\u30d3\u30e5\u30fc\u306e\u57fa\u672c<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-30\">\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306e\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-31\">\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u4f5c\u6210\u3068\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-32\">\u30d3\u30e5\u30fc\u306e\u4f5c\u6210\u3068\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8<\/a>      <\/li>      <li>        <a href=\"#i-33\">\u30ec\u30a4\u30a2\u30a6\u30c8\u3068\u30d1\u30fc\u30b7\u30e3\u30eb<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-34\">\u57fa\u672c\u7684\u306a\u30b9\u30bf\u30a4\u30ea\u30f3\u30b0<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-35\">\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u64cd\u4f5c\u3068\u30e2\u30c7\u30eb\u306e\u6d3b\u7528<\/a>  <\/li>  <li>    <a href=\"#i-36\">Active Record\u3092\u4f7f\u3063\u305f\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u64cd\u4f5c\u306e\u57fa\u790e<\/a>    <ul class=\"menu_level_1\">      <li class=\"first last\">        <a href=\"#i-37\">Active Record\u306e\u57fa\u672c\u64cd\u4f5c<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-40\">\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3068\u30b9\u30ad\u30fc\u30de\u7ba1\u7406\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-41\">\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u57fa\u672c<\/a>      <\/li>      <li>        <a href=\"#i-42\">\u3088\u304f\u4f7f\u3046\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u30b3\u30de\u30f3\u30c9<\/a>      <\/li>      <li>        <a href=\"#i-43\">\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>      <\/li>      <li>        <a href=\"#i-44\">\u30e2\u30c7\u30eb\u306e\u5b9a\u7fa9\u3068\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-45\">\u9ad8\u5ea6\u306a\u30af\u30a8\u30ea\u3068\u6700\u9069\u5316<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-46\">\u30d3\u30e5\u30fc\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3068\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u958b\u767a<\/a>  <\/li>  <li>    <a href=\"#i-47\">ERB\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u4f7f\u3044\u65b9\u3068\u30d8\u30eb\u30d1\u30fc\u30e1\u30bd\u30c3\u30c9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-48\">ERB\u306e\u57fa\u672c\u69cb\u6587<\/a>      <\/li>      <li>        <a href=\"#i-49\">\u4e3b\u8981\u306a\u30d3\u30e5\u30fc\u30d8\u30eb\u30d1\u30fc<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-50\">\u30d1\u30fc\u30b7\u30e3\u30eb\u306e\u6d3b\u7528<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-51\">\u30e2\u30c0\u30f3\u306a\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u6280\u8853\u3068Rails\u306e\u7d71\u5408<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-52\">Webpacker\u306e\u8a2d\u5b9a\u3068\u5229\u7528<\/a>      <\/li>      <li>        <a href=\"#i-53\">Stimulus\u306e\u5c0e\u5165\u3068\u6d3b\u7528<\/a>      <\/li>      <li>        <a href=\"#i-54\">CSS\u8a2d\u8a08\u3068\u30a2\u30bb\u30c3\u30c8\u7ba1\u7406<\/a>      <\/li>      <li>        <a href=\"#i-55\">JavaScript\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u6d3b\u7528<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-56\">\u30a4\u30f3\u30bf\u30e9\u30af\u30c6\u30a3\u30d6\u306aUI\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-57\">\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u3068\u6a29\u9650\u7ba1\u7406\u306e\u5b9f\u88c5<\/a>  <\/li>  <li>    <a href=\"#i-58\">Devise\u3092\u4f7f\u3063\u305f\u5b89\u5168\u306a\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u69cb\u7bc9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-59\">Devise\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/a>      <\/li>      <li>        <a href=\"#i-60\">\u57fa\u672c\u8a2d\u5b9a\uff08config\/initializers\/devise.rb\uff09<\/a>      <\/li>      <li>        <a href=\"#i-61\">\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u3055\u308c\u305fDevise\u30d3\u30e5\u30fc<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-62\">\u30ab\u30b9\u30bf\u30e0Devise\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-63\">CanCanCan\u3092\u6d3b\u7528\u3057\u305f\u67d4\u8edf\u306a\u6a29\u9650\u7ba1\u7406\u306e\u5b9f\u88c5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-64\">CanCanCan\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/a>      <\/li>      <li>        <a href=\"#i-65\">\u6a29\u9650\u306e\u5b9a\u7fa9<\/a>      <\/li>      <li>        <a href=\"#i-66\">\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3067\u306e\u6a29\u9650\u30c1\u30a7\u30c3\u30af<\/a>      <\/li>      <li>        <a href=\"#i-67\">\u30d3\u30e5\u30fc\u3067\u306e\u6a29\u9650\u30c1\u30a7\u30c3\u30af<\/a>      <\/li>      <li>        <a href=\"#i-68\">\u30ed\u30fc\u30eb\u7ba1\u7406\u306e\u5b9f\u88c5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-69\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-70\">\u30c6\u30b9\u30c8\u99c6\u52d5\u958b\u767a\uff08TDD\uff09\u3068RSpec<\/a>  <\/li>  <li>    <a href=\"#i-71\">\u30e2\u30c7\u30eb\u3001\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3001\u30b7\u30b9\u30c6\u30e0\u30c6\u30b9\u30c8\u306e\u66f8\u304d\u65b9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-72\">RSpec\u306e\u57fa\u672c\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-73\">\u30e2\u30c7\u30eb\u30b9\u30da\u30c3\u30af<\/a>      <\/li>      <li>        <a href=\"#i-74\">\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u30b9\u30da\u30c3\u30af<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-75\">\u30b7\u30b9\u30c6\u30e0\u30b9\u30da\u30c3\u30af\uff08\u30d5\u30a3\u30fc\u30c1\u30e3\u30fc\u30c6\u30b9\u30c8\uff09<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-76\">\u30c6\u30b9\u30c8\u30ab\u30d0\u30ec\u30c3\u30b8\u3068\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u624b\u6cd5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-77\">\u30c6\u30b9\u30c8\u30ab\u30d0\u30ec\u30c3\u30b8\u306e\u8a08\u6e2c<\/a>      <\/li>      <li>        <a href=\"#i-78\">\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>      <\/li>      <li>        <a href=\"#i-79\">\u30c6\u30b9\u30c8\u99c6\u52d5\u958b\u767a\u306e\u5b9f\u8df5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-80\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c6\u30b9\u30c8<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-81\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u3068\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3<\/a>  <\/li>  <li>    <a href=\"#i-82\">N+1\u30af\u30a8\u30ea\u554f\u984c\u306e\u89e3\u6c7a\u3068\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u8a2d\u8a08<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-83\">N+1\u554f\u984c\u306e\u7279\u5b9a\u3068\u89e3\u6c7a<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-84\">\u52b9\u679c\u7684\u306a\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u8a2d\u8a08<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-85\">\u30ad\u30e3\u30c3\u30b7\u30e5\u6226\u7565\u3068\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u30b8\u30e7\u30d6\u306e\u6d3b\u7528<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-86\">Rails\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-87\">Redis\/Memcached\u306e\u6d3b\u7528<\/a>      <\/li>      <li>        <a href=\"#i-88\">Sidekiq\u306b\u3088\u308b\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u30b8\u30e7\u30d6<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-89\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-90\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>  <\/li>  <li>    <a href=\"#i-91\">OWASP Top 10\u306b\u57fa\u3065\u304f\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-92\">1. \u30a4\u30f3\u30b8\u30a7\u30af\u30b7\u30e7\u30f3\u5bfe\u7b56<\/a>      <\/li>      <li>        <a href=\"#i-93\">2. \u8a8d\u8a3c\u3068\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-94\">3. \u30af\u30ed\u30b9\u30b5\u30a4\u30c8\u30b9\u30af\u30ea\u30d7\u30c6\u30a3\u30f3\u30b0\uff08XSS\uff09\u5bfe\u7b56<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-95\">\u5b89\u5168\u306aAPI\u306e\u8a2d\u8a08\u3068\u5b9f\u88c5\u65b9\u6cd5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-96\">1. API\u8a8d\u8a3c\u3068JWT\u306e\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-97\">2. \u30ec\u30fc\u30c8\u5236\u9650\u306e\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-98\">3. \u5165\u529b\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u3068\u51fa\u529b\u30a8\u30b9\u30b1\u30fc\u30d7<\/a>      <\/li>      <li>        <a href=\"#i-99\">4. \u30bb\u30ad\u30e5\u30a2\u306a\u30d5\u30a1\u30a4\u30eb\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-100\">5. \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d8\u30c3\u30c0\u30fc\u306e\u8a2d\u5b9a<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-101\">\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u3068CI\/CD<\/a>  <\/li>  <li>    <a href=\"#i-102\">Heroku\u3092\u4f7f\u3063\u305f\u7c21\u5358\u306a\u30c7\u30d7\u30ed\u30a4\u65b9\u6cd5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first last\">        <a href=\"#i-103\">Heroku\u306e\u521d\u671f\u8a2d\u5b9a<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-104\">GitHub Actions\u306b\u3088\u308bCI\/CD\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u306e\u69cb\u7bc9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-105\">CI\/CD\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u306e\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-106\">\u672c\u756a\u74b0\u5883\u306e\u8a2d\u5b9a\u3068\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-107\">\u30c7\u30d7\u30ed\u30a4\u6642\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-108\">\u6b21\u306e\u30b9\u30c6\u30c3\u30d7\uff1a\u4e2d\u7d1a\u8005\u304b\u3089\u4e0a\u7d1a\u8005\u3078<\/a>  <\/li>  <li>    <a href=\"#i-109\">Ruby on Rails\u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u3078\u306e\u53c2\u52a0\u65b9\u6cd5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-110\">\u30aa\u30f3\u30e9\u30a4\u30f3\u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u3078\u306e\u53c2\u52a0<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-111\">\u5b9f\u8df5\u7684\u306a\u53c2\u52a0\u65b9\u6cd5<\/a>      <\/li>    <\/ul>  <\/li>  <li class=\"last\">    <a href=\"#i-112\">\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3078\u306e\u8ca2\u732e\u3068\u30b9\u30ad\u30eb\u5411\u4e0a\u306e\u30d2\u30f3\u30c8<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-113\">\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3078\u306e\u8ca2\u732e\u65b9\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-114\">\u30b9\u30ad\u30eb\u5411\u4e0a\u306e\u305f\u3081\u306e\u30ed\u30fc\u30c9\u30de\u30c3\u30d7<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-115\">\u30ad\u30e3\u30ea\u30a2\u767a\u5c55\u306e\u305f\u3081\u306e\u30a2\u30c9\u30d0\u30a4\u30b9<\/a>      <\/li>    <\/ul>  <\/li><\/ul>\n      <a href=\"#\" class=\"sgb-toc-button js-toc-button\" rel=\"nofollow\" data-open-dialog=\"true\"><i class=\"fa fa-list\"><\/i><span class=\"sgb-toc-button__text\">\u76ee\u6b21\u3078<\/span><\/a>\n    <\/div><\/div><h2 class=\"wp-block-heading\" id=\"i-0\">\u306f\u3058\u3081\u306b\uff1aRuby on Rails\u306e\u9b45\u529b\u3068\u5b66\u3076\u610f\u7fa9<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-1\">Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u306b\u304a\u3051\u308bRuby on Rails\u306e\u4f4d\u7f6e\u3065\u3051<\/h2>\n\n\n\n<p>Ruby on Rails\uff08\u4ee5\u4e0b\u3001Rails\uff09\u306f\u30012024\u5e74\u73fe\u5728\u3082\u6210\u9577\u3092\u7d9a\u3051\u308b\u5f37\u529b\u306aWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3067\u3059\u3002GitHub\u3084Shopify\u3001Cookpad\u3001Airbnb\u306a\u3069\u3001\u4e16\u754c\u7684\u306b\u6709\u540d\u306a\u30b5\u30fc\u30d3\u30b9\u3067\u63a1\u7528\u3055\u308c\u7d9a\u3051\u3066\u3044\u308b\u7406\u7531\u306f\u3001\u305d\u306e\u751f\u7523\u6027\u306e\u9ad8\u3055\u3068\u5805\u7262\u306a\u30a8\u30b3\u30b7\u30b9\u30c6\u30e0\u306b\u3042\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<p>Rails\u306e\u7279\u5fb4\u7684\u306a\u5f37\u307f\u306f\u4ee5\u4e0b\u306e\u70b9\u306b\u3042\u308a\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Convention over Configuration\uff08CoC\uff09<\/strong>: \u8a2d\u5b9a\u3088\u308a\u898f\u7d04\u3092\u91cd\u8996\u3059\u308b\u3053\u3068\u3067\u3001\u958b\u767a\u8005\u306f\u672c\u8cea\u7684\u306a\u696d\u52d9\u30ed\u30b8\u30c3\u30af\u306b\u96c6\u4e2d\u3067\u304d\u307e\u3059\u3002<\/li>\n\n\n\n<li><strong>Don\u2019t Repeat Yourself\uff08DRY\uff09<\/strong>: \u30b3\u30fc\u30c9\u306e\u91cd\u8907\u3092\u907f\u3051\u308b\u3053\u3068\u3067\u3001\u4fdd\u5b88\u6027\u306e\u9ad8\u3044\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u958b\u767a\u3067\u304d\u307e\u3059\u3002<\/li>\n\n\n\n<li><strong>\u5145\u5b9f\u3057\u305f\u30a8\u30b3\u30b7\u30b9\u30c6\u30e0<\/strong>: \u8c4a\u5bcc\u306aGem\u30e9\u30a4\u30d6\u30e9\u30ea\u306b\u3088\u308a\u3001\u591a\u304f\u306e\u6a5f\u80fd\u3092\u5bb9\u6613\u306b\u5b9f\u88c5\u3067\u304d\u307e\u3059\u3002<\/li>\n\n\n\n<li><strong>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56<\/strong>: \u30c7\u30d5\u30a9\u30eb\u30c8\u3067\u5f37\u529b\u306a\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u6a5f\u80fd\u304c\u7d44\u307f\u8fbc\u307e\u308c\u3066\u3044\u307e\u3059\u3002<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-2\">\u3053\u306e\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u3067\u8eab\u306b\u3064\u304f\u30b9\u30ad\u30eb\u3068\u5230\u9054\u76ee\u6a19<\/h2>\n\n\n\n<p>\u672c\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u306f\u3001Rails\u306e\u57fa\u790e\u304b\u3089\u5b9f\u8df5\u7684\u306a\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u307e\u3067\u3001\u6bb5\u968e\u7684\u306b\u5b66\u7fd2\u3067\u304d\u308b\u3088\u3046\u306b\u8a2d\u8a08\u3055\u308c\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-3\">\u7fd2\u5f97\u3067\u304d\u308b\u6280\u8853\u30b9\u30ad\u30eb<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u57fa\u672c\u7684\u306aRails\u958b\u767a\u30b9\u30ad\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>MVC\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u306e\u7406\u89e3\u3068\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u8a08\u3068Active Record<\/li>\n\n\n\n<li>\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3068\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30d3\u30e5\u30fc\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u4f5c\u6210<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u5b9f\u8df5\u7684\u306a\u958b\u767a\u624b\u6cd5<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c6\u30b9\u30c8\u99c6\u52d5\u958b\u767a\uff08TDD\uff09\u306e\u5b9f\u8df5<\/li>\n\n\n\n<li>GitHub\u3092\u4f7f\u7528\u3057\u305f\u30d0\u30fc\u30b8\u30e7\u30f3\u7ba1\u7406<\/li>\n\n\n\n<li>\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u3068CI\/CD\u306e\u69cb\u7bc9<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u3068\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30bb\u30ad\u30e5\u30a2\u306a\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0<\/li>\n\n\n\n<li>\u30b9\u30b1\u30fc\u30e9\u30d6\u30eb\u306a\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u8a2d\u8a08<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-4\">\u5b66\u7fd2\u5f8c\u306e\u5230\u9054\u76ee\u6a19<\/h3>\n\n\n\n<p>\u3053\u306e\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u3092\u5b8c\u4e86\u3059\u308b\u3053\u3068\u3067\u3001\u4ee5\u4e0b\u306e\u3053\u3068\u304c\u9054\u6210\u3067\u304d\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30aa\u30ea\u30b8\u30ca\u30eb\u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u72ec\u529b\u3067\u958b\u767a\u3067\u304d\u308b<\/li>\n\n\n\n<li>\u30c1\u30fc\u30e0\u958b\u767a\u3067\u5fc5\u8981\u3068\u3055\u308c\u308b\u57fa\u672c\u7684\u306a\u30b9\u30ad\u30eb\u3092\u8eab\u306b\u3064\u3051\u308b<\/li>\n\n\n\n<li>\u5b9f\u52d9\u30ec\u30d9\u30eb\u306e\u30b3\u30fc\u30c9\u3092\u8aad\u307f\u66f8\u304d\u3067\u304d\u308b<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30a2\u3067\u4fdd\u5b88\u6027\u306e\u9ad8\u3044\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8a2d\u8a08\u3067\u304d\u308b<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-5\">\u5b66\u7fd2\u306e\u9032\u3081\u65b9<\/h3>\n\n\n\n<p>\u672c\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u306f\u3001\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u65b9\u3005\u306b\u6700\u9069\u306a\u5b66\u7fd2\u30d1\u30b9\u3092\u63d0\u4f9b\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u521d\u5b66\u8005<\/li>\n\n\n\n<li>\u4ed6\u8a00\u8a9e\u304b\u3089Ruby\u306b\u79fb\u884c\u3059\u308b\u958b\u767a\u8005<\/li>\n\n\n\n<li>Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u3092\u5b66\u3073\u305f\u3044\u30a8\u30f3\u30b8\u30cb\u30a2<\/li>\n<\/ul>\n\n\n\n<p>\u5404\u30bb\u30af\u30b7\u30e7\u30f3\u306f\u5b9f\u8df5\u7684\u306a\u4f8b\u3092\u4ea4\u3048\u306a\u304c\u3089\u89e3\u8aac\u3057\u3001\u30cf\u30f3\u30ba\u30aa\u30f3\u5f62\u5f0f\u3067\u5b66\u7fd2\u3092\u9032\u3081\u3089\u308c\u308b\u3088\u3046\u69cb\u6210\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u307e\u305f\u3001\u767a\u5c55\u7684\u306a\u5185\u5bb9\u3082\u542b\u3080\u3053\u3068\u3067\u3001\u4e2d\u7d1a\u8005\u306e\u65b9\u3005\u306b\u3082\u4fa1\u5024\u306e\u3042\u308b\u60c5\u5831\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p>\u3055\u3042\u3001\u30e2\u30c0\u30f3\u306aWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u306e\u4e16\u754c\u3078\u98db\u3073\u8fbc\u307f\u307e\u3057\u3087\u3046\u3002Ruby on Rails\u306e\u9b45\u529b\u3068\u53ef\u80fd\u6027\u304c\u3001\u3042\u306a\u305f\u306e\u958b\u767a\u8005\u3068\u3057\u3066\u306e\u30ad\u30e3\u30ea\u30a2\u3092\u5927\u304d\u304f\u5e83\u3052\u3066\u304f\u308c\u308b\u306f\u305a\u3067\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-6\">Ruby on Rails\u306e\u57fa\u790e\u77e5\u8b58<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-7\">Ruby\u8a00\u8a9e\u306e\u7279\u5fb4\u3068Rails\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u306e\u95a2\u4fc2<\/h2>\n\n\n\n<p>Ruby\u306f\u3001\u307e\u3064\u3082\u3068\u3086\u304d\u3072\u308d\u6c0f\u306b\u3088\u3063\u3066\u958b\u767a\u3055\u308c\u305f\u3001\u30d7\u30ed\u30b0\u30e9\u30de\u30fc\u306e\u751f\u7523\u6027\u3092\u91cd\u8996\u3057\u305f\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u8a00\u8a9e\u3067\u3059\u3002\u305d\u306e\u7279\u5fb4\u7684\u306a\u6027\u8cea\u304c\u3001Ruby on Rails\u306e\u8a2d\u8a08\u601d\u60f3\u306b\u5927\u304d\u306a\u5f71\u97ff\u3092\u4e0e\u3048\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-8\">Ruby\u306e\u4e3b\u8981\u306a\u7279\u5fb4<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u6307\u5411\u8a00\u8a9e<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Ruby\u3067\u306f\u3059\u3079\u3066\u304c\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\n5.times { puts \"Hello\" }  # \u6570\u5024\u3082\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\n\"Hello\".upcase  # \u6587\u5b57\u5217\u3082\u30aa\u30d6\u30b8\u30a7\u30af\u30c8<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30b7\u30f3\u30d7\u30eb\u3067\u8aad\u307f\u3084\u3059\u3044\u6587\u6cd5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u6761\u4ef6\u5206\u5c90\u306e\u4f8b\nif user.admin?\n  puts \"\u7ba1\u7406\u8005\u3067\u3059\"\nelse\n  puts \"\u4e00\u822c\u30e6\u30fc\u30b6\u30fc\u3067\u3059\"\nend\n\n# \u30a4\u30c6\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u4f8b\nusers.each do |user|\n  puts user.name\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u8c4a\u5bcc\u306a\u7d44\u307f\u8fbc\u307f\u30e1\u30bd\u30c3\u30c9<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u914d\u5217\u64cd\u4f5c\u306e\u4f8b\nnumbers = [1, 2, 3, 4, 5]\ndoubled = numbers.map { |n| n * 2 }  # [2, 4, 6, 8, 10]\nsum = numbers.reduce(:+)  # 15<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-9\">Rails\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3068Ruby\u306e\u76f8\u4e57\u52b9\u679c<\/h3>\n\n\n\n<p>Rails\u306f\u3001Ruby\u306e\u7279\u5fb4\u3092\u6700\u5927\u9650\u306b\u6d3b\u7528\u3057\u3066\u4f5c\u3089\u308c\u3066\u3044\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u30e1\u30bf\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0<\/strong>\uff1aRails\u306e\u9b54\u6cd5\u306e\u3088\u3046\u306a\u6a5f\u80fd\u306e\u591a\u304f\u306f\u3001Ruby\u306e\u30e1\u30bf\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u6a5f\u80fd\u3092\u5229\u7528\u3057\u3066\u3044\u307e\u3059<\/li>\n\n\n\n<li><strong>DSL\uff08\u30c9\u30e1\u30a4\u30f3\u7279\u5316\u8a00\u8a9e\uff09<\/strong>\uff1a\u76f4\u611f\u7684\u306a\u8a2d\u5b9a\u3084\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306e\u8a18\u8ff0\u304c\u53ef\u80fd<\/li>\n\n\n\n<li><strong>Gem<\/strong>\uff1a\u8c4a\u5bcc\u306a\u30e9\u30a4\u30d6\u30e9\u30ea\u30a8\u30b3\u30b7\u30b9\u30c6\u30e0\u306b\u3088\u308a\u3001\u6a5f\u80fd\u3092\u5bb9\u6613\u306b\u62e1\u5f35\u3067\u304d\u307e\u3059<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-10\">MVC\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u306e\u6982\u8981\u3068\u91cd\u8981\u6027<\/h2>\n\n\n\n<p>MVC\u306f\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4ee5\u4e0b\u306e3\u3064\u306e\u5f79\u5272\u306b\u5206\u96e2\u3059\u308b\u8a2d\u8a08\u30d1\u30bf\u30fc\u30f3\u3067\u3059\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-11\">Model\uff08\u30e2\u30c7\u30eb\uff09<\/h3>\n\n\n\n<p>\u30c7\u30fc\u30bf\u3068\u30d3\u30b8\u30cd\u30b9\u30ed\u30b8\u30c3\u30af\u3092\u7ba1\u7406\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30e6\u30fc\u30b6\u30fc\u30e2\u30c7\u30eb\u306e\u4f8b\nclass User &lt; ApplicationRecord\n  has_many :posts\n  validates :email, presence: true, uniqueness: true\n\n  def full_name\n    \"#{first_name} #{last_name}\"\n  end\nend<\/pre>\n\n\n\n<p>\u4e3b\u306a\u8cac\u52d9\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3068\u306e\u3084\u308a\u53d6\u308a<\/li>\n\n\n\n<li>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/li>\n\n\n\n<li>\u30d3\u30b8\u30cd\u30b9\u30ed\u30b8\u30c3\u30af<\/li>\n\n\n\n<li>\u30e2\u30c7\u30eb\u9593\u306e\u95a2\u9023\u4ed8\u3051<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-12\">View\uff08\u30d3\u30e5\u30fc\uff09<\/h3>\n\n\n\n<p>\u30e6\u30fc\u30b6\u30fc\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9\u3092\u62c5\u5f53\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># index.html.erb\u306e\u4f8b\n&lt;h1&gt;\u30e6\u30fc\u30b6\u30fc\u4e00\u89a7&lt;\/h1&gt;\n&lt;% @users.each do |user| %&gt;\n  &lt;div class=\"user-card\"&gt;\n    &lt;h2&gt;&lt;%= user.full_name %&gt;&lt;\/h2&gt;\n    &lt;p&gt;&lt;%= user.email %&gt;&lt;\/p&gt;\n  &lt;\/div&gt;\n&lt;% end %&gt;<\/pre>\n\n\n\n<p>\u4e3b\u306a\u8cac\u52d9\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>HTML\u306e\u751f\u6210<\/li>\n\n\n\n<li>\u30c7\u30fc\u30bf\u306e\u8868\u793a\u5f62\u5f0f\u306e\u5b9a\u7fa9<\/li>\n\n\n\n<li>\u30e6\u30fc\u30b6\u30fc\u3068\u306e\u30a4\u30f3\u30bf\u30e9\u30af\u30b7\u30e7\u30f3<\/li>\n\n\n\n<li>\u30ec\u30a4\u30a2\u30a6\u30c8\u3068\u30b9\u30bf\u30a4\u30ea\u30f3\u30b0<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-13\">Controller\uff08\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\uff09<\/h3>\n\n\n\n<p>Model\u3068View\u306e\u6a4b\u6e21\u3057\u3092\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30e6\u30fc\u30b6\u30fc\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u4f8b\nclass UsersController &lt; ApplicationController\n  def index\n    @users = User.all\n  end\n\n  def show\n    @user = User.find(params[:id])\n  end\n\n  def create\n    @user = User.new(user_params)\n    if @user.save\n      redirect_to @user, notice: '\u30e6\u30fc\u30b6\u30fc\u3092\u4f5c\u6210\u3057\u307e\u3057\u305f'\n    else\n      render :new\n    end\n  end\nend<\/pre>\n\n\n\n<p>\u4e3b\u306a\u8cac\u52d9\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u51e6\u7406<\/li>\n\n\n\n<li>\u30e2\u30c7\u30eb\u306e\u64cd\u4f5c<\/li>\n\n\n\n<li>\u30d3\u30e5\u30fc\u306e\u9078\u629e<\/li>\n\n\n\n<li>\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u3068\u30d5\u30e9\u30c3\u30b7\u30e5\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u7ba1\u7406<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-14\">MVC\u306e\u5229\u70b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u95a2\u5fc3\u306e\u5206\u96e2<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30b3\u30fc\u30c9\u306e\u8cac\u52d9\u304c\u660e\u78ba\u306b\u5206\u304b\u308c\u308b<\/li>\n\n\n\n<li>\u4fdd\u5b88\u6027\u304c\u5411\u4e0a\u3059\u308b<\/li>\n\n\n\n<li>\u30c1\u30fc\u30e0\u958b\u767a\u304c\u5bb9\u6613\u306b\u306a\u308b<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30c6\u30b9\u30c8\u306e\u5bb9\u6613\u3055<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5404\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u3092\u72ec\u7acb\u3057\u3066\u30c6\u30b9\u30c8\u53ef\u80fd<\/li>\n\n\n\n<li>\u5358\u4f53\u30c6\u30b9\u30c8\u304c\u66f8\u304d\u3084\u3059\u3044<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30b3\u30fc\u30c9\u306e\u518d\u5229\u7528\u6027<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30e2\u30c7\u30eb\u306e\u30ed\u30b8\u30c3\u30af\u3092\u8907\u6570\u306e\u30d3\u30e5\u30fc\u3067\u4f7f\u7528\u53ef\u80fd<\/li>\n\n\n\n<li>\u30d3\u30e5\u30fc\u3092\u7570\u306a\u308b\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3067\u518d\u5229\u7528\u53ef\u80fd<\/li>\n<\/ul>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u57fa\u790e\u77e5\u8b58\u306f\u3001Rails\u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u958b\u767a\u3059\u308b\u4e0a\u3067\u4e0d\u53ef\u6b20\u306a\u8981\u7d20\u3068\u306a\u308a\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u3053\u308c\u3089\u306e\u6982\u5ff5\u3092\u5b9f\u969b\u306b\u6d3b\u7528\u3059\u308b\u305f\u3081\u306e\u958b\u767a\u74b0\u5883\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u306b\u9032\u307f\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-15\">\u958b\u767a\u74b0\u5883\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-16\">Ruby\u3068Rails\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u65b9\u6cd5\uff08OS\u5225\u30ac\u30a4\u30c9\uff09<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-17\">macOS\u3067\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Homebrew\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Homebrew\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\n\/bin\/bash -c \"$(curl -fsSL https:\/\/raw.githubusercontent.com\/Homebrew\/install\/HEAD\/install.sh)\"<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>rbenv\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># rbenv\u3068ruby-build\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nbrew install rbenv ruby-build\n\n# rbenv\u306e\u521d\u671f\u5316\necho 'eval \"$(rbenv init -)\"' &gt;&gt; ~\/.zshrc  # zsh\u306e\u5834\u5408\nsource ~\/.zshrc<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Ruby\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u5229\u7528\u53ef\u80fd\u306aRuby\u30d0\u30fc\u30b8\u30e7\u30f3\u306e\u78ba\u8a8d\nrbenv install -l\n\n# Ruby 3.3.0\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\uff082024\u5e74\u63a8\u5968\u7248\uff09\nrbenv install 3.3.0\nrbenv global 3.3.0\n\n# \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306e\u78ba\u8a8d\nruby -v<\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>Rails\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u6700\u65b0\u7248Rails\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\ngem install rails\n\n# \u30d0\u30fc\u30b8\u30e7\u30f3\u78ba\u8a8d\nrails -v<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-18\">Windows\u3067\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>WSL2\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Windows\u306e\u5834\u5408\u3001WSL2\uff08Windows Subsystem for Linux\uff09\u306e\u4f7f\u7528\u3092\u5f37\u304f\u63a8\u5968<\/li>\n\n\n\n<li>PowerShell\u3092\u7ba1\u7406\u8005\u3068\u3057\u3066\u5b9f\u884c\u3057\u3001\u4ee5\u4e0b\u306e\u30b3\u30de\u30f3\u30c9\u3092\u5b9f\u884c\uff1a<\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">wsl --install<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Ubuntu\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30d1\u30c3\u30b1\u30fc\u30b8\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\u306e\u66f4\u65b0\nsudo apt update\nsudo apt upgrade\n\n# \u5fc5\u8981\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nsudo apt install git curl libssl-dev libreadline-dev zlib1g-dev autoconf bison build-essential libyaml-dev libreadline-dev libncurses5-dev libffi-dev libgdbm-dev<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>rbenv\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># rbenv\u3068\u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\ncurl -fsSL https:\/\/github.com\/rbenv\/rbenv-installer\/raw\/HEAD\/bin\/rbenv-installer | bash\n\n# PATH\u306e\u8a2d\u5b9a\necho 'export PATH=\"$HOME\/.rbenv\/bin:$PATH\"' &gt;&gt; ~\/.bashrc\necho 'eval \"$(rbenv init -)\"' &gt;&gt; ~\/.bashrc\nsource ~\/.bashrc<\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>Ruby\u3068Rails\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">rbenv install 3.3.0\nrbenv global 3.3.0\ngem install rails<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-19\">Linux\uff08Ubuntu\uff09\u3067\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u5fc5\u8981\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">sudo apt update\nsudo apt install git curl libssl-dev libreadline-dev zlib1g-dev autoconf bison build-essential libyaml-dev libreadline-dev libncurses5-dev libffi-dev libgdbm-dev<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>rbenv\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># rbenv\u3068\u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\ncurl -fsSL https:\/\/github.com\/rbenv\/rbenv-installer\/raw\/HEAD\/bin\/rbenv-installer | bash\n\n# PATH\u306e\u8a2d\u5b9a\necho 'export PATH=\"$HOME\/.rbenv\/bin:$PATH\"' &gt;&gt; ~\/.bashrc\necho 'eval \"$(rbenv init -)\"' &gt;&gt; ~\/.bashrc\nsource ~\/.bashrc<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Ruby\u3068Rails\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">rbenv install 3.3.0\nrbenv global 3.3.0\ngem install rails<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-20\">\u7d71\u5408\u958b\u767a\u74b0\u5883\uff08IDE\uff09\u306e\u9078\u3073\u65b9\u3068\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-21\">\u63a8\u5968IDE<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>VSCode\uff08Visual Studio Code\uff09<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u7121\u6599\u3067\u9ad8\u6a5f\u80fd<\/li>\n\n\n\n<li>\u8c4a\u5bcc\u306a\u62e1\u5f35\u6a5f\u80fd<\/li>\n\n\n\n<li>\u30af\u30ed\u30b9\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u5bfe\u5fdc<\/li>\n<\/ul>\n\n\n\n<p>\u5fc5\u9808\u62e1\u5f35\u6a5f\u80fd\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ruby<\/li>\n\n\n\n<li>Ruby on Rails<\/li>\n\n\n\n<li>Ruby Solargraph<\/li>\n\n\n\n<li>ERB Formatter\/Beautify<\/li>\n\n\n\n<li>GitLens<\/li>\n<\/ul>\n\n\n\n<p>\u8a2d\u5b9a\u4f8b\uff08settings.json\uff09\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">{\n  \"ruby.useLanguageServer\": true,\n  \"ruby.lint\": {\n    \"rubocop\": true\n  },\n  \"ruby.format\": \"rubocop\",\n  \"[ruby]\": {\n    \"editor.formatOnSave\": true,\n    \"editor.defaultFormatter\": \"rebornix.ruby\"\n  }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>RubyMine<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6709\u6599\u3060\u304c\u6700\u3082\u5b8c\u6210\u5ea6\u306e\u9ad8\u3044Ruby\/Rails IDE<\/li>\n\n\n\n<li>\u9ad8\u5ea6\u306a\u30b3\u30fc\u30c9\u88dc\u5b8c<\/li>\n\n\n\n<li>\u30c7\u30d0\u30c3\u30b0\u6a5f\u80fd\u304c\u5145\u5b9f<\/li>\n\n\n\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u7ba1\u7406\u30c4\u30fc\u30eb\u5185\u8535<\/li>\n<\/ul>\n\n\n\n<p>\u4e3b\u306a\u6a5f\u80fd\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30a4\u30f3\u30c6\u30ea\u30b8\u30a7\u30f3\u30c8\u306a\u30b3\u30fc\u30c9\u88dc\u5b8c<\/li>\n\n\n\n<li>\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u30c4\u30fc\u30eb<\/li>\n\n\n\n<li>\u30c6\u30b9\u30c8\u5b9f\u884c\u74b0\u5883<\/li>\n\n\n\n<li>Git\u30af\u30e9\u30a4\u30a2\u30f3\u30c8<\/li>\n\n\n\n<li>\u30c7\u30d0\u30c3\u30ac\u30fc<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-22\">\u958b\u767a\u74b0\u5883\u306e\u691c\u8a3c<\/h3>\n\n\n\n<p>\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u5f8c\u306e\u52d5\u4f5c\u78ba\u8a8d\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u65b0\u3057\u3044Rails\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210\nrails new test_app\ncd test_app\n\n# \u4f9d\u5b58\u95a2\u4fc2\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nbundle install\n\n# Webpacker\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nrails webpacker:install\n\n# \u30b5\u30fc\u30d0\u30fc\u306e\u8d77\u52d5\nrails server<\/pre>\n\n\n\n<p>\u30d6\u30e9\u30a6\u30b6\u3067 http:\/\/localhost:3000 \u306b\u30a2\u30af\u30bb\u30b9\u3057\u3001Rails\u306e\u30a6\u30a7\u30eb\u30ab\u30e0\u30da\u30fc\u30b8\u304c\u8868\u793a\u3055\u308c\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-23\">\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0<\/h3>\n\n\n\n<p>\u3088\u304f\u3042\u308b\u554f\u984c\u3068\u89e3\u6c7a\u65b9\u6cd5\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>gem install\u304c\u5931\u6557\u3059\u308b\u5834\u5408<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u6a29\u9650\u306e\u554f\u984c\u306e\u5834\u5408\nsudo gem install rails\n\n# \u307e\u305f\u306f\ngem install rails --user-install<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Node.js\u304c\u306a\u3044\u5834\u5408<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># macOS\nbrew install node\n\n# Ubuntu\/WSL\ncurl -fsSL https:\/\/deb.nodesource.com\/setup_18.x | sudo -E bash -\nsudo apt-get install -y nodejs<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Yarn\u304c\u5fc5\u8981\u306a\u5834\u5408<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># macOS\nbrew install yarn\n\n# Ubuntu\/WSL\nnpm install -g yarn<\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30a8\u30e9\u30fc<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># PostgreSQL\u306e\u5834\u5408\nsudo apt install postgresql postgresql-contrib libpq-dev<\/pre>\n\n\n\n<p>\u3053\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u30ac\u30a4\u30c9\u306b\u5f93\u3048\u3070\u3001Ruby on Rails\u306e\u958b\u767a\u74b0\u5883\u304c\u6574\u3044\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u3053\u306e\u74b0\u5883\u3092\u4f7f\u3063\u3066\u5b9f\u969b\u306bRails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4f5c\u6210\u3092\u958b\u59cb\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-24\">\u306f\u3058\u3081\u3066\u306eRails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u4f5c\u6210<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-25\">rails new\u30b3\u30de\u30f3\u30c9\u306e\u4f7f\u3044\u65b9\u3068\u521d\u671f\u8a2d\u5b9a<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-26\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210<\/h3>\n\n\n\n<p>\u6700\u521d\u306eRails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4f5c\u6210\u3059\u308b\u305f\u3081\u306b\u3001\u4ee5\u4e0b\u306e\u30b3\u30de\u30f3\u30c9\u3092\u5b9f\u884c\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u57fa\u672c\u7684\u306aRails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4f5c\u6210\nrails new my_first_app\n\n# PostgreSQL\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\nrails new my_first_app --database=postgresql\n\n# API\u30e2\u30fc\u30c9\u3067\u4f5c\u6210\u3059\u308b\u5834\u5408\nrails new my_first_app --api\n\n# \u7279\u5b9a\u306eRails\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u6307\u5b9a\u3059\u308b\u5834\u5408\nrails _7.1.0_ new my_first_app<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-27\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u69cb\u9020\u306e\u7406\u89e3<\/h3>\n\n\n\n<p>\u751f\u6210\u3055\u308c\u305f\u4e3b\u8981\u306a\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3068\u30d5\u30a1\u30a4\u30eb\u306e\u5f79\u5272\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">my_first_app\/\n\u251c\u2500\u2500 app\/                    # \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30e1\u30a4\u30f3\u30b3\u30fc\u30c9\n\u2502   \u251c\u2500\u2500 controllers\/       # \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\n\u2502   \u251c\u2500\u2500 models\/           # \u30e2\u30c7\u30eb\n\u2502   \u251c\u2500\u2500 views\/            # \u30d3\u30e5\u30fc\n\u2502   \u251c\u2500\u2500 assets\/          # CSS\u3001JavaScript\u3001\u753b\u50cf\n\u2502   \u2514\u2500\u2500 helpers\/         # \u30d3\u30e5\u30fc\u30d8\u30eb\u30d1\u30fc\n\u251c\u2500\u2500 config\/               # \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\n\u2502   \u251c\u2500\u2500 routes.rb        # \u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u5b9a\u7fa9\n\u2502   \u2514\u2500\u2500 database.yml     # \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u5b9a\n\u251c\u2500\u2500 db\/                  # \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u95a2\u9023\n\u251c\u2500\u2500 Gemfile              # gem\u4f9d\u5b58\u95a2\u4fc2\n\u2514\u2500\u2500 test\/               # \u30c6\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-28\">\u521d\u671f\u8a2d\u5b9a\u306e\u5b9f\u65bd<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u4f5c\u6210\nrails db:create\n\n# \u4f9d\u5b58\u95a2\u4fc2\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nbundle install\n\n# Webpacker\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\uff08\u5fc5\u8981\u306a\u5834\u5408\uff09\nrails webpacker:install\n\n# \u958b\u767a\u30b5\u30fc\u30d0\u30fc\u306e\u8d77\u52d5\nrails server<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-29\">\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3001\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3001\u30d3\u30e5\u30fc\u306e\u57fa\u672c<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-30\">\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306e\u8a2d\u5b9a<\/h3>\n\n\n\n<p><code>config\/routes.rb<\/code>\u3067URL\u3068\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u7d10\u4ed8\u3051\u307e\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Rails.application.routes.draw do\n  # \u30eb\u30fc\u30c8\u30d1\u30b9\u306e\u8a2d\u5b9a\n  root 'home#index'\n\n  # \u57fa\u672c\u7684\u306aCRUD\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\n  resources :posts\n\n  # \u30ab\u30b9\u30bf\u30e0\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\n  get 'about', to: 'pages#about'\n\n  # \u30cd\u30b9\u30c8\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9\n  resources :users do\n    resources :comments\n  end\nend<\/pre>\n\n\n\n<p>\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306e\u78ba\u8a8d\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u5229\u7528\u53ef\u80fd\u306a\u30eb\u30fc\u30c8\u306e\u4e00\u89a7\u8868\u793a\nrails routes<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-31\">\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u4f5c\u6210\u3068\u5b9f\u88c5<\/h3>\n\n\n\n<p>\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u751f\u6210\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u57fa\u672c\u7684\u306a\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u751f\u6210\nrails generate controller Posts index show new edit\n\n# RESTful\u306a\u30ea\u30bd\u30fc\u30b9\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u751f\u6210\nrails generate scaffold_controller Post title:string content:text<\/pre>\n\n\n\n<p>\u5b9f\u88c5\u4f8b\uff08<code>app\/controllers\/posts_controller.rb<\/code>\uff09\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class PostsController &lt; ApplicationController\n  def index\n    @posts = Post.all\n  end\n\n  def show\n    @post = Post.find(params[:id])\n  end\n\n  def new\n    @post = Post.new\n  end\n\n  def create\n    @post = Post.new(post_params)\n    if @post.save\n      redirect_to @post, notice: '\u6295\u7a3f\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f'\n    else\n      render :new\n    end\n  end\n\n  private\n\n  def post_params\n    params.require(:post).permit(:title, :content)\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-32\">\u30d3\u30e5\u30fc\u306e\u4f5c\u6210\u3068\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8<\/h3>\n\n\n\n<p>ERB\uff08Embedded Ruby\uff09\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u57fa\u672c\uff1a<\/p>\n\n\n\n<p><code>app\/views\/posts\/index.html.erb<\/code>\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;h1&gt;\u6295\u7a3f\u4e00\u89a7&lt;\/h1&gt;\n\n&lt;div class=\"posts\"&gt;\n  &lt;% @posts.each do |post| %&gt;\n    &lt;div class=\"post\"&gt;\n      &lt;h2&gt;&lt;%= post.title %&gt;&lt;\/h2&gt;\n      &lt;p&gt;&lt;%= post.content %&gt;&lt;\/p&gt;\n      &lt;%= link_to '\u8a73\u7d30', post_path(post), class: 'button' %&gt;\n    &lt;\/div&gt;\n  &lt;% end %&gt;\n&lt;\/div&gt;\n\n&lt;%= link_to '\u65b0\u898f\u6295\u7a3f', new_post_path, class: 'button' %&gt;<\/pre>\n\n\n\n<p><code>app\/views\/posts\/show.html.erb<\/code>\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;div class=\"post-detail\"&gt;\n  &lt;h1&gt;&lt;%= @post.title %&gt;&lt;\/h1&gt;\n\n  &lt;div class=\"content\"&gt;\n    &lt;%= @post.content %&gt;\n  &lt;\/div&gt;\n\n  &lt;div class=\"actions\"&gt;\n    &lt;%= link_to '\u7de8\u96c6', edit_post_path(@post), class: 'button' %&gt;\n    &lt;%= link_to '\u524a\u9664', post_path(@post),\n        method: :delete,\n        data: { confirm: '\u672c\u5f53\u306b\u524a\u9664\u3057\u307e\u3059\u304b\uff1f' },\n        class: 'button danger' %&gt;\n  &lt;\/div&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-33\">\u30ec\u30a4\u30a2\u30a6\u30c8\u3068\u30d1\u30fc\u30b7\u30e3\u30eb<\/h3>\n\n\n\n<p>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u5168\u4f53\u306e\u30ec\u30a4\u30a2\u30a6\u30c8\uff08<code>app\/views\/layouts\/application.html.erb<\/code>\uff09\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n  &lt;head&gt;\n    &lt;title&gt;MyFirstApp&lt;\/title&gt;\n    &lt;%= csrf_meta_tags %&gt;\n    &lt;%= csp_meta_tag %&gt;\n\n    &lt;%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %&gt;\n    &lt;%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %&gt;\n  &lt;\/head&gt;\n\n  &lt;body&gt;\n    &lt;%= render 'shared\/header' %&gt;\n\n    &lt;main&gt;\n      &lt;% if notice %&gt;\n        &lt;div class=\"notice\"&gt;&lt;%= notice %&gt;&lt;\/div&gt;\n      &lt;% end %&gt;\n\n      &lt;%= yield %&gt;\n    &lt;\/main&gt;\n\n    &lt;%= render 'shared\/footer' %&gt;\n  &lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n\n\n\n<p>\u30d1\u30fc\u30b7\u30e3\u30eb\u306e\u4f5c\u6210\uff08<code>app\/views\/shared\/_header.html.erb<\/code>\uff09\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;header&gt;\n  &lt;nav&gt;\n    &lt;%= link_to '\u30db\u30fc\u30e0', root_path %&gt;\n    &lt;%= link_to '\u6295\u7a3f\u4e00\u89a7', posts_path %&gt;\n    &lt;%= link_to '\u65b0\u898f\u6295\u7a3f', new_post_path %&gt;\n    &lt;%= link_to 'About', about_path %&gt;\n  &lt;\/nav&gt;\n&lt;\/header&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-34\">\u57fa\u672c\u7684\u306a\u30b9\u30bf\u30a4\u30ea\u30f3\u30b0<\/h3>\n\n\n\n<p><code>app\/assets\/stylesheets\/application.css<\/code>\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* \u57fa\u672c\u30b9\u30bf\u30a4\u30eb *\/\nbody {\n  font-family: Arial, sans-serif;\n  line-height: 1.6;\n  margin: 0;\n  padding: 20px;\n}\n\n.button {\n  display: inline-block;\n  padding: 8px 16px;\n  background-color: #007bff;\n  color: white;\n  text-decoration: none;\n  border-radius: 4px;\n}\n\n.button.danger {\n  background-color: #dc3545;\n}\n\n\/* \u30d5\u30a9\u30fc\u30e0\u8981\u7d20\u306e\u30b9\u30bf\u30a4\u30eb *\/\nform {\n  max-width: 600px;\n  margin: 20px auto;\n}\n\ninput[type=\"text\"],\ntextarea {\n  width: 100%;\n  padding: 8px;\n  margin-bottom: 10px;\n  border: 1px solid #ddd;\n  border-radius: 4px;\n}<\/pre>\n\n\n\n<p>\u3053\u308c\u3067\u57fa\u672c\u7684\u306aRails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4f5c\u6210\u65b9\u6cd5\u3092\u5b66\u3073\u307e\u3057\u305f\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u64cd\u4f5c\u3068\u30e2\u30c7\u30eb\u306e\u6d3b\u7528\u306b\u3064\u3044\u3066\u8a73\u3057\u304f\u898b\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-35\">\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u64cd\u4f5c\u3068\u30e2\u30c7\u30eb\u306e\u6d3b\u7528<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-36\">Active Record\u3092\u4f7f\u3063\u305f\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u64cd\u4f5c\u306e\u57fa\u790e<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-37\">Active Record\u306e\u57fa\u672c\u64cd\u4f5c<\/h3>\n\n\n\n<p>Active Record\u306f\u3001Rails\u306e\u30e2\u30c7\u30eb\u5c64\u3092\u62c5\u5f53\u3057\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3068\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u6a4b\u6e21\u3057\u3059\u308b\u91cd\u8981\u306a\u6a5f\u80fd\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-38\">\u57fa\u672c\u7684\u306aCRUD\u64cd\u4f5c<\/h4>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Create\uff08\u4f5c\u6210\uff09\nuser = User.create(name: \"\u5c71\u7530\u592a\u90ce\", email: \"yamada@example.com\")\n\n# Read\uff08\u8aad\u307f\u53d6\u308a\uff09\nuser = User.find(1)                    # ID\u3067\u691c\u7d22\nuser = User.find_by(email: \"yamada@example.com\")  # \u6761\u4ef6\u3067\u691c\u7d22\nusers = User.where(age: 20..30)        # \u8907\u6570\u6761\u4ef6\u3067\u306e\u691c\u7d22\n\n# Update\uff08\u66f4\u65b0\uff09\nuser.update(name: \"\u5c71\u7530\u6b21\u90ce\")          # \u5c5e\u6027\u3092\u66f4\u65b0\nUser.update_all(status: \"active\")      # \u4e00\u62ec\u66f4\u65b0\n\n# Delete\uff08\u524a\u9664\uff09\nuser.destroy                           # \u5358\u4e00\u30ec\u30b3\u30fc\u30c9\u306e\u524a\u9664\nUser.destroy_all                       # \u5168\u30ec\u30b3\u30fc\u30c9\u306e\u524a\u9664<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-39\">\u30af\u30a8\u30ea\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9<\/h4>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u6761\u4ef6\u6307\u5b9a\nUser.where(status: \"active\")\n    .where(\"age &gt;= ?\", 20)\n    .order(created_at: :desc)\n\n# \u95a2\u9023\u4ed8\u3051\u3092\u542b\u3080\u691c\u7d22\nUser.includes(:posts)                  # N+1\u554f\u984c\u3092\u56de\u907f\nUser.joins(:posts)                     # INNER\u30b8\u30e7\u30a4\u30f3\nUser.left_joins(:posts)                # LEFT OUTER \u30b8\u30e7\u30a4\u30f3\n\n# \u30b9\u30b3\u30fc\u30d7\u306e\u5b9a\u7fa9\nclass User &lt; ApplicationRecord\n  scope :active, -&gt; { where(status: \"active\") }\n  scope :recent, -&gt; { order(created_at: :desc) }\n  scope :with_posts, -&gt; { includes(:posts) }\nend\n\n# \u30b9\u30b3\u30fc\u30d7\u306e\u4f7f\u7528\nUser.active.recent.with_posts<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-40\">\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3068\u30b9\u30ad\u30fc\u30de\u7ba1\u7406\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-41\">\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u57fa\u672c<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u751f\u6210\nrails generate migration CreateUsers name:string email:string\n\n# \u751f\u6210\u3055\u308c\u308b\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u30d5\u30a1\u30a4\u30eb\nclass CreateUsers &lt; ActiveRecord::Migration[7.1]\n  def change\n    create_table :users do |t|\n      t.string :name\n      t.string :email\n      t.timestamps\n    end\n\n    add_index :users, :email, unique: true\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-42\">\u3088\u304f\u4f7f\u3046\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u30b3\u30de\u30f3\u30c9<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30c6\u30fc\u30d6\u30eb\u306e\u4f5c\u6210\ncreate_table :products do |t|\n  t.string :name, null: false\n  t.text :description\n  t.decimal :price, precision: 10, scale: 2\n  t.references :category, foreign_key: true\n  t.timestamps\nend\n\n# \u30ab\u30e9\u30e0\u306e\u8ffd\u52a0\nadd_column :users, :age, :integer\nadd_column :users, :birthday, :date\n\n# \u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u8ffd\u52a0\nadd_index :users, :email, unique: true\nadd_index :products, [:name, :category_id]\n\n# \u5916\u90e8\u30ad\u30fc\u306e\u8ffd\u52a0\nadd_foreign_key :products, :categories<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-43\">\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u53ef\u9006\u6027\u306e\u78ba\u4fdd<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def change\n  # \u53ef\u9006\u7684\u306a\u5909\u66f4\n  add_column :users, :name, :string\n\n  # \u4e0d\u53ef\u9006\u306a\u5909\u66f4\u306e\u5834\u5408\u306fup\/down\u30e1\u30bd\u30c3\u30c9\u3092\u4f7f\u7528\n  reversible do |dir|\n    dir.up { execute \"UPDATE users SET status = 'active'\" }\n    dir.down { execute \"UPDATE users SET status = 'pending'\" }\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30d0\u30c3\u30c1\u51e6\u7406\u306e\u5229\u7528<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def change\n  # \u30c7\u30fc\u30bf\u91cf\u304c\u591a\u3044\u5834\u5408\u306f\u4e00\u62ec\u51e6\u7406\u3092\u4f7f\u7528\n  User.find_each(batch_size: 1000) do |user|\n    user.update(status: 'active')\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-44\">\u30e2\u30c7\u30eb\u306e\u5b9a\u7fa9\u3068\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class User &lt; ApplicationRecord\n  # \u95a2\u9023\u4ed8\u3051\n  has_many :posts, dependent: :destroy\n  has_one :profile\n  belongs_to :department\n\n  # \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\n  validates :name, presence: true\n  validates :email, presence: true, uniqueness: true,\n            format: { with: URI::MailTo::EMAIL_REGEXP }\n  validates :age, numericality: { greater_than_or_equal_to: 0 }\n\n  # \u30ab\u30b9\u30bf\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\n  validate :age_is_reasonable\n\n  # \u30b3\u30fc\u30eb\u30d0\u30c3\u30af\n  before_save :normalize_email\n  after_create :send_welcome_email\n\n  private\n\n  def age_is_reasonable\n    if age.present? &amp;&amp; age &gt; 150\n      errors.add(:age, \"\u306f\u73fe\u5b9f\u7684\u306a\u5024\u3067\u3042\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\")\n    end\n  end\n\n  def normalize_email\n    self.email = email.downcase.strip\n  end\n\n  def send_welcome_email\n    UserMailer.welcome_email(self).deliver_later\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-45\">\u9ad8\u5ea6\u306a\u30af\u30a8\u30ea\u3068\u6700\u9069\u5316<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u8907\u96d1\u306a\u30af\u30a8\u30ea\u306e\u4f8b<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u6295\u7a3f\u6570\u304c\u591a\u3044\u30a2\u30af\u30c6\u30a3\u30d6\u30e6\u30fc\u30b6\u30fc\u3092\u691c\u7d22\nUser.joins(:posts)\n    .where(status: 'active')\n    .group('users.id')\n    .having('COUNT(posts.id) &gt; ?', 5)\n    .order('COUNT(posts.id) DESC')\n\n# \u7279\u5b9a\u671f\u9593\u306e\u7d71\u8a08\u60c5\u5831\u3092\u53d6\u5f97\nPost.where(created_at: 1.month.ago..Time.current)\n    .group('DATE(created_at)')\n    .select('DATE(created_at) as date, COUNT(*) as count')<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>N+1\u554f\u984c\u306e\u89e3\u6c7a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u60aa\u3044\u4f8b\nusers = User.all\nusers.each { |user| puts user.posts.count }  # N+1\u554f\u984c\u767a\u751f\n\n# \u826f\u3044\u4f8b\nusers = User.includes(:posts)\nusers.each { |user| puts user.posts.count }  # N+1\u554f\u984c\u89e3\u6c7a\n\n# \u7279\u5b9a\u306e\u95a2\u9023\u4ed8\u3051\u306e\u307f\u5fc5\u8981\u306a\u5834\u5408\nusers = User.preload(:posts)\n# \u307e\u305f\u306f\nusers = User.eager_load(:posts)<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30af\u30a8\u30ea\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u6d3b\u7528<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u5229\u7528\nUser.cache do\n  User.first\n  User.first  # \u30ad\u30e3\u30c3\u30b7\u30e5\u304b\u3089\u53d6\u5f97\nend\n\n# \u30ab\u30a6\u30f3\u30bf\u30fc\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u8a2d\u5b9a\nclass Post &lt; ApplicationRecord\n  belongs_to :user, counter_cache: true\nend<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u77e5\u8b58\u3092\u6d3b\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u52b9\u7387\u7684\u3067\u4fdd\u5b88\u6027\u306e\u9ad8\u3044\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u64cd\u4f5c\u304c\u5b9f\u73fe\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30d3\u30e5\u30fc\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3068\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u958b\u767a\u306b\u3064\u3044\u3066\u5b66\u3093\u3067\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-46\">\u30d3\u30e5\u30fc\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3068\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u958b\u767a<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-47\">ERB\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u4f7f\u3044\u65b9\u3068\u30d8\u30eb\u30d1\u30fc\u30e1\u30bd\u30c3\u30c9<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-48\">ERB\u306e\u57fa\u672c\u69cb\u6587<\/h3>\n\n\n\n<p>ERB\uff08Embedded Ruby\uff09\u306f\u3001Rails\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30a8\u30f3\u30b8\u30f3\u3067\u3059\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;%# \u57fa\u672c\u7684\u306aRuby\u30b3\u30fc\u30c9\u306e\u57cb\u3081\u8fbc\u307f %&gt;\n&lt;% if user.admin? %&gt;\n  &lt;h1&gt;\u7ba1\u7406\u8005\u30c0\u30c3\u30b7\u30e5\u30dc\u30fc\u30c9&lt;\/h1&gt;\n&lt;% else %&gt;\n  &lt;h1&gt;\u30e6\u30fc\u30b6\u30fc\u30c0\u30c3\u30b7\u30e5\u30dc\u30fc\u30c9&lt;\/h1&gt;\n&lt;% end %&gt;\n\n&lt;%# \u8a55\u4fa1\u7d50\u679c\u306e\u51fa\u529b %&gt;\n&lt;p&gt;\u3053\u3093\u306b\u3061\u306f\u3001&lt;%= current_user.name %&gt;\u3055\u3093&lt;\/p&gt;\n\n&lt;%# \u30b3\u30e1\u30f3\u30c8 %&gt;\n&lt;%# \u3053\u308c\u306f\u30b3\u30e1\u30f3\u30c8\u3067\u3059 %&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-49\">\u4e3b\u8981\u306a\u30d3\u30e5\u30fc\u30d8\u30eb\u30d1\u30fc<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;%# \u30ea\u30f3\u30af\u306e\u751f\u6210 %&gt;\n&lt;%= link_to '\u8a73\u7d30', user_path(@user) %&gt;\n&lt;%= link_to '\u524a\u9664', user_path(@user), method: :delete, data: { confirm: '\u672c\u5f53\u306b\u524a\u9664\u3057\u307e\u3059\u304b\uff1f' } %&gt;\n\n&lt;%# \u30d5\u30a9\u30fc\u30e0\u306e\u4f5c\u6210 %&gt;\n&lt;%= form_with(model: @user, local: true) do |f| %&gt;\n  &lt;div class=\"field\"&gt;\n    &lt;%= f.label :name, '\u540d\u524d' %&gt;\n    &lt;%= f.text_field :name, class: 'form-control' %&gt;\n  &lt;\/div&gt;\n\n  &lt;div class=\"field\"&gt;\n    &lt;%= f.label :email, '\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9' %&gt;\n    &lt;%= f.email_field :email, class: 'form-control' %&gt;\n  &lt;\/div&gt;\n\n  &lt;%= f.submit '\u4fdd\u5b58', class: 'btn btn-primary' %&gt;\n&lt;% end %&gt;\n\n&lt;%# \u65e5\u4ed8\u30fb\u6642\u523b\u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30c8 %&gt;\n&lt;p&gt;\u4f5c\u6210\u65e5\u6642: &lt;%= l @post.created_at, format: :long %&gt;&lt;\/p&gt;\n\n&lt;%# \u6570\u5024\u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30c8 %&gt;\n&lt;p&gt;\u4fa1\u683c: &lt;%= number_to_currency(@product.price, unit: '\u00a5') %&gt;&lt;\/p&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-50\">\u30d1\u30fc\u30b7\u30e3\u30eb\u306e\u6d3b\u7528<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;%# \u30d1\u30fc\u30b7\u30e3\u30eb\u306e\u5b9a\u7fa9\uff08_user.html.erb\uff09 %&gt;\n&lt;div class=\"user-card\"&gt;\n  &lt;h2&gt;&lt;%= user.name %&gt;&lt;\/h2&gt;\n  &lt;p&gt;&lt;%= user.email %&gt;&lt;\/p&gt;\n  &lt;%= render 'user_actions', user: user %&gt;\n&lt;\/div&gt;\n\n&lt;%# \u30d1\u30fc\u30b7\u30e3\u30eb\u306e\u547c\u3073\u51fa\u3057 %&gt;\n&lt;div class=\"users-list\"&gt;\n  &lt;%= render @users %&gt;\n&lt;\/div&gt;\n\n&lt;%# \u30b3\u30ec\u30af\u30b7\u30e7\u30f3\u306e\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0 %&gt;\n&lt;%= render partial: 'user', collection: @users, as: :user %&gt;\n\n&lt;%# \u30ed\u30fc\u30ab\u30eb\u5909\u6570\u306e\u6e21\u3057\u65b9 %&gt;\n&lt;%= render 'shared\/header', title: '\u30e6\u30fc\u30b6\u30fc\u4e00\u89a7', show_search: true %&gt;<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-51\">\u30e2\u30c0\u30f3\u306a\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u6280\u8853\u3068Rails\u306e\u7d71\u5408<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-52\">Webpacker\u306e\u8a2d\u5b9a\u3068\u5229\u7528<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/packs\/application.js\nimport Rails from \"@rails\/ujs\"\nimport Turbolinks from \"turbolinks\"\nimport * as ActiveStorage from \"@rails\/activestorage\"\nimport \"channels\"\n\nRails.start()\nTurbolinks.start()\nActiveStorage.start()\n\n\/\/ \u30ab\u30b9\u30bf\u30e0JavaScript\u306e\u8ffd\u52a0\nimport \"..\/src\/custom\"<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-53\">Stimulus\u306e\u5c0e\u5165\u3068\u6d3b\u7528<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Stimulus\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nyarn add @hotwired\/stimulus<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/controllers\/hello_controller.js\nimport { Controller } from \"@hotwired\/stimulus\"\n\nexport default class extends Controller {\n  static targets = [ \"output\" ]\n\n  connect() {\n    this.outputTarget.textContent = \"Hello, Stimulus!\"\n  }\n\n  greet() {\n    const name = this.element.getAttribute(\"data-name\")\n    this.outputTarget.textContent = `Hello, ${name}!`\n  }\n}<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;%# Stimulus\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u4f7f\u7528 %&gt;\n&lt;div data-controller=\"hello\" data-name=\"Rails\"&gt;\n  &lt;h1 data-hello-target=\"output\"&gt;&lt;\/h1&gt;\n  &lt;button data-action=\"click-&gt;hello#greet\"&gt;Greet&lt;\/button&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-54\">CSS\u8a2d\u8a08\u3068\u30a2\u30bb\u30c3\u30c8\u7ba1\u7406<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/assets\/stylesheets\/application.scss\n@import \"variables\";\n@import \"base\";\n@import \"components\/buttons\";\n@import \"components\/forms\";\n@import \"layouts\/header\";\n@import \"layouts\/footer\";\n\n\/\/ \u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u6307\u5411\u306eCSS\n.button {\n  @apply px-4 py-2 rounded;\n\n  &amp;--primary {\n    @apply bg-blue-500 text-white;\n\n    &amp;:hover {\n      @apply bg-blue-600;\n    }\n  }\n\n  &amp;--danger {\n    @apply bg-red-500 text-white;\n\n    &amp;:hover {\n      @apply bg-red-600;\n    }\n  }\n}\n\n\/\/ \u30ec\u30b9\u30dd\u30f3\u30b7\u30d6\u30c7\u30b6\u30a4\u30f3\n@media (max-width: 768px) {\n  .container {\n    @apply px-4;\n  }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-55\">JavaScript\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u6d3b\u7528<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/src\/modules\/toast.js\nexport class Toast {\n  constructor(message, type = 'info') {\n    this.message = message\n    this.type = type\n  }\n\n  show() {\n    const toast = document.createElement('div')\n    toast.classList.add('toast', `toast--${this.type}`)\n    toast.textContent = this.message\n    document.body.appendChild(toast)\n\n    setTimeout(() =&gt; {\n      toast.remove()\n    }, 3000)\n  }\n}\n\n\/\/ \u4f7f\u7528\u4f8b\nimport { Toast } from '..\/modules\/toast'\n\ndocument.addEventListener('turbolinks:load', () =&gt; {\n  const flash = document.querySelector('.flash')\n  if (flash) {\n    new Toast(flash.textContent, flash.dataset.type).show()\n  }\n})<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-56\">\u30a4\u30f3\u30bf\u30e9\u30af\u30c6\u30a3\u30d6\u306aUI\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/controllers\/dropdown_controller.js\nimport { Controller } from \"@hotwired\/stimulus\"\n\nexport default class extends Controller {\n  static targets = [ \"menu\" ]\n\n  toggle() {\n    this.menuTarget.classList.toggle(\"hidden\")\n  }\n\n  hide(event) {\n    if (!this.element.contains(event.target)) {\n      this.menuTarget.classList.add(\"hidden\")\n    }\n  }\n\n  connect() {\n    this.hideHandler = this.hide.bind(this)\n    document.addEventListener(\"click\", this.hideHandler)\n  }\n\n  disconnect() {\n    document.removeEventListener(\"click\", this.hideHandler)\n  }\n}<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;%# \u30c9\u30ed\u30c3\u30d7\u30c0\u30a6\u30f3\u30e1\u30cb\u30e5\u30fc\u306e\u5b9f\u88c5 %&gt;\n&lt;div data-controller=\"dropdown\"&gt;\n  &lt;button data-action=\"dropdown#toggle\"&gt;\u30e1\u30cb\u30e5\u30fc&lt;\/button&gt;\n\n  &lt;div data-dropdown-target=\"menu\" class=\"hidden\"&gt;\n    &lt;%= link_to \"\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\", profile_path %&gt;\n    &lt;%= link_to \"\u8a2d\u5b9a\", settings_path %&gt;\n    &lt;%= link_to \"\u30ed\u30b0\u30a2\u30a6\u30c8\", logout_path, method: :delete %&gt;\n  &lt;\/div&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u6280\u8853\u3092\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u30e2\u30c0\u30f3\u3067\u4f7f\u3044\u3084\u3059\u3044\u30e6\u30fc\u30b6\u30fc\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9\u3092\u5b9f\u73fe\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u3068\u6a29\u9650\u7ba1\u7406\u306e\u5b9f\u88c5\u306b\u3064\u3044\u3066\u5b66\u3093\u3067\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-57\">\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u3068\u6a29\u9650\u7ba1\u7406\u306e\u5b9f\u88c5<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-58\">Devise\u3092\u4f7f\u3063\u305f\u5b89\u5168\u306a\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u69cb\u7bc9<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-59\">Devise\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Gemfile\u306b\u8ffd\u52a0\ngem 'devise'\n\n# \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3068\u521d\u671f\u8a2d\u5b9a\nrails generate devise:install\nrails generate devise User\nrails db:migrate<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-60\">\u57fa\u672c\u8a2d\u5b9a\uff08config\/initializers\/devise.rb\uff09<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Devise.setup do |config|\n  # \u30e1\u30fc\u30eb\u9001\u4fe1\u5143\u306e\u8a2d\u5b9a\n  config.mailer_sender = 'noreply@example.com'\n\n  # \u30d1\u30b9\u30ef\u30fc\u30c9\u306e\u6700\u5c0f\u6587\u5b57\u6570\n  config.password_length = 8..128\n\n  # \u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u671f\u9650\n  config.timeout_in = 2.weeks\n\n  # \u30ed\u30b0\u30a4\u30f3\u8a66\u884c\u56de\u6570\u306e\u5236\u9650\n  config.maximum_attempts = 5\n  config.unlock_in = 1.hour\n\n  # \u30d1\u30b9\u30ef\u30fc\u30c9\u30ea\u30bb\u30c3\u30c8\u306e\u671f\u9650\n  config.reset_password_within = 6.hours\n\n  # 2\u8981\u7d20\u8a8d\u8a3c\u306e\u8a2d\u5b9a\uff08\u30aa\u30d7\u30b7\u30e7\u30f3\uff09\n  config.second_factor_enabled = true\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-61\">\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u3055\u308c\u305fDevise\u30d3\u30e5\u30fc<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;%# app\/views\/devise\/registrations\/new.html.erb %&gt;\n&lt;div class=\"auth-form\"&gt;\n  &lt;h2&gt;\u30a2\u30ab\u30a6\u30f3\u30c8\u767b\u9332&lt;\/h2&gt;\n\n  &lt;%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %&gt;\n    &lt;%= render \"devise\/shared\/error_messages\", resource: resource %&gt;\n\n    &lt;div class=\"form-group\"&gt;\n      &lt;%= f.label :email, '\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9' %&gt;\n      &lt;%= f.email_field :email, autofocus: true, class: 'form-control' %&gt;\n    &lt;\/div&gt;\n\n    &lt;div class=\"form-group\"&gt;\n      &lt;%= f.label :password, '\u30d1\u30b9\u30ef\u30fc\u30c9' %&gt;\n      &lt;%= f.password_field :password, class: 'form-control' %&gt;\n      &lt;% if @minimum_password_length %&gt;\n        &lt;small class=\"form-text text-muted\"&gt;\n          \u6700\u5c0f&lt;%= @minimum_password_length %&gt;\u6587\u5b57\u5fc5\u8981\u3067\u3059\n        &lt;\/small&gt;\n      &lt;% end %&gt;\n    &lt;\/div&gt;\n\n    &lt;div class=\"form-group\"&gt;\n      &lt;%= f.label :password_confirmation, '\u30d1\u30b9\u30ef\u30fc\u30c9\uff08\u78ba\u8a8d\uff09' %&gt;\n      &lt;%= f.password_field :password_confirmation, class: 'form-control' %&gt;\n    &lt;\/div&gt;\n\n    &lt;%= f.submit \"\u767b\u9332\", class: 'btn btn-primary' %&gt;\n  &lt;% end %&gt;\n\n  &lt;%= render \"devise\/shared\/links\" %&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-62\">\u30ab\u30b9\u30bf\u30e0Devise\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># app\/controllers\/users\/registrations_controller.rb\nclass Users::RegistrationsController &lt; Devise::RegistrationsController\n  before_action :configure_sign_up_params, only: [:create]\n\n  protected\n\n  def configure_sign_up_params\n    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :profile])\n  end\n\n  def after_sign_up_path_for(resource)\n    dashboard_path\n  end\nend<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-63\">CanCanCan\u3092\u6d3b\u7528\u3057\u305f\u67d4\u8edf\u306a\u6a29\u9650\u7ba1\u7406\u306e\u5b9f\u88c5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-64\">CanCanCan\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Gemfile\u306b\u8ffd\u52a0\ngem 'cancancan'\n\n# Ability\u30af\u30e9\u30b9\u306e\u751f\u6210\nrails generate cancan:ability<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-65\">\u6a29\u9650\u306e\u5b9a\u7fa9<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># app\/models\/ability.rb\nclass Ability\n  include CanCan::Ability\n\n  def initialize(user)\n    user ||= User.new # \u30b2\u30b9\u30c8\u30e6\u30fc\u30b6\u30fc\n\n    if user.admin?\n      # \u7ba1\u7406\u8005\u6a29\u9650\n      can :manage, :all\n    else\n      # \u4e00\u822c\u30e6\u30fc\u30b6\u30fc\u6a29\u9650\n      can :read, :all\n      can :create, [Post, Comment]\n      can :update, Post, user_id: user.id\n      can :destroy, Post, user_id: user.id\n\n      # \u7279\u5b9a\u306e\u6761\u4ef6\u4ed8\u304d\u6a29\u9650\n      can :update, Comment do |comment|\n        comment.user_id == user.id &amp;&amp; comment.created_at &gt; 1.hour.ago\n      end\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-66\">\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3067\u306e\u6a29\u9650\u30c1\u30a7\u30c3\u30af<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class PostsController &lt; ApplicationController\n  load_and_authorize_resource\n\n  def index\n    @posts = @posts.accessible_by(current_ability)\n  end\n\n  def show\n    authorize! :read, @post\n  rescue CanCan::AccessDenied\n    redirect_to root_path, alert: '\u6a29\u9650\u304c\u3042\u308a\u307e\u305b\u3093'\n  end\n\n  def update\n    if can? :update, @post\n      if @post.update(post_params)\n        redirect_to @post, notice: '\u66f4\u65b0\u3057\u307e\u3057\u305f'\n      else\n        render :edit\n      end\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-67\">\u30d3\u30e5\u30fc\u3067\u306e\u6a29\u9650\u30c1\u30a7\u30c3\u30af<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;% if can? :create, Post %&gt;\n  &lt;%= link_to '\u65b0\u898f\u6295\u7a3f', new_post_path, class: 'btn btn-primary' %&gt;\n&lt;% end %&gt;\n\n&lt;% @posts.each do |post| %&gt;\n  &lt;div class=\"post\"&gt;\n    &lt;h2&gt;&lt;%= post.title %&gt;&lt;\/h2&gt;\n    &lt;p&gt;&lt;%= post.content %&gt;&lt;\/p&gt;\n\n    &lt;div class=\"actions\"&gt;\n      &lt;% if can? :update, post %&gt;\n        &lt;%= link_to '\u7de8\u96c6', edit_post_path(post) %&gt;\n      &lt;% end %&gt;\n\n      &lt;% if can? :destroy, post %&gt;\n        &lt;%= link_to '\u524a\u9664', post_path(post), \n            method: :delete, \n            data: { confirm: '\u672c\u5f53\u306b\u524a\u9664\u3057\u307e\u3059\u304b\uff1f' } %&gt;\n      &lt;% end %&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;% end %&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-68\">\u30ed\u30fc\u30eb\u7ba1\u7406\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># app\/models\/user.rb\nclass User &lt; ApplicationRecord\n  ROLES = %w[admin moderator author reader].freeze\n\n  validates :role, inclusion: { in: ROLES }\n\n  def has_role?(role_name)\n    role.present? &amp;&amp; role == role_name.to_s\n  end\nend\n\n# app\/models\/ability.rb\u5185\u3067\u306e\u30ed\u30fc\u30eb\u30d9\u30fc\u30b9\u306e\u6a29\u9650\u8a2d\u5b9a\ndef initialize(user)\n  user ||= User.new\n\n  case user.role\n  when 'admin'\n    can :manage, :all\n  when 'moderator'\n    can :manage, [Post, Comment]\n    can :read, User\n  when 'author'\n    can :create, [Post, Comment]\n    can :update, Post, user_id: user.id\n    can :destroy, Post, user_id: user.id\n  else # reader\n    can :read, :all\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-69\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u5f37\u529b\u306a\u30d1\u30b9\u30ef\u30fc\u30c9\u30dd\u30ea\u30b7\u30fc<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/initializers\/devise.rb\nDevise.setup do |config|\n  config.password_length = 12..128\n  config.password_regex = \/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])\/\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/initializers\/session_store.rb\nRails.application.config.session_store :cookie_store, \n  key: '_your_app_session',\n  secure: Rails.env.production?,\n  httponly: true,\n  expire_after: 12.hours<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>CSRF\u4fdd\u8b77<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># app\/controllers\/application_controller.rb\nclass ApplicationController &lt; ActionController::Base\n  protect_from_forgery with: :exception\n  before_action :authenticate_user!\nend<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5b9f\u88c5\u306b\u3088\u308a\u3001\u5b89\u5168\u3067\u67d4\u8edf\u306a\u8a8d\u8a3c\u30fb\u8a8d\u53ef\u30b7\u30b9\u30c6\u30e0\u3092\u69cb\u7bc9\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30c6\u30b9\u30c8\u99c6\u52d5\u958b\u767a\uff08TDD\uff09\u3068RSpec\u306b\u3064\u3044\u3066\u5b66\u3093\u3067\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-70\">\u30c6\u30b9\u30c8\u99c6\u52d5\u958b\u767a\uff08TDD\uff09\u3068RSpec<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-71\">\u30e2\u30c7\u30eb\u3001\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3001\u30b7\u30b9\u30c6\u30e0\u30c6\u30b9\u30c8\u306e\u66f8\u304d\u65b9<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-72\">RSpec\u306e\u57fa\u672c\u8a2d\u5b9a<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Gemfile\u306b\u8ffd\u52a0\ngroup :development, :test do\n  gem 'rspec-rails'\n  gem 'factory_bot_rails'\n  gem 'faker'\nend\n\n# RSpec\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3068\u521d\u671f\u8a2d\u5b9a\nrails generate rspec:install<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-73\">\u30e2\u30c7\u30eb\u30b9\u30da\u30c3\u30af<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># spec\/models\/user_spec.rb\nrequire 'rails_helper'\n\nRSpec.describe User, type: :model do\n  # \u30d5\u30a1\u30af\u30c8\u30ea\u306e\u5b9a\u7fa9\n  let(:user) { create(:user) }\n\n  # \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u30c6\u30b9\u30c8\n  describe 'validations' do\n    it 'is valid with valid attributes' do\n      expect(user).to be_valid\n    end\n\n    it 'is not valid without an email' do\n      user.email = nil\n      expect(user).not_to be_valid\n    end\n\n    it 'is not valid with a duplicate email' do\n      duplicate_user = user.dup\n      duplicate_user.email = user.email\n      expect(duplicate_user).not_to be_valid\n    end\n  end\n\n  # \u30b9\u30b3\u30fc\u30d7\u306e\u30c6\u30b9\u30c8\n  describe 'scopes' do\n    let!(:active_user) { create(:user, status: 'active') }\n    let!(:inactive_user) { create(:user, status: 'inactive') }\n\n    it 'returns only active users' do\n      expect(User.active).to include(active_user)\n      expect(User.active).not_to include(inactive_user)\n    end\n  end\n\n  # \u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u30e1\u30bd\u30c3\u30c9\u306e\u30c6\u30b9\u30c8\n  describe '#full_name' do\n    let(:user) { create(:user, first_name: 'John', last_name: 'Doe') }\n\n    it 'returns the full name' do\n      expect(user.full_name).to eq('John Doe')\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-74\">\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u30b9\u30da\u30c3\u30af<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># spec\/controllers\/posts_controller_spec.rb\nrequire 'rails_helper'\n\nRSpec.describe PostsController, type: :controller do\n  let(:user) { create(:user) }\n  let(:post) { create(:post, user: user) }\n\n  describe 'GET #index' do\n    it 'returns a successful response' do\n      get :index\n      expect(response).to be_successful\n    end\n\n    it 'assigns @posts' do\n      posts = create_list(:post, 3)\n      get :index\n      expect(assigns(:posts)).to match_array(posts)\n    end\n  end\n\n  describe 'POST #create' do\n    context 'when user is signed in' do\n      before { sign_in user }\n\n      context 'with valid parameters' do\n        let(:valid_params) { { post: attributes_for(:post) } }\n\n        it 'creates a new post' do\n          expect {\n            post :create, params: valid_params\n          }.to change(Post, :count).by(1)\n        end\n\n        it 'redirects to the created post' do\n          post :create, params: valid_params\n          expect(response).to redirect_to(Post.last)\n        end\n      end\n\n      context 'with invalid parameters' do\n        let(:invalid_params) { { post: attributes_for(:post, title: nil) } }\n\n        it 'does not create a new post' do\n          expect {\n            post :create, params: invalid_params\n          }.not_to change(Post, :count)\n        end\n\n        it 'renders the new template' do\n          post :create, params: invalid_params\n          expect(response).to render_template(:new)\n        end\n      end\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-75\">\u30b7\u30b9\u30c6\u30e0\u30b9\u30da\u30c3\u30af\uff08\u30d5\u30a3\u30fc\u30c1\u30e3\u30fc\u30c6\u30b9\u30c8\uff09<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># spec\/system\/user_signs_up_spec.rb\nrequire 'rails_helper'\n\nRSpec.describe 'User signs up', type: :system do\n  before do\n    driven_by(:rack_test)\n  end\n\n  scenario 'with valid information' do\n    visit new_user_registration_path\n\n    fill_in '\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9', with: 'user@example.com'\n    fill_in '\u30d1\u30b9\u30ef\u30fc\u30c9', with: 'password123'\n    fill_in '\u30d1\u30b9\u30ef\u30fc\u30c9\uff08\u78ba\u8a8d\uff09', with: 'password123'\n\n    expect {\n      click_button '\u767b\u9332'\n    }.to change(User, :count).by(1)\n\n    expect(page).to have_content('\u30a2\u30ab\u30a6\u30f3\u30c8\u767b\u9332\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f')\n  end\n\n  scenario 'with invalid information' do\n    visit new_user_registration_path\n\n    fill_in '\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9', with: 'invalid-email'\n    fill_in '\u30d1\u30b9\u30ef\u30fc\u30c9', with: 'short'\n\n    expect {\n      click_button '\u767b\u9332'\n    }.not_to change(User, :count)\n\n    expect(page).to have_content('\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f')\n  end\nend<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-76\">\u30c6\u30b9\u30c8\u30ab\u30d0\u30ec\u30c3\u30b8\u3068\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u624b\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-77\">\u30c6\u30b9\u30c8\u30ab\u30d0\u30ec\u30c3\u30b8\u306e\u8a08\u6e2c<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Gemfile\u306b\u8ffd\u52a0\ngem 'simplecov', require: false, group: :test\n\n# spec\/rails_helper.rb\nrequire 'simplecov'\nSimpleCov.start 'rails' do\n  add_filter '\/test\/'\n  add_filter '\/config\/'\n  add_filter '\/vendor\/'\n\n  add_group 'Controllers', 'app\/controllers'\n  add_group 'Models', 'app\/models'\n  add_group 'Helpers', 'app\/helpers'\n  add_group 'Libraries', 'lib'\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-78\">\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u5171\u901a\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\u30b3\u30fc\u30c9<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># spec\/support\/shared_contexts.rb\nRSpec.shared_context 'with authenticated user' do\n  let(:current_user) { create(:user) }\n  before { sign_in current_user }\nend\n\n# \u30b9\u30da\u30c3\u30af\u3067\u306e\u4f7f\u7528\ndescribe PostsController do\n  include_context 'with authenticated user'\n  # \u30c6\u30b9\u30c8\u30b3\u30fc\u30c9\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30e0\u30de\u30c3\u30c1\u30e3\u30fc<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># spec\/support\/matchers\/be_recent.rb\nRSpec::Matchers.define :be_recent do\n  match do |actual|\n    actual.created_at &gt;= 1.week.ago\n  end\nend\n\n# \u30b9\u30da\u30c3\u30af\u3067\u306e\u4f7f\u7528\nit 'creates a recent post' do\n  post = create(:post)\n  expect(post).to be_recent\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30d5\u30a1\u30af\u30c8\u30ea\u306e\u6700\u9069\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># spec\/factories\/users.rb\nFactoryBot.define do\n  factory :user do\n    sequence(:email) { |n| \"user#{n}@example.com\" }\n    password { 'password123' }\n\n    trait :admin do\n      role { 'admin' }\n    end\n\n    trait :with_posts do\n      after(:create) do |user|\n        create_list(:post, 3, user: user)\n      end\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-79\">\u30c6\u30b9\u30c8\u99c6\u52d5\u958b\u767a\u306e\u5b9f\u8df5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Red-Green-Refactor\u30b5\u30a4\u30af\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># 1. \u5931\u6557\u3059\u308b\u30c6\u30b9\u30c8\u3092\u66f8\u304f\uff08Red\uff09\ndescribe Post do\n  it 'calculates reading time' do\n    post = build(:post, content: 'A' * 1000)\n    expect(post.reading_time).to eq(5)\n  end\nend\n\n# 2. \u30c6\u30b9\u30c8\u3092\u901a\u3059\u305f\u3081\u306e\u6700\u5c0f\u9650\u306e\u30b3\u30fc\u30c9\uff08Green\uff09\nclass Post &lt; ApplicationRecord\n  def reading_time\n    (content.length \/ 200.0).ceil\n  end\nend\n\n# 3. \u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\nclass Post &lt; ApplicationRecord\n  WORDS_PER_MINUTE = 200\n\n  def reading_time\n    word_count = content.split.length\n    (word_count \/ WORDS_PER_MINUTE.to_f).ceil\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-80\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c6\u30b9\u30c8<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># spec\/performance\/post_listing_spec.rb\nrequire 'rails_helper'\nrequire 'benchmark'\n\nRSpec.describe 'Post listing performance', type: :request do\n  before do\n    create_list(:post, 100)\n  end\n\n  it 'loads posts quickly' do\n    time = Benchmark.realtime do\n      get posts_path\n    end\n\n    expect(time).to be &lt; 0.1  # 100ms\u4ee5\u5185\n    expect(response).to be_successful\n  end\nend<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u30c6\u30b9\u30c8\u5b9f\u8df5\u306b\u3088\u308a\u3001\u4fe1\u983c\u6027\u306e\u9ad8\u3044\u3001\u4fdd\u5b88\u6027\u306e\u9ad8\u3044\u30b3\u30fc\u30c9\u30d9\u30fc\u30b9\u3092\u7dad\u6301\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u3068\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u306b\u3064\u3044\u3066\u5b66\u3093\u3067\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-81\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u3068\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-82\">N+1\u30af\u30a8\u30ea\u554f\u984c\u306e\u89e3\u6c7a\u3068\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u8a2d\u8a08<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-83\">N+1\u554f\u984c\u306e\u7279\u5b9a\u3068\u89e3\u6c7a<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u554f\u984c\u306e\u7279\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># N+1\u554f\u984c\u304c\u767a\u751f\u3059\u308b\u30b3\u30fc\u30c9\n@posts = Post.all\n@posts.each do |post|\n  puts post.user.name  # \u5404\u6295\u7a3f\u306b\u5bfe\u3057\u3066\u30e6\u30fc\u30b6\u30fc\u60c5\u5831\u3092\u53d6\u5f97\nend\n\n# ActiveRecord\u306e\u30ed\u30b0\nPost Load (0.5ms)  SELECT \"posts\".* FROM \"posts\"\nUser Load (0.3ms)  SELECT \"users\".* FROM \"users\" WHERE \"users\".\"id\" = $1  [[\"id\", 1]]\nUser Load (0.3ms)  SELECT \"users\".* FROM \"users\" WHERE \"users\".\"id\" = $2  [[\"id\", 2]]\nUser Load (0.3ms)  SELECT \"users\".* FROM \"users\" WHERE \"users\".\"id\" = $3  [[\"id\", 3]]<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>includes\/preload\u306b\u3088\u308b\u89e3\u6c7a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># eager\u30ed\u30fc\u30c9\u3092\u4f7f\u7528\u3057\u305f\u89e3\u6c7a\u7b56\n@posts = Post.includes(:user)\n@posts.each do |post|\n  puts post.user.name  # \u8ffd\u52a0\u306e\u30af\u30a8\u30ea\u306f\u767a\u751f\u3057\u306a\u3044\nend\n\n# ActiveRecord\u306e\u30ed\u30b0\nPost Load (0.5ms)  SELECT \"posts\".* FROM \"posts\"\nUser Load (0.5ms)  SELECT \"users\".* FROM \"users\" WHERE \"users\".\"id\" IN ($1, $2, $3)<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>joins\/eager_load\u306e\u4f7f\u3044\u5206\u3051<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># joins\u306e\u4f7f\u7528\u4f8b\uff08\u6761\u4ef6\u691c\u7d22\u6642\uff09\nPost.joins(:user).where(users: { status: 'active' })\n\n# eager_load\u306e\u4f7f\u7528\u4f8b\uff08\u95a2\u9023\u30c7\u30fc\u30bf\u3082\u4f7f\u7528\u3059\u308b\u5834\u5408\uff09\nPost.eager_load(:user).where(users: { status: 'active' })<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-84\">\u52b9\u679c\u7684\u306a\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u8a2d\u8a08<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u57fa\u672c\u7684\u306a\u30a4\u30f3\u30c7\u30c3\u30af\u30b9<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3067\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u8ffd\u52a0\nclass AddIndexesToPosts &lt; ActiveRecord::Migration[7.1]\n  def change\n    add_index :posts, :user_id\n    add_index :posts, :published_at\n    add_index :posts, [:status, :published_at]\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u8907\u5408\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u8a2d\u8a08<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u691c\u7d22\u30d1\u30bf\u30fc\u30f3\u306b\u57fa\u3065\u3044\u305f\u8907\u5408\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\nclass AddCompoundIndexToPosts &lt; ActiveRecord::Migration[7.1]\n  def change\n    # status + published_at\u3067\u306e\u691c\u7d22\u304c\u591a\u3044\u5834\u5408\n    add_index :posts, [:status, :published_at]\n\n    # \u30e6\u30fc\u30b6\u30fc\u3054\u3068\u306e\u6295\u7a3f\u3092\u65e5\u4ed8\u9806\u3067\u53d6\u5f97\u3059\u308b\u5834\u5408\n    add_index :posts, [:user_id, :created_at]\n  end\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30e6\u30cb\u30fc\u30af\u30a4\u30f3\u30c7\u30c3\u30af\u30b9<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class AddUniqueIndexToUsers &lt; ActiveRecord::Migration[7.1]\n  def change\n    add_index :users, :email, unique: true\n    add_index :posts, [:user_id, :slug], unique: true\n  end\nend<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-85\">\u30ad\u30e3\u30c3\u30b7\u30e5\u6226\u7565\u3068\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u30b8\u30e7\u30d6\u306e\u6d3b\u7528<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-86\">Rails\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30d3\u30e5\u30fc\u30ad\u30e3\u30c3\u30b7\u30e5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;%# \u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u30ad\u30e3\u30c3\u30b7\u30e5 %&gt;\n&lt;% cache post do %&gt;\n  &lt;div class=\"post\"&gt;\n    &lt;h2&gt;&lt;%= post.title %&gt;&lt;\/h2&gt;\n    &lt;%= render 'post_content', post: post %&gt;\n  &lt;\/div&gt;\n&lt;% end %&gt;\n\n&lt;%# \u30b3\u30ec\u30af\u30b7\u30e7\u30f3\u30ad\u30e3\u30c3\u30b7\u30e5 %&gt;\n&lt;%= render partial: 'post', collection: @posts, cached: true %&gt;<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30ed\u30b7\u30a2\u30f3\u30c9\u30fc\u30eb\u30ad\u30e3\u30c3\u30b7\u30e5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;%# \u5165\u308c\u5b50\u306b\u306a\u3063\u305f\u30ad\u30e3\u30c3\u30b7\u30e5 %&gt;\n&lt;% cache ['v1', @post] do %&gt;\n  &lt;article&gt;\n    &lt;h1&gt;&lt;%= @post.title %&gt;&lt;\/h1&gt;\n    &lt;% cache ['v1', @post, :comments] do %&gt;\n      &lt;%= render @post.comments %&gt;\n    &lt;% end %&gt;\n  &lt;\/article&gt;\n&lt;% end %&gt;<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u4f4e\u30ec\u30d9\u30eb\u30ad\u30e3\u30c3\u30b7\u30e5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30e2\u30c7\u30eb\u3067\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u5229\u7528\nclass Post &lt; ApplicationRecord\n  def cached_comments_count\n    Rails.cache.fetch([self, 'comments_count']) do\n      comments.count\n    end\n  end\nend\n\n# \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u3067\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u5229\u7528\ndef index\n  @posts = Rails.cache.fetch('recent_posts', expires_in: 15.minutes) do\n    Post.recent.includes(:user).limit(10).to_a\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-87\">Redis\/Memcached\u306e\u6d3b\u7528<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/environments\/production.rb\nconfig.cache_store = :redis_cache_store, {\n  url: ENV['REDIS_URL'],\n  expires_in: 1.day,\n  namespace: 'cache'\n}\n\n# \u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u4f7f\u7528\u4f8b\nclass Post &lt; ApplicationRecord\n  def trending_score\n    Rails.cache.fetch(\"post\/#{id}\/trending_score\", expires_in: 1.hour) do\n      calculate_trending_score\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-88\">Sidekiq\u306b\u3088\u308b\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u30b8\u30e7\u30d6<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30b8\u30e7\u30d6\u306e\u5b9a\u7fa9<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># app\/jobs\/process_post_job.rb\nclass ProcessPostJob &lt; ApplicationJob\n  queue_as :default\n\n  def perform(post_id)\n    post = Post.find(post_id)\n\n    # \u91cd\u3044\u51e6\u7406\u306e\u5b9f\u884c\n    post.generate_thumbnail\n    post.analyze_content\n    post.notify_followers\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30b8\u30e7\u30d6\u306e\u5b9f\u884c<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u5373\u6642\u5b9f\u884c\nProcessPostJob.perform_later(post.id)\n\n# \u30b9\u30b1\u30b8\u30e5\u30fc\u30eb\u5b9f\u884c\nProcessPostJob.set(wait: 1.hour).perform_later(post.id)\n\n# \u512a\u5148\u5ea6\u4ed8\u304d\u30ad\u30e5\u30fc\nclass ImportantJob &lt; ApplicationJob\n  queue_as :high_priority\n\n  def perform\n    # \u91cd\u8981\u306a\u51e6\u7406\n  end\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u5b9a\u671f\u5b9f\u884c\u30b8\u30e7\u30d6<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/initializers\/scheduler.rb\nrequire 'rufus-scheduler'\n\nscheduler = Rufus::Scheduler.singleton\n\nscheduler.every '1h' do\n  CleanupJob.perform_later\nend\n\nscheduler.cron '0 0 * * *' do  # \u6bce\u65e5\u6df1\u591c0\u6642\n  DailyReportJob.perform_later\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-89\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>NewRelic\u306e\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Gemfile\ngem 'newrelic_rpm'\n\n# config\/newrelic.yml\ncommon: &amp;default_settings\n  license_key: '&lt;your-license-key&gt;'\n  app_name: 'Your Application'\n\nproduction:\n  &lt;&lt;: *default_settings<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30e0\u30e1\u30c8\u30ea\u30af\u30b9<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8a08\u6e2c\nclass ApplicationController &lt; ActionController::Base\n  around_action :measure_performance\n\n  private\n\n  def measure_performance\n    start_time = Time.current\n    yield\n    duration = Time.current - start_time\n\n    NewRelic::Agent.record_metric(\n      \"Custom\/#{controller_name}##{action_name}\/duration\",\n      duration\n    )\n  end\nend<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u6700\u9069\u5316\u306b\u3088\u308a\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3068\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u3092\u5927\u5e45\u306b\u5411\u4e0a\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u306b\u3064\u3044\u3066\u5b66\u3093\u3067\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-90\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-91\">OWASP Top 10\u306b\u57fa\u3065\u304f\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-92\">1. \u30a4\u30f3\u30b8\u30a7\u30af\u30b7\u30e7\u30f3\u5bfe\u7b56<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># SQL\u30a4\u30f3\u30b8\u30a7\u30af\u30b7\u30e7\u30f3\u5bfe\u7b56\n# \u60aa\u3044\u4f8b\nUser.where(\"name = '#{params[:name]}'\")  # \u5371\u967a\uff01\n\n# \u826f\u3044\u4f8b\nUser.where(name: params[:name])  # \u30d1\u30e9\u30e1\u30fc\u30bf\u5316\u30af\u30a8\u30ea\n\n# XSS\u5bfe\u7b56\n# \u30d3\u30e5\u30fc\u3067\u306e\u5b89\u5168\u306aHTML\u51fa\u529b\n&lt;%= raw @user.name %&gt;  # \u5371\u967a\uff01\n&lt;%= @user.name %&gt;      # \u81ea\u52d5\u30a8\u30b9\u30b1\u30fc\u30d7\n\n# HTML\u30b5\u30cb\u30bf\u30a4\u30ba\nclass Post &lt; ApplicationRecord\n  before_save :sanitize_content\n\n  private\n\n  def sanitize_content\n    self.content = Rails::Html::SafeListSanitizer.new.sanitize(content)\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-93\">2. \u8a8d\u8a3c\u3068\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30bb\u30c3\u30b7\u30e7\u30f3\u8a2d\u5b9a\n# config\/initializers\/session_store.rb\nRails.application.config.session_store :cookie_store,\n  key: '_app_session',\n  secure: Rails.env.production?,\n  expire_after: 12.hours,\n  httponly: true\n\n# \u5f37\u529b\u306a\u30d1\u30b9\u30ef\u30fc\u30c9\u30dd\u30ea\u30b7\u30fc\nclass User &lt; ApplicationRecord\n  validates :password,\n    length: { minimum: 12 },\n    format: { \n      with: \/\\A(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&amp;])[A-Za-z\\d@$!%*?&amp;]\/,\n      message: '\u306f\u5c11\u306a\u304f\u3068\u30821\u3064\u306e\u5927\u6587\u5b57\u3001\u5c0f\u6587\u5b57\u3001\u6570\u5b57\u3001\u7279\u6b8a\u6587\u5b57\u3092\u542b\u3080\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059'\n    }\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-94\">3. \u30af\u30ed\u30b9\u30b5\u30a4\u30c8\u30b9\u30af\u30ea\u30d7\u30c6\u30a3\u30f3\u30b0\uff08XSS\uff09\u5bfe\u7b56<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30b3\u30f3\u30c6\u30f3\u30c4\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30dd\u30ea\u30b7\u30fc\uff08CSP\uff09\u306e\u8a2d\u5b9a\n# config\/initializers\/content_security_policy.rb\nRails.application.config.content_security_policy do |policy|\n  policy.default_src :self\n  policy.font_src    :self, :https, :data\n  policy.img_src     :self, :https, :data\n  policy.object_src  :none\n  policy.script_src  :self\n  policy.style_src   :self\n  policy.connect_src :self\nend\n\n# XSS\u30d5\u30a3\u30eb\u30bf\u30fc\nclass ApplicationController &lt; ActionController::Base\n  before_action :set_xss_protection_header\n\n  private\n\n  def set_xss_protection_header\n    response.headers['X-XSS-Protection'] = '1; mode=block'\n  end\nend<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-95\">\u5b89\u5168\u306aAPI\u306e\u8a2d\u8a08\u3068\u5b9f\u88c5\u65b9\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-96\">1. API\u8a8d\u8a3c\u3068JWT\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># JWT\u306b\u3088\u308b\u8a8d\u8a3c\nclass JsonWebToken\n  SECRET_KEY = Rails.application.secrets.secret_key_base\n\n  def self.encode(payload, exp = 24.hours.from_now)\n    payload[:exp] = exp.to_i\n    JWT.encode(payload, SECRET_KEY)\n  end\n\n  def self.decode(token)\n    body = JWT.decode(token, SECRET_KEY)[0]\n    HashWithIndifferentAccess.new body\n  rescue JWT::ExpiredSignature, JWT::VerificationError =&gt; e\n    raise ExceptionHandler::InvalidToken, e.message\n  end\nend\n\n# API\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3067\u306e\u5b9f\u88c5\nmodule Api\n  class BaseController &lt; ApplicationController\n    before_action :authenticate_request\n\n    private\n\n    def authenticate_request\n      header = request.headers['Authorization']\n      header = header.split(' ').last if header\n\n      begin\n        @decoded = JsonWebToken.decode(header)\n        @current_user = User.find(@decoded[:user_id])\n      rescue ActiveRecord::RecordNotFound =&gt; e\n        render json: { errors: e.message }, status: :unauthorized\n      rescue JWT::DecodeError =&gt; e\n        render json: { errors: e.message }, status: :unauthorized\n      end\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-97\">2. \u30ec\u30fc\u30c8\u5236\u9650\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/initializers\/rack_attack.rb\nclass Rack::Attack\n  # IP\u30a2\u30c9\u30ec\u30b9\u30d9\u30fc\u30b9\u306e\u5236\u9650\n  throttle('req\/ip', limit: 300, period: 5.minutes) do |req|\n    req.ip\n  end\n\n  # API\u30ad\u30fc\u30d9\u30fc\u30b9\u306e\u5236\u9650\n  throttle('api\/key', limit: 100, period: 1.minute) do |req|\n    req.get_header('X-API-KEY')\n  end\n\n  # \u30ed\u30b0\u30a4\u30f3\u8a66\u884c\u306e\u5236\u9650\n  throttle('logins\/ip', limit: 5, period: 20.seconds) do |req|\n    req.ip if req.path == '\/login' &amp;&amp; req.post?\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-98\">3. \u5165\u529b\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u3068\u51fa\u529b\u30a8\u30b9\u30b1\u30fc\u30d7<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u5f37\u529b\u306a\u5165\u529b\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\nclass Api::V1::PostsController &lt; Api::BaseController\n  def create\n    post = Post.new(post_params)\n    if post.save\n      render json: PostSerializer.new(post), status: :created\n    else\n      render json: { errors: post.errors }, status: :unprocessable_entity\n    end\n  end\n\n  private\n\n  def post_params\n    params.require(:post).permit(\n      :title,\n      :content,\n      :published_at,\n      tag_ids: []\n    )\n  end\nend\n\n# \u30ab\u30b9\u30bf\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\nclass Post &lt; ApplicationRecord\n  validates :title, presence: true, length: { minimum: 5, maximum: 100 }\n  validates :content, presence: true\n  validate :content_contains_no_malicious_code\n\n  private\n\n  def content_contains_no_malicious_code\n    if content.match?(\/(&lt;script|javascript:|data:text\\\/html)\/)\n      errors.add(:content, '\u306b\u306f\u6f5c\u5728\u7684\u306b\u5371\u967a\u306a\u30b3\u30fc\u30c9\u304c\u542b\u307e\u308c\u3066\u3044\u307e\u3059')\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-99\">4. \u30bb\u30ad\u30e5\u30a2\u306a\u30d5\u30a1\u30a4\u30eb\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u5236\u9650\u306e\u8a2d\u5b9a\nclass AttachmentUploader &lt; CarrierWave::Uploader::Base\n  # \u8a31\u53ef\u3059\u308b\u62e1\u5f35\u5b50\n  def extension_allowlist\n    %w(jpg jpeg gif png pdf doc docx)\n  end\n\n  # \u30d5\u30a1\u30a4\u30eb\u30b5\u30a4\u30ba\u306e\u5236\u9650\n  def size_range\n    1.byte..10.megabytes\n  end\n\n  # \u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306e\u4fdd\u8b77\n  def root\n    Rails.root.join('private', 'uploads')\n  end\nend\n\n# \u30d5\u30a1\u30a4\u30eb\u51e6\u7406\u306e\u5b9f\u88c5\nclass Document &lt; ApplicationRecord\n  mount_uploader :file, AttachmentUploader\n\n  before_save :scan_for_viruses\n\n  private\n\n  def scan_for_viruses\n    # \u30a6\u30a4\u30eb\u30b9\u30b9\u30ad\u30e3\u30f3\u306e\u5b9f\u88c5\n    result = ClamAV.scan(file.path)\n    if result.virus?\n      errors.add(:file, '\u306b\u30a6\u30a4\u30eb\u30b9\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f')\n      throw :abort\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-100\">5. \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d8\u30c3\u30c0\u30fc\u306e\u8a2d\u5b9a<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/initializers\/security_headers.rb\nRails.application.config.action_dispatch.default_headers = {\n  'X-Frame-Options' =&gt; 'SAMEORIGIN',\n  'X-XSS-Protection' =&gt; '1; mode=block',\n  'X-Content-Type-Options' =&gt; 'nosniff',\n  'X-Download-Options' =&gt; 'noopen',\n  'X-Permitted-Cross-Domain-Policies' =&gt; 'none',\n  'Referrer-Policy' =&gt; 'strict-origin-when-cross-origin'\n}\n\n# HTTPS\u306e\u5f37\u5236\nclass ApplicationController &lt; ActionController::Base\n  force_ssl if: :ssl_configured?\n\n  private\n\n  def ssl_configured?\n    Rails.env.production?\n  end\nend<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3092\u5b9f\u88c5\u3059\u308b\u3053\u3068\u3067\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u5b89\u5168\u6027\u3092\u5927\u5e45\u306b\u5411\u4e0a\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u3068CI\/CD\u306b\u3064\u3044\u3066\u5b66\u3093\u3067\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-101\">\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u3068CI\/CD<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-102\">Heroku\u3092\u4f7f\u3063\u305f\u7c21\u5358\u306a\u30c7\u30d7\u30ed\u30a4\u65b9\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-103\">Heroku\u306e\u521d\u671f\u8a2d\u5b9a<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Heroku\u306e\u6e96\u5099<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Heroku CLI\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nbrew install heroku\/brew\/heroku  # macOS\nsudo snap install heroku --classic  # Ubuntu\n\n# Heroku\u306b\u30ed\u30b0\u30a4\u30f3\nheroku login\n\n# \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4f5c\u6210\nheroku create my-rails-app\n\n# PostgreSQL\u30a2\u30c9\u30aa\u30f3\u306e\u8ffd\u52a0\nheroku addons:create heroku-postgresql:hobby-dev<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30c7\u30d7\u30ed\u30a4\u306e\u305f\u3081\u306e\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Gemfile\ngroup :production do\n  gem 'pg'\n  gem 'redis'\n  gem 'sidekiq'\nend\n\n# config\/database.yml\nproduction:\n  url: &lt;%= ENV['DATABASE_URL'] %&gt;\n  pool: &lt;%= ENV.fetch(\"RAILS_MAX_THREADS\") { 5 } %&gt;\n\n# config\/environments\/production.rb\nconfig.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?\nconfig.active_storage.service = :amazon  # AWS\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30c7\u30d7\u30ed\u30a4\u306e\u5b9f\u884c<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Git\u30ea\u30dd\u30b8\u30c8\u30ea\u306e\u521d\u671f\u5316\uff08\u672a\u5b9f\u65bd\u306e\u5834\u5408\uff09\ngit init\ngit add .\ngit commit -m \"Initial commit\"\n\n# Heroku\u306b\u30c7\u30d7\u30ed\u30a4\ngit push heroku main\n\n# \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\nheroku run rails db:migrate\n\n# \u74b0\u5883\u5909\u6570\u306e\u8a2d\u5b9a\nheroku config:set RAILS_MASTER_KEY=`cat config\/master.key`\nheroku config:set AWS_ACCESS_KEY_ID=your_access_key\nheroku config:set AWS_SECRET_ACCESS_KEY=your_secret_key<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-104\">GitHub Actions\u306b\u3088\u308bCI\/CD\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u306e\u69cb\u7bc9<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-105\">CI\/CD\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u306e\u8a2d\u5b9a<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>GitHub Actions\u306e\u30ef\u30fc\u30af\u30d5\u30ed\u30fc\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># .github\/workflows\/rails.yml\nname: Rails CI\/CD\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    services:\n      postgres:\n        image: postgres:13\n        env:\n          POSTGRES_USER: postgres\n          POSTGRES_PASSWORD: postgres\n        ports: ['5432:5432']\n        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5\n\n    steps:\n    - uses: actions\/checkout@v3\n\n    - name: Set up Ruby\n      uses: ruby\/setup-ruby@v1\n      with:\n        ruby-version: '3.3.0'\n        bundler-cache: true\n\n    - name: Install dependencies\n      run: |\n        gem install bundler\n        bundle install\n\n    - name: Setup Database\n      env:\n        RAILS_ENV: test\n        DATABASE_URL: postgres:\/\/postgres:postgres@localhost:5432\/test\n      run: |\n        bundle exec rails db:create\n        bundle exec rails db:schema:load\n\n    - name: Run tests\n      env:\n        RAILS_ENV: test\n        DATABASE_URL: postgres:\/\/postgres:postgres@localhost:5432\/test\n      run: bundle exec rspec\n\n    - name: Run security checks\n      run: |\n        gem install brakeman\n        brakeman -z\n\n  deploy:\n    needs: test\n    if: github.ref == 'refs\/heads\/main'\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions\/checkout@v3\n\n    - name: Deploy to Heroku\n      uses: akhileshns\/heroku-deploy@v3.12.14\n      with:\n        heroku_api_key: ${{ secrets.HEROKU_API_KEY }}\n        heroku_app_name: ${{ secrets.HEROKU_APP_NAME }}\n        heroku_email: ${{ secrets.HEROKU_EMAIL }}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30c6\u30b9\u30c8\u30b9\u30a4\u30fc\u30c8\u306e\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># spec\/rails_helper.rb\nrequire 'spec_helper'\nENV['RAILS_ENV'] ||= 'test'\nrequire File.expand_path('..\/config\/environment', __dir__)\n\nabort(\"The Rails environment is running in production mode!\") if Rails.env.production?\nrequire 'rspec\/rails'\nrequire 'capybara\/rspec'\n\nRSpec.configure do |config|\n  config.use_transactional_fixtures = true\n  config.infer_spec_type_from_file_location!\n  config.filter_rails_from_backtrace!\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-106\">\u672c\u756a\u74b0\u5883\u306e\u8a2d\u5b9a\u3068\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Nginx\u306e\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \/etc\/nginx\/sites-available\/myapp.conf\nupstream puma {\n  server unix:\/\/\/home\/deploy\/apps\/myapp\/shared\/tmp\/sockets\/puma.sock;\n}\n\nserver {\n  listen 80;\n  server_name example.com;\n\n  root \/home\/deploy\/apps\/myapp\/current\/public;\n  access_log \/home\/deploy\/apps\/myapp\/current\/log\/nginx.access.log;\n  error_log \/home\/deploy\/apps\/myapp\/current\/log\/nginx.error.log info;\n\n  location ^~ \/assets\/ {\n    gzip_static on;\n    expires max;\n    add_header Cache-Control public;\n  }\n\n  try_files $uri\/index.html $uri @puma;\n  location @puma {\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header Host $http_host;\n    proxy_redirect off;\n    proxy_pass http:\/\/puma;\n  }\n\n  error_page 500 502 503 504 \/500.html;\n  client_max_body_size 10M;\n  keepalive_timeout 10;\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u76e3\u8996\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/initializers\/sentry.rb\nSentry.init do |config|\n  config.dsn = ENV['SENTRY_DSN']\n  config.breadcrumbs_logger = [:active_support_logger, :http_logger]\n  config.traces_sample_rate = 0.5\n  config.environments = %w[production staging]\nend\n\n# config\/initializers\/lograge.rb\nRails.application.configure do\n  config.lograge.enabled = true\n  config.lograge.custom_options = lambda do |event|\n    {\n      params: event.payload[:params].except(*%w(controller action format)),\n      time: Time.current\n    }\n  end\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/initializers\/scout_apm.rb\nScoutApm::Agent.config(\n  name: \"MyApp\",\n  key: ENV['SCOUT_KEY'],\n  monitor: true,\n  dev_trace: false\n)\n\n# \u30ab\u30b9\u30bf\u30e0\u30e1\u30c8\u30ea\u30af\u30b9\u306e\u8ffd\u52a0\nclass ApplicationController &lt; ActionController::Base\n  around_action :track_request_metrics\n\n  private\n\n  def track_request_metrics\n    start = Time.current\n    yield\n    duration = Time.current - start\n\n    ScoutApm::Agent.record_custom_metric(\n      \"Controller\/#{controller_name}\/#{action_name}\",\n      duration\n    )\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-107\">\u30c7\u30d7\u30ed\u30a4\u6642\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30bc\u30ed\u30c0\u30a6\u30f3\u30bf\u30a4\u30e0\u30c7\u30d7\u30ed\u30a4<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/puma.rb\nworkers ENV.fetch(\"WEB_CONCURRENCY\") { 2 }\nthreads_count = ENV.fetch(\"RAILS_MAX_THREADS\") { 5 }\nthreads threads_count, threads_count\n\npreload_app!\n\nrackup DefaultRackup\nport ENV.fetch(\"PORT\") { 3000 }\nenvironment ENV.fetch(\"RAILS_ENV\") { \"development\" }\n\non_worker_boot do\n  ActiveRecord::Base.establish_connection\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30c7\u30d7\u30ed\u30a4\u5f8c\u306e\u30bf\u30b9\u30af\u81ea\u52d5\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># lib\/tasks\/deployment.rake\nnamespace :deployment do\n  desc \"\u5b9f\u884c\u3059\u3079\u304d\u30c7\u30d7\u30ed\u30a4\u5f8c\u306e\u30bf\u30b9\u30af\"\n  task post_deploy: :environment do\n    puts \"\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u30af\u30ea\u30a2...\"\n    Rails.cache.clear\n\n    puts \"\u30b5\u30a4\u30c9\u30ad\u30c3\u30af\u306e\u518d\u8d77\u52d5...\"\n    system \"systemctl restart sidekiq\"\n\n    puts \"\u4e00\u6642\u30d5\u30a1\u30a4\u30eb\u306e\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7...\"\n    system \"find tmp\/cache -type f -mtime +7 -delete\"\n  end\nend<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u8a2d\u5b9a\u3068\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u306b\u3088\u308a\u3001\u5b89\u5b9a\u3057\u305f\u672c\u756a\u74b0\u5883\u306e\u904b\u7528\u3068\u30b9\u30e0\u30fc\u30ba\u306a\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u30d7\u30ed\u30bb\u30b9\u3092\u5b9f\u73fe\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u4e2d\u7d1a\u8005\u304b\u3089\u4e0a\u7d1a\u8005\u3078\u306e\u30b9\u30c6\u30c3\u30d7\u30a2\u30c3\u30d7\u306b\u3064\u3044\u3066\u5b66\u3093\u3067\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-108\">\u6b21\u306e\u30b9\u30c6\u30c3\u30d7\uff1a\u4e2d\u7d1a\u8005\u304b\u3089\u4e0a\u7d1a\u8005\u3078<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-109\">Ruby on Rails\u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u3078\u306e\u53c2\u52a0\u65b9\u6cd5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-110\">\u30aa\u30f3\u30e9\u30a4\u30f3\u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u3078\u306e\u53c2\u52a0<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u516c\u5f0f\u30ea\u30bd\u30fc\u30b9<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/discuss.rubyonrails.org\/\">Ruby on Rails \u516c\u5f0f\u30d5\u30a9\u30fc\u30e9\u30e0<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/weblog.rubyonrails.org\/\">Ruby on Rails \u516c\u5f0f\u30d6\u30ed\u30b0<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/guides.rubyonrails.org\/\">Ruby on Rails \u516c\u5f0f\u30ac\u30a4\u30c9<\/a><\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u65e5\u672c\u306eRails\u30b3\u30df\u30e5\u30cb\u30c6\u30a3<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ruby\/Rails\u52c9\u5f37\u4f1a\u3084\u30ab\u30f3\u30d5\u30a1\u30ec\u30f3\u30b9<\/li>\n\n\n\n<li>RubyKaigi<\/li>\n\n\n\n<li>Rails Developers Meetup<\/li>\n\n\n\n<li>Regional RubyKaigi<\/li>\n\n\n\n<li>\u30aa\u30f3\u30e9\u30a4\u30f3\u30b3\u30df\u30e5\u30cb\u30c6\u30a3<\/li>\n\n\n\n<li>Ruby on Rails\u65e5\u672c\u8a9e\u30d5\u30a9\u30fc\u30e9\u30e0<\/li>\n\n\n\n<li>Ruby\/Rails\u95a2\u9023\u306eSlack\u30ef\u30fc\u30af\u30b9\u30da\u30fc\u30b9<\/li>\n\n\n\n<li>Twitter\/X \u306e #rails #ruby \u30cf\u30c3\u30b7\u30e5\u30bf\u30b0<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u60c5\u5831\u5171\u6709\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Qiita<\/li>\n\n\n\n<li>Zenn<\/li>\n\n\n\n<li>dev.to<\/li>\n\n\n\n<li>Medium<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-111\">\u5b9f\u8df5\u7684\u306a\u53c2\u52a0\u65b9\u6cd5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># 1. \u30a4\u30d9\u30f3\u30c8\u3078\u306e\u53c2\u52a0\n# - \u5730\u57df\u306eRuby\/Rails\u52c9\u5f37\u4f1a\u306b\u53c2\u52a0\n# - \u30aa\u30f3\u30e9\u30a4\u30f3\u3082\u304f\u3082\u304f\u4f1a\u3078\u306e\u53c2\u52a0\n# - \u30ab\u30f3\u30d5\u30a1\u30ec\u30f3\u30b9\u3067\u306e\u767b\u58c7\n\n# 2. \u77e5\u8b58\u306e\u5171\u6709\n# - \u30d6\u30ed\u30b0\u8a18\u4e8b\u306e\u57f7\u7b46\n# - \u6280\u8853\u66f8\u306e\u57f7\u7b46\n# - \u52c9\u5f37\u4f1a\u3067\u306e\u767a\u8868\n\n# 3. \u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u904b\u55b6\u3078\u306e\u53c2\u52a0\n# - \u52c9\u5f37\u4f1a\u306e\u4f01\u753b\u30fb\u904b\u55b6\n# - \u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u306e\u30e2\u30c7\u30ec\u30fc\u30bf\u30fc\n# - \u30e1\u30f3\u30bf\u30fc\u3068\u3057\u3066\u306e\u6d3b\u52d5<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-112\">\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3078\u306e\u8ca2\u732e\u3068\u30b9\u30ad\u30eb\u5411\u4e0a\u306e\u30d2\u30f3\u30c8<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-113\">\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3078\u306e\u8ca2\u732e\u65b9\u6cd5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u6700\u521d\u306e\u30b9\u30c6\u30c3\u30d7<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># 1. \u8ca2\u732e\u3057\u305f\u3044\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u898b\u3064\u3051\u308b\n# - GitHub Explore\n# - RubyGems\n# - awesome-ruby \u30ea\u30dd\u30b8\u30c8\u30ea\n\n# 2. \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u7406\u89e3\ngit clone https:\/\/github.com\/example\/project.git\ncd project\nbundle install\nrails test  # \u30c6\u30b9\u30c8\u30b9\u30a4\u30fc\u30c8\u306e\u5b9f\u884c\n\n# 3. \u8ca2\u732e\u306e\u958b\u59cb\ngit checkout -b fix-issue-123\n# \u30b3\u30fc\u30c9\u306e\u4fee\u6b63\ngit commit -m \"Fix issue #123: \u8a73\u7d30\u306a\u8aac\u660e\"\ngit push origin fix-issue-123\n# \u30d7\u30eb\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u4f5c\u6210<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u52b9\u679c\u7684\u306a\u8ca2\u732e\u306e\u305f\u3081\u306e\u30ac\u30a4\u30c9\u30e9\u30a4\u30f3<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># 1. \u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u30b9\u30bf\u30a4\u30eb\u306e\u9075\u5b88\n# - RuboCop\u306e\u8a2d\u5b9a\u306b\u5f93\u3046\n# - \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u65e2\u5b58\u306e\u30b9\u30bf\u30a4\u30eb\u3092\u7dad\u6301\n\n# 2. \u30c6\u30b9\u30c8\u306e\u4f5c\u6210\nclass UserTest &lt; ActiveSupport::TestCase\n  test \"should validate email format\" do\n    user = User.new(email: \"invalid-email\")\n    assert_not user.valid?\n    assert_includes user.errors[:email], \"\u306f\u4e0d\u6b63\u306a\u5024\u3067\u3059\"\n  end\nend\n\n# 3. \u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306e\u66f4\u65b0\n# - README\u306e\u66f4\u65b0\n# - CHANGELOG.md\u306e\u66f4\u65b0\n# - YARD\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306e\u8ffd\u52a0<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-114\">\u30b9\u30ad\u30eb\u5411\u4e0a\u306e\u305f\u3081\u306e\u30ed\u30fc\u30c9\u30de\u30c3\u30d7<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u6280\u8853\u7684\u30b9\u30ad\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># 1. \u57fa\u790e\u306e\u5f37\u5316\n# - Ruby\u30e1\u30bf\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\nmodule Loggable\n  def self.included(base)\n    base.extend(ClassMethods)\n    base.class_eval do\n      before_action :log_action\n    end\n  end\n\n  module ClassMethods\n    def log_methods(*methods)\n      methods.each do |method|\n        original_method = instance_method(method)\n        define_method(method) do |*args|\n          Rails.logger.info(\"Calling #{method} with #{args}\")\n          original_method.bind(self).call(*args)\n        end\n      end\n    end\n  end\nend\n\n# 2. \u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u30d1\u30bf\u30fc\u30f3\n# - DDD\uff08\u30c9\u30e1\u30a4\u30f3\u99c6\u52d5\u8a2d\u8a08\uff09\u306e\u5b9f\u8df5\nmodule OrderManagement\n  class Order\n    include AggregateRoot\n\n    def place_order(order_items)\n      raise InvalidOrderError if order_items.empty?\n\n      event = OrderPlaced.new(\n        order_id: id,\n        items: order_items,\n        total: calculate_total(order_items)\n      )\n\n      apply_and_persist(event)\n    end\n  end\nend\n\n# 3. \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\n# - \u30d7\u30ed\u30d5\u30a1\u30a4\u30ea\u30f3\u30b0\u30c4\u30fc\u30eb\u306e\u4f7f\u7528\n# - \u30d9\u30f3\u30c1\u30de\u30fc\u30af\u306e\u5b9f\u65bd\nrequire 'benchmark'\n\nBenchmark.bm do |x|\n  x.report(\"optimized:\") { OptimizedQuery.perform }\n  x.report(\"original:\") { OriginalQuery.perform }\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30bd\u30d5\u30c8\u30b9\u30ad\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c1\u30fc\u30e0\u30ea\u30fc\u30c0\u30fc\u30b7\u30c3\u30d7<\/li>\n\n\n\n<li>\u30b3\u30fc\u30c9\u30ec\u30d3\u30e5\u30fc\u30b9\u30ad\u30eb<\/li>\n\n\n\n<li>\u6280\u8853\u6587\u66f8\u4f5c\u6210<\/li>\n\n\n\n<li>\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u30de\u30cd\u30b8\u30e1\u30f3\u30c8<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-115\">\u30ad\u30e3\u30ea\u30a2\u767a\u5c55\u306e\u305f\u3081\u306e\u30a2\u30c9\u30d0\u30a4\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30dd\u30fc\u30c8\u30d5\u30a9\u30ea\u30aa\u306e\u69cb\u7bc9<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># 1. \u500b\u4eba\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u958b\u767a\n# - \u5b9f\u7528\u7684\u306a\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\n# - \u65b0\u3057\u3044\u6280\u8853\u306e\u5b9f\u9a13\n# - \u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30c4\u30fc\u30eb\n\n# 2. \u6280\u8853\u30d6\u30ed\u30b0\u306e\u904b\u55b6\n# - \u5b66\u7fd2\u8a18\u9332\n# - \u554f\u984c\u89e3\u6c7a\u306e\u8a18\u9332\n# - \u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u306e\u4f5c\u6210\n\n# 3. \u767b\u58c7\u30fb\u57f7\u7b46\u6d3b\u52d5\n# - \u52c9\u5f37\u4f1a\u3067\u306e\u767a\u8868\n# - \u6280\u8853\u66f8\u306e\u57f7\u7b46\n# - \u30aa\u30f3\u30e9\u30a4\u30f3\u30bb\u30df\u30ca\u30fc\u306e\u958b\u50ac<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u7d99\u7d9a\u7684\u306a\u5b66\u7fd2\u30ea\u30bd\u30fc\u30b9<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30aa\u30f3\u30e9\u30a4\u30f3\u5b66\u7fd2\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0<\/li>\n\n\n\n<li>Udemy<\/li>\n\n\n\n<li>Coursera<\/li>\n\n\n\n<li>PluralSight<\/li>\n\n\n\n<li>\u6280\u8853\u66f8<\/li>\n\n\n\n<li>Practical Object-Oriented Design in Ruby<\/li>\n\n\n\n<li>Metaprogramming Ruby<\/li>\n\n\n\n<li>Ruby Under a Microscope<\/li>\n\n\n\n<li>\u30dd\u30c3\u30c9\u30ad\u30e3\u30b9\u30c8<\/li>\n\n\n\n<li>Ruby Rogues<\/li>\n\n\n\n<li>Ruby on Rails Podcast<\/li>\n\n\n\n<li>Developer Tea<\/li>\n<\/ul>\n\n\n\n<p>\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u7d39\u4ecb\u3057\u305f\u5185\u5bb9\u3092\u5b9f\u8df5\u3059\u308b\u3053\u3068\u3067\u3001Ruby on Rails\u30a8\u30f3\u30b8\u30cb\u30a2\u3068\u3057\u3066\u306e\u6b21\u306e\u30b9\u30c6\u30fc\u30b8\u3078\u3068\u9032\u3080\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u7d99\u7d9a\u7684\u306a\u5b66\u7fd2\u3068\u5b9f\u8df5\u3001\u305d\u3057\u3066\u30b3\u30df\u30e5\u30cb\u30c6\u30a3\u3078\u306e\u53c2\u52a0\u304c\u3001\u30b9\u30ad\u30eb\u30a2\u30c3\u30d7\u306e\u91cd\u8981\u306a\u9375\u3068\u306a\u308a\u307e\u3059\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Warning: Undefined array key &#8220;is_admin&#8221; in \/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/ &#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":{"0":"post-1478","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-ruby","7":"nothumb"},"_links":{"self":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1478","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1478"}],"version-history":[{"count":1,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1478\/revisions"}],"predecessor-version":[{"id":1479,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1478\/revisions\/1479"}],"wp:attachment":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1478"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1478"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1478"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}