uploadcare-ruby 1.2.2 → 2.0.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.
- 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
|