aptible-resource 0.1.1 → 0.2.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 +4 -4
- data/lib/aptible/resource/base.rb +155 -6
- data/lib/aptible/resource/errors.rb +24 -0
- data/lib/aptible/resource/version.rb +1 -1
- data/spec/aptible/resource/base_spec.rb +219 -0
- metadata +3 -6
- data/lib/aptible/resource/model.rb +0 -115
- data/lib/aptible/resource/token.rb +0 -10
- data/spec/aptible/resource/model_spec.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8992244b07c1cd292461871658dd5ba0b400f9a
|
4
|
+
data.tar.gz: 2b16f2fb43d9f60cd0993d9ee98f9d2528a7f249
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28bd9df8faea54c8e263e8e85da8f3543b397c438937ddcd9383db4b346bf445cc0ddbbbf6c9919c769a6c6d04b8da4e8f65b24dc3fad323576dbca25238bdbc
|
7
|
+
data.tar.gz: 81df2fcbaf1c1e2b98e5b8f99ac41e59fdec11e503c90ba9e6fd8f0fa2349505a2d5554c0adb3114f1b6a12a0cbdc9233fd6e6dded91d15b1181f9c8be1362e1
|
@@ -1,18 +1,18 @@
|
|
1
1
|
require 'fridge'
|
2
|
+
require 'active_support/inflector'
|
2
3
|
|
3
4
|
# Require vendored HyperResource
|
4
5
|
$LOAD_PATH.unshift File.expand_path('../..', __FILE__)
|
5
6
|
require 'hyper_resource'
|
6
7
|
|
7
8
|
require 'aptible/resource/adapter'
|
8
|
-
require 'aptible/resource/
|
9
|
+
require 'aptible/resource/errors'
|
9
10
|
|
10
11
|
module Aptible
|
11
12
|
module Resource
|
13
|
+
# rubocop:disable ClassLength
|
12
14
|
class Base < HyperResource
|
13
|
-
|
14
|
-
|
15
|
-
attr_accessor :token
|
15
|
+
attr_accessor :token, :errors
|
16
16
|
|
17
17
|
def self.get_data_type_from_response(response)
|
18
18
|
return nil unless response && response.body
|
@@ -23,6 +23,125 @@ module Aptible
|
|
23
23
|
Aptible::Resource::Adapter
|
24
24
|
end
|
25
25
|
|
26
|
+
def self.collection_href
|
27
|
+
"/#{basename}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.basename
|
31
|
+
name.split('::').last.downcase.pluralize
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.all(options = {})
|
35
|
+
resource = find_by_url(collection_href, options)
|
36
|
+
return [] unless resource
|
37
|
+
resource.send(basename).entries
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.find(id, options = {})
|
41
|
+
find_by_url("#{collection_href}/#{id}", options)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.find_by_url(url, options = {})
|
45
|
+
# REVIEW: Should exception be raised if return type mismatch?
|
46
|
+
new(options).find_by_url(url)
|
47
|
+
rescue HyperResource::ClientError => e
|
48
|
+
if e.response.status == 404
|
49
|
+
return nil
|
50
|
+
else
|
51
|
+
raise e
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.create!(params = {})
|
56
|
+
token = params.delete(:token)
|
57
|
+
resource = new(token: token)
|
58
|
+
resource.send(basename).create(normalize_params(params))
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.create(params = {})
|
62
|
+
create!(params)
|
63
|
+
rescue HyperResource::ResponseError => e
|
64
|
+
new.tap { |resource| resource.errors = Errors.from_exception(e) }
|
65
|
+
end
|
66
|
+
|
67
|
+
# rubocop:disable PredicateName
|
68
|
+
def self.has_many(relation)
|
69
|
+
define_has_many_getter(relation)
|
70
|
+
define_has_many_setter(relation)
|
71
|
+
end
|
72
|
+
# rubocop:enable PredicateName
|
73
|
+
|
74
|
+
def self.field(name, options = {})
|
75
|
+
define_method name do
|
76
|
+
self.class.cast_field(attributes[name], options[:type])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.belongs_to(relation)
|
81
|
+
define_method relation do
|
82
|
+
get unless loaded
|
83
|
+
if (memoized = instance_variable_get("@#{relation}"))
|
84
|
+
memoized
|
85
|
+
elsif links[relation]
|
86
|
+
instance_variable_set("@#{relation}", links[relation].get)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# rubocop:disable PredicateName
|
92
|
+
def self.has_one(relation)
|
93
|
+
# Better than class << self + alias_method?
|
94
|
+
belongs_to(relation)
|
95
|
+
end
|
96
|
+
# rubocop:enable PredicateName
|
97
|
+
|
98
|
+
def self.define_has_many_getter(relation)
|
99
|
+
define_method relation do
|
100
|
+
get unless loaded
|
101
|
+
if (memoized = instance_variable_get("@#{relation}"))
|
102
|
+
memoized
|
103
|
+
elsif links[relation]
|
104
|
+
instance_variable_set("@#{relation}", links[relation].entries)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# rubocop:disable MethodLength
|
110
|
+
def self.define_has_many_setter(relation)
|
111
|
+
define_method "create_#{relation.to_s.singularize}!" do |params = {}|
|
112
|
+
get unless loaded
|
113
|
+
links[relation].create(self.class.normalize_params(params))
|
114
|
+
end
|
115
|
+
|
116
|
+
define_method "create_#{relation.to_s.singularize}" do |params = {}|
|
117
|
+
begin
|
118
|
+
send "create_#{relation.to_s.singularize}!", params
|
119
|
+
rescue HyperResource::ResponseError => e
|
120
|
+
Base.new(root: root_url, namespace: namespace).tap do |base|
|
121
|
+
base.errors = Errors.from_exception(e)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
# rubocop:enable MethodLength
|
127
|
+
|
128
|
+
def self.normalize_params(params = {})
|
129
|
+
params_array = params.map do |key, value|
|
130
|
+
value.is_a?(HyperResource) ? [key, value.href] : [key, value]
|
131
|
+
end
|
132
|
+
Hash[params_array]
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.cast_field(value, type)
|
136
|
+
if type == Time
|
137
|
+
Time.parse(value) if value
|
138
|
+
elsif type == DateTime
|
139
|
+
DateTime.parse(value) if value
|
140
|
+
else
|
141
|
+
value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
26
145
|
def initialize(options = {})
|
27
146
|
if options.is_a?(Hash)
|
28
147
|
self.token = options[:token]
|
@@ -63,8 +182,38 @@ module Aptible
|
|
63
182
|
when String then token
|
64
183
|
end
|
65
184
|
end
|
185
|
+
|
186
|
+
alias_method :_hyperresource_update, :update
|
187
|
+
def update!(params)
|
188
|
+
_hyperresource_update(self.class.normalize_params(params))
|
189
|
+
rescue HyperResource::ResponseError => e
|
190
|
+
self.errors = Errors.from_exception(e)
|
191
|
+
raise e
|
192
|
+
end
|
193
|
+
|
194
|
+
def update(params)
|
195
|
+
update!(params)
|
196
|
+
rescue HyperResource::ResponseError
|
197
|
+
false
|
198
|
+
end
|
199
|
+
|
200
|
+
def delete
|
201
|
+
super
|
202
|
+
rescue HyperResource::ResponseError
|
203
|
+
# HyperResource/Faraday choke on empty response bodies
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
alias_method :destroy, :delete
|
207
|
+
|
208
|
+
# NOTE: The following does not update the object in-place
|
209
|
+
def reload
|
210
|
+
self.class.find_by_url(href, headers: headers)
|
211
|
+
end
|
212
|
+
|
213
|
+
def errors
|
214
|
+
@errors ||= Aptible::Resource::Errors.new
|
215
|
+
end
|
66
216
|
end
|
217
|
+
# rubocop:enable ClassLength
|
67
218
|
end
|
68
219
|
end
|
69
|
-
|
70
|
-
require 'aptible/resource/token'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Aptible
|
2
|
+
module Resource
|
3
|
+
class Errors
|
4
|
+
attr_accessor :status_code, :messages, :full_messages
|
5
|
+
|
6
|
+
def self.from_exception(exception)
|
7
|
+
new.tap do |errors|
|
8
|
+
response_json = JSON.parse(exception.response.body)
|
9
|
+
errors.messages = { base: response_json['message'] }
|
10
|
+
errors.full_messages = [response_json['message']]
|
11
|
+
errors.status_code = exception.response.status
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def messages
|
16
|
+
@messages ||= {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def full_messages
|
20
|
+
@full_messages ||= []
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,8 +1,107 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Aptible::Resource::Base do
|
4
|
+
let(:hyperresource_exception) { HyperResource::ResponseError.new('403') }
|
5
|
+
let(:error_response) { double 'Faraday::Response' }
|
6
|
+
before { hyperresource_exception.stub(:response) { error_response } }
|
7
|
+
before do
|
8
|
+
error_response.stub(:body) { { message: 'Forbidden' }.to_json }
|
9
|
+
error_response.stub(:status) { 403 }
|
10
|
+
end
|
11
|
+
|
4
12
|
subject { Api.new }
|
5
13
|
|
14
|
+
describe '.collection_href' do
|
15
|
+
it 'should use the pluralized resource name' do
|
16
|
+
url = Api::Mainframe.collection_href
|
17
|
+
expect(url).to eq '/mainframes'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.find' do
|
22
|
+
it 'should call find_by_url' do
|
23
|
+
url = '/mainframes/42'
|
24
|
+
expect(Api::Mainframe).to receive(:find_by_url).with url, {}
|
25
|
+
Api::Mainframe.find(42)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '.all' do
|
30
|
+
let(:mainframe) { double 'Mainframe' }
|
31
|
+
let(:collection) { double 'Api' }
|
32
|
+
|
33
|
+
before do
|
34
|
+
collection.stub(:mainframes) { [mainframe] }
|
35
|
+
Api::Mainframe.any_instance.stub(:find_by_url) { collection }
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should be an array' do
|
39
|
+
expect(Api::Mainframe.all).to be_a Array
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should return the root collection' do
|
43
|
+
expect(Api::Mainframe.all).to eq [mainframe]
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should pass options to the HyperResource initializer' do
|
47
|
+
klass = Api::Mainframe
|
48
|
+
options = { token: 'token' }
|
49
|
+
expect(klass).to receive(:new).with(options).and_return klass.new
|
50
|
+
Api::Mainframe.all(options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '.create' do
|
55
|
+
let(:mainframe) { Api::Mainframe.new }
|
56
|
+
let(:mainframes_link) { HyperResource::Link.new(href: '/mainframes') }
|
57
|
+
|
58
|
+
before { Api.any_instance.stub(:mainframes) { mainframes_link } }
|
59
|
+
before { mainframes_link.stub(:create) { mainframe } }
|
60
|
+
|
61
|
+
it 'should create a new top-level resource' do
|
62
|
+
mainframes_link.stub(:create) { mainframe }
|
63
|
+
expect(mainframes_link).to receive(:create).with(foo: 'bar')
|
64
|
+
Api::Mainframe.create(foo: 'bar')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should populate #errors in the event of an error' do
|
68
|
+
mainframes_link.stub(:create) { fail hyperresource_exception }
|
69
|
+
mainframe = Api::Mainframe.create
|
70
|
+
expect(mainframe.errors.messages).to eq(base: 'Forbidden')
|
71
|
+
expect(mainframe.errors.full_messages).to eq(['Forbidden'])
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should return a Base-classed resource on error' do
|
75
|
+
mainframes_link.stub(:create) { fail hyperresource_exception }
|
76
|
+
expect(Api::Mainframe.create).to be_a Api::Mainframe
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should return the object in the event of successful creation' do
|
80
|
+
mainframes_link.stub(:create) { mainframe }
|
81
|
+
expect(Api::Mainframe.create).to eq mainframe
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '.create!' do
|
86
|
+
let(:mainframe) { Api::Mainframe.new }
|
87
|
+
let(:mainframes_link) { HyperResource::Link.new(href: '/mainframes') }
|
88
|
+
|
89
|
+
before { Api.any_instance.stub(:mainframes) { mainframes_link } }
|
90
|
+
before { mainframes_link.stub(:create) { mainframe } }
|
91
|
+
|
92
|
+
it 'should pass through any exceptions' do
|
93
|
+
mainframes_link.stub(:create) { fail hyperresource_exception }
|
94
|
+
expect do
|
95
|
+
Api::Mainframe.create!
|
96
|
+
end.to raise_error HyperResource::ResponseError
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should return the object in the event of successful creation' do
|
100
|
+
mainframes_link.stub(:create) { mainframe }
|
101
|
+
expect(Api::Mainframe.create!).to eq mainframe
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
6
105
|
describe '#initialize' do
|
7
106
|
it 'should be a HyperResource instance' do
|
8
107
|
expect(subject).to be_a HyperResource
|
@@ -33,4 +132,124 @@ describe Aptible::Resource::Base do
|
|
33
132
|
expect(subject.bearer_token).to eq 'token'
|
34
133
|
end
|
35
134
|
end
|
135
|
+
|
136
|
+
describe '#errors' do
|
137
|
+
it 'should default to an empty error' do
|
138
|
+
expect(subject.errors).to be_a Aptible::Resource::Errors
|
139
|
+
expect(subject.errors.messages).to eq({})
|
140
|
+
expect(subject.errors.full_messages).to eq([])
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#update' do
|
145
|
+
it 'should populate #errors in the event of an error' do
|
146
|
+
HyperResource.any_instance.stub(:put) { fail hyperresource_exception }
|
147
|
+
subject.update({})
|
148
|
+
expect(subject.errors.messages).to eq(base: 'Forbidden')
|
149
|
+
expect(subject.errors.full_messages).to eq(['Forbidden'])
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should return false in the event of an error' do
|
153
|
+
HyperResource.any_instance.stub(:put) { fail hyperresource_exception }
|
154
|
+
expect(subject.update({})).to eq false
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should return the object in the event of a successful update' do
|
158
|
+
HyperResource.any_instance.stub(:put) { subject }
|
159
|
+
expect(subject.update({})).to eq subject
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe '#update!' do
|
164
|
+
it 'should populate #errors in the event of an error' do
|
165
|
+
HyperResource.any_instance.stub(:put) { fail hyperresource_exception }
|
166
|
+
begin
|
167
|
+
subject.update!({})
|
168
|
+
rescue
|
169
|
+
# Allow errors to be populated and tested
|
170
|
+
nil
|
171
|
+
end
|
172
|
+
expect(subject.errors.messages).to eq(base: 'Forbidden')
|
173
|
+
expect(subject.errors.full_messages).to eq(['Forbidden'])
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should pass through any exceptions' do
|
177
|
+
HyperResource.any_instance.stub(:put) { fail hyperresource_exception }
|
178
|
+
expect do
|
179
|
+
subject.update!({})
|
180
|
+
end.to raise_error HyperResource::ResponseError
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should return the object in the event of a successful update' do
|
184
|
+
HyperResource.any_instance.stub(:put) { subject }
|
185
|
+
expect(subject.update!({})).to eq subject
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context '.has_many' do
|
190
|
+
let(:mainframe) { Api::Mainframe.new }
|
191
|
+
let(:mainframes_link) { HyperResource::Link.new(href: '/mainframes') }
|
192
|
+
|
193
|
+
before { Api.has_many :mainframes }
|
194
|
+
before { subject.stub(:loaded) { true } }
|
195
|
+
before { subject.stub(:links) { { mainframes: mainframes_link } } }
|
196
|
+
|
197
|
+
describe '#create_#{relation}' do
|
198
|
+
it 'should populate #errors in the event of an error' do
|
199
|
+
mainframes_link.stub(:create) { fail hyperresource_exception }
|
200
|
+
mainframe = subject.create_mainframe({})
|
201
|
+
expect(mainframe.errors.messages).to eq(base: 'Forbidden')
|
202
|
+
expect(mainframe.errors.full_messages).to eq(['Forbidden'])
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'should return a Base-classed resource on error' do
|
206
|
+
mainframes_link.stub(:create) { fail hyperresource_exception }
|
207
|
+
expect(subject.create_mainframe.class).to eq Aptible::Resource::Base
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'should return the object in the event of successful creation' do
|
211
|
+
mainframes_link.stub(:create) { mainframe }
|
212
|
+
expect(subject.create_mainframe({})).to eq mainframe
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe '#create_#{relation}!' do
|
217
|
+
it 'should pass through any exceptions' do
|
218
|
+
mainframes_link.stub(:create) { fail hyperresource_exception }
|
219
|
+
expect do
|
220
|
+
subject.create_mainframe!({})
|
221
|
+
end.to raise_error HyperResource::ResponseError
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should return the object in the event of successful creation' do
|
225
|
+
mainframes_link.stub(:create) { mainframe }
|
226
|
+
expect(subject.create_mainframe!({})).to eq mainframe
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context '.field' do
|
232
|
+
it 'should define a method for the field' do
|
233
|
+
Api.field :foo, type: String
|
234
|
+
expect(subject.respond_to?(:foo)).to be_true
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should return the raw attribute' do
|
238
|
+
Api.field :foo, type: String
|
239
|
+
subject.stub(:attributes) { { foo: 'bar' } }
|
240
|
+
expect(subject.foo).to eq 'bar'
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'should parse the attribute if DateTime' do
|
244
|
+
Api.field :created_at, type: DateTime
|
245
|
+
subject.stub(:attributes) { { created_at: Time.now.to_json } }
|
246
|
+
expect(subject.created_at).to be_a DateTime
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'should parse the attribute if Time' do
|
250
|
+
Api.field :created_at, type: Time
|
251
|
+
subject.stub(:attributes) { { created_at: Time.now.to_json } }
|
252
|
+
expect(subject.created_at).to be_a Time
|
253
|
+
end
|
254
|
+
end
|
36
255
|
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.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Frank Macreery
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: uri_template
|
@@ -169,8 +169,7 @@ files:
|
|
169
169
|
- lib/aptible/resource.rb
|
170
170
|
- lib/aptible/resource/adapter.rb
|
171
171
|
- lib/aptible/resource/base.rb
|
172
|
-
- lib/aptible/resource/
|
173
|
-
- lib/aptible/resource/token.rb
|
172
|
+
- lib/aptible/resource/errors.rb
|
174
173
|
- lib/aptible/resource/version.rb
|
175
174
|
- lib/hyper_resource.rb
|
176
175
|
- lib/hyper_resource/adapter.rb
|
@@ -185,7 +184,6 @@ files:
|
|
185
184
|
- lib/hyper_resource/response.rb
|
186
185
|
- lib/hyper_resource/version.rb
|
187
186
|
- spec/aptible/resource/base_spec.rb
|
188
|
-
- spec/aptible/resource/model_spec.rb
|
189
187
|
- spec/fixtures/api.rb
|
190
188
|
- spec/fixtures/mainframe.rb
|
191
189
|
- spec/fixtures/token.rb
|
@@ -217,7 +215,6 @@ specification_version: 4
|
|
217
215
|
summary: Foundation classes for Aptible resource server gems
|
218
216
|
test_files:
|
219
217
|
- spec/aptible/resource/base_spec.rb
|
220
|
-
- spec/aptible/resource/model_spec.rb
|
221
218
|
- spec/fixtures/api.rb
|
222
219
|
- spec/fixtures/mainframe.rb
|
223
220
|
- spec/fixtures/token.rb
|
@@ -1,115 +0,0 @@
|
|
1
|
-
require 'active_support/inflector'
|
2
|
-
|
3
|
-
module Aptible
|
4
|
-
module Resource
|
5
|
-
module Model
|
6
|
-
def self.included(base)
|
7
|
-
base.extend ClassMethods
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
def collection_href
|
12
|
-
"/#{basename}"
|
13
|
-
end
|
14
|
-
|
15
|
-
def basename
|
16
|
-
name.split('::').last.downcase.pluralize
|
17
|
-
end
|
18
|
-
|
19
|
-
def all(options = {})
|
20
|
-
resource = find_by_url(collection_href, options)
|
21
|
-
return [] unless resource
|
22
|
-
resource.send(basename).entries
|
23
|
-
end
|
24
|
-
|
25
|
-
def find(id, options = {})
|
26
|
-
find_by_url("#{collection_href}/#{id}", options)
|
27
|
-
end
|
28
|
-
|
29
|
-
def find_by_url(url, options = {})
|
30
|
-
# REVIEW: Should exception be raised if return type mismatch?
|
31
|
-
new(options).find_by_url(url)
|
32
|
-
rescue HyperResource::ClientError => e
|
33
|
-
if e.response.status == 404
|
34
|
-
return nil
|
35
|
-
else
|
36
|
-
raise e
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def create(params)
|
41
|
-
token = params.delete(:token)
|
42
|
-
resource = new(token: token)
|
43
|
-
resource.send(basename).create(normalize_params(params))
|
44
|
-
end
|
45
|
-
|
46
|
-
# rubocop:disable PredicateName
|
47
|
-
def has_many(relation)
|
48
|
-
define_has_many_getter(relation)
|
49
|
-
define_has_many_setter(relation)
|
50
|
-
end
|
51
|
-
# rubocop:enable PredicateName
|
52
|
-
|
53
|
-
def belongs_to(relation)
|
54
|
-
define_method relation do
|
55
|
-
get unless loaded
|
56
|
-
if (memoized = instance_variable_get("@#{relation}"))
|
57
|
-
memoized
|
58
|
-
elsif links[relation]
|
59
|
-
instance_variable_set("@#{relation}", links[relation].get)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# rubocop:disable PredicateName
|
65
|
-
def has_one(relation)
|
66
|
-
# Better than class << self + alias_method?
|
67
|
-
belongs_to(relation)
|
68
|
-
end
|
69
|
-
# rubocop:enable PredicateName
|
70
|
-
|
71
|
-
def define_has_many_getter(relation)
|
72
|
-
define_method relation do
|
73
|
-
get unless loaded
|
74
|
-
if (memoized = instance_variable_get("@#{relation}"))
|
75
|
-
memoized
|
76
|
-
elsif links[relation]
|
77
|
-
instance_variable_set("@#{relation}", links[relation].entries)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def define_has_many_setter(relation)
|
83
|
-
define_method "create_#{relation.to_s.singularize}" do |params = {}|
|
84
|
-
get unless loaded
|
85
|
-
links[relation].create(self.class.normalize_params(params))
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def normalize_params(params = {})
|
90
|
-
params_array = params.map do |key, value|
|
91
|
-
value.is_a?(HyperResource) ? [key, value.href] : [key, value]
|
92
|
-
end
|
93
|
-
Hash[params_array]
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def delete
|
98
|
-
# HyperResource/Faraday choke on empty response bodies
|
99
|
-
super
|
100
|
-
rescue HyperResource::ResponseError
|
101
|
-
nil
|
102
|
-
end
|
103
|
-
alias_method :destroy, :delete
|
104
|
-
|
105
|
-
def update(params)
|
106
|
-
super(self.class.normalize_params(params))
|
107
|
-
end
|
108
|
-
|
109
|
-
# NOTE: The following does not update the object in-place
|
110
|
-
def reload
|
111
|
-
self.class.find_by_url(href, headers: headers)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Aptible::Resource::Model do
|
4
|
-
subject { Api.new }
|
5
|
-
|
6
|
-
describe '.collection_href' do
|
7
|
-
it 'should use the pluralized resource name' do
|
8
|
-
url = Api::Mainframe.collection_href
|
9
|
-
expect(url).to eq '/mainframes'
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
describe '.find' do
|
14
|
-
it 'should call find_by_url' do
|
15
|
-
url = '/mainframes/42'
|
16
|
-
expect(Api::Mainframe).to receive(:find_by_url).with url, {}
|
17
|
-
Api::Mainframe.find(42)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
describe '.all' do
|
22
|
-
let(:mainframe) { double 'Mainframe' }
|
23
|
-
let(:collection) { double 'Api' }
|
24
|
-
|
25
|
-
before do
|
26
|
-
collection.stub(:mainframes) { [mainframe] }
|
27
|
-
Api::Mainframe.any_instance.stub(:find_by_url) { collection }
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'should be an array' do
|
31
|
-
expect(Api::Mainframe.all).to be_a Array
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'should return the root collection' do
|
35
|
-
expect(Api::Mainframe.all).to eq [mainframe]
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should pass options to the HyperResource initializer' do
|
39
|
-
klass = Api::Mainframe
|
40
|
-
options = { token: 'token' }
|
41
|
-
expect(klass).to receive(:new).with(options).and_return klass.new
|
42
|
-
Api::Mainframe.all(options)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
describe '.create' do
|
47
|
-
it 'should create a new top-level resource' do
|
48
|
-
mainframes = double 'Api'
|
49
|
-
Api.stub_chain(:new, :mainframes) { mainframes }
|
50
|
-
expect(mainframes).to receive(:create).with(foo: 'bar')
|
51
|
-
Api::Mainframe.create(foo: 'bar')
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|