Merge remote-tracking branch 'tootsuite/master' into glitchsoc/master
This commit is contained in:
		
						commit
						e768ef35b1
					
				
					 25 changed files with 193 additions and 60 deletions
				
			
		| 
						 | 
				
			
			@ -19,7 +19,6 @@ env:
 | 
			
		|||
    - LOCAL_HTTPS=true
 | 
			
		||||
    - RAILS_ENV=test
 | 
			
		||||
    - PARALLEL_TEST_PROCESSORS=2
 | 
			
		||||
    - "PATH=$HOME:$PATH"
 | 
			
		||||
 | 
			
		||||
addons:
 | 
			
		||||
  postgresql: 9.4
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +48,6 @@ install:
 | 
			
		|||
 | 
			
		||||
before_script:
 | 
			
		||||
  - ./bin/rails parallel:create parallel:load_schema parallel:prepare assets:precompile
 | 
			
		||||
  - ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
  - travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,6 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
 | 
			
		|||
  private
 | 
			
		||||
 | 
			
		||||
  def account_params
 | 
			
		||||
    params.permit(:display_name, :note, :avatar, :header)
 | 
			
		||||
    params.permit(:display_name, :note, :avatar, :header, :locked)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
import api from '../api';
 | 
			
		||||
import { fetchRelationships } from './accounts';
 | 
			
		||||
 | 
			
		||||
export const SEARCH_CHANGE = 'SEARCH_CHANGE';
 | 
			
		||||
