uploadcare-ruby 1.2.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -1
- data/.rspec +1 -0
- data/.travis.yml +19 -5
- data/CHANGELOG.md +12 -30
- data/README.md +149 -72
- data/Rakefile +1 -1
- data/UPGRADE_NOTES.md +36 -0
- data/lib/uploadcare.rb +16 -22
- data/lib/uploadcare/api.rb +3 -1
- data/lib/uploadcare/api/file_list_api.rb +15 -4
- data/lib/uploadcare/api/file_storage_api.rb +34 -0
- data/lib/uploadcare/api/group_list_api.rb +13 -4
- data/lib/uploadcare/api/raw_api.rb +10 -16
- data/lib/uploadcare/api/uploading_api.rb +45 -84
- data/lib/uploadcare/api/uploading_api/upload_params.rb +72 -0
- data/lib/uploadcare/api/validators/file_list_options_validator.rb +73 -0
- data/lib/uploadcare/api/validators/group_list_options_validator.rb +49 -0
- data/lib/uploadcare/resources/file_list.rb +6 -33
- data/lib/uploadcare/resources/group_list.rb +6 -23
- data/lib/uploadcare/resources/resource_list.rb +83 -0
- data/lib/uploadcare/rest/connections/api_connection.rb +25 -3
- data/lib/uploadcare/rest/connections/upload_connection.rb +3 -2
- data/lib/uploadcare/version.rb +1 -1
- data/spec/api/file_list_api_spec.rb +95 -0
- data/spec/api/file_storage_api_spec.rb +88 -0
- data/spec/api/group_list_api_spec.rb +59 -0
- data/spec/api/raw_api_spec.rb +12 -12
- data/spec/api/uploading_api/upload_params_spec.rb +99 -0
- data/spec/api/uploading_api_spec.rb +59 -0
- data/spec/resources/file_list_spec.rb +13 -52
- data/spec/resources/file_spec.rb +6 -3
- data/spec/resources/group_list_spec.rb +15 -20
- data/spec/rest/api_connection_spec.rb +1 -1
- data/spec/shared/resource_list.rb +188 -0
- data/spec/spec_helper.rb +11 -1
- data/spec/uploadcare_spec.rb +9 -32
- data/spec/utils/parser_spec.rb +34 -36
- data/uploadcare-ruby.gemspec +7 -6
- metadata +52 -37
- data/lib/uploadcare/utils/user_agent.rb +0 -44
- data/spec/uploading/uploading_multiple_spec.rb +0 -43
- data/spec/uploading/uploading_spec.rb +0 -40
- data/spec/utils/user_agent_spec.rb +0 -46
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Uploadcare::GroupListApi do
|
4
|
+
let(:api){ API }
|
5
|
+
subject{ api.group_list(limit: 1) }
|
6
|
+
|
7
|
+
before(:each){ allow(api).to receive(:get){ {'results' => []} } }
|
8
|
+
|
9
|
+
it 'returns a group list' do
|
10
|
+
expect( subject ).to be_a(Uploadcare::Api::GroupList)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'stores options in a group list object' do
|
14
|
+
expect( subject.options ).to eq({limit: 1})
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'validation' do
|
18
|
+
it 'passes validation when no options given' do
|
19
|
+
expect{ api.group_list }.not_to raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it "validates that options don't have unsupported keys" do
|
23
|
+
expect{ api.group_list(unknown: 1) }.to raise_error(ArgumentError)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'validates that :limit is an integer from 1 to 1000' do
|
27
|
+
[1, 359, 1000].each do |v|
|
28
|
+
expect{ api.group_list(limit: v) }.not_to raise_error
|
29
|
+
end
|
30
|
+
|
31
|
+
[1.0, -1, 0, 1001, false].each do |v|
|
32
|
+
expect{ api.group_list(limit: v) }.to raise_error(ArgumentError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
valid_ordering = %w{datetime_created -datetime_created}
|
37
|
+
it "validates that :ordering is in [#{valid_ordering.join(', ')}]" do
|
38
|
+
valid_ordering.each do |valid_value|
|
39
|
+
expect{ api.group_list(ordering: valid_value) }.not_to raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
expect{ api.group_list(ordering: 'yes') }.to raise_error(ArgumentError)
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'from' do
|
46
|
+
it 'validates that :from.to_s is a iso8601 string' do
|
47
|
+
valid = [DateTime.now, DateTime.now.iso8601, "2017-01-01T15"]
|
48
|
+
valid.each do |value|
|
49
|
+
expect{ api.group_list(from: value) }.not_to raise_error
|
50
|
+
end
|
51
|
+
|
52
|
+
invalid = [Date.today, Time.now, DateTime.now.rfc2822, "2017-01-01", 123, false]
|
53
|
+
invalid.each do |value|
|
54
|
+
expect{ api.group_list(from: value) }.to raise_error(ArgumentError)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/spec/api/raw_api_spec.rb
CHANGED
@@ -3,23 +3,23 @@ require 'uri'
|
|
3
3
|
require 'socket'
|
4
4
|
|
5
5
|
describe Uploadcare::Api do
|
6
|
-
|
7
|
-
@api = Uploadcare::Api.new(CONFIG)
|
8
|
-
end
|
6
|
+
subject(:api) { Uploadcare::Api.new(CONFIG) }
|
9
7
|
|
10
8
|
it "should initialize api" do
|
11
|
-
|
9
|
+
is_expected.to be_an_instance_of(Uploadcare::Api)
|
12
10
|
end
|
13
11
|
|
14
12
|
it 'should respond to request methods' do
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
is_expected.to respond_to :request
|
14
|
+
is_expected.to respond_to :get
|
15
|
+
is_expected.to respond_to :post
|
16
|
+
is_expected.to respond_to :put
|
17
|
+
is_expected.to respond_to :delete
|
20
18
|
end
|
21
19
|
|
22
|
-
|
23
|
-
|
20
|
+
context 'when performing requests' do
|
21
|
+
subject(:request) { api.request }
|
22
|
+
|
23
|
+
it { is_expected.to be_a Hash }
|
24
24
|
end
|
25
|
-
end
|
25
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Uploadcare::UploadingApi::UploadParams do
|
4
|
+
shared_examples 'handles store flag' do |key_name|
|
5
|
+
let(:true_value) { 1 }
|
6
|
+
let(:false_value) { 0 }
|
7
|
+
|
8
|
+
context 'when neither global nor per-request store option is set' do
|
9
|
+
it { is_expected.not_to include(key_name) }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'accepts :auto value in :store option' do
|
13
|
+
before { request_options.merge!(store: :auto) }
|
14
|
+
it { is_expected.to include(key_name => 'auto') }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when only global :autostore option is set' do
|
18
|
+
context 'to true' do
|
19
|
+
before { global_options.merge!(autostore: true) }
|
20
|
+
it { is_expected.to include(key_name => true_value) }
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'to false' do
|
24
|
+
before { global_options.merge!(autostore: false) }
|
25
|
+
it { is_expected.to include(key_name => false_value) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'per-request :store option is set' do
|
30
|
+
context 'to true' do
|
31
|
+
before { request_options.merge!(store: true) }
|
32
|
+
it { is_expected.to include(key_name => true_value) }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'to false' do
|
36
|
+
before { request_options.merge!(store: false) }
|
37
|
+
it { is_expected.to include(key_name => false_value) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when both global and per-request store options are set' do
|
42
|
+
before do
|
43
|
+
global_options.merge!(autostore: false)
|
44
|
+
request_options.merge!(store: true)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'per-request :store option has higher presidence' do
|
48
|
+
is_expected.to include(key_name => true_value)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:global_options) { {public_key: 'test_public_key'} }
|
54
|
+
let(:request_options) { {} }
|
55
|
+
|
56
|
+
describe '#for_url_upload' do
|
57
|
+
let(:url) { 'http://example.com/image.jpg' }
|
58
|
+
subject(:upload_params) do
|
59
|
+
described_class.new(global_options, request_options).for_url_upload(url)
|
60
|
+
end
|
61
|
+
|
62
|
+
it { is_expected.to include(source_url: URI.parse(url)) }
|
63
|
+
it { is_expected.to include(pub_key: 'test_public_key') }
|
64
|
+
it_behaves_like 'handles store flag', :store
|
65
|
+
|
66
|
+
context 'works with https URLs' do
|
67
|
+
let(:url) { 'https://example.com/image.jpg' }
|
68
|
+
it { expect { upload_params }.not_to raise_error }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'if url is not http/https' do
|
72
|
+
let(:url) { 'ftp://example.com/image.jpg' }
|
73
|
+
it { expect { upload_params }.to raise_error(ArgumentError) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#for_file_upload' do
|
78
|
+
let(:files) { FILES_ARY } # contains 2 files, first is png, second is jpg
|
79
|
+
subject(:upload_params) do
|
80
|
+
described_class.new(global_options, request_options).for_file_upload(files)
|
81
|
+
end
|
82
|
+
|
83
|
+
it { is_expected.to include(UPLOADCARE_PUB_KEY: 'test_public_key') }
|
84
|
+
it_behaves_like 'handles store flag', :UPLOADCARE_STORE
|
85
|
+
|
86
|
+
context 'file params' do
|
87
|
+
subject(:file_params) { upload_params.select { |k, _| k =~ /^file\[\d+\]/ }.values }
|
88
|
+
|
89
|
+
it { expect(file_params.size).to eq(files.size) }
|
90
|
+
it { is_expected.to all(be_a(Faraday::UploadIO)) }
|
91
|
+
it { is_expected.to all(satisfy { |file| file.content_type =~ /image\/(png|jpeg)/}) }
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when any of given objects is not a File' do
|
95
|
+
let(:files) { FILES_ARY + ['not a File'] }
|
96
|
+
it { expect {upload_params}.to raise_error(ArgumentError) }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Uploadcare::Api do
|
4
|
+
shared_examples 'respects :store option' do
|
5
|
+
context 'respects :store option' do
|
6
|
+
let(:upload_options) { {store: store} }
|
7
|
+
subject { Array(uploaded).map(&:load_data) }
|
8
|
+
|
9
|
+
context 'when :store option is true' do
|
10
|
+
let(:store) { true }
|
11
|
+
it { is_expected.to all(be_stored) }
|
12
|
+
end
|
13
|
+
|
14
|
+
define_negated_matcher :be_not_stored, :be_stored
|
15
|
+
context 'when :store option is false' do
|
16
|
+
let(:store) { false }
|
17
|
+
it { is_expected.to all(be_not_stored) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:api) { API }
|
23
|
+
let(:upload_options) { {} }
|
24
|
+
|
25
|
+
context 'when uploading single object' do
|
26
|
+
subject(:uploaded) { api.upload(object, upload_options) }
|
27
|
+
|
28
|
+
context 'when uploading a file' do
|
29
|
+
let(:object) { FILE1 }
|
30
|
+
|
31
|
+
it { is_expected.to be_a(Uploadcare::Api::File) }
|
32
|
+
it { is_expected.to have_attributes(uuid: match(UUID_REGEX)) }
|
33
|
+
include_examples 'respects :store option'
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when uploading from url' do
|
37
|
+
let(:object) { IMAGE_URL }
|
38
|
+
|
39
|
+
it { is_expected.to be_a(Uploadcare::Api::File) }
|
40
|
+
it { is_expected.to have_attributes(uuid: match(UUID_REGEX)) }
|
41
|
+
it { expect { api.upload('invalid.url.') }.to raise_error(ArgumentError) }
|
42
|
+
include_examples 'respects :store option'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when loading multiple objects' do
|
47
|
+
before(:all) { @uploaded_files = API.upload(FILES_ARY) }
|
48
|
+
subject(:uploaded) { @uploaded_files.dup }
|
49
|
+
|
50
|
+
it { is_expected.to be_a(Array) }
|
51
|
+
it { is_expected.to all(be_a(Uploadcare::Api::File)) }
|
52
|
+
it { is_expected.to all(have_attributes(uuid: match(UUID_REGEX))) }
|
53
|
+
it { expect { uploaded.map(&:load_data) }.not_to raise_error }
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when given object is not a File, Array or String' do
|
57
|
+
it { expect { api.upload(12) }.to raise_error(ArgumentError) }
|
58
|
+
end
|
59
|
+
end
|
@@ -1,64 +1,25 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'uri'
|
3
|
-
require 'socket'
|
4
2
|
|
5
|
-
describe Uploadcare::Api::
|
3
|
+
describe Uploadcare::Api::FileList do
|
6
4
|
before :all do
|
7
5
|
@api = API
|
8
|
-
@list = @api.file_list 1
|
9
|
-
end
|
10
|
-
|
11
|
-
it "should return file list" do
|
12
|
-
@list.should be_kind_of(Uploadcare::Api::FileList)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "should respont to results method" do
|
16
|
-
@list.should respond_to :results
|
17
|
-
end
|
18
6
|
|
19
|
-
|
20
|
-
@
|
21
|
-
|
7
|
+
# ensure that current project has at least three files
|
8
|
+
count = @api.get('/files/', limit: 3)['results'].size
|
9
|
+
(3 - count).times{ @api.upload(IMAGE_URL) } if count < 3
|
22
10
|
|
23
|
-
|
24
|
-
@list.results.each do |file|
|
25
|
-
file.should be_kind_of(Uploadcare::Api::File)
|
26
|
-
end
|
11
|
+
@list = @api.file_list(limit: 1)
|
27
12
|
end
|
28
13
|
|
29
|
-
|
30
|
-
|
31
|
-
file.is_loaded?.should be true
|
32
|
-
end
|
33
|
-
end
|
14
|
+
let(:resource_class){ Uploadcare::Api::File }
|
15
|
+
subject{ @list }
|
34
16
|
|
35
|
-
|
36
|
-
next_page = @list.next_page
|
37
|
-
next_page.should be_kind_of(Uploadcare::Api::FileList)
|
38
|
-
end
|
17
|
+
it_behaves_like 'resource list'
|
39
18
|
|
40
|
-
|
41
|
-
|
42
|
-
prev_page = list.previous_page
|
43
|
-
prev_page.should be_kind_of(Uploadcare::Api::FileList)
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should load custom page" do
|
47
|
-
page = @list.go_to(@list.pages - 1)
|
48
|
-
page.should be_kind_of(Uploadcare::Api::FileList)
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should not load next page if there isn't one" do
|
52
|
-
page= @list.go_to @list.pages
|
53
|
-
page.next_page.should be_nil
|
54
|
-
end
|
55
|
-
|
56
|
-
it "should not load prev page if there isn't one" do
|
57
|
-
@list.previous_page.should be_nil
|
58
|
-
end
|
19
|
+
describe '#objects' do
|
20
|
+
subject{ @list.objects }
|
59
21
|
|
60
|
-
|
61
|
-
|
62
|
-
page.should be_nil
|
22
|
+
it{ is_expected.to all(be_a(resource_class)) }
|
23
|
+
it{ is_expected.to all(be_loaded) }
|
63
24
|
end
|
64
|
-
end
|
25
|
+
end
|
data/spec/resources/file_spec.rb
CHANGED
@@ -48,9 +48,10 @@ describe Uploadcare::Api::File do
|
|
48
48
|
|
49
49
|
it 'should be able to tell thenever file was stored' do
|
50
50
|
@file.load
|
51
|
-
@file.
|
52
|
-
@file
|
53
|
-
@file.
|
51
|
+
expect(@file.stored?).to be(true)
|
52
|
+
wait_until_ready(@file)
|
53
|
+
@file.delete
|
54
|
+
expect(@file.stored?).to be(false)
|
54
55
|
end
|
55
56
|
|
56
57
|
it 'should delete itself' do
|
@@ -60,6 +61,7 @@ describe Uploadcare::Api::File do
|
|
60
61
|
it 'should be able to tell thenever file was deleted' do
|
61
62
|
@file.load
|
62
63
|
@file.is_deleted?.should == false
|
64
|
+
wait_until_ready(@file)
|
63
65
|
@file.delete
|
64
66
|
@file.is_deleted?.should == true
|
65
67
|
end
|
@@ -96,6 +98,7 @@ describe Uploadcare::Api::File do
|
|
96
98
|
|
97
99
|
it 'should respond to datetime_removed' do
|
98
100
|
@file.load
|
101
|
+
wait_until_ready(@file)
|
99
102
|
@file.delete
|
100
103
|
@file.datetime_removed.should be_kind_of(DateTime)
|
101
104
|
@file.datetime_deleted.should be_kind_of(DateTime)
|
@@ -1,30 +1,25 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'uri'
|
3
|
-
require 'socket'
|
4
2
|
|
5
|
-
describe Uploadcare::Api::
|
3
|
+
describe Uploadcare::Api::GroupList do
|
6
4
|
before :all do
|
7
5
|
@api = API
|
8
|
-
@list = @api.group_list
|
9
|
-
end
|
10
6
|
|
11
|
-
|
12
|
-
@
|
13
|
-
|
7
|
+
# ensure that current project has at least three groups
|
8
|
+
count = @api.get('/groups/', limit: 3)['results'].size
|
9
|
+
(3 - count).times{ @api.create_group([@api.upload(IMAGE_URL)]) } if count < 3
|
14
10
|
|
15
|
-
|
16
|
-
@list.should respond_to(:results)
|
17
|
-
@list.should respond_to(:groups)
|
18
|
-
@list.groups.should be_kind_of(Array)
|
11
|
+
@list = @api.group_list(limit: 1)
|
19
12
|
end
|
20
13
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
let(:resource_class){ Uploadcare::Api::Group }
|
15
|
+
subject{ @list }
|
16
|
+
|
17
|
+
it_behaves_like 'resource list'
|
18
|
+
|
19
|
+
describe '#objects' do
|
20
|
+
subject{ @list.objects }
|
25
21
|
|
26
|
-
|
27
|
-
|
28
|
-
group.is_loaded?.should == false
|
22
|
+
it{ is_expected.to all(be_a(resource_class)) }
|
23
|
+
it{ is_expected.not_to include(be_loaded) }
|
29
24
|
end
|
30
|
-
end
|
25
|
+
end
|
@@ -19,7 +19,7 @@ describe Uploadcare::Connections::ApiConnection do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'includes correct User-Agent header' do
|
22
|
-
expected = Uploadcare::
|
22
|
+
expected = Uploadcare::user_agent(settings)
|
23
23
|
expect(subject['User-Agent']).to eq expected
|
24
24
|
end
|
25
25
|
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples 'resource list' do
|
4
|
+
describe '#options' do
|
5
|
+
subject{ @list.options }
|
6
|
+
|
7
|
+
it{ is_expected.to be_a(Hash) }
|
8
|
+
it{ is_expected.to be_frozen }
|
9
|
+
|
10
|
+
it 'stores options' do
|
11
|
+
expect( subject ).to eq(limit: 1)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#meta' do
|
16
|
+
subject{ @list.meta }
|
17
|
+
|
18
|
+
it{ is_expected.to be_a(Hash) }
|
19
|
+
it{ is_expected.to be_frozen }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#total' do
|
23
|
+
subject{ @list.total }
|
24
|
+
|
25
|
+
it{ is_expected.to be_an(Integer) }
|
26
|
+
|
27
|
+
it 'returns a "total" value from metadata' do
|
28
|
+
expect( subject ).to eq @list.meta["total"]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#loaded' do
|
33
|
+
subject{ @list.loaded }
|
34
|
+
|
35
|
+
it{ is_expected.to be_an(Integer) }
|
36
|
+
|
37
|
+
it 'contains currently loaded objects count' do
|
38
|
+
size = rand(2..10)
|
39
|
+
allow(@list).to receive(:objects){ Array.new(size) }
|
40
|
+
|
41
|
+
expect( subject ).to eq(size)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#fully_loaded?' do
|
46
|
+
subject{ @list.fully_loaded? }
|
47
|
+
|
48
|
+
context 'if there is no more next pages left' do
|
49
|
+
before { allow(@list).to receive(:meta){ {"next" => nil} } }
|
50
|
+
it { is_expected.to be_truthy }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'if there is a next page' do
|
54
|
+
before { allow(@list).to receive(:meta){ {"next" => 'example.com'} } }
|
55
|
+
it { is_expected.to be_falsey }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#each' do
|
60
|
+
it 'when called without a block, returns an Enumerator' do
|
61
|
+
expect( subject.each ).to be_an(Enumerator)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'when called with a block, returns self' do
|
65
|
+
allow(subject).to receive(:meta){ {"next" => nil} }
|
66
|
+
expect( subject.each{|o| nil } ).to eq(subject)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'when called with a break inside a block, returns nil' do
|
70
|
+
expect( subject.each{|o| break } ).to be_nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#[]' do
|
75
|
+
subject{ @list.dup }
|
76
|
+
|
77
|
+
it "returns instances of a resource class" do
|
78
|
+
expect( subject[0] ).to be_a(resource_class)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'loads additional objects when needed' do
|
82
|
+
expect(@api).to receive(:get)
|
83
|
+
.with(subject.meta["next"]).and_call_original
|
84
|
+
|
85
|
+
expect { subject[1] }.to change { subject.objects.size }.by(1)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'enumerable interface' do
|
90
|
+
subject{ @list.dup }
|
91
|
+
|
92
|
+
it 'is an Enumerable' do
|
93
|
+
expect( subject ).to be_an(Enumerable)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'iterates through objects' do
|
97
|
+
i, uuids = 0, []
|
98
|
+
subject.each{|object| uuids << object.uuid; i+=1; break if i >= 2 }
|
99
|
+
|
100
|
+
expect(uuids).to eq([subject[0].uuid, subject[1].uuid])
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'loads additional objects when needed' do
|
104
|
+
expect(@api).to receive(:get)
|
105
|
+
.with(subject.meta["next"]).and_call_original
|
106
|
+
|
107
|
+
objects = subject.first(2)
|
108
|
+
|
109
|
+
expect(objects.size).to eq(2)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'doesn\'t load more objects than needed' do
|
113
|
+
expect(@api).to receive(:get).once.and_call_original
|
114
|
+
|
115
|
+
objects = subject.first(2)
|
116
|
+
|
117
|
+
expect(objects.size).to eq(2)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "loads different objects" do
|
121
|
+
expect(@api).to receive(:get)
|
122
|
+
.with(subject.meta["next"]).and_call_original
|
123
|
+
|
124
|
+
objects = subject.first(2)
|
125
|
+
|
126
|
+
expect(objects[0].uuid).not_to eq(objects[1].uuid)
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'stops loading objects when no objects left' do
|
130
|
+
allow(@api).to receive(:get).and_wrap_original do |m, *args|
|
131
|
+
m.call(*args).tap{|data| data["next"] = nil}
|
132
|
+
end
|
133
|
+
|
134
|
+
uuids = subject.map{|object| object.uuid}
|
135
|
+
|
136
|
+
expect( uuids.size ).to eq(2)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'preserves loaded objects' do
|
140
|
+
expect( subject.loaded ).to eq(1)
|
141
|
+
|
142
|
+
subject.first(2)
|
143
|
+
|
144
|
+
expect( subject.loaded ).to eq(2)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'updates #meta with data from last api response' do
|
148
|
+
new_meta = {}
|
149
|
+
allow(@api).to receive(:get).and_wrap_original do |m, *args|
|
150
|
+
m.call(*args).tap{|data| new_meta = data.reject{|k,_| k == "results"}}
|
151
|
+
end
|
152
|
+
|
153
|
+
subject.first(2)
|
154
|
+
|
155
|
+
expect( subject.meta ).to eq(new_meta)
|
156
|
+
end
|
157
|
+
|
158
|
+
if Gem.ruby_version >= Gem::Version.new('2.0.0')
|
159
|
+
context 'when lazy enumerator is used' do
|
160
|
+
it 'preserves loaded objects' do
|
161
|
+
expect(subject.loaded).to eq 1
|
162
|
+
|
163
|
+
subject.lazy.first(2)
|
164
|
+
|
165
|
+
expect(subject.loaded).to eq 2
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context 'when a block passed to an enumerator method contains a break' do
|
171
|
+
it 'preserves loaded objects' do
|
172
|
+
i = 0
|
173
|
+
subject.each{|o| i+= 1; break if i >= 2}
|
174
|
+
|
175
|
+
expect( subject.loaded ).to eq(2)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'when a block passed to an enumerator method raises an exception' do
|
180
|
+
it 'preserves loaded objects' do
|
181
|
+
i = 0
|
182
|
+
subject.each{|o| i += 1; raise if i>= 2} rescue nil
|
183
|
+
|
184
|
+
expect( subject.loaded ).to eq(2)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|