{"id":3121,"date":"2021-12-10T13:38:00","date_gmt":"2021-12-10T04:38:00","guid":{"rendered":"https:\/\/suzutukiblog.com\/?p=3121"},"modified":"2022-01-23T07:54:00","modified_gmt":"2022-01-22T22:54:00","slug":"rails-tutorial14-25","status":"publish","type":"post","link":"https:\/\/suzutukiblog.com\/index.php\/2021\/12\/10\/rails-tutorial14-25\/","title":{"rendered":"Rails-tutorial\u306e\u307e\u3068\u308114.25(Following\u3068Followers\u30da\u30fc\u30b8)"},"content":{"rendered":"<p><a href=\"https:\/\/suzutukiblog.com\/index.php\/2021\/04\/26\/rails-tutorial14-2\/\">\u305d\u306e14.2\u304b\u3089\u7d9a\u304f<\/a><\/p>\n<h3>14.2.3 [Following] \u3068 [Followers] \u30da\u30fc\u30b8<\/h3>\n<p>\u30d5\u30a9\u30ed\u30fc\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc\u3092\u8868\u793a\u3059\u308b\u30da\u30fc\u30b8\u3068\u3001\u30d5\u30a9\u30ed\u30ef\u30fc\u3092\u8868\u793a\u3059\u308b\u30da\u30fc\u30b8\u306f\u3001<br \/>\n\u3044\u305a\u308c\u3082\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u30da\u30fc\u30b8\u3068\u30e6\u30fc\u30b6\u30fc\u4e00\u89a7\u30da\u30fc\u30b8\u3092\u5408\u308f\u305b\u305f\u3088\u3046\u306a\u4f5c\u308a\u306b\u306a\u308b\u3068\u3044\u3046\u70b9\u3067\u4f3c\u3066\u3044\u307e\u3059\u3002<br \/>\n\u3069\u3061\u3089\u306b\u3082\u30d5\u30a9\u30ed\u30fc\u306e\u7d71\u8a08\u60c5\u5831\u306a\u3069\u306e\u30e6\u30fc\u30b6\u30fc\u60c5\u5831\u3092<br \/>\n\u8868\u793a\u3059\u308b\u30b5\u30a4\u30c9\u30d0\u30fc\u3068\u3001\u30e6\u30fc\u30b6\u30fc\u306e\u30ea\u30b9\u30c8\u304c\u3042\u308a\u307e\u3059\u3002<br \/>\n\u3055\u3089\u306b\u3001\u30b5\u30a4\u30c9\u30d0\u30fc\u306b\u306f\u5c0f\u3055\u3081\u306e\u30e6\u30fc\u30b6\u30fc\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u753b\u50cf\u306e\u30ea\u30f3\u30af\u3092\u683c\u5b50\u72b6\u306b\u4e26\u3079\u3066\u8868\u793a\u3059\u308b\u4e88\u5b9a\u3067\u3059\u3002<\/p>\n<p>\u30d5\u30a9\u30ed\u30fc\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u30ea\u30f3\u30af\u3068\u30d5\u30a9\u30ed\u30ef\u30fc\u306e\u30ea\u30f3\u30af\u3092<br \/>\n\u52d5\u304f\u3088\u3046\u306b\u3059\u308b\u3053\u3068\u3067Twitter\u306b\u5023\u3063\u3066\u3001<br \/>\n\u3069\u3061\u3089\u306e\u30da\u30fc\u30b8\u3067\u3082\u30e6\u30fc\u30b6\u30fc\u306e\u30ed\u30b0\u30a4\u30f3\u3092\u8981\u6c42\u3059\u308b\u3088\u3046\u306b\u3057\u307e\u3059\u3002<\/p>\n<p>\u30d5\u30a9\u30ed\u30fc\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u30ea\u30f3\u30af\u3068\u30d5\u30a9\u30ed\u30ef\u30fc\u306e\u30ea\u30f3\u30af\u3092\u52d5\u304f\u3088\u3046\u306b\u3059\u308b\u3053\u3068\u3067\u3059\u3002 Twitter\u306b\u5023\u3063\u3066\u3001\u3069\u3061\u3089\u306e\u30da\u30fc\u30b8\u3067\u3082\u30e6\u30fc\u30b6\u30fc\u306e\u30ed\u30b0\u30a4\u30f3\u3092\u8981\u6c42\u3059\u308b\u3088\u3046\u306b\u3057\u307e\u3059\u3002\u305d\u3053\u3067\u524d\u56de\u306e\u30a2\u30af\u30bb\u30b9\u5236\u5fa1\u3068\u540c\u69d8\u306b\u3001\u307e\u305a\u306f\u30c6\u30b9\u30c8\u304b\u3089\u66f8\u3044\u3066\u3044\u304d\u307e\u3057\u3087\u3046\u3002<\/p>\n<p>\u30d5\u30a9\u30ed\u30fc\/\u30d5\u30a9\u30ed\u30ef\u30fc\u30da\u30fc\u30b8\u306e\u8a8d\u53ef\u3092\u30c6\u30b9\u30c8\u3059\u308b red<br \/>\n<strong>test\/controllers\/users_controller_test.rb<\/strong><\/p>\n<pre><strong>require 'test_helper'<\/strong>\r\n\r\n<strong>class UsersControllerTest &lt; ActionDispatch::IntegrationTest<\/strong>\r\n\r\n<strong> def setup<\/strong>\r\n<strong>  @user = users(:michael)<\/strong>\r\n<strong>  @other_user = users(:archer)<\/strong>\r\n<strong> end<\/strong>\r\n<strong>.<\/strong>\r\n<strong>.<\/strong>\r\n<strong>.<\/strong>\r\n<strong> test \"should redirect following when not logged in\" do<\/strong>\r\n<strong>  get following_user_path(@user)<\/strong>\r\n<strong>  assert_redirected_to login_url<\/strong>\r\n<strong> end<\/strong>\r\n\r\n<strong> test \"should redirect followers when not logged in\" do<\/strong>\r\n<strong>  get followers_user_path(@user)<\/strong>\r\n<strong>  assert_redirected_to login_url<\/strong>\r\n<strong> end<\/strong>\r\n<strong>end<\/strong><\/pre>\n<p>\u3053\u306e\u5b9f\u88c5\u306b\u306f\uff11\u3064\u3060\u3051\u30c8\u30ea\u30c3\u30ad\u30fc\u306a\u90e8\u5206\u304c\u3042\u308a\u307e\u3059\u3002<br \/>\n\u305d\u308c\u306fUsers\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u306b2\u3064\u306e\u65b0\u3057\u3044\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u8ffd\u52a0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3068\u3044\u3046\u3053\u3068\u3067\u3059\u3002<\/p>\n<p>\u5b9a\u7fa9\u3057\u305f2\u3064\u306e\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306b\u3082\u3068\u3065\u3044\u3066\u304a\u308a\u3001\u3053\u308c\u3089\u306f\u305d\u308c\u305e\u308cfollowing\u304a\u3088\u3073followers\u3068\u547c\u3076\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<br \/>\n\u305d\u308c\u305e\u308c\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30bf\u30a4\u30c8\u30eb\u3092\u8a2d\u5b9a\u3057\u3001\u30e6\u30fc\u30b6\u30fc\u3092\u691c\u7d22\u3057\u3001@user.following\u307e\u305f\u306f@user.followers\u304b\u3089\u30c7\u30fc\u30bf\u3092\u53d6\u308a\u51fa\u3057\u3001<br \/>\n\u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3\u3092\u884c\u306a\u3063\u3066\u3001\u30da\u30fc\u30b8\u3092\u51fa\u529b\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n<p>following\u30a2\u30af\u30b7\u30e7\u30f3\u3068followers\u30a2\u30af\u30b7\u30e7\u30f3 red<br \/>\n<strong>app\/controllers\/users_controller.rb<\/strong><\/p>\n<pre><strong><span class=\"k\">class<\/span> <span class=\"nc\">UsersController<\/span> <span class=\"o\">&lt;<\/span> <span class=\"no\">ApplicationController<\/span>\r\n  <span class=\"n\">before_action<\/span> <span class=\"ss\">:logged_in_user<\/span><span class=\"p\">,<\/span> <span class=\"ss\">only<\/span><span class=\"p\">:<\/span> <span class=\"o\">[<\/span><span class=\"ss\">:index<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:edit<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:update<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:destroy<\/span><span class=\"p\">,<\/span>\r\n<span class=\"hll\">                                        <span class=\"ss\">:following<\/span><span class=\"p\">,<\/span> <span class=\"ss\">:followers<\/span><span class=\"o\">]<\/span>\r\n<\/span>  <span class=\"o\">.<\/span>\r\n  <span class=\"o\">.<\/span>\r\n  <span class=\"o\">.<\/span>\r\n  <span class=\"k\">def<\/span> <span class=\"nf\">following<\/span>\r\n    <span class=\"vi\">@title<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">\"Following\"<\/span>\r\n    <span class=\"vi\">@user<\/span>  <span class=\"o\">=<\/span> <span class=\"no\">User<\/span><span class=\"o\">.<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">params<\/span><span class=\"o\">[<\/span><span class=\"ss\">:id<\/span><span class=\"o\">]<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"vi\">@users<\/span> <span class=\"o\">=<\/span> <span class=\"vi\">@user<\/span><span class=\"o\">.<\/span><span class=\"n\">following<\/span><span class=\"o\">.<\/span><span class=\"n\">paginate<\/span><span class=\"p\">(<\/span><span class=\"ss\">page<\/span><span class=\"p\">:<\/span> <span class=\"n\">params<\/span><span class=\"o\">[<\/span><span class=\"ss\">:page<\/span><span class=\"o\">]<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"n\">render<\/span> <span class=\"s1\">'show_follow'<\/span>\r\n  <span class=\"k\">end<\/span>\r\n\r\n  <span class=\"k\">def<\/span> <span class=\"nf\">followers<\/span>\r\n    <span class=\"vi\">@title<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">\"Followers\"<\/span>\r\n    <span class=\"vi\">@user<\/span>  <span class=\"o\">=<\/span> <span class=\"no\">User<\/span><span class=\"o\">.<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">params<\/span><span class=\"o\">[<\/span><span class=\"ss\">:id<\/span><span class=\"o\">]<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"vi\">@users<\/span> <span class=\"o\">=<\/span> <span class=\"vi\">@user<\/span><span class=\"o\">.<\/span><span class=\"n\">followers<\/span><span class=\"o\">.<\/span><span class=\"n\">paginate<\/span><span class=\"p\">(<\/span><span class=\"ss\">page<\/span><span class=\"p\">:<\/span> <span class=\"n\">params<\/span><span class=\"o\">[<\/span><span class=\"ss\">:page<\/span><span class=\"o\">]<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"n\">render<\/span> <span class=\"s1\">'show_follow'<\/span>\r\n  <span class=\"k\">end<\/span>\r\n\r\n  <span class=\"kp\">private<\/span>\r\n  <span class=\"o\">.<\/span>\r\n  <span class=\"o\">.<\/span>\r\n  <span class=\"o\">.<\/span>\r\n<span class=\"k\">end<\/span><\/strong><\/pre>\n<p>Rails\u306f\u6163\u7fd2\u306b\u5f93\u3063\u3066\u3001\u30a2\u30af\u30b7\u30e7\u30f3\u306b\u5bfe\u5fdc\u3059\u308b\u30d3\u30e5\u30fc\u3092\u6697\u9ed9\u7684\u306b\u547c\u3073\u51fa\u3057\u307e\u3059\u3002<br \/>\n\u4f8b\u3048\u3070\u3001show\u30a2\u30af\u30b7\u30e7\u30f3\u306e\u6700\u5f8c\u3067show.html.erb\u3092\u547c\u3073\u51fa\u3059\u3001\u3068\u3044\u3063\u305f\u5177\u5408\u3067\u3059\u3002<\/p>\n<p>render\u3092\u660e\u793a\u7684\u306b\u547c\u3073\u51fa\u3057\u3001show_follow\u3068\u3044\u3046\u540c\u3058\u30d3\u30e5\u30fc\u3092\u51fa\u529b\u3057\u3066\u3044\u307e\u3059\u3002\u3064\u307e\u308a\u3001\u4f5c\u6210\u304c\u5fc5\u8981\u306a\u30d3\u30e5\u30fc\u306f\u3053\u308c1\u3064\u3067\u3059\u3002<br \/>\nrender\u3067\u547c\u3073\u51fa\u3057\u3066\u3044\u308b\u30d3\u30e5\u30fc\u304c\u540c\u3058\u3067\u3042\u308b\u7406\u7531\u306f\u3001<br \/>\nERb\u306f\u3069\u3061\u3089\u306e\u5834\u5408\u3067\u3082\u307b\u307c\u540c\u3058\u3067\u3042\u308a\u3001\u3067\u4e21\u65b9\u306e\u5834\u5408\u3092\u30ab\u30d0\u30fc\u3067\u304d\u308b\u305f\u3081\u3067\u3059\u3002<\/p>\n<p>\u2193\u30d5\u30a9\u30ed\u30fc\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc\u3068\u30d5\u30a9\u30ed\u30ef\u30fc\u306e\u4e21\u65b9\u3092\u8868\u793a\u3059\u308bshow_follow\u30d3\u30e5\u30fc green<br \/>\n<strong>app\/views\/users\/show_follow.html.erb<\/strong><\/p>\n<pre><strong>&lt;% provide(:title, @title) %&gt;<\/strong>\r\n<strong>&lt;div class=\"row\"&gt;<\/strong>\r\n<strong> &lt;aside class=\"col-md-4\"&gt;<\/strong>\r\n<strong>  &lt;section class=\"user_info\"&gt;<\/strong>\r\n<strong>   &lt;%= gravatar_for @user %&gt;<\/strong>\r\n<strong>   &lt;h1&gt;&lt;%= @user.name %&gt;&lt;\/h1&gt;<\/strong>\r\n<strong>   &lt;span&gt;&lt;%= link_to \"view my profile\", @user %&gt;&lt;\/span&gt;<\/strong>\r\n<strong>   &lt;span&gt;&lt;b&gt;Microposts:&lt;\/b&gt; &lt;%= @user.microposts.count %&gt;&lt;\/span&gt;<\/strong>\r\n<strong>  &lt;\/section&gt;<\/strong>\r\n<strong> &lt;section class=\"stats\"&gt;<\/strong>\r\n<strong>  &lt;%= render 'shared\/stats' %&gt;<\/strong>\r\n<strong>   &lt;% if @users.any? %&gt;<\/strong>\r\n<strong>    &lt;div class=\"user_avatars\"&gt;<\/strong>\r\n<strong>     &lt;% @users.each do |user| %&gt;<\/strong>\r\n<strong>      &lt;%= link_to gravatar_for(user, size: 30), user %&gt;<\/strong>\r\n<strong>   &lt;% end %&gt;<\/strong>\r\n<strong>    &lt;\/div&gt;<\/strong>\r\n<strong>   &lt;% end %&gt;<\/strong>\r\n<strong>  &lt;\/section&gt;<\/strong>\r\n<strong> &lt;\/aside&gt;<\/strong>\r\n<strong>&lt;div class=\"col-md-8\"&gt;<\/strong>\r\n<strong> &lt;h3&gt;&lt;%= @title %&gt;&lt;\/h3&gt;<\/strong>\r\n<strong>  &lt;% if @users.any? %&gt;<\/strong>\r\n<strong>   &lt;ul class=\"users follow\"&gt;<\/strong>\r\n<strong>    &lt;%= render @users %&gt;<\/strong>\r\n<strong>   &lt;\/ul&gt;<\/strong>\r\n<strong>  &lt;%= will_paginate %&gt;<\/strong>\r\n<strong> &lt;% end %&gt;<\/strong>\r\n<strong> &lt;\/div&gt;<\/strong>\r\n<strong>&lt;\/div&gt;<\/strong><\/pre>\n<p>&nbsp;<\/p>\n<p><!--more-->following\u3068followers\u30a2\u30af\u30b7\u30e7\u30f3\u306f\u3001\uff12\u901a\u308a\u306e\u65b9\u6cd5\u3067\u30d3\u30e5\u30fc\u3092\u547c\u3073\u51fa\u3057\u307e\u3059\u3002\u201cfollowing\u201d\u3092\u3068\u304a\u3063\u3066\u63cf\u753b\u3057\u305f\u30d3\u30e5\u30fc\u3092\u2193\u306b\u3001<br \/>\n\u201cfollowers\u201d\u3092\u3068\u304a\u3063\u3066\u63cf\u753b\u3057\u305f\u30d3\u30e5\u30fc\u3092\u2193\u306b\u793a\u3057\u307e\u3059\u3002<br \/>\n\u3053\u306e\u3068\u304d\u3001\u4e0a\u306e\u30b3\u30fc\u30c9\u3067\u306f\u73fe\u5728\u306e\u30e6\u30fc\u30b6\u30fc\u3092\u4e00\u5207\u4f7f\u3063\u3066\u3044\u306a\u3044\u70b9\u306b\u6ce8\u76ee\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u4ed6\u306e\u30e6\u30fc\u30b6\u30fc\u306e\u30d5\u30a9\u30ed\u30ef\u30fc\u4e00\u89a7\u30da\u30fc\u30b8\u3082\u3046\u307e\u304f\u52d5\u304d\u307e\u3059\u3002<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-3128\" src=\"https:\/\/suzutukiblog.com\/wp-content\/uploads\/2021\/04\/51bd022ec83a4416bead8d2ecc0753e1.png\" alt=\"\" width=\"1252\" height=\"860\" srcset=\"https:\/\/suzutukiblog.com\/wp-content\/uploads\/2021\/04\/51bd022ec83a4416bead8d2ecc0753e1.png 1252w, https:\/\/suzutukiblog.com\/wp-content\/uploads\/2021\/04\/51bd022ec83a4416bead8d2ecc0753e1-300x206.png 300w, https:\/\/suzutukiblog.com\/wp-content\/uploads\/2021\/04\/51bd022ec83a4416bead8d2ecc0753e1-1024x703.png 1024w, https:\/\/suzutukiblog.com\/wp-content\/uploads\/2021\/04\/51bd022ec83a4416bead8d2ecc0753e1-768x528.png 768w, https:\/\/suzutukiblog.com\/wp-content\/uploads\/2021\/04\/51bd022ec83a4416bead8d2ecc0753e1-624x429.png 624w\" sizes=\"auto, (max-width: 1252px) 100vw, 1252px\" \/><\/p>\n<p>show_follow\u306e\u63cf\u753b\u7d50\u679c\u3092\u78ba\u8a8d\u3059\u308b\u305f\u3081\u3001\u7d71\u5408\u30c6\u30b9\u30c8\u3092\u66f8\u3044\u3066\u3044\u304d\u307e\u3059\u3002\u305f\u3060\u3057\u4eca\u56de\u306e\u7d71\u5408\u30c6\u30b9\u30c8\u306f\u57fa\u672c\u7684\u306a\u30c6\u30b9\u30c8\u3060\u3051\u306b\u7559\u3081\u3066\u304a\u308a\u3001<br \/>\n\u7db2\u7f85\u7684\u306a\u30c6\u30b9\u30c8\u306b\u306f\u3057\u3066\u3044\u307e\u305b\u3093\u3002<\/p>\n<p>HTML\u69cb\u9020\u3092\u7db2\u7f85\u7684\u306b\u30c1\u30a7\u30c3\u30af\u3059\u308b\u30c6\u30b9\u30c8\u306f\u58ca\u308c\u3084\u3059\u304f\u3001<br \/>\n\u751f\u7523\u6027\u3092\u9006\u306b\u843d\u3068\u3057\u304b\u306d\u306a\u3044\u304b\u3089\u3067\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u4eca\u56de\u306f\u3001<br \/>\n\u6b63\u3057\u3044\u6570\u304c\u8868\u793a\u3055\u308c\u3066\u3044\u308b\u304b\u3069\u3046\u304b\u3068\u3001<br \/>\n\u6b63\u3057\u3044URL\u304c\u8868\u793a\u3055\u308c\u3066\u3044\u308b\u304b\u3069\u3046\u304b\u306e\uff12\u3064\u306e\u30c6\u30b9\u30c8\u3092\u66f8\u304d\u307e\u3059\u3002<\/p>\n<p>\u7d71\u5408\u30c6\u30b9\u30c8\u3092\u751f\u6210\u3059\u308b\u3068\u3053\u308d\u304b\u3089\u59cb\u3081\u307e\u3059\u3002<\/p>\n<pre><strong>rails generate integration_test following<\/strong><\/pre>\n<p>\u30ea\u30ec\u30fc\u30b7\u30e7\u30f3\u30b7\u30c3\u30d7\u7528\u306efixture\u306b\u30c7\u30fc\u30bf\u3092\u8ffd\u52a0\u3057\u307e\u3057\u3087\u3046\u3002<br \/>\n\u6b21\u306e\u3088\u3046\u306b\u66f8\u304f\u3053\u3068\u3067\u3001<\/p>\n<pre><strong>orange:<\/strong>\r\n<strong>content: \"I just ate an orange!\"<\/strong>\r\n<strong>created_at: &lt;%= 10.minutes.ago %&gt;<\/strong>\r\n<strong>user: michael<\/strong><\/pre>\n<p>\u30e6\u30fc\u30b6\u30fc\u3068\u30de\u30a4\u30af\u30ed\u30dd\u30b9\u30c8\u3092\u95a2\u9023\u4ed8\u3051\u3067\u304d\u305f\u3053\u3068\u3092\u601d\u3044\u51fa\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u4e0a\u306e\u30b3\u30fc\u30c9\u3067\u306f\u30e6\u30fc\u30b6\u30fc\u540d\u3092\u6b21\u306e\u3088\u3046\u306b\u66f8\u3044\u3066\u3044\u307e\u3059\u304c\u3001<\/p>\n<p>user: michael<\/p>\n<p>\u3053\u308c\u306f\u5185\u90e8\u7684\u306b\u306f\u6b21\u306e\u3088\u3046\u306a\u30b3\u30fc\u30c9\u306b\u81ea\u52d5\u7684\u306b\u5909\u63db\u3055\u308c\u307e\u3059\u3002<\/p>\n<p>user_id: 1<\/p>\n<p>\u3053\u306e\u4f8b\u3092\u53c2\u8003\u306b\u3057\u3066Relationship\u7528\u306efixture\u306b\u30c6\u30b9\u30c8\u30c7\u30fc\u30bf\u3092\u8ffd\u52a0\u3059\u308b\u3068\u3001<br \/>\n\u2193\u306e\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>following\/follower\u3092\u30c6\u30b9\u30c8\u3059\u308b\u305f\u3081\u306e\u30ea\u30ec\u30fc\u30b7\u30e7\u30f3\u30b7\u30c3\u30d7\u7528fixture<br \/>\n<strong>test\/fixtures\/relationships.yml<\/strong><\/p>\n<pre><strong>one:<\/strong>\r\n<strong>follower: michael<\/strong>\r\n<strong>followed: lana<\/strong>\r\n\r\n<strong>two:<\/strong>\r\n<strong>follower: michael<\/strong>\r\n<strong>followed: malory<\/strong>\r\n\r\n<strong>three:<\/strong>\r\n<strong>follower: lana<\/strong>\r\n<strong>followed: michael<\/strong>\r\n\r\n<strong>four:<\/strong>\r\n<strong>follower: archer<\/strong>\r\n<strong>followed: michael<\/strong><\/pre>\n<p>\u524d\u534a\u306e2\u3064\u3067Michael\u304cLana\u3068Malory\u3092\u30d5\u30a9\u30ed\u30fc\u3057\u3001\u5f8c\u534a\u306e2\u3064\u3067Lana\u3068Archer\u304cMichael\u3092\u30d5\u30a9\u30ed\u30fc\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<p>\u3042\u3068\u306f\u3001\u6b63\u3057\u3044\u6570\u304b\u3069\u3046\u304b\u3092\u78ba\u8a8d\u3059\u308b\u305f\u3081\u306b\u3001assert_match\u30e1\u30bd\u30c3\u30c9\u3092\u4f7f\u3063\u3066\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u753b\u9762\u306e\u30de\u30a4\u30af\u30ed\u30dd\u30b9\u30c8\u6570\u3092\u30c6\u30b9\u30c8\u3057\u307e\u3059\u3002<br \/>\n\u3055\u3089\u306b\u3001\u6b63\u3057\u3044URL\u304b\u3069\u3046\u304b\u3092\u30c6\u30b9\u30c8\u3059\u308b\u30b3\u30fc\u30c9\u3082\u52a0\u3048\u308b\u3068\u3001<\/p>\n<p>following\/follower\u30da\u30fc\u30b8\u306e\u30c6\u30b9\u30c8 green<br \/>\n<strong>test\/integration\/following_test.rb<\/strong><\/p>\n<pre><strong>require 'test_helper'<\/strong>\r\n\r\n<strong>class FollowingTest &lt; ActionDispatch::IntegrationTest<\/strong>\r\n\r\n<strong> def setup<\/strong>\r\n<strong>   @user = users(:michael)<\/strong>\r\n<strong>   log_in_as(@user)<\/strong>\r\n<strong> end<\/strong>\r\n\r\n<strong>test \"following page\" do<\/strong>\r\n<strong> get following_user_path(@user)<\/strong>\r\n<strong> assert_not @user.following.empty?<\/strong>\r\n<strong> assert_match @user.following.count.to_s, response.body<\/strong>\r\n<strong> @user.following.each do |user|<\/strong>\r\n<strong>  assert_select \"a[href=?]\", user_path(user)<\/strong>\r\n<strong> end<\/strong>\r\n<strong>end<\/strong>\r\n\r\n<strong>test \"followers page\" do<\/strong>\r\n<strong> get followers_user_path(@user)<\/strong>\r\n<strong> assert_not @user.followers.empty?<\/strong>\r\n<strong> assert_match @user.followers.count.to_s, response.body<\/strong>\r\n<strong>  @user.followers.each do |user|<\/strong>\r\n<strong>   assert_select \"a[href=?]\", user_path(user)<\/strong>\r\n<strong>  end<\/strong>\r\n<strong> end<\/strong>\r\n<strong>end<\/strong><\/pre>\n<pre><strong>assert_not @user.following.empty?<\/strong><\/pre>\n<p>\u3053\u306e\u30b3\u30fc\u30c9\u306f\u6b21\u306e\u30b3\u30fc\u30c9\u3092\u78ba\u304b\u3081\u308b\u305f\u3081\u306e\u30c6\u30b9\u30c8\u3067\u3042\u3063\u3066\u3001<\/p>\n<pre><strong>@user.following.each do |user|<\/strong>\r\n<strong>  assert_select \"a[href=?]\", user_path(user)<\/strong>\r\n<strong>end<\/strong><\/pre>\n<p>\u3082\u3057@user.following.empty?\u306e\u7d50\u679c\u304ctrue\u3067\u3042\u308c\u3070\u3001<br \/>\nassert_select\u5185\u306e\u30d6\u30ed\u30c3\u30af\u304c\u5b9f\u884c\u3055\u308c\u306a\u304f\u306a\u308b\u305f\u3081\u3001<br \/>\n\u305d\u306e\u5834\u5408\u306b\u304a\u3044\u3066\u30c6\u30b9\u30c8\u304c\u9069\u5207\u306a\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30e2\u30c7\u30eb\u3092\u78ba\u8a8d\u3067\u304d\u306a\u304f\u306a\u308b\u3053\u3068\u3092\u9632\u3044\u3067\u3044\u307e\u3059\u3002<\/p>\n<h3>\u6f14\u7fd2<\/h3>\n<p>1:\u30d6\u30e9\u30a6\u30b6\u304b\u3089 \/users\/1\/followers \u3068 \/users\/1\/following \u3092\u958b\u304d\u3001<br \/>\n\u305d\u308c\u305e\u308c\u304c\u9069\u5207\u306b\u8868\u793a\u3055\u308c\u3066\u3044\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002<br \/>\n\u30b5\u30a4\u30c9\u30d0\u30fc\u306b\u3042\u308b\u753b\u50cf\u306f\u3001\u30ea\u30f3\u30af\u3068\u3057\u3066\u3046\u307e\u304f\u6a5f\u80fd\u3057\u3066\u3044\u308b\u3067\u3057\u3087\u3046\u304b?<\/p>\n<p>\u6a5f\u80fd\u3057\u3066\u3044\u308b<\/p>\n<p>2:assert_select\u306b\u95a2\u9023\u3059\u308b\u30b3\u30fc\u30c9\u3092\u30b3\u30e1\u30f3\u30c8\u30a2\u30a6\u30c8\u3057\u3066\u307f\u3066\u3001<br \/>\n\u30c6\u30b9\u30c8\u304c\u6b63\u3057\u304f red \u306b\u5909\u308f\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002<\/p>\n<pre><strong>#\u4ee5\u4e0b\u3092\u30b3\u30e1\u30f3\u30c8\u30a2\u30a6\u30c8\u3059\u308b\r\n&lt;% @users.each do |user| %&gt;<\/strong>\r\n<strong>&lt;%= #link_to gravatar_for(user, size: 30), user %&gt;<\/strong>\r\n<strong>&lt;% end %&gt;<\/strong><\/pre>\n<h3>14.2.4 [Follow] \u30dc\u30bf\u30f3 (\u57fa\u672c\u7de8)<\/h3>\n<p>\u30d3\u30e5\u30fc\u304c\u6574\u3063\u3066\u304d\u307e\u3057\u305f\u3002\u3044\u3088\u3044\u3088 [Follow] \/ [Unfollow] \u30dc\u30bf\u30f3\u3092<br \/>\n\u52d5\u4f5c\u3055\u305b\u307e\u3057\u3087\u3046\u3002\u30d5\u30a9\u30ed\u30fc\u3068\u30d5\u30a9\u30ed\u30fc\u89e3\u9664\u306f\u305d\u308c\u305e\u308c\u30ea\u30ec\u30fc\u30b7\u30e7\u30f3\u30b7\u30c3\u30d7\u306e\u4f5c\u6210\u3068\u524a\u9664\u306b\u5bfe\u5fdc\u3057\u3066\u3044\u308b\u305f\u3081\u3001\u307e\u305a\u306fRelationships\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u304c\u5fc5\u8981\u3067\u3059\u3002<br \/>\n\u3044\u3064\u3082\u306e\u3088\u3046\u306b\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u3092\u751f\u6210\u3057\u307e\u3057\u3087\u3046\u3002<\/p>\n<pre><strong>rails generate controller Relationships<\/strong><\/pre>\n<p>Relationships\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u3067\u30a2\u30af\u30bb\u30b9\u5236\u5fa1\u3059\u308b\u3053\u3068\u306f<br \/>\n\u305d\u3053\u307e\u3067\u96e3\u3057\u304f\u3042\u308a\u307e\u305b\u3093\u3002\u3057\u304b\u3057\u3001\u524d\u56de\u306e\u30a2\u30af\u30bb\u30b9\u5236\u5fa1\u306e\u3068\u304d\u3068\u540c\u69d8\u306b\u6700\u521d\u306b\u30c6\u30b9\u30c8\u3092\u66f8\u304d\u3001<br \/>\n\u305d\u308c\u3092\u30d1\u30b9\u3059\u308b\u3088\u3046\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u3067\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30e2\u30c7\u30eb\u3092<br \/>\n\u78ba\u7acb\u3055\u305b\u3066\u3044\u304d\u307e\u3057\u3087\u3046\u3002<\/p>\n<p>\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u3068\u304d\u3001<br \/>\n\u30ed\u30b0\u30a4\u30f3\u6e08\u307f\u306e\u30e6\u30fc\u30b6\u30fc\u3067\u3042\u308b\u304b\u3069\u3046\u304b\u3092\u30c1\u30a7\u30c3\u30af\u3057\u307e\u3059\u3002<br \/>\n\u3082\u3057\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u3044\u306a\u3051\u308c\u3070\u30ed\u30b0\u30a4\u30f3\u30da\u30fc\u30b8\u306b\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u3055\u308c\u308b\u306e\u3067\u3001Relationship\u306e\u30ab\u30a6\u30f3\u30c8\u304c\u5909\u308f\u3063\u3066\u3044\u306a\u3044\u3053\u3068\u3092\u78ba\u8a8d\u3057\u307e\u3059<\/p>\n<p>\u30ea\u30ec\u30fc\u30b7\u30e7\u30f3\u30b7\u30c3\u30d7\u306e\u57fa\u672c\u7684\u306a\u30a2\u30af\u30bb\u30b9\u5236\u5fa1\u306b\u5bfe\u3059\u308b\u30c6\u30b9\u30c8<br \/>\n<strong>test\/controllers\/relationships_controller_test.rb<\/strong><\/p>\n<pre><strong><span class=\"nb\">require<\/span> <span class=\"s1\">'test_helper'<\/span>\r\n\r\n<span class=\"k\">class<\/span> <span class=\"nc\">RelationshipsControllerTest<\/span> <span class=\"o\">&lt;<\/span> <span class=\"no\">ActionDispatch<\/span><span class=\"o\">::<\/span><span class=\"no\">IntegrationTest<\/span>\r\n\r\n  <span class=\"nb\">test<\/span> <span class=\"s2\">\"create should require logged-in user\"<\/span> <span class=\"k\">do<\/span>\r\n    <span class=\"n\">assert_no_difference<\/span> <span class=\"s1\">'Relationship.count'<\/span> <span class=\"k\">do<\/span>\r\n      <span class=\"n\">post<\/span> <span class=\"n\">relationships_path<\/span>\r\n    <span class=\"k\">end<\/span>\r\n    <span class=\"n\">assert_redirected_to<\/span> <span class=\"n\">login_url<\/span>\r\n  <span class=\"k\">end<\/span>\r\n\r\n  <span class=\"nb\">test<\/span> <span class=\"s2\">\"destroy should require logged-in user\"<\/span> <span class=\"k\">do<\/span>\r\n    <span class=\"n\">assert_no_difference<\/span> <span class=\"s1\">'Relationship.count'<\/span> <span class=\"k\">do<\/span>\r\n      <span class=\"n\">delete<\/span> <span class=\"n\">relationship_path<\/span><span class=\"p\">(<\/span><span class=\"n\">relationships<\/span><span class=\"p\">(<\/span><span class=\"ss\">:one<\/span><span class=\"p\">))<\/span>\r\n    <span class=\"k\">end<\/span>\r\n    <span class=\"n\">assert_redirected_to<\/span> <span class=\"n\">login_url<\/span>\r\n  <span class=\"k\">end<\/span>\r\n<span class=\"k\">end<\/span><\/strong><\/pre>\n<p>\u30c6\u30b9\u30c8\u3092\u30d1\u30b9\u3055\u305b\u308b\u305f\u3081\u306b\u3001logged_in_user\u30d5\u30a3\u30eb\u30bf\u30fc\u3092<br \/>\nRelationships\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u306b\u5bfe\u3057\u3066\u8ffd\u52a0\u3057\u307e\u3059<\/p>\n<p>\u30ea\u30ec\u30fc\u30b7\u30e7\u30f3\u30b7\u30c3\u30d7\u306e\u30a2\u30af\u30bb\u30b9\u5236\u5fa1<br \/>\n<strong>app\/controllers\/relationships_controller.rb<\/strong><\/p>\n<pre><strong><span class=\"k\">class<\/span> <span class=\"nc\">RelationshipsController<\/span> <span class=\"o\">&lt;<\/span> <span class=\"no\">ApplicationController<\/span>\r\n  <span class=\"n\">before_action<\/span> <span class=\"ss\">:logged_in_user<\/span>\r\n\r\n  <span class=\"k\">def<\/span> <span class=\"nf\">create<\/span>\r\n  <span class=\"k\">end<\/span>\r\n\r\n  <span class=\"k\">def<\/span> <span class=\"nf\">destroy<\/span>\r\n  <span class=\"k\">end<\/span>\r\n<span class=\"k\">end<\/span><\/strong><\/pre>\n<p>[Follow] \/ [Unfollow] \u30dc\u30bf\u30f3\u3092\u52d5\u4f5c\u3055\u305b\u308b\u305f\u3081\u306b\u306f\u3001<br \/>\n\u30d5\u30a9\u30fc\u30e0\u304b\u3089\u9001\u4fe1\u3055\u308c\u305f\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u4f7f\u3063\u3066\u3001<br \/>\nfollowed_id\u306b\u5bfe\u5fdc\u3059\u308b\u30e6\u30fc\u30b6\u30fc\u3092\u898b\u3064\u3051\u3066\u304f\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<br \/>\n\u305d\u306e\u5f8c\u3001\u898b\u3064\u3051\u3066\u304d\u305f\u30e6\u30fc\u30b6\u30fc\u306b\u5bfe\u3057\u3066\u9069\u5207\u306bfollow\/unfollow\u30e1\u30bd\u30c3\u30c9\u3092\u4f7f\u3044\u307e\u3059\u3002<\/p>\n<div class=\"heading\"><span class=\"description\">Relationships\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9<strong><code class=\"filepath\">app\/controllers\/relationships_controller.rb<\/code><\/strong><\/span><\/div>\n<div class=\"code\">\n<div class=\"highlight\">\n<pre><strong><span class=\"k\">class<\/span> <span class=\"nc\">RelationshipsController<\/span> <span class=\"o\">&lt;<\/span> <span class=\"no\">ApplicationController<\/span>\r\n  <span class=\"n\">before_action<\/span> <span class=\"ss\">:logged_in_user<\/span>\r\n\r\n  <span class=\"k\">def<\/span> <span class=\"nf\">create<\/span>\r\n<span class=\"hll\">    <span class=\"n\">user<\/span> <span class=\"o\">=<\/span> <span class=\"no\">User<\/span><span class=\"o\">.<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">params<\/span><span class=\"o\">[<\/span><span class=\"ss\">:followed_id<\/span><span class=\"o\">]<\/span><span class=\"p\">)<\/span>\r\n<\/span><span class=\"hll\">    <span class=\"n\">current_user<\/span><span class=\"o\">.<\/span><span class=\"n\">follow<\/span><span class=\"p\">(<\/span><span class=\"n\">user<\/span><span class=\"p\">)<\/span>\r\n<\/span><span class=\"hll\">    <span class=\"n\">redirect_to<\/span> <span class=\"n\">user<\/span>\r\n<\/span>  <span class=\"k\">end<\/span>\r\n\r\n  <span class=\"k\">def<\/span> <span class=\"nf\">destroy<\/span>\r\n<span class=\"hll\">    <span class=\"n\">user<\/span> <span class=\"o\">=<\/span> <span class=\"no\">Relationship<\/span><span class=\"o\">.<\/span><span class=\"n\">find<\/span><span class=\"p\">(<\/span><span class=\"n\">params<\/span><span class=\"o\">[<\/span><span class=\"ss\">:id<\/span><span class=\"o\">]<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">followed<\/span>\r\n<\/span><span class=\"hll\">    <span class=\"n\">current_user<\/span><span class=\"o\">.<\/span><span class=\"n\">unfollow<\/span><span class=\"p\">(<\/span><span class=\"n\">user<\/span><span class=\"p\">)<\/span>\r\n<\/span><span class=\"hll\">    <span class=\"n\">redirect_to<\/span> <span class=\"n\">user<\/span>\r\n<\/span>  <span class=\"k\">end<\/span>\r\n<span class=\"k\">end<\/span><\/strong><\/pre>\n<\/div>\n<\/div>\n<p>\u3082\u3057\u30ed\u30b0\u30a4\u30f3\u3057\u3066\u3044\u306a\u3044\u30e6\u30fc\u30b6\u30fc\u304c (<code>curl<\/code>\u306a\u3069\u306e\u30b3\u30de\u30f3\u30c9\u30e9\u30a4\u30f3\u30c4\u30fc\u30eb\u306a\u3069\u3092\u4f7f\u3063\u3066) \u3053\u308c\u3089\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u306b\u76f4\u63a5\u30a2\u30af\u30bb\u30b9\u3059\u308b\u3088\u3046\u306a\u3053\u3068\u304c\u3042\u308c\u3070\u3001current_user\u306fnil\u306b\u306a\u308a\u3001\u3069\u3061\u3089\u306e\u30e1\u30bd\u30c3\u30c9\u3067\u30822\u884c\u76ee\u3067\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3059\u3002<br \/>\n\u30a8\u30e9\u30fc\u306b\u306f\u306a\u308a\u307e\u3059\u304c\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3084\u30c7\u30fc\u30bf\u306b\u5f71\u97ff\u306f\u751f\u3058\u307e\u305b\u3093\u3002<\/p>\n<h3>\u6f14\u7fd2<\/h3>\n<p>1:\u30d6\u30e9\u30a6\u30b6\u4e0a\u304b\u3089 \/users\/2 \u3092\u958b\u304d\u3001[Follow] \u3068 [Unfollow] \u3092\u5b9f\u884c\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002\u3046\u307e\u304f\u6a5f\u80fd\u3057\u3066\u3044\u308b\u3067\u3057\u3087\u3046\u304b?<\/p>\n<p>OK<\/p>\n<p>2:\u5148\u307b\u3069\u306e\u6f14\u7fd2\u3092\u7d42\u3048\u305f\u3089\u3001Rails\u30b5\u30fc\u30d0\u30fc\u306e\u30ed\u30b0\u3092\u898b\u3066\u307f\u307e\u3057\u3087\u3046\u3002<br \/>\n\u30d5\u30a9\u30ed\u30fc\/\u30d5\u30a9\u30ed\u30fc\u89e3\u9664\u304c\u5b9f\u884c\u3055\u308c\u308b\u3068\u3001<br \/>\n\u305d\u308c\u305e\u308c\u3069\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u304c\u63cf\u753b\u3055\u308c\u3066\u3044\u308b\u3067\u3057\u3087\u3046\u304b?<\/p>\n<p>\u4e21\u65b9\u3068\u3082 &#8220;\/users\/2&#8243;\u304c\u63cf\u753b\u3055\u308c\u3066\u3044\u308b<\/p>\n<h3>14.2.5 [Follow] \u30dc\u30bf\u30f3 (Ajax\u7de8)<\/h3>\n<p>Relationships\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u306ecreate\u30a2\u30af\u30b7\u30e7\u30f3\u3068destroy\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u5358\u306b\u5143\u306e\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u306b\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u3057\u3066\u3044\u307e\u3057\u305f\u3002<br \/>\n\u3064\u307e\u308a\u3001\u30e6\u30fc\u30b6\u30fc\u306f\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u30da\u30fc\u30b8\u3092\u6700\u521d\u306b\u8868\u793a\u3057\u3001<br \/>\n\u305d\u308c\u304b\u3089\u30e6\u30fc\u30b6\u30fc\u3092\u30d5\u30a9\u30ed\u30fc\u3057\u3001\u305d\u306e\u5f8c\u3059\u3050\u5143\u306e\u30da\u30fc\u30b8\u306b\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u3055\u308c\u308b\u3068\u3044\u3046\u6d41\u308c\u306b\u306a\u308a\u307e\u3059\u3002<br \/>\n\u30e6\u30fc\u30b6\u30fc\u3092\u30d5\u30a9\u30ed\u30fc\u3057\u305f\u5f8c\u3001\u672c\u5f53\u306b\u305d\u306e\u30da\u30fc\u30b8\u304b\u3089\u96e2\u308c\u3066\u5143\u306e\u30da\u30fc\u30b8\u306b\u623b\u3089\u306a\u3044\u3068\u3044\u3051\u306a\u3044\u306e\u3067\u3057\u3087\u3046\u304b\u3002\u3053\u306e\u70b9\u3092\u8003\u3048\u306a\u304a\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002<\/p>\n<p>Ajax\u3092\u4f7f\u3046\u3053\u3068\u3067\u89e3\u6c7a\u3067\u304d\u307e\u3059\u3002Ajax\u3092\u4f7f\u3048\u3070\u3001<br \/>\nWeb\u30da\u30fc\u30b8\u304b\u3089\u30b5\u30fc\u30d0\u30fc\u306b\u300c\u975e\u540c\u671f\u300d\u3067\u3001<br \/>\n\u30da\u30fc\u30b8\u3092\u79fb\u52d5\u3059\u308b\u3053\u3068\u306a\u304f\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u9001\u4fe1\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<br \/>\nWeb\u30d5\u30a9\u30fc\u30e0\u306bAjax\u3092\u63a1\u7528\u3059\u308b\u306e\u306f\u4eca\u3084\u5f53\u305f\u308a\u524d\u306b\u306a\u308a\u3064\u3064\u3042\u308b\u306e\u3067\u3001Rails\u3067\u3082Ajax\u3092\u7c21\u5358\u306b\u5b9f\u88c5\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002<br \/>\n\u30d5\u30a9\u30ed\u30fc\u7528\u3068\u30d5\u30a9\u30ed\u30fc\u89e3\u9664\u7528\u306e\u30d1\u30fc\u30b7\u30e3\u30eb\u3092\u3053\u308c\u306b\u6cbf\u3063\u3066\u66f4\u65b0\u3059\u308b\u306e\u306f\u7c21\u5358\u3067\u3059\u3002<br \/>\n\u6b21\u306e\u30b3\u30fc\u30c9\u304c\u3042\u308b\u3068\u3059\u308b\u3068\u3001<\/p>\n<pre><strong>form_for<\/strong><\/pre>\n<p>\u4e0a\u306e\u30b3\u30fc\u30c9\u3092\u6b21\u306e\u3088\u3046\u306b\u7f6e\u304d\u63db\u3048\u308b\u3060\u3051\u3067\u3059\u3002<\/p>\n<pre><strong>form_for ..., remote: true<\/strong><\/pre>\n<p>\u305f\u3063\u305f\u3053\u308c\u3060\u3051\u3067\u3001Rails\u306f\u81ea\u52d5\u7684\u306bAjax\u3092\u4f7f\u3046\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002<br \/>\n\u5177\u4f53\u7684\u306a\u66f4\u65b0\u306e\u7d50\u679c\u3092\u3001\u2193\u306b\u793a\u3057\u307e\u3059\u3002<\/p>\n<p>Ajax\u3092\u4f7f\u3063\u305f\u30d5\u30a9\u30ed\u30fc\u30d5\u30a9\u30fc\u30e0<\/p>\n<p><strong><code class=\"filepath\">app\/views\/users\/_follow.html.erb<\/code><\/strong><\/p>\n<pre><strong>&lt;%= form_for(current_user.active_relationships.build, <\/strong><strong>remote: true) do |f| %&gt;<\/strong><\/pre>\n<div class=\"heading\"><span class=\"description\">Ajax\u3092\u4f7f\u3063\u305f\u30d5\u30a9\u30ed\u30fc\u89e3\u9664\u30d5\u30a9\u30fc\u30e0<strong><code class=\"filepath\">app\/views\/users\/_unfollow.html.erb<\/code><\/strong><\/span><\/div>\n<div class=\"code\">\n<div class=\"highlight\">\n<pre><strong><span class=\"cp\">&lt;%=<\/span> <span class=\"n\">form_for<\/span><span class=\"p\">(<\/span><span class=\"n\">current_user<\/span><span class=\"o\">.<\/span><span class=\"n\">active_relationships<\/span><span class=\"o\">.<\/span><span class=\"n\">find_by<\/span><span class=\"p\">(<\/span><span class=\"ss\">followed_id<\/span><span class=\"p\">:<\/span> <span class=\"vi\">@user<\/span><span class=\"o\">.<\/span><span class=\"n\">id<\/span><span class=\"p\">),<\/span>\r\n             <span class=\"ss\">html<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"nb\">method<\/span><span class=\"p\">:<\/span> <span class=\"ss\">:delete<\/span> <span class=\"p\">}, <\/span><span class=\"hll\"><span class=\"ss\">remote<\/span><span class=\"p\">:<\/span> <span class=\"kp\">true<\/span><span class=\"p\">)<\/span> <span class=\"k\">do<\/span> <span class=\"o\">|<\/span><span class=\"n\">f<\/span><span class=\"o\">|<\/span> <span class=\"cp\">%&gt;<\/span>\r\n<\/span>  <span class=\"cp\">&lt;%=<\/span> <span class=\"n\">f<\/span><span class=\"o\">.<\/span><span class=\"n\">submit<\/span> <span class=\"s2\">\"Unfollow\"<\/span><span class=\"p\">,<\/span> <span class=\"ss\">class<\/span><span class=\"p\">:<\/span> <span class=\"s2\">\"btn\"<\/span> <span class=\"cp\">%&gt;<\/span>\r\n<span class=\"cp\">&lt;%<\/span> <span class=\"k\">end<\/span> <span class=\"cp\">%&gt;<\/span><\/strong><\/pre>\n<\/div>\n<\/div>\n<p>ERb\u306b\u3088\u3063\u3066\u5b9f\u969b\u306b\u751f\u6210\u3055\u308c\u308bHTML<\/p>\n<pre><strong>&lt;form action=\"\/relationships\/117\"<\/strong>\r\n<strong>class=\"edit_relationship\" data-remote=\"true\"<\/strong>\r\n<strong>id=\"edit_relationship_117\" method=\"post\"&gt;<\/strong>\r\n<strong>.<\/strong>\r\n<strong>.\r\n.\r\n<span class=\"p\">&lt;\/<\/span><span class=\"nt\">form<\/span><span class=\"p\">&gt;<\/span><\/strong><\/pre>\n<p>form\u30bf\u30b0\u306e\u5185\u90e8\u3067data-remote=&#8221;true&#8221;\u3092\u8a2d\u5b9a\u3057\u3066\u3044\u307e\u3059\u3002<br \/>\n\u3053\u308c\u306f\u3001JavaScript\u306b\u3088\u308b\u30d5\u30a9\u30fc\u30e0\u64cd\u4f5c\u3092\u8a31\u53ef\u3059\u308b\u3053\u3068\u3092Rails\u306b\u77e5\u3089\u305b\u308b\u305f\u3081\u306e\u3082\u306e\u3067\u3059\u3002<br \/>\nRails 2\u4ee5\u524d\u3067\u306f\u3001\u5b8c\u5168\u306aJavaScript\u306e\u30b3\u30fc\u30c9\u3092\u633f\u5165\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3057\u305f\u3002<br \/>\n\u3057\u304b\u3057\u5148\u307b\u3069\u306e\u4f8b\u3067\u898b\u305f\u3088\u3046\u306b\u3001\u73fe\u5728\u306eRails\u3067\u306fHTML\u30d7\u30ed\u30d1\u30c6\u30a3\u3092\u4f7f\u3063\u3066\u7c21\u5358\u306bAjax\u304c\u6271\u3048\u308b\u3088\u3046\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002<br \/>\n\u3053\u308c\u306f\u3001JavaScript\u3092\u524d\u9762\u306b\u51fa\u3059\u3079\u304b\u3089\u305a\u3068\u3044\u3046\u54f2\u5b66\u306b\u5f93\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n<p>\u30d5\u30a9\u30fc\u30e0\u306e\u66f4\u65b0\u304c\u7d42\u308f\u3063\u305f\u306e\u3067\u3001<br \/>\n\u4eca\u5ea6\u306f\u3053\u308c\u306b\u5bfe\u5fdc\u3059\u308bRelationships\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u3092\u6539\u9020\u3057\u3066\u3001<br \/>\nAjax\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5fdc\u7b54\u3067\u304d\u308b\u3088\u3046\u306b\u3057\u307e\u3057\u3087\u3046\u3002<br \/>\n\u3053\u3046\u3044\u3063\u305f\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u7a2e\u985e\u306b\u3088\u3063\u3066\u5fdc\u7b54\u3092\u5834\u5408\u5206\u3051\u3059\u308b\u3068\u304d\u306f\u3001<br \/>\nrespond_to\u30e1\u30bd\u30c3\u30c9\u3068\u3044\u3046\u30e1\u30bd\u30c3\u30c9\u3092\u4f7f\u3044\u307e\u3059\u3002<\/p>\n<pre><strong>respond_to do |format|<\/strong>\r\n<strong>  format.html { redirect_to user }<\/strong>\r\n<strong>  format.js<\/strong>\r\n<strong>end<\/strong><\/pre>\n<p>\u4e0a\u306e(\u30d6\u30ed\u30c3\u30af\u5185\u306e)\u30b3\u30fc\u30c9\u306e\u3046\u3061\u3001<br \/>\n\u3044\u305a\u308c\u304b\u306e1\u884c\u304c\u5b9f\u884c\u3055\u308c\u308b\u3068\u3044\u3046\u70b9\u304c\u91cd\u8981\u3067\u3059<br \/>\n(\u3053\u306e\u305f\u3081respond_to\u30e1\u30bd\u30c3\u30c9\u306f\u3001\u4e0a\u304b\u3089\u9806\u306b\u5b9f\u884c\u3059\u308b\u9010\u6b21\u51e6\u7406\u3068\u3044\u3046\u3088\u308a\u3001if\u6587\u3092\u4f7f\u3063\u305f\u5206\u5c90\u51e6\u7406\u306b\u8fd1\u3044\u30a4\u30e1\u30fc\u30b8\u3067\u3059)\u3002<\/p>\n<p>Relationships\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u3067Ajax\u306b\u5bfe\u5fdc\u3055\u305b\u308b\u305f\u3081\u306b\u3001<br \/>\nrespond_to\u30e1\u30bd\u30c3\u30c9\u3092create\u30a2\u30af\u30b7\u30e7\u30f3\u3068destroy\u30a2\u30af\u30b7\u30e7\u30f3<br \/>\n\u306b\u305d\u308c\u305e\u308c\u8ffd\u52a0\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002<\/p>\n<p>\u5909\u66f4\u306e\u7d50\u679c\u3092\u2193\u306b\u793a\u3057\u307e\u3059\u3002\u3053\u306e\u3068\u304d\u3001\u30e6\u30fc\u30b6\u30fc\u306e\u30ed\u30fc\u30ab\u30eb\u5909\u6570 (user)\u3092\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5909\u6570 (@user) \u306b\u5909\u66f4\u3057\u305f\u70b9\u306b\u6ce8\u76ee\u3057\u3066\u304f\u3060\u3055\u3044\u3002<br \/>\n\u3053\u308c\u306f\u3001\u524d\u306e\u3068\u304d\u306f\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5909\u6570\u306f\u5fc5\u8981\u306a\u304b\u3063\u305f\u306e\u3067\u3059\u304c\u3001<br \/>\n\u5148\u7a0b\u306eAjax\u3092\u5b9f\u88c5\u3057\u305f\u3053\u3068\u306b\u3088\u308a\u3001\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5909\u6570\u304c\u5fc5\u8981\u306b\u306a\u3063\u305f\u305f\u3081\u3067\u3059\u3002<\/p>\n<p>Relationships\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u3067Ajax\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5bfe\u5fdc\u3059\u308b<br \/>\n<strong>app\/controllers\/relationships_controller.rb<\/strong><\/p>\n<pre><strong>class RelationshipsController &lt; ApplicationController<\/strong>\r\n<strong>before_action :logged_in_user<\/strong>\r\n\r\n<strong> def create<\/strong>\r\n<strong>   @user = User.find(params[:followed_id])<\/strong>\r\n<strong>   current_user.follow(@user)<\/strong>\r\n<strong>   respond_to do |format|<\/strong>\r\n<strong>   format.html { redirect_to @user }<\/strong>\r\n<strong>   format.js<\/strong>\r\n<strong>   end<\/strong>\r\n<strong> end<\/strong>\r\n\r\n<strong> def destroy<\/strong>\r\n<strong>   @user = Relationship.find(params[:id]).followed<\/strong>\r\n<strong>   current_user.unfollow(@user)<\/strong>\r\n<strong>   respond_to do |format|<\/strong>\r\n<strong>   format.html { redirect_to @user }<\/strong>\r\n<strong>   format.js<\/strong>\r\n<strong>   end<\/strong>\r\n<strong> end<\/strong>\r\n<strong>end<\/strong><\/pre>\n<p>(\u30d3\u30e5\u30fc\u3067\u5909\u6570\u3092\u4f7f\u3046\u305f\u3081\u3001user\u304c@user\u306b\u5909\u308f\u3063\u305f\u70b9\u306b\u3082\u6c17\u3092\u3064\u3051\u3066\u304f\u3060\u3055\u3044\u3002)<\/p>\n<p>Ajax\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5bfe\u5fdc\u3057\u305f\u306e\u3067\u3001\u4eca\u5ea6\u306f\u30d6\u30e9\u30a6\u30b6\u5074\u3067JavaScript\u304c<br \/>\n\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u305f\u5834\u5408(Ajax\u30ea\u30af\u30a8\u30b9\u30c8\u304c\u9001\u308c\u306a\u3044\u5834\u5408)<br \/>\n\u3067\u3082\u3046\u307e\u304f\u52d5\u304f\u3088\u3046\u306b\u3057\u307e\u3059\u3002<\/p>\n<p>JavaScript\u304c\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u305f\u3068\u304d\u306e\u305f\u3081\u306e\u8a2d\u5b9a<br \/>\n<strong>config\/application.rb<\/strong><\/p>\n<pre><strong>require File.expand_path('..\/boot', __FILE__)<\/strong>\r\n<strong>.<\/strong>\r\n<strong>.<\/strong>\r\n<strong>.<\/strong>\r\n<strong>module SampleApp<\/strong>\r\n<strong> class Application &lt; Rails::Application<\/strong>\r\n<strong>.<\/strong>\r\n<strong>.<\/strong>\r\n<strong>.<\/strong>\r\n<strong># \u8a8d\u8a3c\u30c8\u30fc\u30af\u30f3\u3092remote\u30d5\u30a9\u30fc\u30e0\u306b\u57cb\u3081\u8fbc\u3080<\/strong>\r\n<strong>  config.action_view.embed_authenticity_token_in_remote_forms = true<\/strong>\r\n<strong> end<\/strong>\r\n<strong>end<\/strong><\/pre>\n<p>\u4e00\u65b9\u3067\u3001JavaScript\u304c\u6709\u52b9\u306b\u306a\u3063\u3066\u3044\u3066\u3082\u3001<br \/>\n\u307e\u3060\u5341\u5206\u306b\u5bfe\u5fdc\u3067\u304d\u3066\u3044\u306a\u3044\u90e8\u5206\u304c\u3042\u308a\u307e\u3059\u3002\u3068\u3044\u3046\u306e\u3082\u3001<br \/>\nAjax\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u53d7\u4fe1\u3057\u305f\u5834\u5408\u306f\u3001<br \/>\nRails\u304c\u81ea\u52d5\u7684\u306b\u30a2\u30af\u30b7\u30e7\u30f3\u3068\u540c\u3058\u540d\u524d\u3092\u6301\u3064JavaScript\u7528\u306e<br \/>\n\u57cb\u3081\u8fbc\u307fRuby(.js.erb)\u30d5\u30a1\u30a4\u30eb(create.js.erb\u3084destroy.js.erb\u306a\u3069)\u3092<br \/>\n\u547c\u3073\u51fa\u3059\u304b\u3089\u3067\u3059\u3002<\/p>\n<p>\u3053\u308c\u3089\u306e\u30d5\u30a1\u30a4\u30eb\u3067\u306fJavaScript\u3068ERb \u3092\u30df\u30c3\u30af\u30b9\u3057\u3066\u73fe\u5728\u306e\u30da\u30fc\u30b8\u306b\u5bfe\u3059\u308b\u30a2\u30af\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<br \/>\n\u30e6\u30fc\u30b6\u30fc\u3092\u30d5\u30a9\u30ed\u30fc\u3057\u305f\u3068\u304d\u3084\u3001\u30d5\u30a9\u30ed\u30fc\u89e3\u9664\u3057\u305f\u3068\u304d\u306b\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u30da\u30fc\u30b8\u3092\u66f4\u65b0\u3059\u308b\u305f\u3081\u306b\u3001<br \/>\n\u79c1\u305f\u3061\u304c\u3053\u308c\u304b\u3089\u4f5c\u6210\u304a\u3088\u3073\u7de8\u96c6\u3057\u306a\u3051\u308c\u3070\u306a\u3089\u306a\u3044\u306e\u306f\u3001\u307e\u3055\u306b\u3053\u308c\u3089\u306e\u30d5\u30a1\u30a4\u30eb\u3067\u3059\u3002<\/p>\n<p>JS-ERb\u30d5\u30a1\u30a4\u30eb\u306e\u5185\u90e8\u3067\u306f\u3001DOM(Document Object Model)\u3092\u4f7f\u3063\u3066\u30da\u30fc\u30b8\u3092\u64cd\u4f5c\u3059\u308b\u305f\u3081\u3001Rails\u304cjQuery JavaScript\u30d8\u30eb\u30d1\u30fc\u3092<br \/>\n\u81ea\u52d5\u7684\u306b\u63d0\u4f9b\u3057\u3066\u3044\u307e\u3059\u3002<br \/>\n\u3053\u308c\u306b\u3088\u308ajQuery\u30e9\u30a4\u30d6\u30e9\u30ea\u306e\u81a8\u5927\u306aDOM\u64cd\u4f5c\u7528\u30e1\u30bd\u30c3\u30c9\u304c\u4f7f\u3048\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u304c\u3001\u4eca\u56de\u4f7f\u3046\u306e\u306f\u308f\u305a\u304b2\u3064\u3067\u3059\u3002\uff11\u3064\u305a\u3064\u898b\u3066\u3044\u304d\u307e\u3057\u3087\u3046\u3002<\/p>\n<p>\u307e\u305a\u306f\u30c9\u30eb\u8a18\u53f7 ($) \u3068CSS id\u3092\u4f7f\u3063\u3066\u3001DOM\u8981\u7d20\u306b\u30a2\u30af\u30bb\u30b9\u3059\u308b\u6587\u6cd5\u306b\u3064\u3044\u3066\u77e5\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<br \/>\n\u4f8b\u3048\u3070follow_form\u306e\u8981\u7d20\u3092jQuery\u3067\u64cd\u4f5c\u3059\u308b\u306b\u306f\u3001<br \/>\n\u6b21\u306e\u3088\u3046\u306b\u30a2\u30af\u30bb\u30b9\u3057\u307e\u3059<\/p>\n<pre><strong>$(\"#follow_form\")<\/strong><\/pre>\n<p>\u3053\u308c\u306f\u30d5\u30a9\u30fc\u30e0\u3092\u56f2\u3080div\u30bf\u30b0\u3067\u3042\u308a\u3001\u30d5\u30a9\u30fc\u30e0\u305d\u306e\u3082\u306e\u3067\u306f\u306a\u304b\u3063\u305f\u3053\u3068\u3092\u601d\u3044\u51fa\u3057\u3066\u304f\u3060\u3055\u3044\u3002jQuery\u306e\u6587\u6cd5\u306fCSS\u306e\u8a18\u6cd5\u304b\u3089\u5f71\u97ff\u3092\u53d7\u3051\u3066\u304a\u308a\u3001#\u30b7\u30f3\u30dc\u30eb\u3092\u4f7f\u3063\u3066CSS\u306eid\u3092\u6307\u5b9a\u3057\u307e\u3059\u3002<br \/>\n\u3054\u60f3\u50cf\u306e\u3068\u304a\u308a\u3001jQuery\u306fCSS\u3068\u540c\u69d8\u3001\u30c9\u30c3\u30c8.\u3092\u4f7f\u3063\u3066CSS\u30af\u30e9\u30b9\u3092\u64cd\u4f5c\u3067\u304d\u307e\u3059\u3002<\/p>\n<p>\u6b21\u306b\u5fc5\u8981\u306a\u30e1\u30bd\u30c3\u30c9\u306fhtml\u3067\u3059\u3002\u3053\u308c\u306f\u3001\u5f15\u6570\u306e\u4e2d\u3067\u6307\u5b9a\u3055\u308c\u305f\u8981\u7d20\u306e\u5185\u5074\u306b\u3042\u308bHTML\u3092\u66f4\u65b0\u3057\u307e\u3059\u3002<br \/>\n\u4f8b\u3048\u3070\u30d5\u30a9\u30ed\u30fc\u7528\u30d5\u30a9\u30fc\u30e0\u5168\u4f53\u3092&#8221;foobar&#8221;\u3068\u3044\u3046<br \/>\n\u6587\u5b57\u5217\u3067\u7f6e\u304d\u63db\u3048\u305f\u3044\u5834\u5408\u306f\u3001\u6b21\u306e\u3088\u3046\u306a\u30b3\u30fc\u30c9\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<pre><strong>$(\"#follow_form\").html(\"foobar\")<\/strong><\/pre>\n<p>\u7d14\u7c8b\u306aJavaScript\u3068\u7570\u306a\u308a\u3001JS-ERb\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u7d44\u307f\u8fbc\u307fRuby(ERb)\u304c\u4f7f\u3048\u307e\u3059\u3002create.js.erb\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u3001<br \/>\n\u30d5\u30a9\u30ed\u30fc\u7528\u306e\u30d5\u30a9\u30fc\u30e0\u3092unfollow\u30d1\u30fc\u30b7\u30e3\u30eb\u3067\u66f4\u65b0\u3057\u3001<br \/>\n\u30d5\u30a9\u30ed\u30ef\u30fc\u306e\u30ab\u30a6\u30f3\u30c8\u3092\u66f4\u65b0\u3059\u308b\u306e\u306bERb\u3092\u4f7f\u3063\u3066\u3044\u307e\u3059<br \/>\n(\u3082\u3061\u308d\u3093\u3053\u308c\u306f\u3001\u30d5\u30a9\u30ed\u30fc\u306b\u6210\u529f\u3057\u305f\u5834\u5408\u306e\u52d5\u4f5c\u3067\u3059)\u3002<br \/>\n\u5909\u66f4\u306e\u7d50\u679c\u3092\u2193\u306b\u793a\u3057\u307e\u3059\u3002\u3053\u306e\u30b3\u30fc\u30c9\u3067\u306fescape_javascript\u30e1\u30bd\u30c3\u30c9\u3092\u4f7f\u3063\u3066\u3044\u308b\u70b9\u306b\u6ce8\u76ee\u3057\u3066\u304f\u3060\u3055\u3044\u3002<br \/>\nescape_javascript\u30e1\u30bd\u30c3\u30c9\u306f\u3001JavaScript\u30d5\u30a1\u30a4\u30eb\u5185\u306bHTML\u3092\u633f\u5165\u3059\u308b\u3068\u304d\u306b\u5b9f\u884c\u7d50\u679c\u3092\u30a8\u30b9\u30b1\u30fc\u30d7\u3059\u308b\u305f\u3081\u306b\u5fc5\u8981\u3067\u3059\u3002<\/p>\n<p>JavaScript\u3068\u57cb\u3081\u8fbc\u307fRuby\u3092\u4f7f\u3063\u3066\u30d5\u30a9\u30ed\u30fc\u306e\u95a2\u4fc2\u6027\u3092\u4f5c\u6210\u3059\u308b<br \/>\n<strong>app\/views\/relationships\/create.js.erb<\/strong><\/p>\n<pre><strong>$(\"#follow_form\").<\/strong>\r\n<strong>html(\"&lt;%= escape_javascript(render('users\/unfollow')) %&gt;\");<\/strong>\r\n<strong>$(\"#followers\").html('&lt;%= @user.followers.count %&gt;');<\/strong><\/pre>\n<p>\u5404\u884c\u306e\u672b\u5c3e\u306b\u30bb\u30df\u30b3\u30ed\u30f3 ; \u304c\u3042\u308b\u3053\u3068\u306b\u6ce8\u76ee<\/p>\n<p>destroy.js.erb\u30d5\u30a1\u30a4\u30eb\u306e\u65b9\u3082\u540c\u69d8\u3067\u3059<br \/>\nRuby JavaScript (RJS) \u3092\u4f7f\u3063\u3066\u30d5\u30a9\u30ed\u30fc\u306e\u95a2\u4fc2\u6027\u3092\u524a\u9664\u3059\u308b<strong>app\/views\/relationships\/destroy.js.erb<\/strong><\/p>\n<pre><strong>$(\"#follow_form\").html(\"&lt;%= escape_javascript(render('users\/follow')) %&gt;\");<\/strong>\r\n<strong>$(\"#followers\").html('&lt;%= @user.followers.count %&gt;');<\/strong><\/pre>\n<p>\u3053\u308c\u3089\u306e\u30b3\u30fc\u30c9\u306b\u3088\u308a\u3001\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u30da\u30fc\u30b8\u3092\u66f4\u65b0\u3055\u305b\u305a\u306b<br \/>\n\u30d5\u30a9\u30ed\u30fc\u3068\u30d5\u30a9\u30ed\u30fc\u89e3\u9664\u304c\u3067\u304d\u308b\u3088\u3046\u306b\u306a\u3063\u305f\u306f\u305a\u3067\u3059\u3002<\/p>\n<h3>\u6f14\u7fd2<\/h3>\n<p>\u78ba\u8a8d\u304c\u7d42\u308f\u3063\u305f\u3089\u3001Rails\u30b5\u30fc\u30d0\u30fc\u306e\u30ed\u30b0\u3092\u95b2\u89a7\u3057\u3001\u30d5\u30a9\u30ed\u30fc\/\u30d5\u30a9\u30ed\u30fc\u89e3\u9664\u3092\u5b9f\u884c\u3057\u305f\u76f4\u5f8c\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u304c\u3069\u3046\u306a\u3063\u3066\u3044\u308b\u304b\u78ba\u8a8d\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002<\/p>\n<pre><strong>Rendered relationships\/create.js.erb<\/strong>\r\n\r\n<strong>Rendered relationships\/destroy.js.erb<\/strong><\/pre>\n<p>js.erb\u30d5\u30a1\u30a4\u30eb\u304c\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u3055\u308c\u3066\u3044\u308b\u3002<\/p>\n<h3>14.2.6 \u30d5\u30a9\u30ed\u30fc\u3092\u30c6\u30b9\u30c8\u3059\u308b<\/h3>\n<p>\u30d5\u30a9\u30ed\u30fc\u30dc\u30bf\u30f3\u304c\u52d5\u304f\u3088\u3046\u306b\u306a\u3063\u305f\u306e\u3067\u3001<br \/>\n\u30d0\u30b0\u3092\u691c\u77e5\u3059\u308b\u305f\u3081\u306e\u30b7\u30f3\u30d7\u30eb\u306a\u30c6\u30b9\u30c8\u3092\u66f8\u3044\u3066\u3044\u304d\u307e\u3057\u3087\u3046\u3002<br \/>\n\u30e6\u30fc\u30b6\u30fc\u306e\u30d5\u30a9\u30ed\u30fc\u306b\u5bfe\u3059\u308b\u30c6\u30b9\u30c8\u3067\u306f\u3001<br \/>\n\/relationships\u306b\u5bfe\u3057\u3066POST\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u9001\u308a\u3001<br \/>\n\u30d5\u30a9\u30ed\u30fc\u3055\u308c\u305f\u30e6\u30fc\u30b6\u30fc\u304c1\u4eba\u5897\u3048\u305f\u3053\u3068\u3092\u30c1\u30a7\u30c3\u30af\u3057\u307e\u3059\u3002<\/p>\n<pre><strong>assert_difference '@user.following.count', 1 do<\/strong>\r\n<strong> post relationships_path, params: { followed_id: @other.id }<\/strong>\r\n<strong>end<\/strong><\/pre>\n<p>\u3053\u308c\u306f\u6a19\u6e96\u7684\u306a\u30d5\u30a9\u30ed\u30fc\u306b\u5bfe\u3059\u308b\u30c6\u30b9\u30c8\u3067\u306f\u3042\u308a\u307e\u3059\u304c\u3001Ajax\u7248\u3082\u3084\u308a\u65b9\u306f\u5927\u4f53\u540c\u3058\u3067\u3059\u3002Ajax\u306e\u30c6\u30b9\u30c8\u3067\u306f\u3001xhr :true\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u4f7f\u3046\u3088\u3046\u306b\u3059\u308b\u3060\u3051\u3067\u3059\u3002<\/p>\n<pre><strong>assert_difference '@user.following.count', 1 do<\/strong>\r\n<strong> post relationships_path, params: { followed_id: @other.id }, xhr: true<\/strong>\r\n<strong>end<\/strong><\/pre>\n<p>xhr (XmlHttpRequest) \u3068\u3044\u3046\u30aa\u30d7\u30b7\u30e7\u30f3\u3092true\u306b\u8a2d\u5b9a\u3059\u308b\u3068\u3001<br \/>\nAjax\u3067\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u767a\u884c\u3059\u308b\u3088\u3046\u306b\u5909\u308f\u308a\u307e\u3059\u3002<br \/>\n\u3057\u305f\u304c\u3063\u3066\u3001respond_to\u3067\u306f\u3001<br \/>\nJavaScript\u306b\u5bfe\u5fdc\u3057\u305f\u884c\u304c\u5b9f\u884c\u3055\u308c\u308b\u3088\u3046\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>\u30e6\u30fc\u30b6\u30fc\u3092\u30d5\u30a9\u30ed\u30fc\u89e3\u9664\u3059\u308b\u3068\u304d\u3082\u69cb\u9020\u306f\u307b\u3068\u3093\u3069\u540c\u3058\u3067\u3001<br \/>\npost\u30e1\u30bd\u30c3\u30c9\u3092delete\u30e1\u30bd\u30c3\u30c9\u306b\u7f6e\u304d\u63db\u3048\u3066\u30c6\u30b9\u30c8\u3057\u307e\u3059\u3002<br \/>\n\u3064\u307e\u308a\u3001\u305d\u306e\u30e6\u30fc\u30b6\u30fc\u306eid\u3068\u30ea\u30ec\u30fc\u30b7\u30e7\u30f3\u30b7\u30c3\u30d7\u306eid\u3092\u4f7f\u3063\u3066<br \/>\nDELETE\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u9001\u4fe1\u3057\u3001\u30d5\u30a9\u30ed\u30fc\u3057\u3066\u3044\u308b\u6570\u304c1\u3064\u6e1b\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u307e\u3059\u3002\u3057\u305f\u304c\u3063\u3066\u3001\u5b9f\u969b\u306b\u52a0\u3048\u308b\u30c6\u30b9\u30c8\u306f\u3001<\/p>\n<pre><strong>assert_difference '@user.following.count', -1 do<\/strong>\r\n<strong> delete relationship_path(relationship)<\/strong>\r\n<strong>end<\/strong><\/pre>\n<p>\u4e0a\u306e\u5f93\u6765\u3069\u304a\u308a\u306e\u30c6\u30b9\u30c8\u3068\u3001\u4e0b\u306eAjax\u7528\u306e\u30c6\u30b9\u30c8\u306e\uff12\u3064\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<pre><strong>assert_difference '@user.following.count', -1 do<\/strong>\r\n<strong> delete relationship_path(relationship), xhr: true<\/strong>\r\n<strong>end<\/strong><\/pre>\n<p>[Follow] \/ [Unfollow] \u30dc\u30bf\u30f3\u3092\u30c6\u30b9\u30c8\u3059\u308b green<br \/>\n<strong>test\/integration\/following_test.rb<\/strong><\/p>\n<pre><strong><span class=\"nb\">require<\/span> <span class=\"s1\">'test_helper'<\/span>\r\n\r\n<span class=\"k\">class<\/span> <span class=\"nc\">FollowingTest<\/span> <span class=\"o\">&lt;<\/span> <span class=\"no\">ActionDispatch<\/span><span class=\"o\">::<\/span><span class=\"no\">IntegrationTest<\/span>\r\n\r\n  <span class=\"k\">def<\/span> <span class=\"nf\">setup<\/span>\r\n    <span class=\"vi\">@user<\/span>  <span class=\"o\">=<\/span> <span class=\"n\">users<\/span><span class=\"p\">(<\/span><span class=\"ss\">:michael<\/span><span class=\"p\">)<\/span>\r\n<span class=\"hll\">    <span class=\"vi\">@other<\/span> <span class=\"o\">=<\/span> <span class=\"n\">users<\/span><span class=\"p\">(<\/span><span class=\"ss\">:archer<\/span><span class=\"p\">)<\/span>\r\n<\/span>    <span class=\"n\">log_in_as<\/span><span class=\"p\">(<\/span><span class=\"vi\">@user<\/span><span class=\"p\">)<\/span>\r\n  <span class=\"k\">end<\/span>\r\n  <span class=\"o\">.<\/span>\r\n  <span class=\"o\">.<\/span>\r\n  <span class=\"o\">.<\/span>\r\n  <span class=\"nb\">test<\/span> <span class=\"s2\">\"should follow a user the standard way\"<\/span> <span class=\"k\">do<\/span>\r\n    <span class=\"n\">assert_difference<\/span> <span class=\"s1\">'@user.following.count'<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span> <span class=\"k\">do<\/span>\r\n      <span class=\"n\">post<\/span> <span class=\"n\">relationships_path<\/span><span class=\"p\">,<\/span> <span class=\"ss\">params<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"ss\">followed_id<\/span><span class=\"p\">:<\/span> <span class=\"vi\">@other<\/span><span class=\"o\">.<\/span><span class=\"n\">id<\/span> <span class=\"p\">}<\/span>\r\n    <span class=\"k\">end<\/span>\r\n  <span class=\"k\">end<\/span>\r\n\r\n  <span class=\"nb\">test<\/span> <span class=\"s2\">\"should follow a user with Ajax\"<\/span> <span class=\"k\">do<\/span>\r\n    <span class=\"n\">assert_difference<\/span> <span class=\"s1\">'@user.following.count'<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span> <span class=\"k\">do<\/span>\r\n      <span class=\"n\">post<\/span> <span class=\"n\">relationships_path<\/span><span class=\"p\">,<\/span> <span class=\"ss\">xhr<\/span><span class=\"p\">:<\/span> <span class=\"kp\">true<\/span><span class=\"p\">,<\/span> <span class=\"ss\">params<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"ss\">followed_id<\/span><span class=\"p\">:<\/span> <span class=\"vi\">@other<\/span><span class=\"o\">.<\/span><span class=\"n\">id<\/span> <span class=\"p\">}<\/span>\r\n    <span class=\"k\">end<\/span>\r\n  <span class=\"k\">end<\/span>\r\n\r\n  <span class=\"nb\">test<\/span> <span class=\"s2\">\"should unfollow a user the standard way\"<\/span> <span class=\"k\">do<\/span>\r\n    <span class=\"vi\">@user<\/span><span class=\"o\">.<\/span><span class=\"n\">follow<\/span><span class=\"p\">(<\/span><span class=\"vi\">@other<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"n\">relationship<\/span> <span class=\"o\">=<\/span> <span class=\"vi\">@user<\/span><span class=\"o\">.<\/span><span class=\"n\">active_relationships<\/span><span class=\"o\">.<\/span><span class=\"n\">find_by<\/span><span class=\"p\">(<\/span><span class=\"ss\">followed_id<\/span><span class=\"p\">:<\/span> <span class=\"vi\">@other<\/span><span class=\"o\">.<\/span><span class=\"n\">id<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"n\">assert_difference<\/span> <span class=\"s1\">'@user.following.count'<\/span><span class=\"p\">,<\/span> <span class=\"o\">-<\/span><span class=\"mi\">1<\/span> <span class=\"k\">do<\/span>\r\n      <span class=\"n\">delete<\/span> <span class=\"n\">relationship_path<\/span><span class=\"p\">(<\/span><span class=\"n\">relationship<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"k\">end<\/span>\r\n  <span class=\"k\">end<\/span>\r\n\r\n  <span class=\"nb\">test<\/span> <span class=\"s2\">\"should unfollow a user with Ajax\"<\/span> <span class=\"k\">do<\/span>\r\n    <span class=\"vi\">@user<\/span><span class=\"o\">.<\/span><span class=\"n\">follow<\/span><span class=\"p\">(<\/span><span class=\"vi\">@other<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"n\">relationship<\/span> <span class=\"o\">=<\/span> <span class=\"vi\">@user<\/span><span class=\"o\">.<\/span><span class=\"n\">active_relationships<\/span><span class=\"o\">.<\/span><span class=\"n\">find_by<\/span><span class=\"p\">(<\/span><span class=\"ss\">followed_id<\/span><span class=\"p\">:<\/span> <span class=\"vi\">@other<\/span><span class=\"o\">.<\/span><span class=\"n\">id<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"n\">assert_difference<\/span> <span class=\"s1\">'@user.following.count'<\/span><span class=\"p\">,<\/span> <span class=\"o\">-<\/span><span class=\"mi\">1<\/span> <span class=\"k\">do<\/span>\r\n      <span class=\"n\">delete<\/span> <span class=\"n\">relationship_path<\/span><span class=\"p\">(<\/span><span class=\"n\">relationship<\/span><span class=\"p\">),<\/span> <span class=\"ss\">xhr<\/span><span class=\"p\">:<\/span> <span class=\"kp\">true<\/span>\r\n    <span class=\"k\">end<\/span>\r\n  <span class=\"k\">end<\/span>\r\n<span class=\"k\">end<\/span><\/strong><\/pre>\n<h3>\u6f14\u7fd2<\/h3>\n<p>1:respond_to\u30d6\u30ed\u30c3\u30af\u5185\u306e\u5404\u884c\u3092\u9806\u306b\u30b3\u30e1\u30f3\u30c8\u30a2\u30a6\u30c8\u3057\u3066\u3044\u304d\u3001<br \/>\n\u30c6\u30b9\u30c8\u304c\u6b63\u3057\u304f\u30a8\u30e9\u30fc\u3092\u691c\u77e5\u3067\u304d\u308b\u304b\u3069\u3046\u304b\u78ba\u8a8d\u3057\u3066\u307f\u307e\u3057\u3087\u3046\u3002<br \/>\n\u5b9f\u969b\u3001\u3069\u306e\u30c6\u30b9\u30c8\u30b1\u30fc\u30b9\u304c\u843d\u3061\u305f\u3067\u3057\u3087\u3046\u304b?<\/p>\n<pre><strong># format.html { redirect_to @user }\r\n<\/strong>\r\n<strong>ActionController::UnknownFormat:<\/strong>\r\n<strong>\r\napp\/controllers\/relationships_controller.rb:7:in `create'<\/strong>\r\n<strong>\r\nActionController::UnknownFormat<\/strong>\r\n<strong>\r\napp\/controllers\/relationships_controller.rb:16:in `destroy'<\/strong><\/pre>\n<p>2:xhr: true\u304c\u3042\u308b\u884c\u306e\u3046\u3061\u3001\u7247\u65b9\u306e\u307f\u3092\u524a\u9664\u3059\u308b\u3068\u3069\u3046\u3044\u3063\u305f\u7d50\u679c\u306b<br \/>\n\u306a\u308b\u3067\u3057\u3087\u3046\u304b? \u3053\u306e\u3068\u304d\u767a\u751f\u3059\u308b\u554f\u984c\u306e\u539f\u56e0\u3068\u3001\u306a\u305c\u5148\u307b\u3069\u3067\u78ba\u8a8d\u3057\u305f\u30c6\u30b9\u30c8\u304c\u3053\u306e\u554f\u984c\u3092\u691c\u77e5\u3067\u304d\u305f\u306e\u304b\u8003\u3048\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<pre><strong>\"@user.following.count\" didn't change by 1.<\/strong>\r\n<strong>Expected: 3<\/strong>\r\n<strong>Actual: 2<\/strong><\/pre>\n<p>Ajax\u3092\u7528\u3044\u305f\u30d5\u30a9\u30ed\u30fc\u3067\u3001<br \/>\nPost\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u9001\u4fe1\u3057\u3066\u3044\u306a\u3044\u305f\u3081\u30d5\u30a9\u30ed\u30fc\u6570\u304c\u5897\u3048\u306a\u3044\u304b\u3089\u30a8\u30e9\u30fc\u306b\u306a\u308b\u3002<\/p>\n<p><a href=\"https:\/\/suzutukiblog.com\/index.php\/2021\/04\/26\/rails-tutorial14-3\/\">14.3\u306b\u7d9a\u304f<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u305d\u306e14.2\u304b\u3089\u7d9a\u304f 14.2.3 [Following] \u3068 [Followers] \u30da\u30fc\u30b8 \u30d5\u30a9\u30ed\u30fc\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc\u3092\u8868\u793a\u3059\u308b\u30da\u30fc\u30b8\u3068\u3001\u30d5\u30a9\u30ed\u30ef\u30fc\u3092\u8868\u793a\u3059\u308b\u30da\u30fc\u30b8\u306f\u3001 \u3044\u305a\u308c\u3082\u30d7\u30ed\u30d5\u30a3\u30fc\u30eb\u30da\u30fc\u30b8\u3068\u30e6\u30fc\u30b6\u30fc\u4e00\u89a7\u30da\u30fc\u30b8\u3092 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2764,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[27,13],"tags":[],"class_list":["post-3121","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-programming-note","category-rails"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/posts\/3121","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/comments?post=3121"}],"version-history":[{"count":9,"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/posts\/3121\/revisions"}],"predecessor-version":[{"id":4590,"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/posts\/3121\/revisions\/4590"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/media\/2764"}],"wp:attachment":[{"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/media?parent=3121"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/categories?post=3121"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/suzutukiblog.com\/index.php\/wp-json\/wp\/v2\/tags?post=3121"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}