bucket_brigade 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -5,7 +5,8 @@ like Riak CS.
5
5
  Usage:
6
6
 
7
7
  ```ruby
8
- require 'bucket_brigade/bucket_config
8
+ require 'bucket_brigade/bucket_config'
9
+ require 'bucket_brigade/bucket_list'
9
10
 
10
11
  bucket_config1 = BucketBrigade::BucketConfig.new(
11
12
  'http://192.168.37.73:8080',
@@ -21,15 +22,14 @@ bucket_config2 = BucketBrigade::BucketConfig.new(
21
22
  'test-bucket'
22
23
  )
23
24
 
24
-
25
- bucket_list = BucketBrigade::BucketList.new(
25
+ bucket_list = BucketBrigade::BucketList.build(
26
26
  bucket_config1,
27
27
  bucket_config2,
28
- )
28
+ )
29
29
 
30
30
  open('/tmp/file') do |f|
31
31
  bucket_list.put('test-key', f)
32
32
  end
33
33
 
34
- puts bucket_list.get('test-key')
34
+ puts bucket_list.get('test-key').read
35
35
  ```
@@ -1,8 +1,11 @@
1
1
  # encoding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bucket_brigade/version'
2
5
 
3
6
  Gem::Specification.new do |s|
4
7
  s.name = 'bucket_brigade'
5
- s.version = '0.0.1'
8
+ s.version = BucketBrigade::VERSION
6
9
  s.summary = 'S3 bucket cache hierarchy'
7
10
  s.description = 'S3 bucket cache hierarchy'
8
11
  s.author = 'Pivotal Labs'
@@ -1,19 +1,17 @@
1
1
  require 'bucket_brigade'
2
-
3
2
  require 'aws-sdk'
4
-
5
3
  require 'tempfile'
6
4
 
7
5
  class BucketBrigade::Bucket
8
6
  def initialize(bucket_config)
9
- @s3_bucket = AWS::S3.new(
10
- s3_endpoint: bucket_config.host,
11
- s3_port: bucket_config.port,
12
- access_key_id: bucket_config.access_key_id,
13
- secret_access_key: bucket_config.secret_access_key,
14
- use_ssl: bucket_config.use_ssl,
15
- s3_force_path_style: true
16
- ).buckets[bucket_config.bucket]
7
+ @bucket_config = bucket_config
8
+ end
9
+
10
+ def can_connect?
11
+ s3.buckets.first
12
+ true
13
+ rescue Timeout::Error
14
+ false
17
15
  end
18
16
 
19
17
  def has_key?(key)
@@ -33,7 +31,7 @@ class BucketBrigade::Bucket
33
31
  end
34
32
 
35
33
  def object(key)
36
- @s3_bucket.objects[key]
34
+ s3_bucket.objects[key]
37
35
  end
38
36
 
39
37
  def copy(key, other)
@@ -48,4 +46,23 @@ class BucketBrigade::Bucket
48
46
 
49
47
  temp.close!
50
48
  end
49
+
50
+ private
51
+
52
+ attr_reader :bucket_config
53
+
54
+ def s3
55
+ @s3 ||= AWS::S3.new(
56
+ s3_endpoint: bucket_config.host,
57
+ s3_port: bucket_config.port,
58
+ access_key_id: bucket_config.access_key_id,
59
+ secret_access_key: bucket_config.secret_access_key,
60
+ use_ssl: bucket_config.use_ssl,
61
+ s3_force_path_style: true
62
+ )
63
+ end
64
+
65
+ def s3_bucket
66
+ @s3_bucket ||= s3.buckets[bucket_config.bucket]
67
+ end
51
68
  end
@@ -4,17 +4,32 @@ require 'bucket_brigade/bucket'
4
4
  require 'aws-sdk'
5
5
 
6
6
  class BucketBrigade::BucketList
7
- def initialize(*bucket_configs)
8
- @buckets = bucket_configs.map do |bucket_config|
9
- BucketBrigade::Bucket.new(bucket_config)
7
+ def self.build(bucket_configs)
8
+ good_buckets = []
9
+
10
+ bucket_configs.each do |bucket_config|
11
+ bucket = BucketBrigade::Bucket.new(bucket_config)
12
+ if bucket.can_connect?
13
+ good_buckets << bucket
14
+ else
15
+ warn "Unable to connect to bucket at #{bucket_config.endpoint}"
16
+ end
10
17
  end
18
+
19
+ new(good_buckets)
20
+ end
21
+
22
+ attr_reader :buckets
23
+
24
+ def initialize(buckets)
25
+ @buckets = buckets
11
26
  end
12
27
 
13
28
  def get(key)
14
- @buckets.each_with_index do |bucket, i|
29
+ buckets.each_with_index do |bucket, i|
15
30
  if bucket.has_key?(key)
16
31
  if i > 0
17
- prev_bucket = @buckets[i - 1]
32
+ prev_bucket = buckets[i - 1]
18
33
  bucket.copy(key, prev_bucket)
19
34
  return prev_bucket.get(key)
20
35
  end
@@ -25,9 +40,9 @@ class BucketBrigade::BucketList
25
40
  end
26
41
 
27
42
  def put(key, io)
28
- @buckets.last.put(key, io)
43
+ buckets.last.put(key, io)
29
44
  # invalidate all the way down
30
- @buckets.first(@buckets.size - 1).each do |bucket|
45
+ buckets.first(buckets.size - 1).each do |bucket|
31
46
  bucket.delete(key)
32
47
  end
33
48
  end
@@ -0,0 +1,3 @@
1
+ module BucketBrigade
2
+ VERSION = '0.1.0'
3
+ end
@@ -25,11 +25,11 @@ describe 'put and get data from the cache' do
25
25
  'bucket-brigade-test'
26
26
  )
27
27
 
28
- bucket_list = BucketBrigade::BucketList.new(
28
+ bucket_list = BucketBrigade::BucketList.build([
29
29
  bucket_config1,
30
30
  bucket_config2,
31
31
  bucket_config3
32
- )
32
+ ])
33
33
 
34
34
  io = StringIO.new
35
35
  io.write('test 1 2 3')
@@ -2,148 +2,181 @@ require 'bucket_brigade/bucket_config'
2
2
  require 'bucket_brigade/bucket_list'
3
3
 
4
4
  describe BucketBrigade::BucketList do
5
+ describe '.from_configs' do
6
+ describe 'when one of the buckets cannot connect' do
7
+ let(:working_bucket) { double('working bucket', can_connect?: true) }
8
+ let(:bad_bucket) { double('bad bucket', can_connect?: false) }
9
+ let(:working_config) { double('working config') }
10
+ let(:bad_config) { double('bad config', endpoint: 'bad.example.com') }
11
+ let(:s3_object) { double('s3 object') }
5
12
 
6
- let(:bucket_configs) {
7
- [
13
+ before do
14
+ expect(BucketBrigade::Bucket).to receive(:new).with(working_config).and_return(working_bucket)
15
+ expect(BucketBrigade::Bucket).to receive(:new).with(bad_config).and_return(bad_bucket)
16
+ end
17
+
18
+ subject(:bucket_list) { described_class.build([bad_config, working_config]) }
19
+
20
+ it 'creates a bucket list with only the working bucket' do
21
+ expect(bucket_list.buckets).to eq([working_bucket])
22
+ end
23
+
24
+ it 'only uses the working bucket' do
25
+ expect(working_bucket).to receive(:has_key?).with('key').and_return(true)
26
+ expect(working_bucket).to receive(:get).with('key').and_return(s3_object)
27
+ expect(bad_bucket).not_to receive(:get)
28
+
29
+ expect(bucket_list.get('key')).to eq(s3_object)
30
+ end
31
+ end
32
+ end
33
+
34
+ describe 'instance methods' do
35
+ let(:bucket_1) do
36
+ BucketBrigade::Bucket.new(
8
37
  BucketBrigade::BucketConfig.new(
9
- 'http://test-endpoint-1:1234',
10
- 'test-access-key-id-1',
11
- 'test-secret-access-key-1',
12
- 'test-bucket-1'
13
- ),
38
+ 'http://test-endpoint-1:1234',
39
+ 'test-access-key-id-1',
40
+ 'test-secret-access-key-1',
41
+ 'test-bucket-1'
42
+ )
43
+ )
44
+ end
45
+
46
+ let(:bucket_2) do
47
+ BucketBrigade::Bucket.new(
14
48
  BucketBrigade::BucketConfig.new(
15
- 'http://test-endpoint-2:1234',
16
- 'test-access-key-id-2',
17
- 'test-secret-access-key-2',
18
- 'test-bucket-2'
49
+ 'http://test-endpoint-2:1234',
50
+ 'test-access-key-id-2',
51
+ 'test-secret-access-key-2',
52
+ 'test-bucket-2'
19
53
  )
20
- ]
21
- }
54
+ )
55
+ end
22
56
 
