File: collection.rb

package info (click to toggle)
ruby-aws-sdk 1.67.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,840 kB
  • sloc: ruby: 28,436; makefile: 7
file content (263 lines) | stat: -rw-r--r-- 8,627 bytes parent folder | download | duplicates (4)
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

module AWS
  module Core

    # Provides useful methods for enumerating items in a collection.
    module Collection

      autoload :Simple, 'aws/core/collection/simple'
      autoload :WithNextToken, 'aws/core/collection/with_next_token'
      autoload :WithLimitAndNextToken, 'aws/core/collection/with_limit_and_next_token'

      include Enumerable

      # Yields once for every item in this collection.
      #
      #     collection.each {|item| ... }
      #
      # @note If you want fewer than all items, it is generally better
      #   to call {#page} than {#each} with a `:limit`.
      #
      # @param [Hash] options
      #
      # @option options [Integer] :limit (nil) The maximum number of
      #   items to enumerate from this collection.
      #
      # @option options [next_token] :next_token (nil)
      #   Acts as an offset.  `:next_token` may be returned by {#each} and
      #   {#each_batch} when a `:limit` is provided.
      #
      # @return [nil_or_next_token] Returns nil if all items were enumerated.
      #   If some items were excluded because of a `:limit` option then
      #   a `next_token` is returned.  Calling an enumerable method on
      #   the same collection with the `next_token` acts like an offset.
      #
      def each options = {}, &block
        each_batch(options) do |batch|
          batch.each(&block)
        end
      end

      # Yields items from this collection in batches.
      #
      #     collection.each_batch do |batch|
      #       batch.each do |item|
      #         # ...
      #       end
      #     end
      #
      # ## Variable Batch Sizes
      #
      # Each AWS service has its own rules on how it returns results.
      # Because of this batch size may very based on:
      #
      # * Service limits (e.g. S3 limits keys to 1000 per response)
      #
      # * The size of response objects (SimpleDB limits responses to 1MB)
      #
      # * Time to process the request
      #
      # Because of these variables, batch sizes may not be consistent for
      # a single collection.  Each batch represents all of the items returned
      # in a single resopnse.
      #
      # @note If you require fixed batch sizes, see {#in_groups_of}.
      # @param (see #each)
      # @option (see #each)
      # @return (see #each)
      def each_batch options = {}, &block
        _each_batch(options.dup, &block)
      end

      # Use this method when you want to call a method provided by
      # Enumerable, but you need to pass options:
      #
      #     # raises an error because collect does not accept arguments
      #     collection.collect(:limit => 10) {|i| i.name }
      #
      #     # not an issue with the enum method
      #     collection.enum(:limit => 10).collect(&:name)
      #
      # @param (see #each)
      # @option (see #each)
      # @return [Enumerable::Enumerator] Returns an enumerator for this
      #   collection.
      #
      def enum options = {}
        to_enum(:each, options)
      end
      alias_method :enumerator, :enum

      # Returns the first item from this collection.
      #
      # @return [item_or_nil] Returns the first item from this collection or
      #   nil if the collection is empty.
      #
      def first options = {}
        enum(options.merge(:limit => 1)).first
      end

      # Yields items from this collection in groups of an exact
      # size (except for perhaps the last group).
      #
      #     collection.in_groups_of (10, :limit => 30) do |group|
      #
      #       # each group should be exactly 10 items unless
      #       # fewer than 30 items are returned by the service
      #       group.each do |item|
      #         #...
      #       end
      #
      #     end
      #
      # @param [Integer] size Size each each group of objects
      #   should be yielded in.
      # @param [Hash] options
      # @option (see #each)
      # @return (see #each)
      def in_groups_of size, options = {}, &block

        group = []

        nil_or_next_token = each_batch(options) do |batch|
          batch.each do |item|
            group << item
            if group.size == size
              yield(group)
              group = []
            end
          end
        end

        yield(group) unless group.empty?

        nil_or_next_token

      end

      # Returns a single page of results in a kind-of array ({PageResult}).
      #
      #     items = collection.page(:per_page => 10) # defaults to 10 items
      #     items.is_a?(Array) # => true
      #     items.size         # => 8
      #     items.per_page     # => 10
      #     items.last_page?   # => true
      #
      # If you need to display a "next page" link in a web view you can
      # use the #more? method.  Just make sure the generated link
      # contains the `next_token`.
      #
      #     <% if items.more? %>
      #       <%= link_to('Next Page', params.merge(:next_token => items.next_token) %>
      #     <% end %>
      #
      # Then in your controller you can find the next page of results:
      #
      #     items = collection.page(:next_token => params[:next_token])
      #
      # Given a {PageResult} you can also get more results directly:
      #
      #     more_items = items.next_page
      #
      # @note This method does not accept a `:page` option, which means you
      #   can only start at the begining of the collection and request
      #   the next page of results.  You can not choose an offset
      #   or know how many pages of results there will be.
      #
      # @param [Hash] options A hash of options that modifies the
      #   items returned in the page of results.
      #
      # @option options [Integer] :per_page (10) The number of results
      #   to return for each page.
      #
      # @option options [String] :next_token (nil) A token that indicates
      #   an offset to use when paging items.  Next tokens are returned
      #   by {PageResult#next_token}.
      #
      #   Next tokens should only be consumed by the same collection that
      #   created them.
      #
      def page options = {}

        each_opts = options.dup

        per_page = each_opts.delete(:per_page)
        per_page = [nil,''].include?(per_page) ? 10 : per_page.to_i

        each_opts[:limit] = per_page

        items = []
        next_token = each(each_opts) do |item|
          items << item
        end

        Core::PageResult.new(self, items, per_page, next_token)

      end

      protected

      def _each_batch options, &block
        # should be defined in the collection modules
        raise NotImplementedError
      end

      def _each_item next_token, options = {}, &block
        # should be defined in classes included the collection modules
        raise NotImplementedError
      end

      def _extract_next_token options
        next_token = options.delete(:next_token)
        next_token = nil if next_token == ''
        next_token
      end

      def _extract_batch_size options
        batch_size = options.delete(:batch_size)
        batch_size = nil if batch_size == ''
        batch_size = batch_size.to_i if batch_size
        batch_size
      end

      def _extract_limit options
        limit = options.delete(:limit) || _limit
        limit = nil if limit == ''
        limit = limit.to_i if limit
        limit
      end

      # Override this method in collection classes that provide
      # an alternative way to provide the limit than passinging
      # it to the enumerable method as :limit.
      #
      # An example of when this would be useful:
      #
      #     collection.limit(10).each {|item| ... }
      #
      # The collection class including this module should define _limit
      # and return the cached limit value (of 10 from this example).
      # This value may still be overridden by a locally passed
      # `:limit` option:
      #
      #     # limit 5 wins out
      #     collection.limit(10).each(:limit => 5) {|item| ... }
      #
      def _limit
        nil
      end

    end
  end
end