Class: PackAPI::Pagination::SnapshotPaginator

Inherits:
Object
  • Object
show all
Defined in:
lib/pack_api/pagination/snapshot_paginator.rb

Overview

Current Page Snapshot Query is a query that targets the records in a given page of a record set (regardless of how those records may change state). Whereas the contents of the nth page in a record set may change, the cached results query for that same page will not.

This class represents the paginator for such a query.

Constant Summary collapse

METADATA_KEY =
:snapshot

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(paginator) ⇒ SnapshotPaginator

Returns a new instance of SnapshotPaginator.



38
39
40
41
42
# File 'lib/pack_api/pagination/snapshot_paginator.rb', line 38

def initialize(paginator)
  raise PackAPI::InternalError, 'Paginator does not represent CachedResultsQuery' if paginator..nil?

  @paginator = paginator
end

Instance Attribute Details

#cursorObject

Returns the value of attribute cursor.



14
15
16
# File 'lib/pack_api/pagination/snapshot_paginator.rb', line 14

def cursor
  @cursor
end

#paginatorObject

Returns the value of attribute paginator.



14
15
16
# File 'lib/pack_api/pagination/snapshot_paginator.rb', line 14

def paginator
  @paginator
end

#resultsObject

Returns the value of attribute results.



14
15
16
# File 'lib/pack_api/pagination/snapshot_paginator.rb', line 14

def results
  @results
end

Class Method Details

.cursor_for_results(results, table_name:, collection_key:) ⇒ Object

Create a snapshot from the current page results of a record set.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/pack_api/pagination/snapshot_paginator.rb', line 18

def self.cursor_for_results(results, table_name:, collection_key:)
  offsets = {}
  case_statement = results.map.with_index do |record, index|
    record_id = record.send(collection_key)
    offsets[record_id.to_s] = index.to_s
    # Use SQL standard CAST to convert both sides to strings for comparison
    "WHEN CAST(\"#{table_name}\".\"#{collection_key}\" AS VARCHAR) = '#{record_id}' THEN #{index}"
  end
  sort_sql = "CASE #{case_statement.join("\n")} END"
  filters = { collection_key => results.pluck(collection_key) }
  paginator = PaginatorBuilder.build do |builder|
    builder.set_params(query: { filters: },
                       metadata: { METADATA_KEY => true, offsets:, collection_key: },
                       sort: Arel.sql(sort_sql),
                       total_items: results.size,
                       per_page: results.size)
  end
  paginator.current_page_cursor
end

.generated?(paginator) ⇒ Boolean

Is the paginator one produced by this class?

Returns:

  • (Boolean)


84
85
86
# File 'lib/pack_api/pagination/snapshot_paginator.rb', line 84

def self.generated?(paginator)
  paginator.&.fetch(METADATA_KEY, nil).presence
end

Instance Method Details

#apply_to(query, record_id: nil) ⇒ Object

Update the query to focus the result onto the given record within the snapshot. If no record_id is provided, it will attempt to guess the record_id based on the current paginator state. NOTE This method assumes that the paginator.total_items has already been updated to reflect the query’s count.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/pack_api/pagination/snapshot_paginator.rb', line 48

def apply_to(query, record_id: nil)
  if has_valid_offsets?
    # if no record_id is provided, no customization is needed
    return query unless record_id.present?

    self.target_record_id = record_id
    updated_query = query.offset(paginator.offset).limit(paginator.limit)
    @results = updated_query.to_a
    @cursor = paginator.current_page_cursor
    updated_query
  else
    # unless we have either a record_id or an offset, there is no customization needed
    return query unless paginator.offset.present? || record_id.present?

    # Step 1: If we don't have a record_id, try to guess what record_id the user was trying to access
    record_id ||= target_record_id

    # Step 2: Fetch all the records in the snapshot -- remove the offset and limits on the current query
    snapshot_results = query.unscope(:offset, :limit).to_a

    # Step 3: Update the offsets based on the current query results
    update_offsets(snapshot_results)

    # Step 4: get the correct offset for the given record_id
    self.target_record_id = record_id

    # Step 5: Filter the results to just the record_id
    collection_key = paginator.[:collection_key]
    @results = snapshot_results.select { it.send(collection_key).to_s == record_id.to_s }
    @cursor = paginator.current_page_cursor
    query.offset(paginator.offset).limit(paginator.limit)
  end
end