hyperresource 0.1.9.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/hyper_resource.rb +85 -74
- data/lib/hyper_resource/adapter/hal_json.rb +13 -8
- data/lib/hyper_resource/attributes.rb +7 -7
- data/lib/hyper_resource/exceptions.rb +26 -14
- data/lib/hyper_resource/link.rb +2 -1
- data/lib/hyper_resource/links.rb +4 -4
- data/lib/hyper_resource/modules/http.rb +102 -74
- data/lib/hyper_resource/modules/internal_attributes.rb +88 -0
- data/lib/hyper_resource/objects.rb +14 -17
- data/lib/hyper_resource/version.rb +2 -2
- metadata +17 -3
- data/lib/hyper_resource/modules/utils.rb +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d9374b1ec64fdce21a54dfa34bd053c991f0541
|
4
|
+
data.tar.gz: 0442f93a99aaed8e1e023ad9aa8ff00ecc2978f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3e3388c09971d33a9c0062377ef356bcb35e2770c060d8d61ce0a8cf0a992b2f255726dbdaeffd94adee8ee91964eae2beff2e13551a3ac76a59d3a7e0add34
|
7
|
+
data.tar.gz: 2eeb5c948b3736691cbf273310f4c98d8413ea2fa2a8725675985d9749bd035f42793828b1300e23452ac92fbf85b9a4de67153fec07f08790066a3cb577f4e9
|
data/lib/hyper_resource.rb
CHANGED
@@ -1,58 +1,36 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'hyper_resource/attributes'
|
2
|
+
require 'hyper_resource/exceptions'
|
3
|
+
require 'hyper_resource/link'
|
4
|
+
require 'hyper_resource/links'
|
5
|
+
require 'hyper_resource/objects'
|
6
|
+
require 'hyper_resource/response'
|
7
|
+
require 'hyper_resource/version'
|
3
8
|
|
4
|
-
|
5
|
-
|
6
|
-
end
|
9
|
+
require 'hyper_resource/adapter'
|
10
|
+
require 'hyper_resource/adapter/hal_json'
|
7
11
|
|
8
|
-
require '
|
12
|
+
require 'hyper_resource/modules/http'
|
13
|
+
require 'hyper_resource/modules/internal_attributes'
|
9
14
|
|
10
|
-
|
11
|
-
include HyperResource::Modules::Utils
|
12
|
-
include HyperResource::Modules::HTTP
|
15
|
+
require 'rubygems' if RUBY_VERSION[0..2] == '1.8'
|
13
16
|
|
14
|
-
|
17
|
+
require 'pp'
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
:auth, ## e.g. {:basic => ['username', 'password']}
|
19
|
-
:headers, ## e.g. {'Accept' => 'application/vnd.example+json'}
|
20
|
-
:namespace, ## e.g. 'ExampleAPI', or the class ExampleAPI itself
|
21
|
-
:adapter ## subclass of HR::Adapter
|
22
|
-
]
|
23
|
-
end
|
19
|
+
## HyperResource is the main resource base class. Normally it will be used
|
20
|
+
## through subclassing, though it may also be used directly.
|
24
21
|
|
25
|
-
|
26
|
-
[ :root,
|
27
|
-
:href,
|
28
|
-
:auth,
|
29
|
-
:headers,
|
30
|
-
:namespace,
|
31
|
-
:adapter,
|
22
|
+
class HyperResource
|
32
23
|
|
33
|
-
|
34
|
-
|
35
|
-
|
24
|
+
include HyperResource::Modules::HTTP
|
25
|
+
include HyperResource::Modules::InternalAttributes
|
26
|
+
include Enumerable
|
36
27
|
|
37
|
-
|
38
|
-
:links,
|
39
|
-
:objects,
|
28
|
+
private
|
40
29
|
|
41
|
-
|
42
|
-
]
|
43
|
-
end
|
30
|
+
DEFAULT_HEADERS = { 'Accept' => 'application/json' }
|
44
31
|
|
45
32
|
public
|
46
33
|
|
47
|
-
_hr_class_attributes.each {|attr| class_attribute attr}
|
48
|
-
(_hr_attributes & _hr_class_attributes).each {|attr| fallback_attribute attr}
|
49
|
-
(_hr_attributes - _hr_class_attributes).each {|attr| attr_accessor attr}
|
50
|
-
|
51
|
-
# :nodoc:
|
52
|
-
DEFAULT_HEADERS = {
|
53
|
-
'Accept' => 'application/json'
|
54
|
-
}
|
55
|
-
|
56
34
|
## Create a new HyperResource, given a hash of options. These options
|
57
35
|
## include:
|
58
36
|
##
|
@@ -107,14 +85,15 @@ public
|
|
107
85
|
end
|
108
86
|
|
109
87
|
|
110
|
-
public
|
111
|
-
|
112
88
|
## Returns true if one or more of this object's attributes has been
|
113
89
|
## reassigned.
|
114
90
|
def changed?(*args)
|
115
91
|
attributes.changed?(*args)
|
116
92
|
end
|
117
93
|
|
94
|
+
|
95
|
+
#### Filters
|
96
|
+
|
118
97
|
## +incoming_body_filter+ filters a hash of attribute keys and values
|
119
98
|
## on their way from a response body to a HyperResource. Override this
|
120
99
|
## in a subclass of HyperResource to implement filters on incoming data.
|
@@ -138,13 +117,23 @@ public
|
|
138
117
|
end
|
139
118
|
|
140
119
|
|
141
|
-
|
142
|
-
## in this resource. Equivalent to +self.objects.first+.
|
143
|
-
def first; self.objects.first end
|
120
|
+
#### Enumerable support
|
144
121
|
|
145
122
|
## Returns the *i*th object in the first collection of objects embedded
|
146
|
-
## in this resource.
|
147
|
-
def [](i)
|
123
|
+
## in this resource. Returns nil on failure.
|
124
|
+
def [](i)
|
125
|
+
get unless loaded
|
126
|
+
self.objects.first[1][i] rescue nil
|
127
|
+
end
|
128
|
+
|
129
|
+
## Iterates over the objects in the first collection of embedded objects
|
130
|
+
## in this resource.
|
131
|
+
def each(&block)
|
132
|
+
get unless loaded
|
133
|
+
self.objects.first[1].each(&block) rescue nil
|
134
|
+
end
|
135
|
+
|
136
|
+
#### Magic
|
148
137
|
|
149
138
|
## method_missing will load this resource if not yet loaded, then
|
150
139
|
## attempt to delegate to +attributes+, then +objects+, then +links+.
|
@@ -171,23 +160,34 @@ public
|
|
171
160
|
end
|
172
161
|
|
173
162
|
|
174
|
-
def inspect #
|
163
|
+
def inspect # @private
|
175
164
|
"#<#{self.class}:0x#{"%x" % self.object_id} @root=#{self.root.inspect} "+
|
176
165
|
"@href=#{self.href.inspect} @loaded=#{self.loaded} "+
|
177
166
|
"@namespace=#{self.namespace.inspect} ...>"
|
178
167
|
end
|
179
168
|
|
180
|
-
## +response_body+
|
181
|
-
|
182
|
-
|
169
|
+
## +response_body+, +response_object+, and +deserialized_response+
|
170
|
+
## are deprecated in favor of +body+. (Sorry. Naming things is hard.)
|
171
|
+
def response_body # @private
|
172
|
+
_hr_deprecate('HyperResource#response_body is deprecated. '+
|
173
|
+
'Please use HyperResource#body instead.')
|
174
|
+
body
|
175
|
+
end
|
176
|
+
def response_object # @private
|
177
|
+
_hr_deprecate('HyperResource#response_object is deprecated. '+
|
178
|
+
'Please use HyperResource#body instead.')
|
179
|
+
body
|
180
|
+
end
|
181
|
+
def deserialized_response # @private
|
182
|
+
_hr_deprecate('HyperResource#deserialized_response is deprecated. '+
|
183
|
+
'Please use HyperResource#body instead.')
|
184
|
+
body
|
185
|
+
end
|
183
186
|
|
184
|
-
#######################################################################
|
185
|
-
#### Underscored functions are not meant to be used outside of ####
|
186
|
-
#### HyperResource machinery. You have been warned. ####
|
187
187
|
|
188
188
|
|
189
189
|
## Return a new HyperResource based on this object and a given href.
|
190
|
-
def
|
190
|
+
def _hr_new_from_link(href) # @private
|
191
191
|
self.class.new(:root => self.root,
|
192
192
|
:auth => self.auth,
|
193
193
|
:headers => self.headers,
|
@@ -197,24 +197,23 @@ public
|
|
197
197
|
|
198
198
|
|
199
199
|
## Returns the class into which the given response should be cast.
|
200
|
-
## If the object is not loaded yet, or if +
|
200
|
+
## If the object is not loaded yet, or if +namespace+ is
|
201
201
|
## not set, returns +self+.
|
202
202
|
##
|
203
|
-
## Otherwise, +
|
203
|
+
## Otherwise, +response_class+ uses +get_data_type_from_response+ to
|
204
204
|
## determine subclass name, glues it to the given namespace, and
|
205
205
|
## creates the class if it's not there yet. E.g., given a namespace of
|
206
206
|
## +FooAPI+ and a response content-type of
|
207
207
|
## "application/vnd.foocorp.fooapi.v1+json;type=User", this should
|
208
208
|
## return +FooAPI::User+ (even if +FooAPI::User+ hadn't existed yet).
|
209
|
-
|
210
|
-
def self._get_response_class(response, namespace) # :nodoc:
|
209
|
+
def self.response_class(response, namespace)
|
211
210
|
if self.to_s == 'HyperResource'
|
212
211
|
return self unless namespace
|
213
212
|
end
|
214
213
|
|
215
214
|
namespace ||= self.to_s
|
216
215
|
|
217
|
-
type_name = self.
|
216
|
+
type_name = self.get_data_type_from_response(response)
|
218
217
|
return self unless type_name
|
219
218
|
|
220
219
|
class_name = "#{namespace}::#{type_name}"
|
@@ -236,45 +235,57 @@ public
|
|
236
235
|
eval(class_name)
|
237
236
|
end
|
238
237
|
|
239
|
-
def
|
238
|
+
def _hr_response_class # @private
|
240
239
|
self.namespace ||= self.class.to_s unless self.class.to_s=='HyperResource'
|
241
|
-
self.class.
|
240
|
+
self.class.response_class(self.response, self.namespace)
|
242
241
|
end
|
243
242
|
|
244
243
|
|
245
|
-
## Inspects the given
|
246
|
-
## resource's data type.
|
244
|
+
## Inspects the given Faraday::Response, and returns a string describing
|
245
|
+
## this resource's data type.
|
247
246
|
##
|
248
247
|
## By default, this method looks for a +type=...+ modifier in the
|
249
248
|
## response's +Content-type+ and returns that value, capitalized.
|
250
249
|
##
|
251
250
|
## Override this method in a subclass to alter HyperResource's behavior.
|
252
|
-
|
253
|
-
def self._get_response_data_type(response) # :nodoc:
|
251
|
+
def self.get_data_type_from_response(response)
|
254
252
|
return nil unless response
|
255
253
|
return nil unless content_type = response['content-type']
|
256
254
|
return nil unless m=content_type.match(/;\s* type=([0-9A-Za-z:]+)/x)
|
257
255
|
m[1][0,1].upcase + m[1][1..-1]
|
258
256
|
end
|
259
257
|
|
260
|
-
|
261
|
-
|
258
|
+
## Uses +HyperResource.get_response_data_type+ to determine the proper
|
259
|
+
## data type for this object. Override to change behavior (though you
|
260
|
+
## probably just want to override the class method).
|
261
|
+
def get_data_type_from_response
|
262
|
+
self.class.get_data_type_from_response(self.response)
|
262
263
|
end
|
263
264
|
|
264
265
|
private
|
265
266
|
|
266
267
|
## Return this object, "cast" into its proper response class.
|
267
|
-
def to_response_class
|
268
|
-
response_class = self.
|
268
|
+
def to_response_class
|
269
|
+
response_class = self._hr_response_class
|
269
270
|
return self if self.class == response_class
|
270
271
|
response_class.new(self)
|
271
272
|
end
|
272
273
|
|
274
|
+
## Use the given resource's data to initialize this one.
|
273
275
|
def init_from_resource(resource)
|
274
276
|
(self.class._hr_attributes - [:attributes, :links, :objects]).each do |attr|
|
275
277
|
self.send("#{attr}=".to_sym, resource.send(attr))
|
276
278
|
end
|
277
|
-
self.adapter.apply(self.
|
279
|
+
self.adapter.apply(self.body, self)
|
278
280
|
end
|
279
281
|
|
282
|
+
|
283
|
+
## Show a deprecation message.
|
284
|
+
def self._hr_deprecate(message) # @private
|
285
|
+
STDERR.puts "#{message} (called from #{caller[2]})"
|
286
|
+
end
|
287
|
+
|
288
|
+
def _hr_deprecate(*args) # @private
|
289
|
+
self.class._hr_deprecate(*args)
|
290
|
+
end
|
280
291
|
end
|
@@ -3,6 +3,11 @@ require 'json'
|
|
3
3
|
|
4
4
|
class HyperResource
|
5
5
|
class Adapter
|
6
|
+
|
7
|
+
## HyperResource::Adapter::HAL_JSON provides support for the HAL+JSON
|
8
|
+
## hypermedia format by implementing the interface defined in
|
9
|
+
## HyperResource::Adapter.
|
10
|
+
|
6
11
|
class HAL_JSON < Adapter
|
7
12
|
class << self
|
8
13
|
|
@@ -42,24 +47,24 @@ class HyperResource
|
|
42
47
|
resp['_embedded'].each do |name, collection|
|
43
48
|
if collection.is_a? Hash
|
44
49
|
r = rc.new(:root => rsrc.root, :namespace => rsrc.namespace)
|
45
|
-
r.
|
50
|
+
r.body = collection
|
46
51
|
objs[name] = apply(collection, r)
|
47
52
|
else
|
48
53
|
objs[name] = collection.map do |obj|
|
49
54
|
r = rc.new(:root => rsrc.root, :namespace => rsrc.namespace)
|
50
|
-
r.
|
55
|
+
r.body = obj
|
51
56
|
apply(obj, r)
|
52
57
|
end
|
53
58
|
end
|
54
59
|
end
|
55
60
|
|
56
|
-
objs.
|
61
|
+
objs._hr_create_methods!
|
57
62
|
end
|
58
63
|
|
59
64
|
|
60
65
|
def apply_links(resp, rsrc)
|
61
66
|
return unless resp['_links']
|
62
|
-
rsrc.links = rsrc.
|
67
|
+
rsrc.links = rsrc._hr_response_class::Links.new(rsrc)
|
63
68
|
links = rsrc.links
|
64
69
|
|
65
70
|
resp['_links'].each do |rel, link_spec|
|
@@ -72,16 +77,16 @@ class HyperResource
|
|
72
77
|
end
|
73
78
|
end
|
74
79
|
|
75
|
-
links.
|
80
|
+
links._hr_create_methods!
|
76
81
|
end
|
77
82
|
|
78
|
-
def new_link_from_spec(resource, link_spec)
|
83
|
+
def new_link_from_spec(resource, link_spec)
|
79
84
|
resource.class::Link.new(resource, link_spec)
|
80
85
|
end
|
81
86
|
|
82
87
|
|
83
88
|
def apply_attributes(resp, rsrc)
|
84
|
-
rsrc.attributes = rsrc.
|
89
|
+
rsrc.attributes = rsrc._hr_response_class::Attributes.new(rsrc)
|
85
90
|
|
86
91
|
given_attrs = resp.reject{|k,v| %w(_links _embedded).include?(k)}
|
87
92
|
filtered_attrs = rsrc.incoming_body_filter(given_attrs)
|
@@ -90,7 +95,7 @@ class HyperResource
|
|
90
95
|
rsrc.attributes[attr] = filtered_attrs[attr]
|
91
96
|
end
|
92
97
|
|
93
|
-
rsrc.attributes.
|
98
|
+
rsrc.attributes._hr_create_methods!
|
94
99
|
end
|
95
100
|
|
96
101
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
class HyperResource
|
2
2
|
class Attributes < Hash
|
3
3
|
|
4
|
-
attr_accessor :_resource #
|
4
|
+
attr_accessor :_resource # @private
|
5
5
|
|
6
|
-
def initialize(resource=nil) #
|
6
|
+
def initialize(resource=nil) # @private
|
7
7
|
self._resource = resource || HyperResource.new
|
8
8
|
end
|
9
9
|
|
10
10
|
## Creates accessor methods in self.class and self._resource.class.
|
11
11
|
## Protects against method creation into HyperResource::Attributes and
|
12
12
|
## HyperResource classes. Just subclasses, please!
|
13
|
-
def
|
13
|
+
def _hr_create_methods!(opts={}) # @private
|
14
14
|
return if self.class.to_s == 'HyperResource::Attributes' ||
|
15
15
|
self._resource.class.to_s == 'HyperResource'
|
16
16
|
|
@@ -58,25 +58,25 @@ class HyperResource
|
|
58
58
|
@_hr_changed.select{|k,v| v}.keys.inject({}) {|h,k| h[k]=self[k]; h}
|
59
59
|
end
|
60
60
|
|
61
|
-
def []=(attr, value) #
|
61
|
+
def []=(attr, value) # @private
|
62
62
|
_hr_mark_changed(attr)
|
63
63
|
super(attr.to_s, value)
|
64
64
|
end
|
65
65
|
|
66
|
-
def [](key) #
|
66
|
+
def [](key) # @private
|
67
67
|
return super(key.to_s) if self.has_key?(key.to_s)
|
68
68
|
return super(key.to_sym) if self.has_key?(key.to_sym)
|
69
69
|
nil
|
70
70
|
end
|
71
71
|
|
72
|
-
def method_missing(method, *args) #
|
72
|
+
def method_missing(method, *args) # @private
|
73
73
|
return self[method] if self[method]
|
74
74
|
raise NoMethodError, "undefined method `#{method}' for #{self.inspect}"
|
75
75
|
end
|
76
76
|
|
77
77
|
private
|
78
78
|
|
79
|
-
def _hr_mark_changed(attr, is_changed=true) #
|
79
|
+
def _hr_mark_changed(attr, is_changed=true) # @private
|
80
80
|
attr = attr.to_sym
|
81
81
|
@_hr_changed ||= Hash.new(false)
|
82
82
|
@_hr_changed[attr] = is_changed
|
@@ -1,28 +1,40 @@
|
|
1
1
|
class HyperResource
|
2
|
-
class Exception < ::
|
3
|
-
|
4
|
-
attr_accessor :
|
5
|
-
attr_accessor :cause # Internal exception which led to this
|
2
|
+
class Exception < ::StandardError
|
3
|
+
## The internal exception which led to this one, if any.
|
4
|
+
attr_accessor :cause
|
6
5
|
|
7
|
-
def initialize(message,
|
8
|
-
self.
|
9
|
-
|
10
|
-
|
6
|
+
def initialize(message, attrs={}) # @private
|
7
|
+
self.cause = attrs[:cause]
|
8
|
+
super(message)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ResponseError < Exception
|
13
|
+
## The +Faraday::Response+ object which led to this exception.
|
14
|
+
attr_accessor :response
|
15
|
+
|
16
|
+
## The deserialized response body which led to this exception.
|
17
|
+
## May be blank, e.g. in case of deserialization errors.
|
18
|
+
attr_accessor :body
|
19
|
+
|
20
|
+
def initialize(message, attrs={}) # @private
|
21
|
+
self.response = attrs[:response]
|
22
|
+
self.body = attrs[:body]
|
11
23
|
|
12
24
|
## Try to help out with the message
|
13
|
-
if self.
|
14
|
-
if error = self.
|
25
|
+
if self.body
|
26
|
+
if error = self.body['error']
|
15
27
|
message = "#{message} (#{error})"
|
16
28
|
end
|
17
29
|
elsif self.response
|
18
30
|
message = "#{message} (\"#{self.response.inspect}\")"
|
19
31
|
end
|
20
32
|
|
21
|
-
super(message)
|
33
|
+
super(message, attrs)
|
22
34
|
end
|
23
35
|
end
|
24
36
|
|
25
|
-
class
|
26
|
-
class
|
27
|
-
class ServerError < Exception; end
|
37
|
+
class ClientError < ResponseError; end
|
38
|
+
class ServerError < ResponseError; end
|
28
39
|
end
|
40
|
+
|
data/lib/hyper_resource/link.rb
CHANGED
@@ -7,6 +7,7 @@ class HyperResource::Link
|
|
7
7
|
:params,
|
8
8
|
:parent_resource
|
9
9
|
|
10
|
+
## Returns true if this link is templated.
|
10
11
|
def templated?; templated end
|
11
12
|
|
12
13
|
def initialize(resource=nil, link_spec={})
|
@@ -40,7 +41,7 @@ class HyperResource::Link
|
|
40
41
|
|
41
42
|
## Returns a HyperResource representing this link
|
42
43
|
def resource
|
43
|
-
parent_resource.
|
44
|
+
parent_resource._hr_new_from_link(self.href)
|
44
45
|
end
|
45
46
|
|
46
47
|
## Returns a HyperResource representing this link, and fetches it.
|
data/lib/hyper_resource/links.rb
CHANGED
@@ -9,7 +9,7 @@ class HyperResource
|
|
9
9
|
## Creates accessor methods in self.class and self._resource.class.
|
10
10
|
## Protects against method creation into HyperResource::Links and
|
11
11
|
## HyperResource classes. Just subclasses, please!
|
12
|
-
def
|
12
|
+
def _hr_create_methods!(opts={}) # @private
|
13
13
|
return if self.class.to_s == 'HyperResource::Links' ||
|
14
14
|
self._resource.class.to_s == 'HyperResource'
|
15
15
|
|
@@ -33,17 +33,17 @@ class HyperResource
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
def []=(attr, value) #
|
36
|
+
def []=(attr, value) # @private
|
37
37
|
super(attr.to_s, value)
|
38
38
|
end
|
39
39
|
|
40
|
-
def [](key) #
|
40
|
+
def [](key) # @private
|
41
41
|
return super(key.to_s) if self.has_key?(key.to_s)
|
42
42
|
return super(key.to_sym) if self.has_key?(key.to_sym)
|
43
43
|
nil
|
44
44
|
end
|
45
45
|
|
46
|
-
def method_missing(method, *args) #
|
46
|
+
def method_missing(method, *args) # @private
|
47
47
|
unless self[method]
|
48
48
|
raise NoMethodError, "undefined method `#{method}' for #{self.inspect}"
|
49
49
|
end
|
@@ -3,93 +3,121 @@ require 'uri'
|
|
3
3
|
require 'json'
|
4
4
|
|
5
5
|
class HyperResource
|
6
|
-
module Modules
|
7
|
-
|
8
|
-
## Loads and returns the resource pointed to by +href+. The returned
|
9
|
-
## resource will be blessed into its "proper" class, if
|
10
|
-
## +self.class.namespace != nil+.
|
11
|
-
def get
|
12
|
-
self.response = faraday_connection.get(self.href || '')
|
13
|
-
finish_up
|
14
|
-
end
|
6
|
+
module Modules
|
7
|
+
module HTTP
|
15
8
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
## Loads and returns the resource pointed to by +href+. The returned
|
10
|
+
## resource will be blessed into its "proper" class, if
|
11
|
+
## +self.class.namespace != nil+.
|
12
|
+
def get
|
13
|
+
self.response = faraday_connection.get(self.href || '')
|
14
|
+
finish_up
|
21
15
|
end
|
22
|
-
finish_up
|
23
|
-
end
|
24
16
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
req.body = adapter.serialize(params)
|
17
|
+
## By default, calls +post+ with the given arguments. Override to
|
18
|
+
## change this behavior.
|
19
|
+
def create(*args)
|
20
|
+
post(*args)
|
30
21
|
end
|
31
|
-
finish_up
|
32
|
-
end
|
33
22
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
23
|
+
## POSTs the given attributes to this resource's href, and returns
|
24
|
+
## the response resource.
|
25
|
+
def post(attrs)
|
26
|
+
self.response = faraday_connection.post do |req|
|
27
|
+
req.body = adapter.serialize(attrs)
|
28
|
+
end
|
29
|
+
finish_up
|
30
|
+
end
|
38
31
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
32
|
+
## By default, calls +put+ with the given arguments. Override to
|
33
|
+
## change this behavior.
|
34
|
+
def update(*args)
|
35
|
+
put(*args)
|
36
|
+
end
|
37
|
+
|
38
|
+
## PUTs this resource's attributes to this resource's href, and returns
|
39
|
+
## the response resource. If attributes are given, +put+ uses those
|
40
|
+
## instead.
|
41
|
+
def put(attrs=nil)
|
42
|
+
attrs ||= self.attributes
|
43
|
+
self.response = faraday_connection.put do |req|
|
44
|
+
req.body = adapter.serialize(attrs)
|
45
|
+
end
|
46
|
+
finish_up
|
47
|
+
end
|
48
|
+
|
49
|
+
## PATCHes this resource's changed attributes to this resource's href,
|
50
|
+
## and returns the response resource. If attributes are given, +patch+
|
51
|
+
## uses those instead.
|
52
|
+
def patch(attrs=nil)
|
53
|
+
attrs ||= self.attributes.changed_attributes
|
54
|
+
self.response = faraday_connection.patch do |req|
|
55
|
+
req.body = adapter.serialize(attrs)
|
56
|
+
end
|
57
|
+
finish_up
|
51
58
|
end
|
52
|
-
Thread.current[key] = fc
|
53
|
-
end
|
54
59
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
self.response_object = self.adapter.deserialize(self.response.body)
|
60
|
-
rescue StandardError => e
|
61
|
-
raise HyperResource::ResponseError.new(
|
62
|
-
"Error when deserializing response body",
|
63
|
-
:response => self.response,
|
64
|
-
:cause => e
|
65
|
-
)
|
60
|
+
## DELETEs this resource's href, and returns the response resource.
|
61
|
+
def delete
|
62
|
+
self.response = faraday_connection.delete
|
63
|
+
finish_up
|
66
64
|
end
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
return
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
66
|
+
## Returns a raw Faraday connection to this resource's URL, with proper
|
67
|
+
## headers (including auth). Threadsafe.
|
68
|
+
def faraday_connection(url=nil)
|
69
|
+
url ||= URI.join(self.root, self.href)
|
70
|
+
key = "faraday_connection_#{url}"
|
71
|
+
return Thread.current[key] if Thread.current[key]
|
72
|
+
|
73
|
+
fc = Faraday.new(:url => url)
|
74
|
+
fc.headers.merge!('User-Agent' => "HyperResource #{HyperResource::VERSION}")
|
75
|
+
fc.headers.merge!(self.headers || {})
|
76
|
+
if ba=self.auth[:basic]
|
77
|
+
fc.basic_auth(*ba)
|
78
|
+
end
|
79
|
+
Thread.current[key] = fc
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def finish_up
|
85
|
+
begin
|
86
|
+
self.body = self.adapter.deserialize(self.response.body)
|
87
|
+
rescue StandardError => e
|
88
|
+
raise HyperResource::ResponseError.new(
|
89
|
+
"Error when deserializing response body",
|
90
|
+
:response => self.response,
|
91
|
+
:cause => e
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
self.adapter.apply(self.body, self)
|
96
|
+
self.loaded = true
|
97
|
+
|
98
|
+
status = self.response.status
|
99
|
+
if status / 100 == 2
|
100
|
+
return to_response_class
|
101
|
+
elsif status / 100 == 3
|
102
|
+
## TODO redirect logic?
|
103
|
+
elsif status / 100 == 4
|
104
|
+
raise HyperResource::ClientError.new(status.to_s,
|
105
|
+
:response => self.response,
|
106
|
+
:body => self.body)
|
107
|
+
elsif status / 100 == 5
|
108
|
+
raise HyperResource::ServerError.new(status.to_s,
|
87
109
|
:response => self.response,
|
88
|
-
:
|
110
|
+
:body => self.body)
|
111
|
+
|
112
|
+
else ## 1xx? really?
|
113
|
+
raise HyperResource::ResponseError.new("Got status #{status}, wtf?",
|
114
|
+
:response => self.response,
|
115
|
+
:body => self.body)
|
89
116
|
|
117
|
+
end
|
90
118
|
end
|
91
|
-
end
|
92
119
|
|
93
|
-
|
120
|
+
end
|
121
|
+
end
|
94
122
|
end
|
95
123
|
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module HyperResource::Modules
|
2
|
+
module InternalAttributes
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
|
7
|
+
base._hr_class_attributes.each do |attr|
|
8
|
+
base._hr_class_attribute attr
|
9
|
+
end
|
10
|
+
|
11
|
+
(base._hr_attributes - base._hr_class_attributes).each do |attr|
|
12
|
+
base.send(:attr_accessor, attr)
|
13
|
+
end
|
14
|
+
|
15
|
+
## Fallback attributes fall back from instance to class.
|
16
|
+
(base._hr_attributes & base._hr_class_attributes).each do |attr|
|
17
|
+
base._hr_fallback_attribute attr
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
|
23
|
+
def _hr_class_attributes # @private
|
24
|
+
[ :root, ## e.g. 'https://example.com/api/v1'
|
25
|
+
:auth, ## e.g. {:basic => ['username', 'password']}
|
26
|
+
:headers, ## e.g. {'Accept' => 'application/vnd.example+json'}
|
27
|
+
:namespace, ## e.g. 'ExampleAPI', or the class ExampleAPI itself
|
28
|
+
:adapter ## subclass of HR::Adapter
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
def _hr_attributes # @private
|
33
|
+
[ :root,
|
34
|
+
:href,
|
35
|
+
:auth,
|
36
|
+
:headers,
|
37
|
+
:namespace,
|
38
|
+
:adapter,
|
39
|
+
|
40
|
+
:request,
|
41
|
+
:response,
|
42
|
+
:body,
|
43
|
+
|
44
|
+
:attributes,
|
45
|
+
:links,
|
46
|
+
:objects,
|
47
|
+
|
48
|
+
:loaded
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
## Inheritable class attribute, kinda like in Rails.
|
53
|
+
def _hr_class_attribute(*names)
|
54
|
+
names.map(&:to_sym).each do |name|
|
55
|
+
instance_eval <<-EOT
|
56
|
+
def #{name}=(val)
|
57
|
+
@#{name} = val
|
58
|
+
end
|
59
|
+
def #{name}
|
60
|
+
return @#{name} if defined?(@#{name})
|
61
|
+
return superclass.#{name} if superclass.respond_to?(:#{name})
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
EOT
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
## Instance attributes which fall back to class attributes.
|
69
|
+
def _hr_fallback_attribute(*names)
|
70
|
+
names.map(&:to_sym).each do |name|
|
71
|
+
class_eval <<-EOT
|
72
|
+
def #{name}=(val)
|
73
|
+
@#{name} = val
|
74
|
+
end
|
75
|
+
def #{name}
|
76
|
+
return @#{name} if defined?(@#{name})
|
77
|
+
return self.class.#{name} if self.class.respond_to?(:#{name})
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
EOT
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end # ClassMethods
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
@@ -9,7 +9,7 @@ class HyperResource
|
|
9
9
|
## Creates accessor methods in self.class and self._resource.class.
|
10
10
|
## Protects against method creation into HyperResource::Objects and
|
11
11
|
## HyperResource classes. Just subclasses, please!
|
12
|
-
def
|
12
|
+
def _hr_create_methods!(opts={}) # @private
|
13
13
|
return if self.class.to_s == 'HyperResource::Objects' ||
|
14
14
|
self._resource.class.to_s == 'HyperResource'
|
15
15
|
|
@@ -29,28 +29,25 @@ class HyperResource
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
alias_method :first_orig, :first
|
34
|
-
def first
|
35
|
-
self.first_orig[1][0]
|
36
|
-
end
|
37
|
-
|
38
|
-
## Returns the ith item in the first collection in +self+.
|
39
|
-
def ith(i)
|
40
|
-
self.first_orig[1][i]
|
41
|
-
end
|
42
|
-
|
43
|
-
def []=(attr, value) # :nodoc:
|
32
|
+
def []=(attr, value) # @private
|
44
33
|
super(attr.to_s, value)
|
45
34
|
end
|
46
35
|
|
47
|
-
|
48
|
-
|
49
|
-
|
36
|
+
## When +key+ is a string, returns the array of objects under that name.
|
37
|
+
## When +key+ is a number, returns +ith(key)+. Returns nil on lookup
|
38
|
+
## failure.
|
39
|
+
def [](key)
|
40
|
+
case key
|
41
|
+
when String, Symbol
|
42
|
+
return super(key.to_s) if self.has_key?(key.to_s)
|
43
|
+
return super(key.to_sym) if self.has_key?(key.to_sym)
|
44
|
+
when Fixnum
|
45
|
+
return ith(key)
|
46
|
+
end
|
50
47
|
nil
|
51
48
|
end
|
52
49
|
|
53
|
-
def method_missing(method, *args) #
|
50
|
+
def method_missing(method, *args) # @private
|
54
51
|
return self[method] if self[method]
|
55
52
|
raise NoMethodError, "undefined method `#{method}' for #{self.inspect}"
|
56
53
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hyperresource
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pete Gamache
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: uri_template
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - '>='
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: 1.4.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: yard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.8.5
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.8.5
|
111
125
|
description: |2
|
112
126
|
HyperResource is a hypermedia client library for Ruby. Its goals are to
|
113
127
|
interface directly with well-behaved hypermedia APIs, to allow the data
|
@@ -125,7 +139,7 @@ files:
|
|
125
139
|
- lib/hyper_resource/link.rb
|
126
140
|
- lib/hyper_resource/links.rb
|
127
141
|
- lib/hyper_resource/modules/http.rb
|
128
|
-
- lib/hyper_resource/modules/
|
142
|
+
- lib/hyper_resource/modules/internal_attributes.rb
|
129
143
|
- lib/hyper_resource/objects.rb
|
130
144
|
- lib/hyper_resource/response.rb
|
131
145
|
- lib/hyper_resource/version.rb
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module HyperResource::Modules
|
2
|
-
module Utils
|
3
|
-
|
4
|
-
def self.included(base)
|
5
|
-
base.extend(ClassMethods)
|
6
|
-
end
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
|
10
|
-
## Inheritable class attribute, kinda like in Rails.
|
11
|
-
def class_attribute(*names)
|
12
|
-
names.map(&:to_sym).each do |name|
|
13
|
-
instance_eval <<-EOT
|
14
|
-
def #{name}=(val)
|
15
|
-
@#{name} = val
|
16
|
-
end
|
17
|
-
def #{name}
|
18
|
-
return @#{name} if defined?(@#{name})
|
19
|
-
return superclass.#{name} if superclass.respond_to?(:#{name})
|
20
|
-
nil
|
21
|
-
end
|
22
|
-
EOT
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
## Instance attributes which fall back to class attributes.
|
27
|
-
def fallback_attribute(*names)
|
28
|
-
names.map(&:to_sym).each do |name|
|
29
|
-
class_eval <<-EOT
|
30
|
-
def #{name}=(val)
|
31
|
-
@#{name} = val
|
32
|
-
end
|
33
|
-
def #{name}
|
34
|
-
return @#{name} if defined?(@#{name})
|
35
|
-
return self.class.#{name} if self.class.respond_to?(:#{name})
|
36
|
-
nil
|
37
|
-
end
|
38
|
-
EOT
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
end # module ClassMethods
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|