export const SEARCH_CLEAR  = 'SEARCH_CLEAR';
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +39,7 @@ export function submitSearch() {
 | 
			
		|||
      },
 | 
			
		||||
    }).then(response => {
 | 
			
		||||
      dispatch(fetchSearchSuccess(response.data));
 | 
			
		||||
      dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
 | 
			
		||||
    }).catch(error => {
 | 
			
		||||
      dispatch(fetchSearchFail(error));
 | 
			
		||||
    });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -227,12 +227,8 @@ export default class MediaGallery extends React.PureComponent {
 | 
			
		|||
    const style = {};
 | 
			
		||||
 | 
			
		||||
    if (this.isStandaloneEligible()) {
 | 
			
		||||
      if (!visible && width) {
 | 
			
		||||
        // only need to forcibly set the height in "sensitive" mode
 | 
			
		||||
      if (width) {
 | 
			
		||||
        style.height = width / this.props.media.getIn([0, 'meta', 'small', 'aspect']);
 | 
			
		||||
      } else {
 | 
			
		||||
        // layout automatically, using image's natural aspect ratio
 | 
			
		||||
        style.height = '';
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      // crop the image
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -122,5 +122,6 @@ button {
 | 
			
		|||
    height: 100%;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    outline: 0 !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -686,12 +686,13 @@
 | 
			
		|||
  background: transparent;
 | 
			
		||||
  border: 0;
 | 
			
		||||
  color: lighten($ui-base-color, 8%);
 | 
			
		||||
  font-weight: 500;
 | 
			
		||||
  font-weight: 700;
 | 
			
		||||
  font-size: 11px;
 | 
			
		||||
  padding: 0 6px;
 | 
			
		||||
  text-transform: uppercase;
 | 
			
		||||
  line-height: inherit;
 | 
			
		||||
  line-height: 20px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.status__prepend-icon-wrapper {
 | 
			
		||||
| 
						 | 
				
			
			@ -899,6 +900,11 @@
 | 
			
		|||
      height: 24px;
 | 
			
		||||
      margin: -1px 0 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .status__content__spoiler-link {
 | 
			
		||||
      line-height: 24px;
 | 
			
		||||
      margin: -1px 0 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .video-player {
 | 
			
		||||
| 
						 | 
				
			
			@ -2667,12 +2673,16 @@ a.status-card {
 | 
			
		|||
  background: $base-overlay-background;
 | 
			
		||||
  color: $ui-primary-color;
 | 
			
		||||
  border: 0;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  appearance: none;
 | 
			
		||||
 | 
			
		||||
  &:hover,
 | 
			
		||||
  &:active,
 | 
			
		||||
  &:focus {
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    color: lighten($ui-primary-color, 8%);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2685,7 +2695,7 @@ a.status-card {
 | 
			
		|||
.media-spoiler__trigger {
 | 
			
		||||
  display: block;
 | 
			
		||||
  font-size: 11px;
 | 
			
		||||
  font-weight: 500;
 | 
			
		||||
  font-weight: 700;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.spoiler-button {
 | 
			
		||||
| 
						 | 
				
			
			@ -4091,6 +4101,7 @@ a.status-card {
 | 
			
		|||
  box-sizing: border-box;
 | 
			
		||||
  margin-top: 8px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4101,6 +4112,8 @@ a.status-card {
 | 
			
		|||
  display: block;
 | 
			
		||||
  float: left;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
 | 
			
		||||
  &.standalone {
 | 
			
		||||
    .media-gallery__item-gifv-thumbnail {
 | 
			
		||||
| 
						 | 
				
			
			@ -4113,6 +4126,7 @@ a.status-card {
 | 
			
		|||
  cursor: zoom-in;
 | 
			
		||||
  display: block;
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  color: $ui-secondary-color;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  line-height: 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,10 +146,10 @@
 | 
			
		|||
 | 
			
		||||
      a.status__content__spoiler-link {
 | 
			
		||||
        color: $primary-text-color;
 | 
			
		||||
        background: $ui-primary-color;
 | 
			
		||||
        background: $ui-base-color;
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
          background: lighten($ui-primary-color, 8%);
 | 
			
		||||
          background: lighten($ui-base-color, 8%);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -214,10 +214,10 @@
 | 
			
		|||
 | 
			
		||||
      a.status__content__spoiler-link {
 | 
			
		||||
        color: $primary-text-color;
 | 
			
		||||
        background: $ui-primary-color;
 | 
			
		||||
        background: $ui-base-color;
 | 
			
		||||
 | 
			
		||||
        &:hover {
 | 
			
		||||
          background: lighten($ui-primary-color, 8%);
 | 
			
		||||
          background: lighten($ui-base-color, 8%);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -260,16 +260,8 @@
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  .media-spoiler {
 | 
			
		||||
    background: $ui-primary-color;
 | 
			
		||||
    color: $white;
 | 
			
		||||
    transition: all 40ms linear;
 | 
			
		||||
 | 
			
		||||
    &:hover,
 | 
			
		||||
    &:active,
 | 
			
		||||
    &:focus {
 | 
			
		||||
      background: darken($ui-primary-color, 2%);
 | 
			
		||||
      color: unset;
 | 
			
		||||
    }
 | 
			
		||||
    background: $ui-base-color;
 | 
			
		||||
    color: $ui-primary-color;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .pre-header {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -74,7 +74,7 @@ class ActivityPub::Activity
 | 
			
		|||
 | 
			
		||||
    # Only continue if the status is supposed to have
 | 
			
		||||
    # arrived in real-time
 | 
			
		||||
    return unless @options[:override_timestamps]
 | 
			
		||||
    return unless @options[:override_timestamps] || status.within_realtime_window?
 | 
			
		||||
 | 
			
		||||
    distribute_to_followers(status)
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,7 +61,7 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
 | 
			
		|||
    Rails.logger.debug "Queuing remote status #{status.id} (#{id}) for distribution"
 | 
			
		||||
 | 
			
		||||
    LinkCrawlWorker.perform_async(status.id) unless status.spoiler_text?
 | 
			
		||||
    DistributionWorker.perform_async(status.id) if @options[:override_timestamps]
 | 
			
		||||
    DistributionWorker.perform_async(status.id) if @options[:override_timestamps] || status.within_realtime_window?
 | 
			
		||||
 | 
			
		||||
    status
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,12 +16,16 @@ class AccountDomainBlock < ApplicationRecord
 | 
			
		|||
  belongs_to :account
 | 
			
		||||
  validates :domain, presence: true, uniqueness: { scope: :account_id }
 | 
			
		||||
 | 
			
		||||
  after_create  :remove_blocking_cache
 | 
			
		||||
  after_destroy :remove_blocking_cache
 | 
			
		||||
  after_commit :remove_blocking_cache
 | 
			
		||||
  after_commit :remove_relationship_cache
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def remove_blocking_cache
 | 
			
		||||
    Rails.cache.delete("exclude_domains_for:#{account_id}")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def remove_relationship_cache
 | 
			
		||||
    Rails.cache.delete_matched("relationship:#{account_id}:*")
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,14 +12,14 @@
 | 
			
		|||
 | 
			
		||||
class Block < ApplicationRecord
 | 
			
		||||
  include Paginable
 | 
			
		||||
  include RelationshipCacheable
 | 
			
		||||
 | 
			
		||||
  belongs_to :account
 | 
			
		||||
  belongs_to :target_account, class_name: 'Account'
 | 
			
		||||
 | 
			
		||||
  validates :account_id, uniqueness: { scope: :target_account_id }
 | 
			
		||||
 | 
			
		||||
  after_create  :remove_blocking_cache
 | 
			
		||||
  after_destroy :remove_blocking_cache
 | 
			
		||||
  after_commit :remove_blocking_cache
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,9 +7,15 @@ module AccountAvatar
 | 
			
		|||
 | 
			
		||||
  class_methods do
 | 
			
		||||
    def avatar_styles(file)
 | 
			
		||||
      styles = { original: '120x120#' }
 | 
			
		||||
      styles   = {}
 | 
			
		||||
      geometry = Paperclip::Geometry.from_file(file)
 | 
			
		||||
 | 
			
		||||
      styles[:original] = '120x120#' if geometry.width != geometry.height || geometry.width > 120 || geometry.height > 120
 | 
			
		||||
      styles[:static]   = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif'
 | 
			
		||||
 | 
			
		||||
      styles
 | 
			
		||||
    rescue Paperclip::Errors::NotIdentifiedByImageMagickError
 | 
			
		||||
      {}
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private :avatar_styles
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +23,7 @@ module AccountAvatar
 | 
			
		|||
 | 
			
		||||
  included do
 | 
			
		||||
    # Avatar upload
 | 
			
		||||
    has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-quality 80 -strip' }
 | 
			
		||||
    has_attached_file :avatar, styles: ->(f) { avatar_styles(f) }, convert_options: { all: '-strip' }
 | 
			
		||||
    validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES
 | 
			
		||||
    validates_attachment_size :avatar, less_than: 2.megabytes
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,9 +7,15 @@ module AccountHeader
 | 
			
		|||
 | 
			
		||||
  class_methods do
 | 
			
		||||
    def header_styles(file)
 | 
			
		||||
      styles = { original: '700x335#' }
 | 
			
		||||
      styles   = {}
 | 
			
		||||
      geometry = Paperclip::Geometry.from_file(file)
 | 
			
		||||
 | 
			
		||||
      styles[:original] = '700x335#' unless geometry.width == 700 && geometry.height == 335
 | 
			
		||||
      styles[:static]   = { format: 'png', convert_options: '-coalesce' } if file.content_type == 'image/gif'
 | 
			
		||||
 | 
			
		||||
      styles
 | 
			
		||||
    rescue Paperclip::Errors::NotIdentifiedByImageMagickError
 | 
			
		||||
      {}
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private :header_styles
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +23,7 @@ module AccountHeader
 | 
			
		|||
 | 
			
		||||
  included do
 | 
			
		||||
    # Header upload
 | 
			
		||||
    has_attached_file :header, styles: ->(f) { header_styles(f) }, convert_options: { all: '-quality 80 -strip' }
 | 
			
		||||
    has_attached_file :header, styles: ->(f) { header_styles(f) }, convert_options: { all: '-strip' }
 | 
			
		||||
    validates_attachment_content_type :header, content_type: IMAGE_MIME_TYPES
 | 
			
		||||
    validates_attachment_size :header, less_than: 2.megabytes
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								app/models/concerns/relationship_cacheable.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/models/concerns/relationship_cacheable.rb
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RelationshipCacheable
 | 
			
		||||
  extend ActiveSupport::Concern
 | 
			
		||||
 | 
			
		||||
  included do
 | 
			
		||||
    after_commit :remove_relationship_cache
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def remove_relationship_cache
 | 
			
		||||
    Rails.cache.delete("relationship:#{account_id}:#{target_account_id}")
 | 
			
		||||
    Rails.cache.delete("relationship:#{target_account_id}:#{account_id}")
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
 | 
			
		||||
class Follow < ApplicationRecord
 | 
			
		||||
  include Paginable
 | 
			
		||||
  include RelationshipCacheable
 | 
			
		||||
 | 
			
		||||
  belongs_to :account, counter_cache: :following_count
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@
 | 
			
		|||
 | 
			
		||||
class FollowRequest < ApplicationRecord
 | 
			
		||||
  include Paginable
 | 
			
		||||
  include RelationshipCacheable
 | 
			
		||||
 | 
			
		||||
  belongs_to :account
 | 
			
		||||
  belongs_to :target_account, class_name: 'Account'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,14 +13,14 @@
 | 
			
		|||
 | 
			
		||||
class Mute < ApplicationRecord
 | 
			
		||||
  include Paginable
 | 
			
		||||
  include RelationshipCacheable
 | 
			
		||||
 | 
			
		||||
  belongs_to :account
 | 
			
		||||
  belongs_to :target_account, class_name: 'Account'
 | 
			
		||||
 | 
			
		||||
  validates :account_id, uniqueness: { scope: :target_account_id }
 | 
			
		||||
 | 
			
		||||
  after_create  :remove_blocking_cache
 | 
			
		||||
  after_destroy :remove_blocking_cache
 | 
			
		||||
  after_commit :remove_blocking_cache
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,6 +83,8 @@ class Status < ApplicationRecord
 | 
			
		|||
 | 
			
		||||
  delegate :domain, to: :account, prefix: true
 | 
			
		||||
 | 
			
		||||
  REAL_TIME_WINDOW = 6.hours
 | 
			
		||||
 | 
			
		||||
  def searchable_by(preloaded = nil)
 | 
			
		||||
    ids = [account_id]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -111,6 +113,10 @@ class Status < ApplicationRecord
 | 
			
		|||
    !reblog_of_id.nil?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def within_realtime_window?
 | 
			
		||||
    created_at >= REAL_TIME_WINDOW.ago
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def verb
 | 
			
		||||
    if destroyed?
 | 
			
		||||
      :delete
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,11 +5,67 @@ class AccountRelationshipsPresenter
 | 
			
		|||
              :muting, :requested, :domain_blocking
 | 
			
		||||
 | 
			
		||||
  def initialize(account_ids, current_account_id, **options)
 | 
			
		||||
    @following       = Account.following_map(account_ids, current_account_id).merge(options[:following_map] || {})
 | 
			
		||||
    @followed_by     = Account.followed_by_map(account_ids, current_account_id).merge(options[:followed_by_map] || {})
 | 
			
		||||
    @blocking        = Account.blocking_map(account_ids, current_account_id).merge(options[:blocking_map] || {})
 | 
			
		||||
    @muting          = Account.muting_map(account_ids, current_account_id).merge(options[:muting_map] || {})
 | 
			
		||||
    @requested       = Account.requested_map(account_ids, current_account_id).merge(options[:requested_map] || {})
 | 
			
		||||
    @domain_blocking = Account.domain_blocking_map(account_ids, current_account_id).merge(options[:domain_blocking_map] || {})
 | 
			
		||||
    @account_ids        = account_ids.map { |a| a.is_a?(Account) ? a.id : a }
 | 
			
		||||
    @current_account_id = current_account_id
 | 
			
		||||
 | 
			
		||||
    @following       = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id))
 | 
			
		||||
    @followed_by     = cached[:followed_by].merge(Account.followed_by_map(@uncached_account_ids, @current_account_id))
 | 
			
		||||
    @blocking        = cached[:blocking].merge(Account.blocking_map(@uncached_account_ids, @current_account_id))
 | 
			
		||||
    @muting          = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id))
 | 
			
		||||
    @requested       = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id))
 | 
			
		||||
    @domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id))
 | 
			
		||||
 | 
			
		||||
    cache_uncached!
 | 
			
		||||
 | 
			
		||||
    @following.merge!(options[:following_map] || {})
 | 
			
		||||
    @followed_by.merge!(options[:followed_by_map] || {})
 | 
			
		||||
    @blocking.merge!(options[:blocking_map] || {})
 | 
			
		||||
    @muting.merge!(options[:muting_map] || {})
 | 
			
		||||
    @requested.merge!(options[:requested_map] || {})
 | 
			
		||||
    @domain_blocking.merge!(options[:domain_blocking_map] || {})
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def cached
 | 
			
		||||
    return @cached if defined?(@cached)
 | 
			
		||||
 | 
			
		||||
    @cached = {
 | 
			
		||||
      following: {},
 | 
			
		||||
      followed_by: {},
 | 
			
		||||
      blocking: {},
 | 
			
		||||
      muting: {},
 | 
			
		||||
      requested: {},
 | 
			
		||||
      domain_blocking: {},
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @uncached_account_ids = []
 | 
			
		||||
 | 
			
		||||
    @account_ids.each do |account_id|
 | 
			
		||||
      maps_for_account = Rails.cache.read("relationship:#{@current_account_id}:#{account_id}")
 | 
			
		||||
 | 
			
		||||
      if maps_for_account.is_a?(Hash)
 | 
			
		||||
        @cached.merge!(maps_for_account)
 | 
			
		||||
      else
 | 
			
		||||
        @uncached_account_ids << account_id
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    @cached
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def cache_uncached!
 | 
			
		||||
    @uncached_account_ids.each do |account_id|
 | 
			
		||||
      maps_for_account = {
 | 
			
		||||
        following:       { account_id => following[account_id] },
 | 
			
		||||
        followed_by:     { account_id => followed_by[account_id] },
 | 
			
		||||
        blocking:        { account_id => blocking[account_id] },
 | 
			
		||||
        muting:          { account_id => muting[account_id] },
 | 
			
		||||
        requested:       { account_id => requested[account_id] },
 | 
			
		||||
        domain_blocking: { account_id => domain_blocking[account_id] },
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,10 @@ Rails.application.configure do
 | 
			
		|||
  config.consider_all_requests_local       = true
 | 
			
		||||
  config.action_controller.perform_caching = false
 | 
			
		||||
 | 
			
		||||
  # The default store, file_store is shared by processses parallely executed
 | 
			
		||||
  # and should not be used.
 | 
			
		||||
  config.cache_store = :memory_store
 | 
			
		||||
 | 
			
		||||
  # Raise exceptions instead of rendering exception templates.
 | 
			
		||||
  config.action_dispatch.show_exceptions = false
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ RSpec.describe NotificationMailer, type: :mailer do
 | 
			
		|||
 | 
			
		||||
  shared_examples 'localized subject' do |*args, **kwrest|
 | 
			
		||||
    it 'renders subject localized for the locale of the receiver' do
 | 
			
		||||
      locale = I18n.available_locales.sample
 | 
			
		||||
      locale = %i(de en).sample
 | 
			
		||||
      receiver.update!(locale: locale)
 | 
			
		||||
      expect(mail.subject).to eq I18n.t(*args, kwrest.merge(locale: locale))
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,11 +42,6 @@ RSpec.describe Setting, type: :model do
 | 
			
		|||
        described_class[key]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'calls Rails.cache.fetch' do
 | 
			
		||||
        expect(Rails).to receive_message_chain(:cache, :fetch).with(cache_key)
 | 
			
		||||
        described_class[key]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      context 'Rails.cache does not exists' do
 | 
			
		||||
        before do
 | 
			
		||||
          allow(RailsSettings::Settings).to receive(:object).with(key).and_return(object)
 | 
			
		||||
| 
						 | 
				
			
			@ -103,6 +98,14 @@ RSpec.describe Setting, type: :model do
 | 
			
		|||
          Rails.cache.write(cache_key, cache_value)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        it 'does not query the database' do
 | 
			
		||||
          expect do |callback|
 | 
			
		||||
            ActiveSupport::Notifications.subscribed callback, 'sql.active_record' do
 | 
			
		||||
              described_class[key]
 | 
			
		||||
            end
 | 
			
		||||
          end.not_to yield_control
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        it 'returns the cached value' do
 | 
			
		||||
          expect(described_class[key]).to eq cache_value
 | 
			
		||||
        end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -90,9 +90,7 @@ describe InstancePresenter do
 | 
			
		|||
 | 
			
		||||
  describe "user_count" do
 | 
			
		||||
    it "returns the number of site users" do
 | 
			
		||||
      cache = double
 | 
			
		||||
      allow(Rails).to receive(:cache).and_return(cache)
 | 
			
		||||
      allow(cache).to receive(:fetch).with("user_count").and_return(123)
 | 
			
		||||
      Rails.cache.write 'user_count', 123
 | 
			
		||||
 | 
			
		||||
      expect(instance_presenter.user_count).to eq(123)
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			@ -100,9 +98,7 @@ describe InstancePresenter do
 | 
			
		|||
 | 
			
		||||
  describe "status_count" do
 | 
			
		||||
    it "returns the number of local statuses" do
 | 
			
		||||
      cache = double
 | 
			
		||||
      allow(Rails).to receive(:cache).and_return(cache)
 | 
			
		||||
      allow(cache).to receive(:fetch).with("local_status_count").and_return(234)
 | 
			
		||||
      Rails.cache.write 'local_status_count', 234
 | 
			
		||||
 | 
			
		||||
      expect(instance_presenter.status_count).to eq(234)
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			@ -110,9 +106,7 @@ describe InstancePresenter do
 | 
			
		|||
 | 
			
		||||
  describe "domain_count" do
 | 
			
		||||
    it "returns the number of known domains" do
 | 
			
		||||
      cache = double
 | 
			
		||||
      allow(Rails).to receive(:cache).and_return(cache)
 | 
			
		||||
      allow(cache).to receive(:fetch).with("distinct_domain_count").and_return(345)
 | 
			
		||||
      Rails.cache.write 'distinct_domain_count', 345
 | 
			
		||||
 | 
			
		||||
      expect(instance_presenter.domain_count).to eq(345)
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,8 @@ RSpec.configure do |config|
 | 
			
		|||
  end
 | 
			
		||||
 | 
			
		||||
  config.after :each do
 | 
			
		||||
    Rails.cache.clear
 | 
			
		||||
 | 
			
		||||
    keys = Redis.current.keys
 | 
			
		||||
    Redis.current.del(keys) if keys.any?
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,35 @@
 | 
			
		|||
require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
RSpec.describe FetchRemoteStatusService do
 | 
			
		||||
  let(:account) { Fabricate(:account) }
 | 
			
		||||
  let(:prefetched_body) { nil }
 | 
			
		||||
  let(:valid_domain) { Rails.configuration.x.local_domain }
 | 
			
		||||
 | 
			
		||||
  let(:note) do
 | 
			
		||||
    {
 | 
			
		||||
      '@context': 'https://www.w3.org/ns/activitystreams',
 | 
			
		||||
      id: "https://#{valid_domain}/@foo/1234",
 | 
			
		||||
      type: 'Note',
 | 
			
		||||
      content: 'Lorem ipsum',
 | 
			
		||||
      attributedTo: ActivityPub::TagManager.instance.uri_for(account),
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'protocol is :activitypub' do
 | 
			
		||||
    subject { described_class.new.call(note[:id], prefetched_body, protocol) }
 | 
			
		||||
    let(:prefetched_body) { Oj.dump(note) }
 | 
			
		||||
    let(:protocol) { :activitypub }
 | 
			
		||||
 | 
			
		||||
    before do
 | 
			
		||||
      account.update(uri: ActivityPub::TagManager.instance.uri_for(account))
 | 
			
		||||
      subject
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it 'creates status' do
 | 
			
		||||
      status = account.statuses.first
 | 
			
		||||
 | 
			
		||||
      expect(status).to_not be_nil
 | 
			
		||||
      expect(status.text).to eq 'Lorem ipsum'
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue