s3mpi 0.0.8.1 → 0.1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 15963407068406352b0c91e2305bf5ef1c97d487
4
- data.tar.gz: b1107fa62e9bc82a5278585c76427fb395cdda50
3
+ metadata.gz: 731d6db40c690f6acfbccc15328e39c16367d7d3
4
+ data.tar.gz: 02bfc423b1e53b87af1675bccfa6cc2141ac283a
5
5
  SHA512:
6
- metadata.gz: 548cd924ed23bb7e9dd0a4723182c0b0706bc195ae6b66316965d79990219acc92050a28e92557c44af3e56b8820dd2240e1737c24536993c7561bbd86c7851e
7
- data.tar.gz: 00206b142dd05916ee9b138250d87ff416e68ad343142e19f7d595457f8e951d9872aa9b8a14ef8acc4c45d2c8e973ab2f0d4f8bc3ccc7404baa61267d9d8f61
6
+ metadata.gz: 76e4335229d0b998df64da0c80fe006b76c18bfdd841079d8897baae10202f5ee8cf4f12684a673c42da174c222d507bd43227727810d2f93ca9b13d08ad3a9f
7
+ data.tar.gz: 3eda26cf66c847cd1264cc5f969af053514ac0ddb26963924d3806ae43bbc69bbefb7e0e85ce755efa0b601bc1c2f181cbec1178f2a24b6039e1f610f0014a3e
@@ -1,4 +1,3 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- # env: CODECLIMATE_REPO_TOKEN=540a9a9dd5d7371276ffd320c85d936726dd362b6b2c0333017c13ff5e133a7d
3
+ - 2.2
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- S3 uploads of Ruby objects [![Build Status](https://travis-ci.org/robertzk/s3mpi.svg?branch=master)](https://travis-ci.org/robertzk/s3mpi) [![Code Climate](https://codeclimate.com/github/robertzk/s3mpi.png)](https://codeclimate.com/github/robertzk/s3mpi)
1
+ S3 uploads of Ruby objects [![Build Status](https://travis-ci.org/robertzk/s3mpi-ruby.svg?branch=master)](https://travis-ci.org/robertzk/s3mpi-ruby)
2
2
  ===========
3
3
 
4
4
  Upload and download Ruby objects to S3 using a very convenient API.
@@ -22,5 +22,3 @@ we can store and read Ruby objects:
22
22
  MPI.store({ some: "ruby", object: 5 }, "some_object")
23
23
  MPI.read('some_object')
24
24
  ```
25
-
26
-
@@ -1,5 +1,4 @@
1
1
  require 's3mpi/version'
2
- require 's3mpi/converters'
3
2
  require 's3mpi/interface'
4
3
 
5
4
  module S3MPI
@@ -1,6 +1,21 @@
1
1
  require 's3mpi/converters/csv'
2
+ require 's3mpi/converters/json'
3
+ require 's3mpi/converters/identity'
2
4
 
3
5
  module S3MPI
4
6
  module Converters
7
+
8
+ class UnknownConverterError < StandardError; end
9
+
10
+ def converter(as)
11
+ case as
12
+ when :json then Converters::JSON
13
+ when :csv then Converters::CSV
14
+ when :string, :identity then Converters::Identity
15
+ else
16
+ raise UnknownConverterError, "#{as.inspect} is not a known converter!"
17
+ end
18
+ end
19
+
5
20
  end
6
- end
21
+ end
@@ -5,17 +5,7 @@ module S3MPI
5
5
  module CSV
6
6
  extend self
7
7
 
8
- # Read a CSV file and convert it to an array of hashes
9
- #
10
- # @param [String] csv_file_path
11
- # Path to the CSV file.
12
- #
13
- # @param [Hash] options
14
- # Passed to CSV.parse
15
- def file_to_obj(csv_file_path, options = Hash.new)
16
- csv_data = File.read(csv_file_path)
17
- string_to_obj(csv_data, options)
18
- end
8
+ class HeaderError < StandardError; end
19
9
 
20
10
  # Convert CSV string data to an array of hashes
21
11
  #
@@ -24,14 +14,46 @@ module S3MPI
24
14
  #
25
15
  # @param [Hash] options
26
16
  # Passed to CSV.parse
27
- def string_to_obj(csv_data, options = Hash.new)
17
+ #
18
+ # @return [Array]
19
+ def parse(csv_data, options = Hash.new)
28
20
  options = options.merge({
29
21
  headers: true,
30
22
  converters: :all
31
-
32
23
  })
33
24
  ::CSV.parse(csv_data, options).map(&:to_hash)
34
25
  end
26
+
27
+ # Convert an array of hashes to CSV string data
28
+ #
29
+ # @param [Array] array_of_hashes
30
+ # An Array of Hashes
31
+ #
32
+ # @param [Hash] options
33
+ # Passed to CSV.generate
34
+ #
35
+ # @return [String]
36
+ def generate(array_of_hashes, options = Hash.new)
37
+ return "" if array_of_hashes.empty?
38
+ headers = inspect_headers(array_of_hashes)
39
+ ::CSV.generate(options) do |csv|
40
+ csv << headers
41
+ array_of_hashes.each do |hash|
42
+ csv << hash.values_at(*headers)
43
+ end
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def inspect_headers(data)
50
+ data.first.keys.tap do |headers|
51
+ sorted = headers.sort
52
+ error = data.any?{ |hash| hash.keys.sort != sorted }
53
+ raise HeaderError, "the rows have inconsistent headers!" if error
54
+ end
55
+ end
56
+
35
57
  end
36
58
  end
37
- end
59
+ end
@@ -0,0 +1,16 @@
1
+ module S3MPI
2
+ module Converters
3
+ module Identity
4
+ extend self
5
+
6
+ def parse(data, options = nil)
7
+ data
8
+ end
9
+
10
+ def generate(data)
11
+ data
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ require 'json'
2
+
3
+ module S3MPI
4
+ module Converters
5
+ module JSON
6
+ extend self
7
+
8
+ def parse(json_string, options = Hash.new)
9
+ opts = { quirks_mode: true }.merge(options)
10
+ ::JSON.parse(json_string || 'null', **opts)
11
+ end
12
+
13
+ def generate(object, options = Hash.new)
14
+ object.to_json(options)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -1,12 +1,12 @@
1
- require 'json'
2
- require 's3mpi/format'
3
1
  require 's3mpi/s3'
4
2
  require 's3mpi/converters'
5
3
 
6
4
  module S3MPI
7
5
  class Interface
8
- include Format
9
6
  include S3
7
+ include Converters
8
+
9
+ UUID = Object.new.freeze
10
10
 
11
11
  # Return S3 bucket under use.
12
12
  #
@@ -18,96 +18,69 @@ module S3MPI
18
18
  # @return [String]
19
19
  attr_reader :path
20
20
 
21
+ # Return the default converter for store & read.
22
+ #
23
+ # @return [String]
24
+ attr_reader :default_converter
25
+
21
26
  # Create a new S3MPI object that responds to #read and #store.
22
27
  #
23
28
  # @return [S3MPI::Interface]
24
29
  #
25
30
  # @api public
26
- def initialize _bucket, path = ''
27
- @bucket = _bucket
28
- @path = path
31
+ def initialize bucket, path = '', default_converter = :json
32
+ @bucket = parse_bucket(bucket)
33
+ @path = path.freeze
34
+ converter(default_converter) # verify it is valid
35
+ @default_converter = default_converter
29
36
  end
30
37
 
31
38
  # Store a Ruby object in an S3 bucket.
32
- #
39
+ #
33
40
  # @param [Object] obj
34
- # Any JSON-serializable Ruby object (usually a hash or array).
41
+ # Any convertable Ruby object (usually a hash or array).
35
42
  # @param [String] key
36
43
  # The key under which to save the object in the S3 bucket.
37
- # @param [Integer] try
44
+ # @param [Symbol] :as
45
+ # Which converter to use e.g. :json, :csv, :string
46
+ # @param [Integer] tries
38
47
  # The number of times to attempt to store the object.
39
- def store(obj, key = SecureRandom.uuid, try = 1)
40
- s3_object(key).write(obj.to_json)
41
- rescue # This should really rescue specific errors
42
- (try -= 1) > 0 ? retry : raise
48
+ def store(obj, key = UUID, as: default_converter, tries: 1)
49
+ key = SecureRandom.uuid if key.equal?(UUID)
50
+ s3_object(key).write(converter(as).generate(obj))
51
+ rescue AWS::Errors::Base
52
+ (tries -= 1) > 0 ? retry : raise
43
53
  end
44
54
 
55
+ def store_csv(obj, key = UUID); store(obj, key, as: :csv); end
56
+ def store_json(obj, key = UUID); store(obj, key, as: :json); end
57
+ def store_string(obj, key = UUID); store(obj, key, as: :string); end
45
58
 
46
- # Load a CSV file, and store the contents in S3
47
- # Proxies to store
48
- #
49
- # @param [String] csv_file_path
50
- # Path to the CSV file.
59
+ # Read and de-serialize an object from an S3 bucket.
51
60
  #
52
- # @param [Hash] options Options hash.
53
- # Passed to CSV.parse
54
- def store_csv(csv_file_path, options = Hash.new)
55
- store(Converters::CSV.file_to_obj(csv_file_path, options))
56
- end
57
-
58
- # Store a raw object
59
- # @param [Object] obj
60
- # The object to store.
61
- # @param [String] key
62
- # The key under which the object is saved in the S3 bucket.
63
- def store_raw(obj, key)
64
- s3_object(key).write(obj)
65
- end
66
-
67
- # Read a JSON-serialized Ruby object from an S3 bucket.
68
- #
69
61
  # @param [String] key
70
62
  # The key under which to save the object in the S3 bucket.
71
- def read key = nil
72
- parse_json_allowing_quirks_mode s3_object(key).read
63
+ # @param [Symbol] :as
64
+ # Which converter to use e.g. :json, :csv, :string
65
+ def read(key = nil, as: default_converter)
66
+ converter(as).parse(s3_object(key).read)
73
67
  rescue AWS::S3::Errors::NoSuchKey
74
68
  nil
75
69
  end
76
70
 
77
- # Read a CSV file from an S3 bucket. Return as array of hashes.
78
- #
79
- # @param [String] key
80
- # The key under which the file is saved in the S3 bucket.
81
- def read_csv(key=nil)
82
- Converters::CSV.string_to_obj(s3_object(key).read)
83
- rescue AWS::S3::Errors::NoSuchKey
84
- nil
85
- end
71
+ def read_csv(key = nil); read(key, as: :csv); end
72
+ def read_json(key = nil); read(key, as: :json); end
73
+ def read_string(key = nil); read(key, as: :string); end
86
74
 
87
75
  # Check whether a key exists for this MPI interface.
88
- #
76
+ #
89
77
  # @param [String] key
90
78
  # The key under which to save the object in the S3 bucket.
91
- #
79
+ #
92
80
  # @return [TrueClass,FalseClass]
93
81
  def exists?(key)
94
82
  s3_object(key).exists?
95
83
  end
96
84
 
97
- # Fetch the S3 object as an AWS::S3::S3Object.
98
- #
99
- # @param [String] name
100
- # The key under which to save the object in the S3 bucket.
101
- #
102
- # @return [AWS::S3::S3Object]
103
- def object(key)
104
- AWS::S3::S3Object.new(bucket, "#{@path}#{key}")
105
- end
106
-
107
- def bucket
108
- @_bucket ||= parse_bucket @bucket
109
- end
110
85
  end
111
86
  end
112
-
113
-
@@ -3,12 +3,24 @@ require 'aws-sdk-v1'
3
3
  module S3MPI
4
4
  module S3
5
5
 
6
- def parse_bucket(bucket)
6
+ def s3_object(name)
7
+ bucket.objects[full_object_key(name)]
8
+ end
9
+
10
+ private
11
+
12
+ def parse_bucket(bucket)
7
13
  AWS::S3.new.buckets[bucket]
8
14
  end
9
15
 
10
- def s3_object name
11
- bucket.objects[[path, name].join('/')]
16
+ def full_object_key(name)
17
+ [path, name].flatten.reject{ |x| blank?(x) }.join('/')
18
+ end
19
+
20
+ def blank?(x)
21
+ return true if !x
22
+ return true if x.is_a?(String) && x.strip == ""
23
+ false
12
24
  end
13
25
 
14
26
  end
@@ -1,9 +1,9 @@
1
1
  module S3MPI
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 0
5
- PATCH = 8
6
- BUILD = 1
4
+ MINOR = 1
5
+ PATCH = 0
6
+ BUILD = 2
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.');
9
9
  end
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.add_development_dependency 'rake', '~> 10.1.0'
28
28
  s.add_development_dependency 'rspec', '~> 3.4'
29
+ s.add_development_dependency 'pry', '~> 0.10.4'
29
30
 
30
31
  s.extra_rdoc_files = ['README.md', 'LICENSE']
31
32
  end
32
-
@@ -1,25 +1,46 @@
1
1
  require 'spec_helper'
2
- require_relative 'parsed_csv_shared_examples'
3
2
 
4
- module S3MPI
5
- module Converters
6
- describe CSV do
3
+ describe S3MPI::Converters::CSV do
4
+ let(:csv_file_path) { 'spec/support/test.csv' }
5
+ let(:csv_data_string) { File.read csv_file_path }
7
6
 
8
- let(:csv_file_path) { 'spec/support/test.csv' }
9
- let(:csv_data_string) { File.read csv_file_path }
7
+ let(:csv_as_array_of_hashes) {
8
+ [
9
+ {'integer' => 1, 'string' => 'user1@test.com', 'float' => 1.11},
10
+ {'integer' => 2, 'string' => 'user2@test.com', 'float' => 2.22},
11
+ {'integer' => 3, 'string' => 'user3@test.com', 'float' => 3.33},
12
+ ]
13
+ }
10
14
 
11
- describe '#convert_to_obj' do
12
- subject { described_class.string_to_obj csv_data_string }
15
+ describe '#parse' do
16
+ subject { described_class.parse csv_data_string }
13
17
 
14
- it_behaves_like 'a parsed CSV'
15
- end
18
+ it 'converts CSV data to an array of hashes' do
19
+ expect(subject).to be_kind_of Array
16
20
 
17
- describe '#file_to_obj' do
18
- subject { described_class.file_to_obj csv_file_path }
21
+ subject.each do |row|
22
+ expect(row).to be_kind_of Hash
23
+ end
24
+ end
19
25
 
20
- it_behaves_like 'a parsed CSV'
26
+ it 'preserves integers' do
27
+ subject.each do |row|
28
+ expect(row['integer']).to eq(row['integer'].to_s.to_i)
29
+ end
30
+ end
21
31
 
32
+ it 'preserves floats' do
33
+ subject.each do |row|
34
+ expect(row['float']).to eq(row['float'].to_s.to_f)
22
35
  end
23
36
  end
37
+
38
+ it { is_expected.to eql csv_as_array_of_hashes }
39
+ end
40
+
41
+ describe '#generate' do
42
+ subject { described_class.generate csv_as_array_of_hashes }
43
+
44
+ it { is_expected.to eql csv_data_string }
24
45
  end
25
46
  end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe S3MPI::Converters::Identity do
4
+
5
+ describe '#parse' do
6
+ let(:data){ "any\nrandom\nstring" }
7
+
8
+ it 'returns the input data' do
9
+ expect(described_class.parse(data)).to equal data
10
+ end
11
+ end
12
+
13
+ describe '#generate' do
14
+ let(:data){ "any\nrandom\nstring" }
15
+
16
+ it 'returns the input data' do
17
+ expect(described_class.generate(data)).to equal data
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe S3MPI::Converters::JSON do
4
+
5
+ describe '#parse' do
6
+ it 'loads a JSON hash' do
7
+ expect(described_class.parse('{ "a": 1, "b": "test" }')
8
+ ).to eql({ "a" => 1, "b" => "test"})
9
+ end
10
+
11
+ it 'loads a JSON array' do
12
+ expect(described_class.parse('["a", 1, "b", 2]')
13
+ ).to eql(["a", 1, "b", 2])
14
+ end
15
+
16
+ it 'loads a JSON array of hashes' do
17
+ expect(described_class.parse('[{ "a": "one" }, {"b": "two" }]')
18
+ ).to eql([{ "a" => "one" }, {"b" => "two"}])
19
+ end
20
+
21
+ it 'loads a single JSON element' do
22
+ expect(described_class.parse('"hello world"')
23
+ ).to eql("hello world")
24
+ end
25
+
26
+ it 'loads the JSON null as nil' do
27
+ expect(described_class.parse('null')).to be_nil
28
+ end
29
+ end
30
+
31
+ describe '#generate' do
32
+ it 'dumps a Hash' do
33
+ expect(described_class.generate({ "a" => 1, "b" => "test"})
34
+ ).to eql('{"a":1,"b":"test"}')
35
+ end
36
+
37
+ it 'dumps an Array' do
38
+ expect(described_class.generate(["a", 1, "b", 2])
39
+ ).to eql('["a",1,"b",2]')
40
+ end
41
+
42
+ it 'dumps an Array of Hashes' do
43
+ expect(described_class.generate([{ "a" => "one" }, {"b" => "two"}])
44
+ ).to eql('[{"a":"one"},{"b":"two"}]')
45
+ end
46
+
47
+ it 'dumps a String' do
48
+ expect(described_class.generate("hello world")
49
+ ).to eql('"hello world"')
50
+ end
51
+
52
+ it 'dumps nil as the JSON null' do
53
+ expect(described_class.generate(nil)).to eql 'null'
54
+ end
55
+ end
56
+
57
+ end
@@ -1,41 +1,186 @@
1
1
  require 'spec_helper'
2
- require 's3mpi/interface'
3
- require 's3mpi/converters/csv'
4
- require_relative '../s3mpi/converters/parsed_csv_shared_examples'
5
2
 
6
- module S3MPI
3
+ describe S3MPI::Interface do
4
+ let(:bucket) { 'some_bucket' }
5
+ let(:path) { 'some_folder' }
7
6
 
8
- describe Interface do
7
+ let(:interface) { described_class.new(bucket, path) }
9
8
 
10
- let(:bucket) { 'some_bucket' }
11
- let(:path) { 'some_folder' }
12
- let(:csv_file_path) { 'spec/support/test.csv' }
13
- let(:csv_data_string) { File.read csv_file_path }
14
- let(:csv_as_array_of_hashes) {
15
- [
16
- {'integer' => 1, 'string' => 'user1@test.com', 'float' => 1.11},
17
- {'integer' => 2, 'string' => 'user2@test.com', 'float' => 2.22},
18
- {'integer' => 3, 'string' => 'user3@test.com', 'float' => 3.33},
19
- ]
20
- }
9
+ describe '#bucket' do
10
+ subject{ interface.bucket }
11
+ it { is_expected.to be_a AWS::S3::Bucket }
12
+ it { is_expected.to have_attributes(name: bucket) }
13
+ end
14
+
15
+ describe '#path' do
16
+ subject{ interface.path }
17
+ it { is_expected.to eql path }
18
+ end
19
+
20
+ describe '#default_converter' do
21
+ subject{ interface.default_converter }
22
+
23
+ context 'default' do
24
+ it { is_expected.to eql :json }
25
+ end
26
+
27
+ context 'set on initialization to :csv' do
28
+ let(:interface) { described_class.new(bucket, path, :csv) }
29
+ it { is_expected.to eql :csv }
30
+ end
31
+
32
+ it "initialization fails if it is invalid" do
33
+ expect{ described_class.new(bucket, path, :foo) }.to raise_error \
34
+ S3MPI::Converters::UnknownConverterError
35
+ end
36
+ end
37
+
38
+ describe '#converter' do
39
+ def expect_converter(key, klass)
40
+ expect(interface.converter(key)).to eql klass
41
+ end
42
+
43
+ it('json'){ expect_converter :json, S3MPI::Converters::JSON }
44
+ it('csv') { expect_converter :csv, S3MPI::Converters::CSV }
45
+ it('string'){ expect_converter :string, S3MPI::Converters::Identity }
46
+ it('identity'){ expect_converter :identity, S3MPI::Converters::Identity }
47
+
48
+ it "raises for unknown keys" do
49
+ err = S3MPI::Converters::UnknownConverterError
50
+ msg = ":foo is not a known converter!"
51
+ expect{ interface.converter(:foo) }.to raise_error(err, msg)
52
+ end
53
+ end
54
+
55
+ describe '#s3_object' do
56
+ subject { interface.s3_object(name) }
57
+
58
+ let(:name) { 'foo/bar/baz' }
59
+
60
+ it { is_expected.to be_a AWS::S3::S3Object }
61
+ it { is_expected.to have_attributes(bucket: interface.bucket) }
62
+ it { is_expected.to have_attributes(key: "#{path}/#{name}") }
63
+
64
+ { 'empty' => '', 'whitespace' => ' ', 'nil' => nil }.each do |desc, value|
65
+ context "#{desc} path" do
66
+ let(:path) { value }
67
+ it { is_expected.to have_attributes(key: name) }
68
+ end
69
+
70
+ context "#{desc} name" do
71
+ let(:name) { value }
72
+ it { is_expected.to have_attributes(key: path) }
73
+ end
74
+ end
75
+ end
76
+
77
+ context "with a pinned s3_object" do
78
+ def pin_s3_objects!
79
+ s3_objects_to_pin.each do |key, s3_obj|
80
+ allow(interface).to receive(:s3_object).with(key).and_return(s3_obj)
81
+ end
82
+ end
21
83
 
22
- let(:interface) { described_class.new(bucket: bucket, path: path) }
23
- subject { interface }
84
+ def s3_objects_to_pin(*keys)
85
+ (@to_pin ||= {}).tap do |h|
86
+ keys.each { |k| h[k] = interface.s3_object(k) }
87
+ end
88
+ end
24
89
 
25
- describe '.store_csv' do
26
- it 'sends the CSV as a row of hashes to .store' do
27
- expect(subject).to receive(:store).with(csv_as_array_of_hashes)
90
+ let(:name) { 'name' }
28
91
 
29
- subject.store_csv csv_file_path
92
+ before { s3_objects_to_pin(name) && AWS.stub! }
93
+ subject { pin_s3_objects! && s3_objects_to_pin[name] }
94
+
95
+ describe '#exists?' do
96
+ it 'calls .exists? on the s3 object' do
97
+ expect(subject).to receive(:exists?).and_return(retval = double)
98
+ expect(interface.exists?(name)).to equal retval
30
99
  end
31
100
  end
32
101
 
33
- describe '.read_csv' do
102
+ let(:as_json) { '{"a":1,"b":"two","c":[3,3,3]}' }
103
+ let(:as_hash) { { "a" => 1, "b" => "two", "c" => [3,3,3] } }
104
+
105
+ let(:as_csv) { "x,y,z\n1,2,3\n4,5,6\n7,8,9\n" }
106
+ let(:as_list) { [{"x" => 1, "y" => 2, "z" => 3},
107
+ {"x" => 4, "y" => 5, "z" => 6},
108
+ {"x" => 7, "y" => 8, "z" => 9}] }
109
+
110
+ let(:random_str) { 10.times.map{ SecureRandom.uuid}.join("\n") }
34
111
 
35
- before { allow(interface).to receive(:s3_object) { double(read: csv_data_string) } }
36
- subject { interface.read_csv csv_file_path }
37
- it_behaves_like 'a parsed CSV'
112
+ describe '#read' do
113
+ it 'can parse the raw string with the json converter' do
114
+ expect(subject).to receive(:read).and_return(as_json).twice
115
+ expect(interface.read(name, as: :json)).to eql(as_hash)
116
+ expect(interface.read_json(name)).to eql(as_hash)
117
+ end
118
+
119
+ it 'can parse the raw string with the csv converter' do
120
+ expect(subject).to receive(:read).and_return(as_csv).twice
121
+ expect(interface.read(name, as: :csv)).to eql(as_list)
122
+ expect(interface.read_csv(name)).to eql(as_list)
123
+ end
38
124
 
125
+ it 'can parse the raw string with the identity converter' do
126
+ expect(subject).to receive(:read).and_return(random_str).twice
127
+ expect(interface.read(name, as: :identity)).to eql(random_str)
128
+ expect(interface.read_string(name)).to eql(random_str)
129
+ end
130
+
131
+ it 'defaults to the default_converter' do
132
+ allow(interface).to receive(:default_converter).and_return(:foo)
133
+ expect(subject).to receive(:read).and_return(as_json)
134
+ expect(interface).to receive(:converter
135
+ ).with(:foo).and_return(S3MPI::Converters::Identity)
136
+ expect(interface.read(name)).to eql as_json
137
+ end
138
+ end
139
+
140
+ describe '#store' do
141
+ it 'can generate json and write it to s3' do
142
+ expect(subject).to receive(:write).with(as_json).twice
143
+ interface.store(as_hash, name, as: :json)
144
+ interface.store_json(as_hash, name)
145
+ end
146
+
147
+ it 'can generate csv and write it to s3' do
148
+ expect(subject).to receive(:write).with(as_csv).twice
149
+ interface.store(as_list, name, as: :csv)
150
+ interface.store_csv(as_list, name)
151
+ end
152
+
153
+ it 'can write the raw string to s3' do
154
+ expect(subject).to receive(:write).with(random_str).twice
155
+ interface.store(random_str, name, as: :identity)
156
+ interface.store_string(random_str, name)
157
+ end
158
+
159
+ it 'uses a UUID if the key is not explicitly passed' do
160
+ s3_objects_to_pin('some_uuid')
161
+ expect(subject).not_to receive(:write)
162
+ expect(SecureRandom).to receive(:uuid).and_return('some_uuid')
163
+ expect(s3_objects_to_pin['some_uuid']).to receive(:write).with(as_json)
164
+ interface.store(as_hash)
165
+ end
166
+
167
+ it 'defaults to the default_converter' do
168
+ expect(subject).to receive(:write).with(random_str)
169
+ allow(interface).to receive(:default_converter).and_return(:foo)
170
+ expect(interface).to receive(:converter
171
+ ).with(:foo).and_return(S3MPI::Converters::Identity)
172
+ interface.store(random_str, name)
173
+ end
174
+
175
+ it 'retries on AWS::Errors::ServerError' do
176
+ error = AWS::S3::Errors::RequestTimeout
177
+ expect(subject).to receive(:write).and_raise(error).twice
178
+ expect(subject).to receive(:write).and_return("SUCCESS")
179
+ generator = S3MPI::Converters::JSON
180
+ expect(generator).to receive(:generate).with(as_hash).exactly(3).times
181
+ expect(interface.store(as_hash, name, tries: 3)).to eql "SUCCESS"
182
+ end
39
183
  end
40
184
  end
185
+
41
186
  end
@@ -1,9 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require 's3mpi'
3
3
 
4
- Dir["#{File.dirname(__FILE__)}/{support,shared}/**/*.rb"].each { |f| require f }
5
-
6
4
  RSpec.configure do |config|
7
5
  # Nothing yet...
8
6
  end
9
-
@@ -1,4 +1,4 @@
1
1
  integer,string,float
2
2
  1,user1@test.com,1.11
3
3
  2,user2@test.com,2.22
4
- 3,user3@test.com,3.33
4
+ 3,user3@test.com,3.33
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: s3mpi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8.1
4
+ version: 0.1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Krzyzanowski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-07 00:00:00.000000000 Z
11
+ date: 2017-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-v1
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.10.4
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.10.4
55
69
  description: Passing objects between Ruby consoles can be cumbersome if the user has
56
70
  performed some serialization and deserialization procedure. S3MPI aims to enable
57
71
  simple reading and writing to S3 buckets without the typical overhead of the AWS
@@ -72,15 +86,16 @@ files:
72
86
  - lib/s3mpi.rb
73
87
  - lib/s3mpi/converters.rb
74
88
  - lib/s3mpi/converters/csv.rb
75
- - lib/s3mpi/format.rb
89
+ - lib/s3mpi/converters/identity.rb
90
+ - lib/s3mpi/converters/json.rb
76
91
  - lib/s3mpi/interface.rb
77
92
  - lib/s3mpi/s3.rb
78
93
  - lib/s3mpi/version.rb
79
94
  - s3mpi.gemspec
80
95
  - spec/s3mpi/converters/csv_spec.rb
81
- - spec/s3mpi/converters/parsed_csv_shared_examples.rb
96
+ - spec/s3mpi/converters/identity_spec.rb
97
+ - spec/s3mpi/converters/json_spec.rb
82
98
  - spec/s3mpi/interface_spec.rb
83
- - spec/s3mpi/s3_spec.rb
84
99
  - spec/spec_helper.rb
85
100
  - spec/support/test.csv
86
101
  homepage: https://github.com/robertzk/s3mpi-ruby
@@ -103,14 +118,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
118
  version: '0'
104
119
  requirements: []
105
120
  rubyforge_project:
106
- rubygems_version: 2.5.1
121
+ rubygems_version: 2.4.8
107
122
  signing_key:
108
123
  specification_version: 4
109
124
  summary: Upload and download files to S3 using a very convenient API.
110
125
  test_files:
111
126
  - spec/s3mpi/converters/csv_spec.rb
112
- - spec/s3mpi/converters/parsed_csv_shared_examples.rb
127
+ - spec/s3mpi/converters/identity_spec.rb
128
+ - spec/s3mpi/converters/json_spec.rb
113
129
  - spec/s3mpi/interface_spec.rb
114
- - spec/s3mpi/s3_spec.rb
115
130
  - spec/spec_helper.rb
116
131
  - spec/support/test.csv
@@ -1,14 +0,0 @@
1
- require 'json'
2
-
3
- module S3MPI
4
- module Format
5
-
6
- def parse_json_allowing_quirks_mode obj
7
- JSON.parse(obj || 'null')
8
- rescue JSON::ParserError => e
9
- JSON.parse(obj || 'null', quirks_mode: true)
10
- end
11
-
12
- end
13
- end
14
-
@@ -1,21 +0,0 @@
1
- RSpec.shared_examples 'a parsed CSV' do
2
- it 'converts CSV data to an array of hashes' do
3
- expect(subject).to be_kind_of Array
4
-
5
- subject.each do |row|
6
- expect(row).to be_kind_of Hash
7
- end
8
- end
9
-
10
- it 'preserves integers' do
11
- subject.each do |row|
12
- expect(row['integer']).to eq(row['integer'].to_s.to_i)
13
- end
14
- end
15
-
16
- it 'preserves floats' do
17
- subject.each do |row|
18
- expect(row['float']).to eq(row['float'].to_s.to_f)
19
- end
20
- end
21
- end
@@ -1,19 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module S3MPI
4
-
5
- describe Format do
6
- let(:format_mixin) {
7
- klass = Class.new
8
- klass.send :include, Format
9
- klass.new
10
- }
11
-
12
- it 'expects #parse_json_allowing_quirks_mode to parse JSON' do
13
- obj = {"a" => 1, "b" => "test"}
14
- expect(format_mixin.parse_json_allowing_quirks_mode(obj.to_json)).to eql(obj)
15
- end
16
-
17
- end
18
-
19
- end