Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
3e0cd21e
Commit
3e0cd21e
authored
Sep 28, 2018
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-09-28
parents
a431e373
ba66e0cc
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
455 additions
and
63 deletions
+455
-63
app/models/repository.rb
app/models/repository.rb
+6
-2
app/views/shared/members/_member.html.haml
app/views/shared/members/_member.html.haml
+1
-1
changelogs/unreleased/mk-asymmetric-exists-cache.yml
changelogs/unreleased/mk-asymmetric-exists-cache.yml
+6
-0
changelogs/unreleased/rename-local-variable.yml
changelogs/unreleased/rename-local-variable.yml
+5
-0
lib/gitlab/repository_cache.rb
lib/gitlab/repository_cache.rb
+16
-0
lib/gitlab/repository_cache_adapter.rb
lib/gitlab/repository_cache_adapter.rb
+133
-48
spec/controllers/projects/pipelines_controller_spec.rb
spec/controllers/projects/pipelines_controller_spec.rb
+5
-0
spec/javascripts/vue_shared/components/table_pagination_spec.js
...avascripts/vue_shared/components/table_pagination_spec.js
+11
-11
spec/lib/gitlab/repository_cache_adapter_spec.rb
spec/lib/gitlab/repository_cache_adapter_spec.rb
+138
-0
spec/lib/gitlab/repository_cache_spec.rb
spec/lib/gitlab/repository_cache_spec.rb
+85
-0
spec/models/repository_spec.rb
spec/models/repository_spec.rb
+49
-1
No files found.
app/models/repository.rb
View file @
3e0cd21e
...
...
@@ -515,7 +515,7 @@ class Repository
raw_repository
.
exists?
end
cache_method
:exists?
cache_method
_asymmetrically
:exists?
# We don't need to cache the output of this method because both exists? and
# has_visible_content? are already memoized and cached. There's no guarantee
...
...
@@ -617,7 +617,7 @@ class Repository
Licensee
::
License
.
new
(
license_key
)
end
cache_method
:license
,
memoize_only:
tru
e
memoize_method
:licens
e
def
gitignore
file_on_head
(
:gitignore
)
...
...
@@ -1034,6 +1034,10 @@ class Repository
@cache
||=
Gitlab
::
RepositoryCache
.
new
(
self
)
end
def
request_store_cache
@request_store_cache
||=
Gitlab
::
RepositoryCache
.
new
(
self
,
backend:
Gitlab
::
SafeRequestStore
)
end
def
tags_sorted_by_committed_date
tags
.
sort_by
do
|
tag
|
# Annotated tags can point to any object (e.g. a blob), but generally
...
...
app/views/shared/members/_member.html.haml
View file @
3e0cd21e
...
...
@@ -22,7 +22,7 @@
%strong
Blocked
-
if
user
.
two_factor_enabled?
%label
.
label.label
-info
%label
.
badge.badge
-info
2FA
-
if
source
.
instance_of?
(
Group
)
&&
source
!=
@group
...
...
changelogs/unreleased/mk-asymmetric-exists-cache.yml
0 → 100644
View file @
3e0cd21e
---
title
:
'
Resolve
"Geo:
Does
not
mark
repositories
as
missing
on
primary
due
to
stale
cache"'
merge_request
:
21789
author
:
type
:
fixed
changelogs/unreleased/rename-local-variable.yml
0 → 100644
View file @
3e0cd21e
---
title
:
Rename block scope local variable in table pagination spec
merge_request
:
21969
author
:
George Tsiolis
type
:
other
lib/gitlab/repository_cache.rb
View file @
3e0cd21e
...
...
@@ -29,5 +29,21 @@ module Gitlab
def
read
(
key
)
backend
.
read
(
cache_key
(
key
))
end
def
write
(
key
,
value
)
backend
.
write
(
cache_key
(
key
),
value
)
end
def
fetch_without_caching_false
(
key
,
&
block
)
value
=
read
(
key
)
return
value
if
value
value
=
yield
# Don't cache false values
write
(
key
,
value
)
if
value
value
end
end
end
lib/gitlab/repository_cache_adapter.rb
View file @
3e0cd21e
module
Gitlab
module
RepositoryCacheAdapter
extend
ActiveSupport
::
Concern
include
Gitlab
::
Utils
::
StrongMemoize
class_methods
do
# Wraps around the given method and caches its output in Redis and an instance
# variable.
# Caches and strongly memoizes the method.
#
# This only works for methods that do not take any arguments.
def
cache_method
(
name
,
fallback:
nil
,
memoize_only:
false
)
original
=
:"_uncached_
#{
name
}
"
#
# name - The name of the method to be cached.
# fallback - A value to fall back to if the repository does not exist, or
# in case of a Git error. Defaults to nil.
def
cache_method
(
name
,
fallback:
nil
)
uncached_name
=
alias_uncached_method
(
name
)
define_method
(
name
)
do
cache_method_output
(
name
,
fallback:
fallback
)
do
__send__
(
uncached_name
)
# rubocop:disable GitlabSecurity/PublicSend
end
end
end
alias_method
(
original
,
name
)
# Caches truthy values from the method. All values are strongly memoized,
# and cached in RequestStore.
#
# Currently only used to cache `exists?` since stale false values are
# particularly troublesome. This can occur, for example, when an NFS mount
# is temporarily down.
#
# This only works for methods that do not take any arguments.
#
# name - The name of the method to be cached.
def
cache_method_asymmetrically
(
name
)
uncached_name
=
alias_uncached_method
(
name
)
define_method
(
name
)
do
cache_method_output
(
name
,
fallback:
fallback
,
memoize_only:
memoize_only
)
do
__send__
(
original
)
# rubocop:disable GitlabSecurity/PublicSend
cache_method_output
_asymmetrically
(
name
)
do
__send__
(
uncached_name
)
# rubocop:disable GitlabSecurity/PublicSend
end
end
end
# Strongly memoizes the method.
#
# This only works for methods that do not take any arguments.
#
# name - The name of the method to be memoized.
# fallback - A value to fall back to if the repository does not exist, or
# in case of a Git error. Defaults to nil. The fallback value
# is not memoized.
def
memoize_method
(
name
,
fallback:
nil
)
uncached_name
=
alias_uncached_method
(
name
)
define_method
(
name
)
do
memoize_method_output
(
name
,
fallback:
fallback
)
do
__send__
(
uncached_name
)
# rubocop:disable GitlabSecurity/PublicSend
end
end
end
# Prepends "_uncached_" to the target method name
#
# Returns the uncached method name
def
alias_uncached_method
(
name
)
uncached_name
=
:"_uncached_
#{
name
}
"
alias_method
(
uncached_name
,
name
)
uncached_name
end
end
# RequestStore-backed RepositoryCache to be used. Should be overridden by
# the including class
def
request_store_cache
raise
NotImplementedError
end
# RepositoryCache to be used. Should be overridden by the including class
...
...
@@ -30,65 +87,93 @@ module Gitlab
raise
NotImplementedError
end
# Caches
the supplied block both in a cache and in an instance variable
.
# Caches
and strongly memoizes the supplied block
.
#
# The cache key and instance variable are named the same way as the value of
# the `key` argument.
# name - The name of the method to be cached.
# fallback - A value to fall back to if the repository does not exist, or
# in case of a Git error. Defaults to nil.
def
cache_method_output
(
name
,
fallback:
nil
,
&
block
)
memoize_method_output
(
name
,
fallback:
fallback
)
do
cache
.
fetch
(
name
,
&
block
)
end
end
# Caches truthy values from the supplied block. All values are strongly
# memoized, and cached in RequestStore.
#
#
This method will return `nil` if the corresponding instance variable is also
#
set to `nil`. This ensures we don't keep yielding the block when it returns
#
`nil`
.
#
Currently only used to cache `exists?` since stale false values are
#
particularly troublesome. This can occur, for example, when an NFS mount
#
is temporarily down
.
#
# key - The name of the key to cache the data in.
# fallback - A value to fall back to in the event of a Git error.
def
cache_method_output
(
key
,
fallback:
nil
,
memoize_only:
false
,
&
block
)
ivar
=
cache_instance_variable_name
(
key
)
if
instance_variable_defined?
(
ivar
)
instance_variable_get
(
ivar
)
else
# If the repository doesn't exist and a fallback was specified we return
# that value inmediately. This saves us Rugged/gRPC invocations.
return
fallback
unless
fallback
.
nil?
||
cache
.
repository
.
exists?
begin
value
=
if
memoize_only
yield
else
cache
.
fetch
(
key
,
&
block
)
end
instance_variable_set
(
ivar
,
value
)
rescue
Gitlab
::
Git
::
Repository
::
NoRepository
# Even if the above `#exists?` check passes these errors might still
# occur (for example because of a non-existing HEAD). We want to
# gracefully handle this and not cache anything
fallback
# name - The name of the method to be cached.
def
cache_method_output_asymmetrically
(
name
,
&
block
)
memoize_method_output
(
name
)
do
request_store_cache
.
fetch
(
name
)
do
cache
.
fetch_without_caching_false
(
name
,
&
block
)
end
end
end
# Strongly memoizes the supplied block.
#
# name - The name of the method to be memoized.
# fallback - A value to fall back to if the repository does not exist, or
# in case of a Git error. Defaults to nil. The fallback value is
# not memoized.
def
memoize_method_output
(
name
,
fallback:
nil
,
&
block
)
no_repository_fallback
(
name
,
fallback:
fallback
)
do
strong_memoize
(
memoizable_name
(
name
),
&
block
)
end
end
# Returns the fallback value if the repository does not exist
def
no_repository_fallback
(
name
,
fallback:
nil
,
&
block
)
# Avoid unnecessary gRPC invocations
return
fallback
if
fallback
&&
fallback_early?
(
name
)
yield
rescue
Gitlab
::
Git
::
Repository
::
NoRepository
# Even if the `#exists?` check in `fallback_early?` passes, these errors
# might still occur (for example because of a non-existing HEAD). We
# want to gracefully handle this and not memoize anything.
fallback
end
# Expires the caches of a specific set of methods
def
expire_method_caches
(
methods
)
methods
.
each
do
|
key
|
unless
cached_methods
.
include?
(
key
.
to_sym
)
Rails
.
logger
.
error
"Requested to expire non-existent method '
#{
key
}
' for Repository"
methods
.
each
do
|
name
|
unless
cached_methods
.
include?
(
name
.
to_sym
)
Rails
.
logger
.
error
"Requested to expire non-existent method '
#{
name
}
' for Repository"
next
end
cache
.
expire
(
key
)
cache
.
expire
(
name
)
ivar
=
cache_instance_variable_name
(
key
)
remove_instance_variable
(
ivar
)
if
instance_variable_defined?
(
ivar
)
clear_memoization
(
memoizable_name
(
name
))
end
expire_request_store_method_caches
(
methods
)
end
private
def
cache_instance_variable_name
(
key
)
:"@
#{
key
.
to_s
.
tr
(
'?!'
,
''
)
}
"
def
memoizable_name
(
name
)
"
#{
name
.
to_s
.
tr
(
'?!'
,
''
)
}
"
end
def
expire_request_store_method_caches
(
methods
)
methods
.
each
do
|
name
|
request_store_cache
.
expire
(
name
)
end
end
# All cached repository methods depend on the existence of a Git repository,
# so if the repository doesn't exist, we already know not to call it.
def
fallback_early?
(
method_name
)
# Avoid infinite loop
return
false
if
method_name
==
:exists?
!
exists?
end
end
end
spec/controllers/projects/pipelines_controller_spec.rb
View file @
3e0cd21e
...
...
@@ -90,6 +90,11 @@ describe Projects::PipelinesController do
context
'when performing gitaly calls'
,
:request_store
do
it
'limits the Gitaly requests'
do
# Isolate from test preparation (Repository#exists? is also cached in RequestStore)
RequestStore
.
end!
RequestStore
.
clear!
RequestStore
.
begin!
expect
{
get_pipelines_index_json
}
.
to
change
{
Gitlab
::
GitalyClient
.
get_request_count
}.
by
(
2
)
end
...
...
spec/javascripts/vue_shared/components/table_pagination_spec.js
View file @
3e0cd21e
...
...
@@ -5,13 +5,13 @@ describe('Pagination component', () => {
let
component
;
let
PaginationComponent
;
let
spy
;
let
mountComponet
;
let
mountCompone
n
t
;
beforeEach
(()
=>
{
spy
=
jasmine
.
createSpy
(
'
spy
'
);
PaginationComponent
=
Vue
.
extend
(
paginationComp
);
mountComponet
=
function
(
props
)
{
mountCompone
n
t
=
function
(
props
)
{
return
new
PaginationComponent
({
propsData
:
props
,
}).
$mount
();
...
...
@@ -20,7 +20,7 @@ describe('Pagination component', () => {
describe
(
'
render
'
,
()
=>
{
it
(
'
should not render anything
'
,
()
=>
{
component
=
mountComponet
({
component
=
mountCompone
n
t
({
pageInfo
:
{
nextPage
:
1
,
page
:
1
,
...
...
@@ -37,7 +37,7 @@ describe('Pagination component', () => {
describe
(
'
prev button
'
,
()
=>
{
it
(
'
should be disabled and non clickable
'
,
()
=>
{
component
=
mountComponet
({
component
=
mountCompone
n
t
({
pageInfo
:
{
nextPage
:
2
,
page
:
1
,
...
...
@@ -59,7 +59,7 @@ describe('Pagination component', () => {
});
it
(
'
should be enabled and clickable
'
,
()
=>
{
component
=
mountComponet
({
component
=
mountCompone
n
t
({
pageInfo
:
{
nextPage
:
3
,
page
:
2
,
...
...
@@ -78,7 +78,7 @@ describe('Pagination component', () => {
describe
(
'
first button
'
,
()
=>
{
it
(
'
should call the change callback with the first page
'
,
()
=>
{
component
=
mountComponet
({
component
=
mountCompone
n
t
({
pageInfo
:
{
nextPage
:
3
,
page
:
2
,
...
...
@@ -102,7 +102,7 @@ describe('Pagination component', () => {
describe
(
'
last button
'
,
()
=>
{
it
(
'
should call the change callback with the last page
'
,
()
=>
{
component
=
mountComponet
({
component
=
mountCompone
n
t
({
pageInfo
:
{
nextPage
:
3
,
page
:
2
,
...
...
@@ -126,7 +126,7 @@ describe('Pagination component', () => {
describe
(
'
next button
'
,
()
=>
{
it
(
'
should be disabled and non clickable
'
,
()
=>
{
component
=
mountComponet
({
component
=
mountCompone
n
t
({
pageInfo
:
{
nextPage
:
5
,
page
:
5
,
...
...
@@ -148,7 +148,7 @@ describe('Pagination component', () => {
});
it
(
'
should be enabled and clickable
'
,
()
=>
{
component
=
mountComponet
({
component
=
mountCompone
n
t
({
pageInfo
:
{
nextPage
:
4
,
page
:
3
,
...
...
@@ -168,7 +168,7 @@ describe('Pagination component', () => {
describe
(
'
numbered buttons
'
,
()
=>
{
it
(
'
should render 5 pages
'
,
()
=>
{
component
=
mountComponet
({
component
=
mountCompone
n
t
({
pageInfo
:
{
nextPage
:
4
,
page
:
3
,
...
...
@@ -185,7 +185,7 @@ describe('Pagination component', () => {
});
it
(
'
should render the spread operator
'
,
()
=>
{
component
=
mountComponet
({
component
=
mountCompone
n
t
({
pageInfo
:
{
nextPage
:
4
,
page
:
3
,
...
...
spec/lib/gitlab/repository_cache_adapter_spec.rb
View file @
3e0cd21e
...
...
@@ -65,6 +65,144 @@ describe Gitlab::RepositoryCacheAdapter do
end
end
describe
'#cache_method_output_asymmetrically'
,
:use_clean_rails_memory_store_caching
,
:request_store
do
let
(
:request_store_cache
)
{
repository
.
send
(
:request_store_cache
)
}
context
'with a non-existing repository'
do
let
(
:project
)
{
create
(
:project
)
}
# No repository
let
(
:object
)
{
double
}
subject
do
repository
.
cache_method_output_asymmetrically
(
:cats
)
do
object
.
cats_call_stub
end
end
it
'returns the output of the original method'
do
expect
(
object
).
to
receive
(
:cats_call_stub
).
and_return
(
'output'
)
expect
(
subject
).
to
eq
(
'output'
)
end
end
context
'with a method throwing a non-existing-repository error'
do
subject
do
repository
.
cache_method_output_asymmetrically
(
:cats
)
do
raise
Gitlab
::
Git
::
Repository
::
NoRepository
end
end
it
'returns nil'
do
expect
(
subject
).
to
eq
(
nil
)
end
it
'does not cache the data'
do
subject
expect
(
repository
.
instance_variable_defined?
(
:@cats
)).
to
eq
(
false
)
expect
(
cache
.
exist?
(
:cats
)).
to
eq
(
false
)
end
end
context
'with an existing repository'
do
let
(
:object
)
{
double
}
context
'when it returns truthy'
do
before
do
expect
(
object
).
to
receive
(
:cats
).
once
.
and_return
(
'truthy output'
)
end
it
'caches the output in RequestStore'
do
expect
do
repository
.
cache_method_output_asymmetrically
(
:cats
)
{
object
.
cats
}
end
.
to
change
{
request_store_cache
.
read
(
:cats
)
}.
from
(
nil
).
to
(
'truthy output'
)
end
it
'caches the output in RepositoryCache'
do
expect
do
repository
.
cache_method_output_asymmetrically
(
:cats
)
{
object
.
cats
}
end
.
to
change
{
cache
.
read
(
:cats
)
}.
from
(
nil
).
to
(
'truthy output'
)
end
end
context
'when it returns false'
do
before
do
expect
(
object
).
to
receive
(
:cats
).
once
.
and_return
(
false
)
end
it
'caches the output in RequestStore'
do
expect
do
repository
.
cache_method_output_asymmetrically
(
:cats
)
{
object
.
cats
}
end
.
to
change
{
request_store_cache
.
read
(
:cats
)
}.
from
(
nil
).
to
(
false
)
end
it
'does NOT cache the output in RepositoryCache'
do
expect
do
repository
.
cache_method_output_asymmetrically
(
:cats
)
{
object
.
cats
}
end
.
not_to
change
{
cache
.
read
(
:cats
)
}.
from
(
nil
)
end
end
end
end
describe
'#memoize_method_output'
do
let
(
:fallback
)
{
10
}
context
'with a non-existing repository'
do
let
(
:project
)
{
create
(
:project
)
}
# No repository
subject
do
repository
.
memoize_method_output
(
:cats
,
fallback:
fallback
)
do
repository
.
cats_call_stub
end
end
it
'returns the fallback value'
do
expect
(
subject
).
to
eq
(
fallback
)
end
it
'avoids calling the original method'
do
expect
(
repository
).
not_to
receive
(
:cats_call_stub
)
subject
end
it
'does not set the instance variable'
do
subject
expect
(
repository
.
instance_variable_defined?
(
:@cats
)).
to
eq
(
false
)
end
end
context
'with a method throwing a non-existing-repository error'
do
subject
do
repository
.
memoize_method_output
(
:cats
,
fallback:
fallback
)
do
raise
Gitlab
::
Git
::
Repository
::
NoRepository
end
end
it
'returns the fallback value'
do
expect
(
subject
).
to
eq
(
fallback
)
end
it
'does not set the instance variable'
do
subject
expect
(
repository
.
instance_variable_defined?
(
:@cats
)).
to
eq
(
false
)
end
end
context
'with an existing repository'
do
it
'sets the instance variable'
do
repository
.
memoize_method_output
(
:cats
,
fallback:
fallback
)
do
'block output'
end
expect
(
repository
.
instance_variable_get
(
:@cats
)).
to
eq
(
'block output'
)
end
end
end
describe
'#expire_method_caches'
do
it
'expires the caches of the given methods'
do
expect
(
cache
).
to
receive
(
:expire
).
with
(
:rendered_readme
)
...
...
spec/lib/gitlab/repository_cache_spec.rb
View file @
3e0cd21e
...
...
@@ -47,4 +47,89 @@ describe Gitlab::RepositoryCache do
expect
(
backend
).
to
have_received
(
:fetch
).
with
(
"baz:
#{
namespace
}
"
,
&
p
)
end
end
describe
'#fetch_without_caching_false'
,
:use_clean_rails_memory_store_caching
do
let
(
:key
)
{
:foo
}
let
(
:backend
)
{
Rails
.
cache
}
it
'requires a block'
do
expect
do
cache
.
fetch_without_caching_false
(
key
)
end
.
to
raise_error
(
LocalJumpError
)
end
context
'when the key does not exist in the cache'
do
context
'when the result of the block is truthy'
do
it
'returns the result of the block'
do
result
=
cache
.
fetch_without_caching_false
(
key
)
{
true
}
expect
(
result
).
to
be
true
end
it
'caches the value'
do
expect
(
backend
).
to
receive
(
:write
).
with
(
"
#{
key
}
:
#{
namespace
}
"
,
true
)
cache
.
fetch_without_caching_false
(
key
)
{
true
}
end
end
context
'when the result of the block is falsey'
do
let
(
:p
)
{
->
{
false
}
}
it
'returns the result of the block'
do
result
=
cache
.
fetch_without_caching_false
(
key
,
&
p
)
expect
(
result
).
to
be
false
end
it
'does not cache the value'
do
expect
(
backend
).
not_to
receive
(
:write
).
with
(
"
#{
key
}
:
#{
namespace
}
"
,
true
)
cache
.
fetch_without_caching_false
(
key
,
&
p
)
end
end
end
context
'when the cached value is truthy'
do
before
do
backend
.
write
(
"
#{
key
}
:
#{
namespace
}
"
,
true
)
end
it
'returns the cached value'
do
result
=
cache
.
fetch_without_caching_false
(
key
)
{
'block result'
}
expect
(
result
).
to
be
true
end
it
'does not execute the block'
do
expect
do
|
b
|
cache
.
fetch_without_caching_false
(
key
,
&
b
)
end
.
not_to
yield_control
end
it
'does not write to the cache'
do
expect
(
backend
).
not_to
receive
(
:write
)
cache
.
fetch_without_caching_false
(
key
)
{
'block result'
}
end
end
context
'when the cached value is falsey'
do
before
do
backend
.
write
(
"
#{
key
}
:
#{
namespace
}
"
,
false
)
end
it
'returns the result of the block'
do
result
=
cache
.
fetch_without_caching_false
(
key
)
{
'block result'
}
expect
(
result
).
to
eq
'block result'
end
it
'writes the truthy value to the cache'
do
expect
(
backend
).
to
receive
(
:write
).
with
(
"
#{
key
}
:
#{
namespace
}
"
,
'block result'
)
cache
.
fetch_without_caching_false
(
key
)
{
'block result'
}
end
end
end
end
spec/models/repository_spec.rb
View file @
3e0cd21e
...
...
@@ -1044,6 +1044,47 @@ describe Repository do
expect_to_raise_storage_error
{
broken_repository
.
exists?
}
end
end
context
'asymmetric caching'
,
:use_clean_rails_memory_store_caching
,
:request_store
do
let
(
:cache
)
{
repository
.
send
(
:cache
)
}
let
(
:request_store_cache
)
{
repository
.
send
(
:request_store_cache
)
}
context
'when it returns true'
do
before
do
expect
(
repository
.
raw_repository
).
to
receive
(
:exists?
).
once
.
and_return
(
true
)
end
it
'caches the output in RequestStore'
do
expect
do
repository
.
exists?
end
.
to
change
{
request_store_cache
.
read
(
:exists?
)
}.
from
(
nil
).
to
(
true
)
end
it
'caches the output in RepositoryCache'
do
expect
do
repository
.
exists?
end
.
to
change
{
cache
.
read
(
:exists?
)
}.
from
(
nil
).
to
(
true
)
end
end
context
'when it returns false'
do
before
do
expect
(
repository
.
raw_repository
).
to
receive
(
:exists?
).
once
.
and_return
(
false
)
end
it
'caches the output in RequestStore'
do
expect
do
repository
.
exists?
end
.
to
change
{
request_store_cache
.
read
(
:exists?
)
}.
from
(
nil
).
to
(
false
)
end
it
'does NOT cache the output in RepositoryCache'
do
expect
do
repository
.
exists?
end
.
not_to
change
{
cache
.
read
(
:exists?
)
}.
from
(
nil
)
end
end
end
end
describe
'#has_visible_content?'
do
...
...
@@ -1716,12 +1757,19 @@ describe Repository do
describe
'#expire_exists_cache'
do
let
(
:cache
)
{
repository
.
send
(
:cache
)
}
let
(
:request_store_cache
)
{
repository
.
send
(
:request_store_cache
)
}
it
'expires the cache'
do
expect
(
cache
).
to
receive
(
:expire
).
with
(
:exists?
)
repository
.
expire_exists_cache
end
it
'expires the request store cache'
,
:request_store
do
expect
(
request_store_cache
).
to
receive
(
:expire
).
with
(
:exists?
)
repository
.
expire_exists_cache
end
end
describe
'#xcode_project?'
do
...
...
@@ -1892,7 +1940,7 @@ describe Repository do
match
[
1
].
to_sym
if
match
end
.
compact
expect
(
methods
).
to
match_array
(
Repository
::
CACHED_METHODS
+
Repository
::
MEMOIZED_CACHED_METHODS
)
expect
(
Repository
::
CACHED_METHODS
+
Repository
::
MEMOIZED_CACHED_METHODS
).
to
include
(
*
methods
)
end
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment