Restful refactor of accounts/ routes (#2133)
* Add routing specs for accounts followers and following actions * Use more restful route naming for public account follow pages Moves two actions: - accounts#followers to accounts/follower_accounts#index - accounts#following to accounts/following_accounts#index Adds routing spec to ensure prior URLs are preserved.
This commit is contained in:
		
							
								
								
									
										12
									
								
								app/controllers/account_follow_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/controllers/account_follow_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class AccountFollowController < ApplicationController
 | 
			
		||||
  include AccountControllerConcern
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
 | 
			
		||||
  def create
 | 
			
		||||
    FollowService.new.call(current_user.account, @account.acct)
 | 
			
		||||
    redirect_to account_path(@account)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										12
									
								
								app/controllers/account_unfollow_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/controllers/account_unfollow_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class AccountUnfollowController < ApplicationController
 | 
			
		||||
  include AccountControllerConcern
 | 
			
		||||
 | 
			
		||||
  before_action :authenticate_user!
 | 
			
		||||
 | 
			
		||||
  def create
 | 
			
		||||
    UnfollowService.new.call(current_user.account, @account)
 | 
			
		||||
    redirect_to account_path(@account)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -1,12 +1,7 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class AccountsController < ApplicationController
 | 
			
		||||
  layout 'public'
 | 
			
		||||
 | 
			
		||||
  before_action :set_account
 | 
			
		||||
  before_action :set_link_headers
 | 
			
		||||
  before_action :authenticate_user!, only: [:follow, :unfollow]
 | 
			
		||||
  before_action :check_account_suspension
 | 
			
		||||
  include AccountControllerConcern
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
    respond_to do |format|
 | 
			
		||||
@@ -24,39 +19,9 @@ class AccountsController < ApplicationController
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def follow
 | 
			
		||||
    FollowService.new.call(current_user.account, @account.acct)
 | 
			
		||||
    redirect_to account_path(@account)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def unfollow
 | 
			
		||||
    UnfollowService.new.call(current_user.account, @account)
 | 
			
		||||
    redirect_to account_path(@account)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def followers
 | 
			
		||||
    @followers = @account.followers.order('follows.created_at desc').page(params[:page]).per(12)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def following
 | 
			
		||||
    @following = @account.following.order('follows.created_at desc').page(params[:page]).per(12)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_account
 | 
			
		||||
    @account = Account.find_local!(params[:username])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_link_headers
 | 
			
		||||
    response.headers['Link'] = LinkHeader.new([[webfinger_account_url, [%w(rel lrdd), %w(type application/xrd+xml)]], [account_url(@account, format: 'atom'), [%w(rel alternate), %w(type application/atom+xml)]]])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def webfinger_account_url
 | 
			
		||||
    webfinger_url(resource: @account.to_webfinger_s)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_account_suspension
 | 
			
		||||
    gone if @account.suspended?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								app/controllers/concerns/account_controller_concern.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/controllers/concerns/account_controller_concern.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module AccountControllerConcern
 | 
			
		||||
  extend ActiveSupport::Concern
 | 
			
		||||
 | 
			
		||||
  FOLLOW_PER_PAGE = 12
 | 
			
		||||
 | 
			
		||||
  included do
 | 
			
		||||
    layout 'public'
 | 
			
		||||
    before_action :set_account
 | 
			
		||||
    before_action :set_link_headers
 | 
			
		||||
    before_action :check_account_suspension
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def set_account
 | 
			
		||||
    @account = Account.find_local!(params[:account_username])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def set_link_headers
 | 
			
		||||
    response.headers['Link'] = LinkHeader.new(
 | 
			
		||||
      [
 | 
			
		||||
        webfinger_account_link,
 | 
			
		||||
        atom_account_url_link,
 | 
			
		||||
      ]
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def webfinger_account_link
 | 
			
		||||
    [
 | 
			
		||||
      webfinger_account_url,
 | 
			
		||||
      [%w(rel lrdd), %w(type application/xrd+xml)],
 | 
			
		||||
    ]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def atom_account_url_link
 | 
			
		||||
    [
 | 
			
		||||
      account_url(@account, format: 'atom'),
 | 
			
		||||
      [%w(rel alternate), %w(type application/atom+xml)],
 | 
			
		||||
    ]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def webfinger_account_url
 | 
			
		||||
    webfinger_url(resource: @account.to_webfinger_s)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def check_account_suspension
 | 
			
		||||
    gone if @account.suspended?
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										15
									
								
								app/controllers/follower_accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								app/controllers/follower_accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class FollowerAccountsController < ApplicationController
 | 
			
		||||
  include AccountControllerConcern
 | 
			
		||||
 | 
			
		||||
  def index
 | 
			
		||||
    @accounts = ordered_accounts.page(params[:page]).per(FOLLOW_PER_PAGE)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def ordered_accounts
 | 
			
		||||
    @account.followers.order('follows.created_at desc')
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										15
									
								
								app/controllers/following_accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								app/controllers/following_accounts_controller.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class FollowingAccountsController < ApplicationController
 | 
			
		||||
  include AccountControllerConcern
 | 
			
		||||
 | 
			
		||||
  def index
 | 
			
		||||
    @accounts = ordered_accounts.page(params[:page]).per(FOLLOW_PER_PAGE)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def ordered_accounts
 | 
			
		||||
    @account.following.order('follows.created_at desc')
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										7
									
								
								app/views/accounts/_follow_grid.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/views/accounts/_follow_grid.html.haml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
.accounts-grid
 | 
			
		||||
  - if accounts.empty?
 | 
			
		||||
    = render partial: 'accounts/nothing_here'
 | 
			
		||||
  - else
 | 
			
		||||
    = render partial: 'accounts/grid_card', collection: accounts, as: :account, cached: true
 | 
			
		||||
 | 
			
		||||
= paginate accounts
 | 
			
		||||
@@ -2,9 +2,9 @@
 | 
			
		||||
  - if user_signed_in? && current_account.id != account.id && !current_account.requested?(account)
 | 
			
		||||
    .controls
 | 
			
		||||
      - if current_account.following?(account)
 | 
			
		||||
        = link_to t('accounts.unfollow'), unfollow_account_path(account), data: { method: :post }, class: 'button'
 | 
			
		||||
        = link_to t('accounts.unfollow'), account_unfollow_path(account), data: { method: :post }, class: 'button'
 | 
			
		||||
      - else
 | 
			
		||||
        = link_to t('accounts.follow'), follow_account_path(account), data: { method: :post }, class: 'button'
 | 
			
		||||
        = link_to t('accounts.follow'), account_follow_path(account), data: { method: :post }, class: 'button'
 | 
			
		||||
  - elsif !user_signed_in?
 | 
			
		||||
    .controls
 | 
			
		||||
      .remote-follow
 | 
			
		||||
@@ -24,11 +24,11 @@
 | 
			
		||||
        = link_to short_account_url(account), class: 'u-url u-uid' do
 | 
			
		||||
          %span.counter-label= t('accounts.posts')
 | 
			
		||||
          %span.counter-number= number_with_delimiter account.statuses_count
 | 
			
		||||
      .counter{ class: active_nav_class(following_account_url(account)) }
 | 
			
		||||
        = link_to following_account_url(account) do
 | 
			
		||||
      .counter{ class: active_nav_class(account_following_index_url(account)) }
 | 
			
		||||
        = link_to account_following_index_url(account) do
 | 
			
		||||
          %span.counter-label= t('accounts.following')
 | 
			
		||||
          %span.counter-number= number_with_delimiter account.following_count
 | 
			
		||||
      .counter{ class: active_nav_class(followers_account_url(account)) }
 | 
			
		||||
        = link_to followers_account_url(account) do
 | 
			
		||||
      .counter{ class: active_nav_class(account_followers_url(account)) }
 | 
			
		||||
        = link_to account_followers_url(account) do
 | 
			
		||||
          %span.counter-label= t('accounts.followers')
 | 
			
		||||
          %span.counter-number= number_with_delimiter account.followers_count
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								app/views/follower_accounts/index.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app/views/follower_accounts/index.html.haml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
- content_for :page_title do
 | 
			
		||||
  = t('accounts.people_who_follow', name: display_name(@account))
 | 
			
		||||
 | 
			
		||||
= render 'accounts/header', account: @account
 | 
			
		||||
 | 
			
		||||
= render 'accounts/follow_grid', accounts: @accounts
 | 
			
		||||
							
								
								
									
										6
									
								
								app/views/following_accounts/index.html.haml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app/views/following_accounts/index.html.haml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
- content_for :page_title do
 | 
			
		||||
  = t('accounts.people_followed_by', name: display_name(@account))
 | 
			
		||||
 | 
			
		||||
= render 'accounts/header', account: @account
 | 
			
		||||
 | 
			
		||||
= render 'accounts/follow_grid', accounts: @accounts
 | 
			
		||||
@@ -37,13 +37,10 @@ Rails.application.routes.draw do
 | 
			
		||||
    get :remote_follow,  to: 'remote_follow#new'
 | 
			
		||||
    post :remote_follow, to: 'remote_follow#create'
 | 
			
		||||
 | 
			
		||||
    member do
 | 
			
		||||
      get :followers
 | 
			
		||||
      get :following
 | 
			
		||||
 | 
			
		||||
      post :follow
 | 
			
		||||
      post :unfollow
 | 
			
		||||
    end
 | 
			
		||||
    resources :followers, only: [:index], controller: :follower_accounts
 | 
			
		||||
    resources :following, only: [:index], controller: :following_accounts
 | 
			
		||||
    resource :follow, only: [:create], controller: :account_follow
 | 
			
		||||
    resource :unfollow, only: [:create], controller: :account_unfollow
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  get '/@:username', to: 'accounts#show', as: :short_account
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								spec/controllers/account_follow_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								spec/controllers/account_follow_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
describe AccountFollowController do
 | 
			
		||||
  render_views
 | 
			
		||||
  let(:user) { Fabricate(:user) }
 | 
			
		||||
  let(:alice) { Fabricate(:account, username: 'alice') }
 | 
			
		||||
 | 
			
		||||
  describe 'POST #create' do
 | 
			
		||||
    before do
 | 
			
		||||
      sign_in(user)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it 'redirects to account path' do
 | 
			
		||||
      service = double
 | 
			
		||||
      allow(FollowService).to receive(:new).and_return(service)
 | 
			
		||||
      allow(service).to receive(:call)
 | 
			
		||||
 | 
			
		||||
      post :create, params: { account_username: alice.username }
 | 
			
		||||
 | 
			
		||||
      expect(service).to have_received(:call).with(user.account, 'alice')
 | 
			
		||||
      expect(response).to redirect_to(account_path(alice))
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										24
									
								
								spec/controllers/account_unfollow_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								spec/controllers/account_unfollow_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
describe AccountUnfollowController do
 | 
			
		||||
  render_views
 | 
			
		||||
  let(:user) { Fabricate(:user) }
 | 
			
		||||
  let(:alice) { Fabricate(:account, username: 'alice') }
 | 
			
		||||
 | 
			
		||||
  describe 'POST #create' do
 | 
			
		||||
    before do
 | 
			
		||||
      sign_in(user)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it 'redirects to account path' do
 | 
			
		||||
      service = double
 | 
			
		||||
      allow(UnfollowService).to receive(:new).and_return(service)
 | 
			
		||||
      allow(service).to receive(:call)
 | 
			
		||||
 | 
			
		||||
      post :create, params: { account_username: alice.username }
 | 
			
		||||
 | 
			
		||||
      expect(service).to have_received(:call).with(user.account, alice)
 | 
			
		||||
      expect(response).to redirect_to(account_path(alice))
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -44,18 +44,4 @@ RSpec.describe AccountsController, type: :controller do
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe 'GET #followers' do
 | 
			
		||||
    it 'returns http success' do
 | 
			
		||||
      get :followers, params: { username: alice.username }
 | 
			
		||||
      expect(response).to have_http_status(:success)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe 'GET #following' do
 | 
			
		||||
    it 'returns http success' do
 | 
			
		||||
      get :following, params: { username: alice.username }
 | 
			
		||||
      expect(response).to have_http_status(:success)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								spec/controllers/follower_accounts_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								spec/controllers/follower_accounts_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
describe FollowerAccountsController do
 | 
			
		||||
  render_views
 | 
			
		||||
  let(:alice) { Fabricate(:account, username: 'alice') }
 | 
			
		||||
 | 
			
		||||
  describe 'GET #index' do
 | 
			
		||||
    it 'returns http success' do
 | 
			
		||||
      get :index, params: { account_username: alice.username }
 | 
			
		||||
 | 
			
		||||
      expect(response).to have_http_status(:success)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										14
									
								
								spec/controllers/following_accounts_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								spec/controllers/following_accounts_controller_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
describe FollowingAccountsController do
 | 
			
		||||
  render_views
 | 
			
		||||
  let(:alice) { Fabricate(:account, username: 'alice') }
 | 
			
		||||
 | 
			
		||||
  describe 'GET #index' do
 | 
			
		||||
    it 'returns http success' do
 | 
			
		||||
      get :index, params: { account_username: alice.username }
 | 
			
		||||
 | 
			
		||||
      expect(response).to have_http_status(:success)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										33
									
								
								spec/requests/link_headers_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								spec/requests/link_headers_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
describe 'Link headers' do
 | 
			
		||||
  describe 'on the account show page' do
 | 
			
		||||
    let(:account) { Fabricate(:account, username: 'test') }
 | 
			
		||||
 | 
			
		||||
    before do
 | 
			
		||||
      get short_account_path(username: account)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it 'contains webfinger url in link header' do
 | 
			
		||||
      link_header = link_header_with_type('application/xrd+xml')
 | 
			
		||||
 | 
			
		||||
      expect(link_header.href).to match 'http://www.example.com/.well-known/webfinger?resource=acct%3Atest%40cb6e6126.ngrok.io'
 | 
			
		||||
      expect(link_header.attr_pairs.first).to eq %w[rel lrdd]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it 'contains atom url in link header' do
 | 
			
		||||
      link_header = link_header_with_type('application/atom+xml')
 | 
			
		||||
 | 
			
		||||
      expect(link_header.href).to eq 'http://www.example.com/users/test.atom'
 | 
			
		||||
      expect(link_header.attr_pairs.first).to eq %w[rel alternate]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def link_header_with_type(type)
 | 
			
		||||
      response.headers['Link'].links.find do |link|
 | 
			
		||||
        link.attr_pairs.any? { |pair| pair == ['type', type] }
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										31
									
								
								spec/routing/accounts_routing_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								spec/routing/accounts_routing_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
describe 'Routes under accounts/' do
 | 
			
		||||
  describe 'the route for accounts who are followers of an account' do
 | 
			
		||||
    it 'routes to the followers action with the right username' do
 | 
			
		||||
      expect(get('/users/name/followers')).
 | 
			
		||||
        to route_to('follower_accounts#index', account_username: 'name')
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe 'the route for accounts who are followed by an account' do
 | 
			
		||||
    it 'routes to the following action with the right username' do
 | 
			
		||||
      expect(get('/users/name/following')).
 | 
			
		||||
        to route_to('following_accounts#index', account_username: 'name')
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe 'the route for following an account' do
 | 
			
		||||
    it 'routes to the follow create action with the right username' do
 | 
			
		||||
      expect(post('/users/name/follow')).
 | 
			
		||||
        to route_to('account_follow#create', account_username: 'name')
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe 'the route for unfollowing an account' do
 | 
			
		||||
    it 'routes to the unfollow create action with the right username' do
 | 
			
		||||
      expect(post('/users/name/unfollow')).
 | 
			
		||||
        to route_to('account_unfollow#create', account_username: 'name')
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
		Reference in New Issue
	
	Block a user