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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 297e7456b1c2ea2b42d9ed4ca690f98a90c5e33a
4
- data.tar.gz: 4e89207bc9ab1bfaf0148ff99e57517bf1c80414
3
+ metadata.gz: 8d9374b1ec64fdce21a54dfa34bd053c991f0541
4
+ data.tar.gz: 0442f93a99aaed8e1e023ad9aa8ff00ecc2978f5
5
5
  SHA512:
6
- metadata.gz: 14720ef3ba836d285ae6b0b0ed2e9577c307538a12f833cfbed99f519f38a45e7c0e7c65ae2e55cf6f0725aa07d2be0e675db5c296de7c63babe8713c73b034d
7
- data.tar.gz: dc6f658a981ac5b2f5e9596e677aac6f4b2ae45f712ce10fe39eecc69045f3e66beea8a90208034fee8de071cd9bbc513b0cb6d828658fba1f86acd92c5fa0c1
6
+ metadata.gz: c3e3388c09971d33a9c0062377ef356bcb35e2770c060d8d61ce0a8cf0a992b2f255726dbdaeffd94adee8ee91964eae2beff2e13551a3ac76a59d3a7e0add34
7
+ data.tar.gz: 2eeb5c948b3736691cbf273310f4c98d8413ea2fa2a8725675985d9749bd035f42793828b1300e23452ac92fbf85b9a4de67153fec07f08790066a3cb577f4e9
@@ -1,58 +1,36 @@
1
- this_dir = File.dirname(__FILE__)
2
- Dir.glob(this_dir + '/hyper_resource/**/*.rb') {|f| require f}
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
- if RUBY_VERSION[0..2] == '1.8'
5
- require 'rubygems'
6
- end
9
+ require 'hyper_resource/adapter'
10
+ require 'hyper_resource/adapter/hal_json'
7
11
 
8
- require 'pp'
12
+ require 'hyper_resource/modules/http'
13
+ require 'hyper_resource/modules/internal_attributes'
9
14
 
10
- class HyperResource
11
- include HyperResource::Modules::Utils
12
- include HyperResource::Modules::HTTP
15
+ require 'rubygems' if RUBY_VERSION[0..2] == '1.8'
13
16
 
14
- private
17
+ require 'pp'
15
18
 
16
- def self._hr_class_attributes
17
- [ :root, ## e.g. 'https://example.com/api/v1'
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
- def self._hr_attributes
26
- [ :root,
27
- :href,
28
- :auth,
29
- :headers,
30
- :namespace,
31
- :adapter,
22
+ class HyperResource
32
23
 
33
- :request,
34
- :response,
35
- :response_object,
24
+ include HyperResource::Modules::HTTP
25
+ include HyperResource::Modules::InternalAttributes
26
+ include Enumerable
36
27
 
37
- :attributes,
38
- :links,
39
- :objects,
28
+ private
40
29
 
41
- :loaded
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
- ## Returns the first object in the first collection of objects embedded
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. Equivalent to +self.objects[i]+.
147
- def [](i); self.objects.ith(i) end
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 # :nodoc:
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+ is deprecated in favor of +response_object+.
181
- def response_body; response_object end # :nodoc:
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 _new_from_link(href) # :nodoc:
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 +opts[:namespace]+ is
200
+ ## If the object is not loaded yet, or if +namespace+ is
201
201
  ## not set, returns +self+.
202
202
  ##
203
- ## Otherwise, +_get_response_class+ uses +_get_response_data_type+ to
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._get_response_data_type(response)
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 _get_response_class # :nodoc:
238
+ def _hr_response_class # @private
240
239
  self.namespace ||= self.class.to_s unless self.class.to_s=='HyperResource'
241
- self.class._get_response_class(self.response, self.namespace)
240
+ self.class.response_class(self.response, self.namespace)
242
241
  end
243
242
 
244
243
 
245
- ## Inspects the given response, and returns a string describing this
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
- def _get_response_data_type # :nodoc:
261
- self.class._get_response_data_type(self.response)
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 # :nodoc:
268
- response_class = self._get_response_class
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.response_object, 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.response_object = collection
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.response_object = obj
55
+ r.body = obj
51
56
  apply(obj, r)
52
57
  end
53
58
  end
54
59
  end
55
60
 
56
- objs.create_methods!
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._get_response_class::Links.new(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.create_methods!
80
+ links._hr_create_methods!
76
81
  end
77
82
 
78
- def new_link_from_spec(resource, link_spec) # :nodoc:
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._get_response_class::Attributes.new(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.create_methods!
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 # :nodoc:
4
+ attr_accessor :_resource # @private
5
5
 
6
- def initialize(resource=nil) # :nodoc:
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 create_methods!(opts={})
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) # :nodoc:
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) # :nodoc:
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) # :nodoc:
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) # :nodoc:
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 < ::Exception
3
- attr_accessor :response # Response body which led to this
4
- attr_accessor :response_object # Response object which led to this
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, opts={})
8
- self.response = opts[:response]
9
- self.response_object = opts[:response_object]
10
- self.cause = opts[:cause]
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.response_object
14
- if error = self.response_object['error']
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 ResponseError < Exception; end
26
- class ClientError < Exception; end
27
- class ServerError < Exception; end
37
+ class ClientError < ResponseError; end
38
+ class ServerError < ResponseError; end
28
39
  end
