aptible-resource 0.3.5 → 0.3.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8fe1293ad299a7eccf1b4138383c225b9957437d
4
- data.tar.gz: 9c7ca8e1b928ee4518b213cbf3869c02d68e24a3
3
+ metadata.gz: 31f491541f4e8330accd97a53c1896fe7fca26e2
4
+ data.tar.gz: fa2f72d22a0736be3d38d61d4731e7582ef64c78
5
5
  SHA512:
6
- metadata.gz: d97541a3c1e762fae33810ac916f8143b759730f12edd809a4218e9cdfd5a10c37ae0fbd0e9a3a48b1afa63072ae759c0afde9092068b93d4f4b9f4cb34590bc
7
- data.tar.gz: 4ba7510ecde1e02264c534f7ac0b448f74349e26d4fcb37a964da5b31dcbde90b0b5215355006d7a52d103f94510403bbe8fe66ab6d288b0ce0c230efc1aedfb
6
+ metadata.gz: ea9ed96d16623b924cc4bbd463e128bab5d8ce1463a10451faa3de64be1b079c88aec56efad7ad079b7fea3877cd906c9bae5b24b2048d08ad99bdfa5f23a4a0
7
+ data.tar.gz: 406299c0d2a4cff3faca28f86c736374a27f38cde09ea6d050a127f74853e6d5916477d4bf03cc5feb228a4ee9cc7c8151151dbace303c8f44fab0a847c8333b
data/.travis.yml CHANGED
@@ -2,4 +2,6 @@ sudo: false
2
2
 
3
3
  rvm:
4
4
  - 2.0.0
5
- - jruby
5
+ - 2.1.0
6
+ - 2.2.0
7
+ - jruby-9.0.5.0
@@ -38,18 +38,29 @@ module Aptible
38
38
  name.split('::').last.underscore.pluralize
39
39
  end
40
40
 
41
- # rubocop:disable AbcSize
42
- def self.all(options = {})
43
- resource = find_by_url(options[:href] || collection_href, options)
44
- return [] unless resource
45
- if resource.links.key?('next')
46
- options[:href] = resource.links['next'].href
47
- resource.entries + all(options)
48
- else
49
- resource.entries
41
+ def self.each_page(options = {})
42
+ return unless block_given?
43
+ href = options[:href] || collection_href
44
+ while href
45
+ # TODO: Breaking here is consistent with the existing behavior of
46
+ # .all, but it essentially swallows an error if the page you're
47
+ # hitting happens to 404. This should probably be addressed in an
48
+ # effort to make this API client more strict.
49
+ resource = find_by_url(href, options)
50
+ break if resource.nil?
51
+
52
+ yield resource.entries
53
+
54
+ next_link = resource.links['next']
55
+ href = next_link ? next_link.href : nil
50
56
  end
51
57
  end
52
- # rubocop: enable AbcSize
58
+
59
+ def self.all(options = {})
60
+ out = []
61
+ each_page(options) { |page| out.concat page }
62
+ out
63
+ end
53
64
 
54
65
  def self.where(options = {})
55
66
  params = options.except(:token, :root, :namespace, :headers)
@@ -89,7 +100,7 @@ module Aptible
89
100
 
90
101
  # rubocop:disable PredicateName
91
102
  def self.has_many(relation)
92
- define_has_many_getter(relation)
103
+ define_has_many_getters(relation)
93
104
  define_has_many_setter(relation)
94
105
  end
95
106
  # rubocop:enable PredicateName
@@ -126,7 +137,9 @@ module Aptible
126
137
  end
127
138
  # rubocop:enable PredicateName
128
139
 
129
- def self.define_has_many_getter(relation)
140
+ # rubocop:disable MethodLength
141
+ # rubocop:disable AbcSize
142
+ def self.define_has_many_getters(relation)
130
143
  define_method relation do
131
144
  get unless loaded
132
145
  if (memoized = instance_variable_get("@#{relation}"))
@@ -137,7 +150,17 @@ module Aptible
137
150
  instance_variable_set("@#{relation}", depaginated)
138
151
  end
139
152
  end
153
+
154
+ define_method "each_#{relation.to_s.singularize}" do |&block|
155
+ return if block.nil?
156
+ self.class.each_page(href: links[relation].base_href,
157
+ headers: headers) do |page|
158
+ page.each { |entry| block.call entry }
159
+ end
160
+ end
140
161
  end
162
+ # rubocop:enable AbcSize
163
+ # rubocop:enable MethodLength
141
164
 
142
165
  def self.embeds_one(relation)
143
166
  define_method relation do
@@ -1,5 +1,5 @@
1
1
  module Aptible
2
2
  module Resource
3
- VERSION = '0.3.5'
3
+ VERSION = '0.3.6'
4
4
  end
5
5
  end
@@ -9,6 +9,39 @@ describe Aptible::Resource::Base do
9
9
  error_response.stub(:status) { 403 }
10
10
  end
11
11
 
12
+ shared_context 'paginated collection' do
13
+ let(:urls) { ['/mainframes', '/mainframes?page=1'] }
14
+ let(:calls) { [] }
15
+
16
+ before do
17
+ pages = {}
18
+
19
+ urls.each_with_index do |url, idx|
20
+ collection = double("Collection for #{url}")
21
+ links = {}
22
+
23
+ allow(collection).to receive(:entries).and_return(["At #{url}"])
24
+ allow(collection).to receive(:links).and_return(links)
25
+
26
+ next_url = urls[idx + 1]
27
+ if next_url
28
+ links['next'] = HyperResource::Link.new(nil, 'href' => next_url)
29
+ end
30
+
31
+ pages[url] = collection
32
+ end
33
+
34
+ [Api, Api::Mainframe].each do |klass|
35
+ allow_any_instance_of(klass).to receive(:find_by_url) do |u, _|
36
+ calls << u
37
+ page = pages[u]
38
+ fail "Accessed unexpected URL #{u}" if page.nil?
39
+ page
40
+ end
41
+ end
42
+ end
43
+ end
44
+
12
45
  subject { Api.new }
13
46
 
14
47
  describe '.collection_href' do
@@ -33,47 +66,72 @@ describe Aptible::Resource::Base do
33
66
  end
34
67
 
35
68
  describe '.all' do
36
- let(:mainframe) { double 'Mainframe' }
37
- let(:collection) { double 'Api' }
69
+ context 'when not paginated' do
70
+ let(:mainframe) { double 'Mainframe' }
71
+ let(:collection) { double 'Api' }
38
72
 
39
- before do
40
- collection.stub(:entries) { [mainframe] }
41
- collection.stub(:links) { Hash.new }
42
- Api::Mainframe.any_instance.stub(:find_by_url) { collection }
43
- end
73
+ before do
74
+ collection.stub(:entries) { [mainframe] }
75
+ collection.stub(:links) { Hash.new }
76
+ Api::Mainframe.any_instance.stub(:find_by_url) { collection }
77
+ end
44
78
 
45
- it 'should be an array' do
46
- expect(Api::Mainframe.all).to be_a Array
47
- end
79
+ it 'should be an array' do
80
+ expect(Api::Mainframe.all).to be_a Array
81
+ end
48
82
 
49
- it 'should return the root collection' do
50
- expect(Api::Mainframe.all).to eq [mainframe]
51
- end
83
+ it 'should return the root collection' do
84
+ expect(Api::Mainframe.all).to eq [mainframe]
85
+ end
52
86
 
53
- it 'should pass options to the HyperResource initializer' do
54
- klass = Api::Mainframe
55
- options = { token: 'token' }
56
- expect(klass).to receive(:new).with(options).and_return klass.new
57
- Api::Mainframe.all(options)
87
+ it 'should pass options to the HyperResource initializer' do
88
+ klass = Api::Mainframe
89
+ options = { token: 'token' }
90
+ expect(klass).to receive(:new).with(options).and_return klass.new
91
+ Api::Mainframe.all(options)
92
+ end
58
93
  end
59
94
 
60
95
  context 'when paginated' do
