s3_selector 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 60db797c466d34b7363115daa3e6baecfdf5ab12e5362d6fc13c6fd2a2fcc977
4
+ data.tar.gz: d3e600200aeb2087b6186bea76445415fcff1d8d444fc7ba9048434032810eb1
5
+ SHA512:
6
+ metadata.gz: 530d505fb1c38cd4375f4c43c0ecdb3fee8713981f2a7b5c535d7d7956846c95556c874d3f33b34ffa9f5ecb8c3c1e4790cfc0455542cdf3c516cc605bc531bd
7
+ data.tar.gz: 8414902bd41d62597eb6b583d1c5feecfa2c7fc50a04bf055c7ff21249d12ebcb467aad6b8e0aa483fd3435d954bbb1e80e14a36e5523ac656353db9589f31ca
@@ -0,0 +1,21 @@
1
+ name: 'Lint'
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ run-rubocop:
7
+ name: Lint code
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ rubyVersion: ["2.7.6"]
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: ${{ matrix.rubyVersion }}
18
+ bundler-cache: true
19
+ cache-version: "${{ matrix.rubyVersion }}-0"
20
+ - name: Run RuboCop
21
+ run: bundle exec rubocop
@@ -0,0 +1,31 @@
1
+ name: Test
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ run-tests:
7
+ name: Test code
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ ruby:
13
+ - 3.1.2
14
+ - 3.0.4
15
+ - 2.7.6
16
+ steps:
17
+ - uses: actions/checkout@v3
18
+ - uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby }}
21
+ bundler-cache: true
22
+ cache-version: ${{ matrix.ruby }}-0
23
+ - name: Run RSpec
24
+ env:
25
+ CI: true
26
+ run: bundle exec rspec
27
+ - name: Coveralls
28
+ uses: coverallsapp/github-action@v1.1.2
29
+ continue-on-error: true
30
+ with:
31
+ github-token: ${{ secrets.GITHUB_TOKEN }}
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .DS_Store
13
+ .vscode
14
+
15
+ *.gem
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.1.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 1.0.0
2
+
3
+ Initial release. Extracted from an internal library.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,114 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ s3_selector (0.1.1)
5
+ aws-sdk-athena
6
+ aws-sdk-s3
7
+ concurrent_executor
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ addressable (2.8.5)
13
+ public_suffix (>= 2.0.2, < 6.0)
14
+ ast (2.4.2)
15
+ aws-eventstream (1.2.0)
16
+ aws-partitions (1.797.0)
17
+ aws-sdk-athena (1.74.0)
18
+ aws-sdk-core (~> 3, >= 3.177.0)
19
+ aws-sigv4 (~> 1.1)
20
+ aws-sdk-core (3.180.1)
21
+ aws-eventstream (~> 1, >= 1.0.2)
22
+ aws-partitions (~> 1, >= 1.651.0)
23
+ aws-sigv4 (~> 1.5)
24
+ jmespath (~> 1, >= 1.6.1)
25
+ aws-sdk-kms (1.71.0)
26
+ aws-sdk-core (~> 3, >= 3.177.0)
27
+ aws-sigv4 (~> 1.1)
28
+ aws-sdk-s3 (1.132.0)
29
+ aws-sdk-core (~> 3, >= 3.179.0)
30
+ aws-sdk-kms (~> 1)
31
+ aws-sigv4 (~> 1.6)
32
+ aws-sigv4 (1.6.0)
33
+ aws-eventstream (~> 1, >= 1.0.2)
34
+ benchmark-ips (2.12.0)
35
+ coderay (1.1.3)
36
+ concurrent_executor (1.3.0)
37
+ crack (0.4.5)
38
+ rexml
39
+ diff-lcs (1.5.0)
40
+ docile (1.4.0)
41
+ hashdiff (1.0.1)
42
+ jmespath (1.6.2)
43
+ json (2.6.3)
44
+ language_server-protocol (3.17.0.3)
45
+ method_source (1.0.0)
46
+ parallel (1.23.0)
47
+ parser (3.2.2.3)
48
+ ast (~> 2.4.1)
49
+ racc
50
+ pry (0.14.2)
51
+ coderay (~> 1.1)
52
+ method_source (~> 1.0)
53
+ public_suffix (5.0.3)
54
+ racc (1.7.1)
55
+ rainbow (3.1.1)
56
+ rake (10.5.0)
57
+ regexp_parser (2.8.1)
58
+ rexml (3.2.6)
59
+ rspec (3.12.0)
60
+ rspec-core (~> 3.12.0)
61
+ rspec-expectations (~> 3.12.0)
62
+ rspec-mocks (~> 3.12.0)
63
+ rspec-core (3.12.2)
64
+ rspec-support (~> 3.12.0)
65
+ rspec-expectations (3.12.3)
66
+ diff-lcs (>= 1.2.0, < 2.0)
67
+ rspec-support (~> 3.12.0)
68
+ rspec-mocks (3.12.6)
69
+ diff-lcs (>= 1.2.0, < 2.0)
70
+ rspec-support (~> 3.12.0)
71
+ rspec-support (3.12.1)
72
+ rubocop (1.55.1)
73
+ json (~> 2.3)
74
+ language_server-protocol (>= 3.17.0)
75
+ parallel (~> 1.10)
76
+ parser (>= 3.2.2.3)
77
+ rainbow (>= 2.2.2, < 4.0)
78
+ regexp_parser (>= 1.8, < 3.0)
79
+ rexml (>= 3.2.5, < 4.0)
80
+ rubocop-ast (>= 1.28.1, < 2.0)
81
+ ruby-progressbar (~> 1.7)
82
+ unicode-display_width (>= 2.4.0, < 3.0)
83
+ rubocop-ast (1.29.0)
84
+ parser (>= 3.2.1.0)
85
+ ruby-progressbar (1.13.0)
86
+ simplecov (0.22.0)
87
+ docile (~> 1.1)
88
+ simplecov-html (~> 0.11)
89
+ simplecov_json_formatter (~> 0.1)
90
+ simplecov-html (0.12.3)
91
+ simplecov_json_formatter (0.1.4)
92
+ unicode-display_width (2.4.2)
93
+ webmock (3.18.1)
94
+ addressable (>= 2.8.0)
95
+ crack (>= 0.3.2)
96
+ hashdiff (>= 0.4.0, < 2.0.0)
97
+
98
+ PLATFORMS
99
+ x86_64-darwin-21
100
+ x86_64-linux
101
+
102
+ DEPENDENCIES
103
+ benchmark-ips
104
+ bundler (~> 2.0)
105
+ pry
106
+ rake (~> 10.0)
107
+ rspec (~> 3.0)
108
+ rubocop
109
+ s3_selector!
110
+ simplecov
111
+ webmock
112
+
113
+ BUNDLED WITH
114
+ 2.4.13
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # S3 Selector
2
+
3
+ When selecting large amounts of data via the AWS SDK, it collects all results into memory and then provides you an enumerable. This is bad for very large result sets. This gem solves that by streaming results in, instead.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 's3_selector'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install s3_selector
20
+
21
+ ## Usage
22
+
23
+ ### With S3 Files
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "instrument_all_the_things"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,163 @@
1
+ class ResultsStream
2
+ DEFAULT_S3_SELECT_THREADS = 5
3
+ DELIMITER = "\n".bytes.first.freeze
4
+
5
+ attr_accessor :s3_files, :number_of_threads, :query, :s3_client, :region,
6
+ :input_format, :input_format_options
7
+
8
+ def initialize(
9
+ s3_files:,
10
+ query: 'SELECT * FROM S3Object',
11
+ s3_client: Aws::S3::Client.new,
12
+ number_of_threads: DEFAULT_S3_SELECT_THREADS,
13
+ region: 'us-east-1',
14
+ input_format: :parquet,
15
+ input_format_options: {}
16
+ )
17
+ self.s3_files = s3_files
18
+ self.s3_client = s3_client
19
+ self.query = query
20
+ self.number_of_threads = number_of_threads
21
+ self.region = region
22
+ self.input_format = input_format
23
+ self.input_format_options = input_format_options
24
+ end
25
+
26
+ def records
27
+ Enumerator.new do |yielder|
28
+ if s3_files.length == 1
29
+ read_s3_file(file: s3_files.first) { |data| yielder << data }
30
+ else
31
+ ConcurrentExecutor.consume_enumerable(s3_files) do |s3_file|
32
+ read_s3_file(file: s3_file) { |data| yielder << data }
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def signer
41
+ @signer ||= Aws::Sigv4::Signer.new(
42
+ service: 's3',
43
+ region: region,
44
+ credentials_provider: Aws::CredentialProviderChain.new.resolve,
45
+ uri_escape_path: false,
46
+ )
47
+ end
48
+
49
+ def s3_select_request_body
50
+ @s3_select_request_body ||= <<~XML
51
+ <SelectObjectContentRequest xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
52
+ <Expression>#{query}</Expression>
53
+ <ExpressionType>SQL</ExpressionType>
54
+ <InputSerialization>
55
+ #{input_serialization_body}
56
+ </InputSerialization>
57
+ <OutputSerialization>
58
+ <JSON>
59
+ <RecordDelimiter>\n</RecordDelimiter>
60
+ </JSON>
61
+ </OutputSerialization>
62
+ </SelectObjectContentRequest>
63
+ XML
64
+ end
65
+
66
+ def input_serialization_body
67
+ case input_format
68
+ when :parquet then '<Parquet />'
69
+ when :json then json_input_serialization_body
70
+ else
71
+ raise "unknown input format #{input_format}"
72
+ end
73
+ end
74
+
75
+ def json_input_serialization_body
76
+ format = input_format_options&.fetch(:type, '')&.to_s&.upcase
77
+
78
+ raise 'When querying JSON you must specify a input format option `:type` of either DOCUMENT or LINES' unless %w[DOCUMENT LINES].include?(format)
79
+
80
+ compression = input_format_options&.fetch(:compression, '')&.to_s&.upcase
81
+
82
+ <<~BODY
83
+ #{compression && compression != '' ? "<CompressionType>#{compression}</CompressionType>" : ''}
84
+ <JSON>
85
+ <Type>#{format}</Type>
86
+ </JSON>
87
+ BODY
88
+ end
89
+
90
+ def decoder
91
+ # Do NOT memoize this. Need a fresh stream decoder per HTTP request
92
+ Aws::EventStream::Decoder.new
93
+ end
94
+
95
+ def bytes_to_utf8(bytes)
96
+ bytes.pack('C*').force_encoding('utf-8')
97
+ end
98
+
99
+ def split_and_ensure_delimiter(buffer)
100
+ found = []
101
+ position = 0
102
+ last_slice_end = 0
103
+
104
+ while position < buffer.length
105
+ if buffer[position] == DELIMITER
106
+ found << buffer[last_slice_end..(position - 1)]
107
+ last_slice_end = position += 1
108
+ else
109
+ position += 1
110
+ end
111
+ end
112
+
113
+ [found, buffer[last_slice_end..]]
114
+ end
115
+
116
+ def read_s3_file(file:)
117
+ file_request_decoder = decoder
118
+
119
+ url = URI("#{file.public_url}?select&select-type=2")
120
+
121
+ signature = signer.sign_request(
122
+ http_method: 'POST',
123
+ url: url,
124
+ headers: { 'Content-Type' => 'application/octet-stream' },
125
+ body: s3_select_request_body,
126
+ )
127
+
128
+ request = Net::HTTP::Post.new(url.request_uri, signature.headers.merge({ 'Content-Type' => 'application/octet-stream' }))
129
+ request.body = s3_select_request_body
130
+
131
+ mon = Monitor.new
132
+
133
+ Net::HTTP.start(url.host, url.port, use_ssl: true) do |http|
134
+ bytes = []
135
+ http.request(request) do |response|
136
+ response.read_body do |chunk|
137
+ message, chunk_eof = file_request_decoder.decode_chunk(chunk)
138
+ if message && message.headers[':event-type'].value == 'Records'
139
+ lines = []
140
+
141
+ mon.synchronize do
142
+ bytes.concat(message.payload.read.bytes)
143
+ lines, bytes = split_and_ensure_delimiter(bytes)
144
+ end
145
+
146
+ lines.each do |line|
147
+ yield JSON.parse(bytes_to_utf8(line))
148
+ rescue JSON::ParserError
149
+ end
150
+ end
151
+
152
+ if chunk_eof && !!bytes && !bytes.empty?
153
+ begin
154
+ yield JSON.parse(bytes_to_utf8(bytes))
155
+ bytes = []
156
+ rescue JSON::ParserError
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module S3Selector
4
+ VERSION = '0.1.1'
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 's3_selector/version'
4
+ require 'aws-sdk-s3'
5
+ require 'concurrent_executor'
6
+
7
+ module S3Selector
8
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 's3_selector/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 's3_selector'
9
+ spec.version = S3Selector::VERSION
10
+ spec.authors = ['Nathaniel Rowe']
11
+ spec.email = ['nathaniel.rowe@terminus.com']
12
+
13
+ spec.summary = 'Stream s3 select results'
14
+ spec.description = 'Stream s3 select results'
15
+ spec.homepage = 'https://github.com/GetTerminus/s3_selector'
16
+
17
+ spec.metadata['allowed_push_host'] = 'https://www.rubygems.org'
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/GetTerminus/s3_selector'
21
+ # spec.metadata['changelog_uri'] = 'http://google.com'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|Gemfile.lock|vendor)/}) }
27
+ end
28
+ spec.bindir = 'exe'
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ['lib']
31
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
32
+
33
+ spec.add_dependency 'aws-sdk-s3'
34
+ spec.add_dependency 'aws-sdk-athena'
35
+ spec.add_dependency 'concurrent_executor'
36
+
37
+
38
+ spec.add_development_dependency 'benchmark-ips'
39
+ spec.add_development_dependency 'bundler', '~> 2.0'
40
+ spec.add_development_dependency 'pry'
41
+ spec.add_development_dependency 'rake', '~> 10.0'
42
+ spec.add_development_dependency 'rspec', '~> 3.0'
43
+ spec.add_development_dependency 'rubocop'
44
+ spec.add_development_dependency 'simplecov'
45
+ spec.add_development_dependency 'webmock'
46
+ end
metadata ADDED
@@ -0,0 +1,214 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: s3_selector
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Nathaniel Rowe
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-08-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-s3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-athena
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: concurrent_executor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: benchmark-ips
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '10.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '10.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: webmock
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: Stream s3 select results
168
+ email:
169
+ - nathaniel.rowe@terminus.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".github/workflows/lint.yaml"
175
+ - ".github/workflows/test.yaml"
176
+ - ".gitignore"
177
+ - ".ruby-version"
178
+ - CHANGELOG.md
179
+ - Gemfile
180
+ - Gemfile.lock
181
+ - README.md
182
+ - Rakefile
183
+ - bin/console
184
+ - bin/setup
185
+ - lib/s3_selector.rb
186
+ - lib/s3_selector/results_stream.rb
187
+ - lib/s3_selector/version.rb
188
+ - s3_selector.gemspec
189
+ homepage: https://github.com/GetTerminus/s3_selector
190
+ licenses: []
191
+ metadata:
192
+ allowed_push_host: https://www.rubygems.org
193
+ homepage_uri: https://github.com/GetTerminus/s3_selector
194
+ source_code_uri: https://github.com/GetTerminus/s3_selector
195
+ post_install_message:
196
+ rdoc_options: []
197
+ require_paths:
198
+ - lib
199
+ required_ruby_version: !ruby/object:Gem::Requirement
200
+ requirements:
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: 2.7.0
204
+ required_rubygems_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ requirements: []
210
+ rubygems_version: 3.3.12
211
+ signing_key:
212
+ specification_version: 4
213
+ summary: Stream s3 select results
214
+ test_files: []