hyperresource 0.1.9.5 → 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/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
|