61
- before do
62
- page = double('page')
63
- allow(page).to receive(:href).and_return(
64
- '/next/page', '/next/page', '/next/page'
65
- )
66
- allow(collection).to receive(:links).and_return(
67
- { 'next' => page }, { 'next' => page }, {}
68
- )
96
+ include_context 'paginated collection'
97
+
98
+ it 'should collect entries from all pages' do
99
+ records = Api::Mainframe.all
100
+ expect(records.size).to eq(2)
101
+ expect(records.first).to eq('At /mainframes')
102
+ expect(records.second).to eq('At /mainframes?page=1')
103
+ expect(calls).to eq(urls)
69
104
  end
70
105
 
71
- it 'should collect entries on all pages' do
72
- expect(Api::Mainframe.all).to eq [mainframe] + [mainframe]
106
+ it "should return an empty list for a URL that doesn't exist" do
107
+ allow_any_instance_of(Api::Mainframe).to receive(:find_by_url) { nil }
108
+ expect(Api::Mainframe.all).to eq([])
73
109
  end
74
110
  end
75
111
  end
76
112
 
113
+ describe '.each_page' do
114
+ include_context 'paginated collection'
115
+
116
+ it 'should iterate over all pages' do
117
+ pages = 0
118
+ Api::Mainframe.each_page { pages += 1 }
119
+ expect(pages).to eq(2)
120
+ expect(calls).to eq(urls)
121
+ end
122
+
123
+ it 'should find all records' do
124
+ records = ['At /mainframes', 'At /mainframes?page=1']
125
+ Api::Mainframe.each_page { |p| expect(p).to eq([records.shift]) }
126
+ expect(calls).to eq(urls)
127
+ end
128
+
129
+ it 'should not access more URLs if the consumer breaks' do
130
+ Api::Mainframe.each_page { break }
131
+ expect(calls).to eq(['/mainframes'])
132
+ end
133
+ end
134
+
77
135
  describe '.create' do
78
136
  let(:mainframe) { Api::Mainframe.new }
79
137
  let(:mainframes_link) { HyperResource::Link.new(href: '/mainframes') }
@@ -263,10 +321,38 @@ describe Aptible::Resource::Base do
263
321
  end
264
322
 
265
323
  describe '#{relation}s' do
266
- it 'should defer to self.class.all' do
267
- expect(subject.class).to receive(:all).with(href: '/mainframes',
268
- headers: subject.headers)
269
- subject.mainframes
324
+ include_context 'paginated collection'
325
+
326
+ it 'should return all records' do
327
+ records = subject.mainframes
328
+
329
+ expect(records.size).to eq(2)
330
+ expect(records.first).to eq('At /mainframes')
331
+ expect(records.second).to eq('At /mainframes?page=1')
332
+ expect(calls).to eq(urls)
333
+ end
334
+ end
335
+
336
+ describe 'each_#{relation}' do
337
+ include_context 'paginated collection'
338
+
339
+ it 'should iterate over all records' do
340
+ records = []
341
+ subject.each_mainframe { |mainframe| records << mainframe }
342
+
343
+ expect(records.size).to eq(2)
344
+ expect(records.first).to eq('At /mainframes')
345
+ expect(records.second).to eq('At /mainframes?page=1')
346
+ expect(calls).to eq(urls)
347
+ end
348
+
349
+ it 'should stop iterating when the consumer breaks' do
350
+ subject.each_mainframe { |_| break }
351
+ expect(calls).to eq([urls[0]])
352
+ end
353
+
354
+ it 'should not fail if not passed a block' do
355
+ subject.each_mainframe
270
356
  end
271
357
  end
272
358
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aptible-resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Macreery
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-24 00:00:00.000000000 Z
11
+ date: 2016-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uri_template
@@ -225,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
225
  version: '0'
226
226
  requirements: []
227
227
  rubyforge_project:
228
- rubygems_version: 2.5.1
228
+ rubygems_version: 2.6.4
229
229
  signing_key:
230
230
  specification_version: 4
231
231
  summary: Foundation classes for Aptible resource server gems