40
+
@@ -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._new_from_link(self.href)
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.
@@ -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 create_methods!(opts={})
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) # :nodoc:
36
+ def []=(attr, value) # @private
37
37
  super(attr.to_s, value)
38
38
  end
39
39
 
40
- def [](key) # :nodoc:
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) # :nodoc:
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; module HTTP
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
- def create(*args); post(*args) end
17
- def post(params=nil)
18
- params ||= self.attributes
19
- self.response = faraday_connection.post do |req|
20
- req.body = adapter.serialize(params)
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
- def update(*args); put(*args) end
26
- def put(params=nil)
27
- params ||= self.attributes.changed_attributes
28
- self.response = faraday_connection.put do |req|
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
- def delete
35
- self.response = faraday_connection.delete
36
- finish_up
37
- end
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
- ## Returns a raw Faraday connection to this resource's URL, with proper
40
- ## headers (including auth).
41
- def faraday_connection(url=nil)
42
- url ||= URI.join(self.root, self.href)
43
- key = "faraday_connection_#{url}"
44
- return Thread.current[key] if Thread.current[key]
45
-
46
- fc = Faraday.new(:url => url)
47
- fc.headers.merge!('User-Agent' => "HyperResource #{HyperResource::VERSION}")
48
- fc.headers.merge!(self.headers || {})
49
- if ba=self.auth[:basic]
50
- fc.basic_auth(*ba)
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
- private
56
-
57
- def finish_up
58
- begin
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
- self.adapter.apply(self.response_object, self)
69
- self.loaded = true
70
-
71
- status = self.response.status
72
- if status / 100 == 2
73
- return to_response_class
74
- elsif status / 100 == 3
75
- ## TODO redirect logic?
76
- elsif status / 100 == 4
77
- raise HyperResource::ClientError.new(status.to_s,
78
- :response => self.response,
79
- :response_object => self.response_object)
80
- elsif status / 100 == 5
81
- raise HyperResource::ServerError.new(status.to_s,
82
- :response => self.response,
83
- :response_object => self.response_object)
84
-
85
- else ## 1xx? really?
86
- raise HyperResource::ResponseError.new("Got status #{status}, wtf?",
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
- :response_object => self.response_object)
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
- end end
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 create_methods!(opts={})
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
- ## Returns the first item in the first collection in +self+.
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
- def [](key) # :nodoc:
48
- return super(key.to_s) if self.has_key?(key.to_s)
49
- return super(key.to_sym) if self.has_key?(key.to_sym)
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) # :nodoc:
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
@@ -1,4 +1,4 @@
1
1
  class HyperResource
2
- VERSION = '0.1.9.5'
3
- VERSION_DATE = '2013-10-11'
2
+ VERSION = '0.2.0'
3
+ VERSION_DATE = '2013-10-31'
4
4
  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.1.9.5
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 00:00:00.000000000 Z
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/utils.rb
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