File: pageable_response.rb

package info (click to toggle)
ruby-aws-sdk-core 3.104.3-3%2Bdeb11u2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,444 kB
  • sloc: ruby: 11,201; makefile: 4
file content (166 lines) | stat: -rw-r--r-- 5,302 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# frozen_string_literal: true

module Aws
  # Decorates a {Seahorse::Client::Response} with paging convenience methods.
  # Some AWS calls provide paged responses to limit the amount of data returned
  # with each response. To optimize for latency, some APIs may return an
  # inconsistent number of responses per page. You should rely on the values of
  # the `next_page?` method or using enumerable methods such as `each` rather
  # than the number of items returned to iterate through results. See below for
  # examples.
  #
  # @note Methods such as `to_json` will enumerate all of the responses before
  #   returning the full response as JSON.
  #
  # # Paged Responses Are Enumerable
  # The simplest way to handle paged response data is to use the built-in
  # enumerator in the response object, as shown in the following example.
  #
  #     s3 = Aws::S3::Client.new
  #
  #     s3.list_objects(bucket:'aws-sdk').each do |response|
  #       puts response.contents.map(&:key)
  #     end
  #
  # This yields one response object per API call made, and enumerates objects
  # in the named bucket. The SDK retrieves additional pages of data to
  # complete the request.
  #
  # # Handling Paged Responses Manually
  # To handle paging yourself, use the response’s `next_page?` method to verify
  # there are more pages to retrieve, or use the last_page? method to verify
  # there are no more pages to retrieve.
  #
  # If there are more pages, use the `next_page` method to retrieve the
  # next page of results, as shown in the following example.
  #
  #     s3 = Aws::S3::Client.new
  #
  #     # Get the first page of data
  #     response = s3.list_objects(bucket:'aws-sdk')
  #
  #     # Get additional pages
  #     while response.next_page? do
  #       response = response.next_page
  #       # Use the response data here...
  #       puts response.contents.map(&:key)
  #     end
  #
  module PageableResponse

    def self.extended(base)
      base.send(:extend, Enumerable)
      base.send(:extend, UnsafeEnumerableMethods)
      base.instance_variable_set("@last_page", nil)
      base.instance_variable_set("@more_results", nil)
    end

    # @return [Paging::Pager]
    attr_accessor :pager

    # Returns `true` if there are no more results.  Calling {#next_page}
    # when this method returns `false` will raise an error.
    # @return [Boolean]
    def last_page?
      if @last_page.nil?
        @last_page = !@pager.truncated?(self)
      end
      @last_page
    end

    # Returns `true` if there are more results.  Calling {#next_page} will
    # return the next response.
    # @return [Boolean]
    def next_page?
      !last_page?
    end

    # @return [Seahorse::Client::Response]
    def next_page(params = {})
      if last_page?
        raise LastPageError.new(self)
      else
        next_response(params)
      end
    end

    # Yields the current and each following response to the given block.
    # @yieldparam [Response] response
    # @return [Enumerable,nil] Returns a new Enumerable if no block is given.
    def each(&block)
      return enum_for(:each_page) unless block_given?
      response = self
      yield(response)
      until response.last_page?
        response = response.next_page
        yield(response)
      end
    end
    alias each_page each

    private

    # @param [Hash] params A hash of additional request params to
    #   merge into the next page request.
    # @return [Seahorse::Client::Response] Returns the next page of
    #   results.
    def next_response(params)
      params = next_page_params(params)
      request = context.client.build_request(context.operation_name, params)
      request.send_request
    end

    # @param [Hash] params A hash of additional request params to
    #   merge into the next page request.
    # @return [Hash] Returns the hash of request parameters for the
    #   next page, merging any given params.
    def next_page_params(params)
      context[:original_params].merge(@pager.next_tokens(self).merge(params))
    end

    # Raised when calling {PageableResponse#next_page} on a pager that
    # is on the last page of results.  You can call {PageableResponse#last_page?}
    # or {PageableResponse#next_page?} to know if there are more pages.
    class LastPageError < RuntimeError

      # @param [Seahorse::Client::Response] response
      def initialize(response)
        @response = response
        super("unable to fetch next page, end of results reached")
      end

      # @return [Seahorse::Client::Response]
      attr_reader :response

    end

    # A handful of Enumerable methods, such as #count are not safe
    # to call on a pageable response, as this would trigger n api calls
    # simply to count the number of response pages, when likely what is
    # wanted is to access count on the data. Same for #to_h.
    # @api private
    module UnsafeEnumerableMethods

      def count
        if data.respond_to?(:count)
          data.count
        else
          raise NoMethodError, "undefined method `count'"
        end
      end

      def respond_to?(method_name, *args)
        if method_name == :count
          data.respond_to?(:count)
        else
          super
        end
      end

      def to_h
        data.to_h
      end

    end
  end
end