23
- let(:s3_1) { instance_double(AWS::S3) }
24
- let(:s3_2) { instance_double(AWS::S3) }
57
+ let(:buckets) { [bucket_1, bucket_2] }
25
58
 
26
- let(:bucket_collection_1) { instance_double(AWS::S3::BucketCollection) }
27
- let(:bucket_collection_2) { instance_double(AWS::S3::BucketCollection) }
59
+ let(:s3_1) { instance_double(AWS::S3, buckets: bucket_collection_1) }
60
+ let(:s3_2) { instance_double(AWS::S3, buckets: bucket_collection_2) }
28
61
 
29
- let(:bucket_1) { instance_double(AWS::S3::Bucket) }
30
- let(:bucket_2) { instance_double(AWS::S3::Bucket) }
62
+ let(:bucket_collection_1) { instance_double(AWS::S3::BucketCollection, first: nil) }
63
+ let(:bucket_collection_2) { instance_double(AWS::S3::BucketCollection, first: nil) }
31
64
 
32
- let(:object_collection_1) { instance_double(AWS::S3::ObjectCollection) }
33
- let(:object_collection_2) { instance_double(AWS::S3::ObjectCollection) }
65
+ let(:s3_bucket_1) { instance_double(AWS::S3::Bucket, objects: object_collection_1) }
66
+ let(:s3_bucket_2) { instance_double(AWS::S3::Bucket, objects: object_collection_2) }
34
67
 
35
- let(:object_1) { instance_double(AWS::S3::S3Object) }
36
- let(:object_2) { instance_double(AWS::S3::S3Object) }
68
+ let(:object_collection_1) { instance_double(AWS::S3::ObjectCollection) }
69
+ let(:object_collection_2) { instance_double(AWS::S3::ObjectCollection) }
37
70
 
