feat(cache.procedure_dossier_pagination): add Cache::ProcedureDossierPagination to next/previous dossiers by statut. also responsible to fetch next/previous page
This commit is contained in:
parent
b8481796c7
commit
1b8db6abee
2 changed files with 193 additions and 0 deletions
107
app/models/cache/procedure_dossier_pagination.rb
vendored
Normal file
107
app/models/cache/procedure_dossier_pagination.rb
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Cache::ProcedureDossierPagination
|
||||
HALF_WINDOW = 50
|
||||
TRESHOLD_BEFORE_REFRESH = 1
|
||||
CACHE_EXPIRACY = 8.hours
|
||||
|
||||
attr_reader :procedure_presentation, :statut, :cache
|
||||
delegate :procedure, :instructeur, to: :procedure_presentation
|
||||
|
||||
def initialize(procedure_presentation:, statut:)
|
||||
@procedure_presentation = procedure_presentation
|
||||
@statut = statut
|
||||
@cache = Kredis.json(cache_key, expires_in: CACHE_EXPIRACY)
|
||||
end
|
||||
|
||||
def save_context(ids:, incoming_page:)
|
||||
value = { ids: }
|
||||
value[:incoming_page] = incoming_page if incoming_page
|
||||
write_cache(value)
|
||||
end
|
||||
|
||||
def next_dossier_id(from_id:)
|
||||
index = ids&.index(from_id.to_i)
|
||||
|
||||
return nil if index.nil? # not found
|
||||
|
||||
if refresh_cache_after?(from_id:)
|
||||
renew_ids(from_id:)
|
||||
index = ids.index(from_id.to_i)
|
||||
end
|
||||
return nil if index.blank?
|
||||
return nil if index + 1 > ids.size # out of bound end
|
||||
|
||||
ids[index + 1]
|
||||
end
|
||||
|
||||
def previous_dossier_id(from_id:)
|
||||
index = ids&.index(from_id.to_i)
|
||||
|
||||
return nil if index.nil? # not found
|
||||
|
||||
if refresh_cache_before?(from_id:)
|
||||
renew_ids(from_id:)
|
||||
index = ids.index(from_id.to_i)
|
||||
end
|
||||
return nil if index.blank?
|
||||
return nil if index - 1 < 0 # out of bound start
|
||||
|
||||
ids[index - 1]
|
||||
end
|
||||
|
||||
def incoming_page
|
||||
read_cache[:incoming_page]
|
||||
end
|
||||
|
||||
# test only
|
||||
def raw
|
||||
read_cache
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cache_key
|
||||
[procedure.id, instructeur.id, statut].join(":")
|
||||
end
|
||||
|
||||
def write_cache(value)
|
||||
cache.value = value
|
||||
@read_cache = nil
|
||||
end
|
||||
|
||||
def read_cache
|
||||
@read_cache ||= Hash(cache.value).with_indifferent_access
|
||||
end
|
||||
|
||||
def ids = read_cache[:ids]
|
||||
|
||||
def refresh_cache_after?(from_id:) = from_id.in?(ids.last(TRESHOLD_BEFORE_REFRESH))
|
||||
|
||||
def refresh_cache_before?(from_id:) = from_id.in?(ids.first(TRESHOLD_BEFORE_REFRESH))
|
||||
|
||||
def renew_ids(from_id:)
|
||||
value = read_cache
|
||||
value[:ids] = fetch_ids_around(from_id:)
|
||||
|
||||
write_cache(value)
|
||||
end
|
||||
|
||||
def fetch_all_ids
|
||||
dossiers = Dossier.where(groupe_instructeur_id: GroupeInstructeur.joins(:instructeurs, :procedure).where(procedure: procedure, instructeurs: [instructeur]).select(:id))
|
||||
DossierFilterService.filtered_sorted_ids(dossiers, statut, procedure_presentation.filters_for(statut), procedure_presentation.sorted_column, instructeur, count: 0)
|
||||
end
|
||||
|
||||
def fetch_ids_around(from_id:)
|
||||
all_ids = fetch_all_ids
|
||||
from_id_at = all_ids.index(from_id)
|
||||
|
||||
if from_id_at.present?
|
||||
new_page_starts_at = [0, from_id_at - HALF_WINDOW].max # avoid index below 0
|
||||
new_page_ends_at = [from_id_at + HALF_WINDOW, all_ids.size].min # avoid index above all_ids.size
|
||||
all_ids.slice(new_page_starts_at, new_page_ends_at)
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
86
spec/models/cache/procedure_dossier_pagination_spec.rb
vendored
Normal file
86
spec/models/cache/procedure_dossier_pagination_spec.rb
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe Cache::ProcedureDossierPagination do
|
||||
let(:instructeur) { double(id: 1) }
|
||||
let(:procedure) { double(id: 1) }
|
||||
let(:procedure_presentation) { double(instructeur:, procedure:) }
|
||||
let(:instance) { described_class.new(procedure_presentation:, statut: 'a-suivre') }
|
||||
|
||||
before do
|
||||
instance.save_context(ids: cached_ids, incoming_page: nil)
|
||||
end
|
||||
|
||||
describe 'next_dossier_id' do
|
||||
context 'when procedure.dossiers.by_statut has only one page' do
|
||||
let(:cached_ids) { [3, 4] }
|
||||
before do
|
||||
allow(instance).to receive(:fetch_all_ids).and_return(cached_ids)
|
||||
end
|
||||
|
||||
it 'find next until the end' do
|
||||
expect(instance.next_dossier_id(from_id: cached_ids.last)).to eq(nil)
|
||||
expect(instance.next_dossier_id(from_id: cached_ids.first)).to eq(cached_ids.last)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure.dossiers.by_statut has more than one page' do
|
||||
let(:cached_ids) { [2, 3, 4] }
|
||||
let(:next_page_ids) { (0..10).to_a }
|
||||
|
||||
subject { instance.next_dossier_id(from_id: cached_ids.last) }
|
||||
before do
|
||||
allow(instance).to receive(:fetch_all_ids).and_return(next_page_ids)
|
||||
end
|
||||
|
||||
it 'refreshes paginated_ids' do
|
||||
expect { subject }.to change { instance.send(:ids) }.from(cached_ids).to(next_page_ids)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure.dossiers.by_statut does not include searched dossiers anymore' do
|
||||
let(:cached_ids) { [] }
|
||||
let(:next_page_ids) { [] }
|
||||
before { allow(instance).to receive(:fetch_all_ids).and_return(next_page_ids) }
|
||||
|
||||
it 'works' do
|
||||
expect(instance.next_dossier_id(from_id: 50)).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'previous_dossier_id' do
|
||||
context 'when procedure.dossiers.by_statut has only one page' do
|
||||
let(:cached_ids) { [3, 4] }
|
||||
before do
|
||||
allow(instance).to receive(:fetch_all_ids).and_return(cached_ids)
|
||||
end
|
||||
|
||||
it 'find next until the end' do
|
||||
expect(instance.previous_dossier_id(from_id: cached_ids.last)).to eq(cached_ids.first)
|
||||
expect(instance.previous_dossier_id(from_id: cached_ids.first)).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure.dossiers.by_statut has more than one page' do
|
||||
let(:cached_ids) { [11, 12, 13] }
|
||||
subject { instance.previous_dossier_id(from_id: cached_ids.first) }
|
||||
let(:next_page_ids) { (11..20).to_a }
|
||||
before do
|
||||
allow(instance).to receive(:fetch_all_ids).and_return(next_page_ids)
|
||||
end
|
||||
|
||||
it 'works' do
|
||||
expect { subject }.to change { instance.send(:ids) }.from(cached_ids).to(next_page_ids)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure.dossiers.by_statut does not include searched dossiers anymore' do
|
||||
let(:cached_ids) { [] }
|
||||
before { allow(instance).to receive(:fetch_all_ids).and_return([]) }
|
||||
|
||||
it 'works' do
|
||||
expect(instance.previous_dossier_id(from_id: 50)).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue