Class: HyperResource

Inherits:
Object
  • Object
show all
Includes:
Enumerable, Modules::HTTP, Modules::InternalAttributes
Defined in:
lib/hyper_resource.rb,
lib/hyper_resource/links.rb,
lib/hyper_resource/adapter.rb,
lib/hyper_resource/objects.rb,
lib/hyper_resource/version.rb,
lib/hyper_resource/attributes.rb,
lib/hyper_resource/exceptions.rb,
lib/hyper_resource/modules/http.rb,
lib/hyper_resource/adapter/hal_json.rb,
lib/hyper_resource/modules/http/wrap_errors.rb

Overview

HyperResource is the main resource base class. Normally it will be used through subclassing, though it may also be used directly.

Direct Known Subclasses

Aptible::Resource::Base

Defined Under Namespace

Modules: Modules Classes: Adapter, Attributes, ClientError, Exception, Link, Links, Objects, Response, ResponseError, ServerError

Constant Summary collapse

VERSION =
'0.2.5'.freeze
VERSION_DATE =
'2014-04-02'.freeze

Constants included from Modules::HTTP

Modules::HTTP::MAX_COORDINATOR_RETRIES

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Modules::InternalAttributes

included

Methods included from Modules::HTTP

#create, #delete, #faraday_connection, #get, #patch, #post, #put, #update

Constructor Details

#initialize(opts = {}) ⇒ HyperResource

Create a new HyperResource, given a hash of options. These options include:

root

The root URL of the resource.

auth

Authentication information. Currently only {basic: [‘key’, ‘secret’]} is supported.

namespace

Class or class name, into which resources should be instantiated.

headers

Headers to send along with requests for this resource (as well as its eventual child resources, if any).

faraday_options

Configuration passed to Faraday::Connection.initialize, such as {request: {timeout: 30}}.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/hyper_resource.rb', line 52

def initialize(opts={})
  return init_from_resource(opts) if opts.kind_of?(HyperResource)

  self.root       = opts[:root] || self.class.root
  self.href       = opts[:href] || ''
  self.auth       = (self.class.auth || {}).merge(opts[:auth] || {})
  self.namespace  = opts[:namespace] || self.class.namespace
  self.headers    = DEFAULT_HEADERS.merge(self.class.headers || {}).
                                    merge(opts[:headers]     || {})
  self.faraday_options = opts[:faraday_options] ||
                             self.class.faraday_options || {}

  ## There's a little acrobatics in getting Attributes, Links, and Objects
  ## into the correct subclass.
  if self.class != HyperResource
    if self.class::Attributes == HyperResource::Attributes
      Object.module_eval(
        "class #{self.class}::Attributes < HyperResource::Attributes; end"
      )
    end
    if self.class::Links == HyperResource::Links
      Object.module_eval(
        "class #{self.class}::Links < HyperResource::Links; end"
      )
    end
    if self.class::Objects == HyperResource::Objects
      Object.module_eval(
        "class #{self.class}::Objects < HyperResource::Objects; end"
      )
    end
  end

  self.attributes = self.class::Attributes.new(self)
  self.links      = self.class::Links.new(self)
  self.objects    = self.class::Objects.new(self)

  self.loaded     = false

  self.adapter    = opts[:adapter] || self.class.adapter ||
                    HyperResource::Adapter::HAL_JSON
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object

method_missing will load this resource if not yet loaded, then attempt to delegate to attributes, then objects, then links. Override with care.

Raises:

  • (NoMethodError)


148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/hyper_resource.rb', line 148

def method_missing(method, *args)
  self.get unless self.loaded

  method = method.to_s
  if method[-1,1] == '='
    return attributes[method[0..-2]] = args.first if attributes[method[0..-2]]
  else
    return attributes[method] if attributes && attributes.has_key?(method)
    return objects[method] if objects && objects[method]
    if links && links[method]
      if args.count > 0
        return links[method].where(*args)
      else
        return links[method]
      end
    end
  end

  raise NoMethodError, "undefined method `#{method}' for #{self.inspect}"
end

Class Method Details

.get_data_type_from_response(response) ⇒ Object

Inspects the given Faraday::Response, and returns a string describing this resource’s data type.

By default, this method looks for a type=… modifier in the response’s Content-type and returns that value, capitalized.

Override this method in a subclass to alter HyperResource’s behavior.



264
265
266
267
268
269
# File 'lib/hyper_resource.rb', line 264

def self.get_data_type_from_response(response)
  return nil unless response
  return nil unless content_type = response['content-type']
  return nil unless m=content_type.match(/;\s* type=([0-9A-Za-z:]+)/x)
  m[1][0,1].upcase + m[1][1..-1]
end

.namespaced_class(type_name, namespace) ⇒ Object



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/hyper_resource.rb', line 231

def self.namespaced_class(type_name, namespace)
  class_name = "#{namespace}::#{type_name}"
  class_name.gsub!(/[^_0-9A-Za-z:]/, '')  ## sanitize class_name

  ## Return data type class if it exists
  klass = eval(class_name) rescue :sorry_dude
  return klass if klass.is_a?(Class)

  ## Data type class didn't exist -- create namespace (if necessary),
  ## then the data type class
  if namespace != ''
    nsc = eval(namespace) rescue :bzzzzzt
    unless nsc.is_a?(Class)
      Object.module_eval "class #{namespace} < #{self}; end"
    end
  end
  Object.module_eval "class #{class_name} < #{namespace}; end"
  eval(class_name)
end

.response_class(response, namespace) ⇒ Object

Returns the class into which the given response should be cast. If the object is not loaded yet, or if namespace is not set, returns self.

Otherwise, response_class uses get_data_type_from_response to determine subclass name, glues it to the given namespace, and creates the class if it’s not there yet. E.g., given a namespace of FooAPI and a response content-type of “application/vnd.foocorp.fooapi.v1+json;type=User”, this should return FooAPI::User (even if FooAPI::User hadn’t existed yet).



218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/hyper_resource.rb', line 218

def self.response_class(response, namespace)
  if self.to_s == 'HyperResource'
    return self unless namespace
  end

  namespace ||= self.to_s

  type_name = self.get_data_type_from_response(response)
  return self unless type_name

  namespaced_class(type_name, namespace)
end

Instance Method Details

#[](i) ⇒ Object

Returns the *i*th object in the first collection of objects embedded in this resource. Returns nil on failure.



131
132
133
134
# File 'lib/hyper_resource.rb', line 131

def [](i)
  get unless loaded
  self.objects.first[1][i] rescue nil
end

Return a new HyperResource based on this object and a given href.



197
198
199
200
201
202
203
204
205
# File 'lib/hyper_resource.rb', line 197

def _hr_new_from_link(href) # @private
  self.class.new(:root            => self.root,
                 :auth            => self.auth,
                 :headers         => self.headers,
                 :namespace       => self.namespace,
                 :faraday_options => self.faraday_options,
                 :token           => self.token,
                 :href            => href)
end

#_hr_response_classObject



251
252
253
254
# File 'lib/hyper_resource.rb', line 251

def _hr_response_class # @private
  self.namespace ||= self.class.to_s unless self.class.to_s=='HyperResource'
  self.class.response_class(self.response, self.namespace)
end

#changed?(*args) ⇒ Boolean

Returns true if one or more of this object’s attributes has been reassigned.

Returns:

  • (Boolean)


97
98
99
# File 'lib/hyper_resource.rb', line 97

def changed?(*args)
  attributes.changed?(*args)
end

#deserialized_responseObject



188
189
190
191
192
# File 'lib/hyper_resource.rb', line 188

def deserialized_response # @private
  _hr_deprecate('HyperResource#deserialized_response is deprecated. '+
                'Please use HyperResource#body instead.')
  body
end

#each(&block) ⇒ Object

Iterates over the objects in the first collection of embedded objects in this resource.



138
139
140
141
# File 'lib/hyper_resource.rb', line 138

def each(&block)
  get unless loaded
  self.objects.first[1].each(&block) rescue nil
end

#get_data_type_from_responseObject

Uses HyperResource.get_response_data_type to determine the proper data type for this object. Override to change behavior (though you probably just want to override the class method).



274
275
276
# File 'lib/hyper_resource.rb', line 274

def get_data_type_from_response
  self.class.get_data_type_from_response(self.response)
end

#incoming_body_filter(attr_hash) ⇒ Object

incoming_body_filter filters a hash of attribute keys and values on their way from a response body to a HyperResource. Override this in a subclass of HyperResource to implement filters on incoming data.



107
108
109
# File 'lib/hyper_resource.rb', line 107

def incoming_body_filter(attr_hash)
  attr_hash
end

#inspectObject



170
171
172
173
174
# File 'lib/hyper_resource.rb', line 170

def inspect # @private
  "#<#{self.class}:0x#{"%x" % self.object_id} @root=#{self.root.inspect} "+
  "@href=#{self.href.inspect} @loaded=#{self.loaded} "+
  "@namespace=#{self.namespace.inspect} ...>"
end

#outgoing_body_filter(attr_hash) ⇒ Object

outgoing_body_filter filters a hash of attribute keys and values on their way from a HyperResource to a request body. Override this in a subclass of HyperResource to implement filters on outgoing data.



114
115
116
# File 'lib/hyper_resource.rb', line 114

def outgoing_body_filter(attr_hash)
  attr_hash
end

#outgoing_uri_filter(attr_hash) ⇒ Object

outgoing_uri_filter filters a hash of attribute keys and values on their way from a HyperResource to a URL. Override this in a subclass of HyperResource to implement filters on outgoing URI parameters.



122
123
124
# File 'lib/hyper_resource.rb', line 122

def outgoing_uri_filter(attr_hash)
  attr_hash
end

#response_bodyObject

response_body, response_object, and deserialized_response

are deprecated in favor of +body+.  (Sorry. Naming things is hard.)


178
179
180
181
182
# File 'lib/hyper_resource.rb', line 178

def response_body # @private
  _hr_deprecate('HyperResource#response_body is deprecated. '+
                'Please use HyperResource#body instead.')
  body
end

#response_objectObject



183
184
185
186
187
# File 'lib/hyper_resource.rb', line 183

def response_object # @private
  _hr_deprecate('HyperResource#response_object is deprecated. '+
                'Please use HyperResource#body instead.')
  body
end