38
- before do
39
- allow(AWS::S3).to receive(:new).with(
40
- s3_endpoint: 'test-endpoint-1',
41
- s3_port: 1234,
42
- access_key_id: 'test-access-key-id-1',
43
- secret_access_key: 'test-secret-access-key-1',
44
- use_ssl: false,
71
+ let(:object_1) { instance_double(AWS::S3::S3Object) }
72
+ let(:object_2) { instance_double(AWS::S3::S3Object) }
73
+
74
+ before do
75
+ allow(AWS::S3).to receive(:new).with(
76
+ s3_endpoint: 'test-endpoint-1',
77
+ s3_port: 1234,
78
+ access_key_id: 'test-access-key-id-1',
79
+ secret_access_key: 'test-secret-access-key-1',
80
+ use_ssl: false,
45
81
  s3_force_path_style: true
46
- ).and_return(s3_1)
47
- allow(s3_1).to receive(:buckets).and_return(bucket_collection_1)
48
- allow(bucket_collection_1).to receive(:[]).with('test-bucket-1').and_return(bucket_1)
49
- allow(bucket_1).to receive(:objects).and_return(object_collection_1)
50
- allow(object_collection_1).to receive(:[]).with('test-key').and_return(object_1)
51
-
52
- allow(AWS::S3).to receive(:new).with(
53
- s3_endpoint: 'test-endpoint-2',
54
- s3_port: 1234,
55
- access_key_id: 'test-access-key-id-2',
56
- secret_access_key: 'test-secret-access-key-2',
57
- use_ssl: false,
82
+ ).and_return(s3_1)
83
+ allow(bucket_collection_1).to receive(:[]).with('test-bucket-1').and_return(s3_bucket_1)
84
+ allow(object_collection_1).to receive(:[]).with('test-key').and_return(object_1)
85
+
86
+ allow(AWS::S3).to receive(:new).with(
87
+ s3_endpoint: 'test-endpoint-2',
88
+ s3_port: 1234,
89
+ access_key_id: 'test-access-key-id-2',
90
+ secret_access_key: 'test-secret-access-key-2',
91
+ use_ssl: false,
58
92
  s3_force_path_style: true
59
- ).and_return(s3_2)
60
- allow(s3_2).to receive(:buckets).and_return(bucket_collection_2)
61
- allow(bucket_collection_2).to receive(:[]).with('test-bucket-2').and_return(bucket_2)
62
- allow(bucket_2).to receive(:objects).and_return(object_collection_2)
63
- allow(object_collection_2).to receive(:[]).with('test-key').and_return(object_2)
64
- end
93
+ ).and_return(s3_2)
94
+ allow(bucket_collection_2).to receive(:[]).with('test-bucket-2').and_return(s3_bucket_2)
95
+ allow(object_collection_2).to receive(:[]).with('test-key').and_return(object_2)
96
+ end
65
97
 
66
- subject(:bucket_list) { described_class.new(*bucket_configs) }
98
+ subject(:bucket_list) { described_class.new(buckets) }
67
99
 
68
- describe '#get' do
69
- context 'when the object is found in the first bucket' do
70
- before do
71
- allow(object_1).to receive(:exists?).and_return(true)
72
- end
100
+ describe '#get' do
101
+ context 'when the object is found in the first bucket' do
102
+ before do
103
+ allow(object_1).to receive(:exists?).and_return(true)
104
+ end
73
105
 
74
- it 'returns the object from the first bucket' do
75
- expect(bucket_list.get('test-key')).to eq(object_1)
106
+ it 'returns the object from the first bucket' do
107
+ expect(bucket_list.get('test-key')).to eq(object_1)
108
+ end
76
109
  end
77
- end
78
110
 
79
- context 'when the object is not found in any buckets' do
80
- before do
81
- allow(object_1).to receive(:exists?).and_return(false)
82
- allow(object_2).to receive(:exists?).and_return(false)
83
- end
111
+ context 'when the object is not found in any buckets' do
112
+ before do
113
+ allow(object_1).to receive(:exists?).and_return(false)
114
+ allow(object_2).to receive(:exists?).and_return(false)
115
+ end
84
116
 
85
- it 'returns nil' do
86
- expect(bucket_list.get('test-key')).to eq(nil)
117
+ it 'returns nil' do
118
+ expect(bucket_list.get('test-key')).to eq(nil)
119
+ end
87
120
  end
88
- end
89
-
90
- context 'when the object is found in the second bucket' do
91
- let(:temp) { instance_double(Tempfile) }
92
121
 
93
- before do
94
- allow(object_1).to receive(:exists?).and_return(false)
95
- allow(object_2).to receive(:exists?).and_return(true)
122
+ context 'when the object is found in the second bucket' do
123
+ let(:temp) { instance_double(Tempfile) }
96
124
 
97
- allow(Tempfile).to receive(:new).with('bucket-copy').and_return(temp)
98
- allow(temp).to receive(:rewind)
99
- allow(temp).to receive(:close!)
100
- end
125
+ before do
126
+ allow(object_1).to receive(:exists?).and_return(false)
127
+ allow(object_2).to receive(:exists?).and_return(true)
101
128
 
102
- it 'writes the object to the first bucket' do
103
- allow(object_2).to receive(:read)
104
- expect(object_1).to receive(:write).with(temp)
105
- bucket_list.get('test-key')
106
- end
129
+ allow(Tempfile).to receive(:new).with('bucket-copy').and_return(temp)
130
+ allow(temp).to receive(:rewind)
131
+ allow(temp).to receive(:close!)
132
+ end
107
133
 
108
- it 'returns the object from the first bucket' do
109
- allow(object_2).to receive(:read)
134
+ it 'writes the object to the first bucket' do
135
+ allow(object_2).to receive(:read)
136
+ expect(object_1).to receive(:write).with(temp)
137
+ bucket_list.get('test-key')
138
+ end
110
139
 
111
- allow(object_1).to receive(:write).with(temp) {
112
- allow(object_1).to receive(:exists?).and_return(true)
113
- }
114
- expect(bucket_list.get('test-key')).to eq(object_1)
115
- end
140
+ it 'returns the object from the first bucket' do
141
+ allow(object_2).to receive(:read)
116
142
 
117
- end
143
+ allow(object_1).to receive(:write).with(temp) {
144
+ allow(object_1).to receive(:exists?).and_return(true)
145
+ }
146
+ expect(bucket_list.get('test-key')).to eq(object_1)
147
+ end
118
148
 
119
- context 'when the object is found in both buckets' do
120
- before do
121
- allow(object_1).to receive(:exists?).and_return(true)
122
- allow(object_2).to receive(:exists?).and_return(true)
123
149
  end
124
150
 
125
- it 'returns the object from the first bucket' do
126
- expect(bucket_list.get('test-key')).to eq(object_1)
151
+ context 'when the object is found in both buckets' do
152
+ before do
153
+ allow(object_1).to receive(:exists?).and_return(true)
154
+ allow(object_2).to receive(:exists?).and_return(true)
155
+ end
156
+
157
+ it 'returns the object from the first bucket' do
158
+ expect(bucket_list.get('test-key')).to eq(object_1)
159
+ end
127
160
  end
128
161
  end
129
- end
130
162
 
131
- describe '#put' do
132
- context 'when writing an object' do
133
- let(:io) { instance_double(File) }
163
+ describe '#put' do
164
+ context 'when writing an object' do
165
+ let(:io) { instance_double(File) }
134
166
 
135
- it 'puts the object in the last bucket' do
136
- allow(object_1).to receive(:delete)
167
+ it 'puts the object in the last bucket' do
168
+ allow(object_1).to receive(:delete)
137
169
 
138
- expect(object_2).to receive(:write).with(io)
139
- bucket_list.put('test-key', io)
140
- end
170
+ expect(object_2).to receive(:write).with(io)
171
+ bucket_list.put('test-key', io)
172
+ end
141
173
 
142
- it 'delete the object from the first bucket' do
143
- allow(object_2).to receive(:write).with(io)
174
+ it 'delete the object from the first bucket' do
175
+ allow(object_2).to receive(:write).with(io)
144
176
 
145
- expect(object_1).to receive(:delete)
146
- bucket_list.put('test-key', io)
177
+ expect(object_1).to receive(:delete)
178
+ bucket_list.put('test-key', io)
179
+ end
147
180
  end
148
181
  end
149
182
  end
@@ -1,10 +1,8 @@
1
1
  require 'bucket_brigade/bucket'
2
2
  require 'bucket_brigade/bucket_config'
3
-
4
3
  require 'aws-sdk'
5
4
 
6
5
  describe BucketBrigade::Bucket do
7
-
8
6
  subject(:bucket) { described_class.new(bucket_config) }
9
7
 
10
8
  let(:bucket_config) {
@@ -36,6 +34,37 @@ describe BucketBrigade::Bucket do
36
34
  allow(object_collection).to receive(:[]).with('test-key').and_return(object)
37
35
  end
38
36
 
37
+ describe '#can_connect?' do
38
+ context 'when able to connect to the S3 bucket' do
39
+ it 'returns true' do
40
+ allow(bucket_collection).to receive(:first)
41
+ expect(bucket.can_connect?).to eq(true)
42
+ end
43
+ end
44
+
45
+ context 'when unable to connect to the S3 bucket' do
46
+ before do
47
+ bad_bucket_collection = double
48
+ expect(bad_bucket_collection).to receive(:first).and_raise(error_class)
49
+ expect(s3).to receive(:buckets).and_return(bad_bucket_collection)
50
+ end
51
+
52
+ context 'because of a read timeout' do
53
+ let(:error_class) { Net::ReadTimeout }
54
+ it 'returns false' do
55
+ expect(bucket.can_connect?).to eq(false)
56
+ end
57
+ end
58
+
59
+ context 'because of an open timeout' do
60
+ let(:error_class) { Net::OpenTimeout }
61
+ it 'returns false' do
62
+ expect(bucket.can_connect?).to eq(false)
63
+ end
64
+ end
65
+ end
66
+ end
67
+
39
68
  describe '#has_key' do
40
69
  context 'when the object does not exist' do
41
70
  before { allow(object).to receive(:exists?).and_return(false) }
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bucket_brigade
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Pivotal Labs
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-08-01 00:00:00.000000000 Z
12
+ date: 2014-08-14 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: rspec
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :development
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
@@ -27,6 +30,7 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: rspec-legacy_formatters
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
35
  - - ~>
32
36
  - !ruby/object:Gem::Version
@@ -34,6 +38,7 @@ dependencies:
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ~>
39
44
  - !ruby/object:Gem::Version
@@ -41,6 +46,7 @@ dependencies:
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: aws-sdk
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
51
  - - ~>
46
52
  - !ruby/object:Gem::Version
@@ -48,6 +54,7 @@ dependencies:
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
59
  - - ~>
53
60
  - !ruby/object:Gem::Version
@@ -67,6 +74,7 @@ files:
67
74
  - lib/bucket_brigade/bucket.rb
68
75
  - lib/bucket_brigade/bucket_config.rb
69
76
  - lib/bucket_brigade/bucket_list.rb
77
+ - lib/bucket_brigade/version.rb
70
78
  - spec/integration/put_get_spec.rb
71
79
  - spec/unit/bucket_brigade/bucket_config_spec.rb
72
80
  - spec/unit/bucket_brigade/bucket_list_spec.rb
@@ -74,25 +82,26 @@ files:
74
82
  homepage: https://github.com/pivotal-cf-experimental/bucket_brigade
75
83
  licenses:
76
84
  - Apache 2.0
77
- metadata: {}
78
85
  post_install_message:
79
86
  rdoc_options: []
80
87
  require_paths:
81
88
  - lib
82
89
  required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
83
91
  requirements:
84
- - - '>='
92
+ - - ! '>='
85
93
  - !ruby/object:Gem::Version
86
94
  version: '0'
87
95
  required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
88
97
  requirements:
89
- - - '>='
98
+ - - ! '>='
90
99
  - !ruby/object:Gem::Version
91
100
  version: '0'
92
101
  requirements: []
93
102
  rubyforge_project:
94
- rubygems_version: 2.0.14
103
+ rubygems_version: 1.8.23
95
104
  signing_key:
96
- specification_version: 4
105
+ specification_version: 3
97
106
  summary: S3 bucket cache hierarchy
98
107
  test_files: []
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 8cdb69753762ea4144d0ac29a7debeff8fcefe8d
4
- data.tar.gz: 9dfc9ad55eba0aa9e4b923949e477023a13b7c49
5
- SHA512:
6
- metadata.gz: 31a691b784f454d86ee13e308622cb51ebaff8bc3a9a3c714fa94745025e4ba71738ce82513a36f9ba6a9e2b9a7bd5252408ff1e9a09e77a02b0dfd71bc56009
7
- data.tar.gz: a1f16f71752d1488930d0308fcb3865791a87f3dd59799051431ba5171795f2ed2902bca6b26ce6530e80461374f6d411442a06cc527bfddbe7884f23a1b51f6