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 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