Module: Eipiai::Resource

Includes:
Representable, Objectifiable
Included in:
ApiResource, HealthResource
Defined in:
lib/eipiai/webmachine/resources/base.rb,
lib/eipiai/webmachine/resources/concerns/objectifiable.rb,
lib/eipiai/webmachine/resources/concerns/representable.rb

Overview

Resource

The base resource which can be included in regular Webmachine::Resource objects. It provides sensible defaults for a full-features REST API endpoint.

Defined Under Namespace

Modules: Objectifiable, Representable

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Representable

#from_hash, #path, #represented

Class Method Details

.included(base) ⇒ Object

Includes the correct resource into the class, depending on its name.

If the resource is called ‘ItemResource`, the `Item` part is considered singular, and thus the `SingularResource` is included into the class.



24
25
26
27
28
29
30
31
32
# File 'lib/eipiai/webmachine/resources/base.rb', line 24

def self.included(base)
  subject_class_name = base.name.demodulize.chomp('Resource')

  if subject_class_name == subject_class_name.singularize
    base.send(:include, SingularResource)
  else
    base.send(:include, CollectionResource)
  end
end

Instance Method Details

#base_uriObject



218
219
220
# File 'lib/eipiai/webmachine/resources/base.rb', line 218

def base_uri
  request.base_uri
end

#content_types_acceptedObject



133
134
135
# File 'lib/eipiai/webmachine/resources/base.rb', line 133

def content_types_accepted
  [['application/json', :from_json]]
end

#content_types_providedObject



129
130
131
# File 'lib/eipiai/webmachine/resources/base.rb', line 129

def content_types_provided
  [['application/hal+json', :to_hal_json], ['application/json', :to_json]]
end

#create_uriObject



222
223
224
# File 'lib/eipiai/webmachine/resources/base.rb', line 222

def create_uri
  Addressable::URI.join(base_uri, create_path)
end

#malformed_request?true, false

malformed_request?

return ‘true` if content_type is of type `application/json`, and the JSON request body cannot be parsed.

Examples:

valid JSON payload

post('/items', '{ "uid": "hello" }', 'Content-Type': 'application/json')
resource.malformed_request? # => false

invalid JSON payload

post('/items', '{ invalid! }', 'Content-Type': 'application/json')
resource.malformed_request? # => true

Returns:

  • (true, false)


100
101
102
103
104
105
106
# File 'lib/eipiai/webmachine/resources/base.rb', line 100

def malformed_request?
  return false unless request.json? && request.body.to_s.present?

  JSON.parse(request.body.to_s) && false
rescue JSON::ParserError
  json_error_body(:invalid_json)
end

#new_objectObject?

new_object

New object, instantiated by passing the provided ‘params` into the object’s ‘#from_hash` method.

The only requirement is that the object responds to ‘#from_hash`, that method accepts a hash of parameters, and it returns the object itself.

Examples:

resource.new_object.class # => Item

Returns:

  • (Object, nil)

    instantiated object, or nil if not found



212
213
214
215
216
# File 'lib/eipiai/webmachine/resources/base.rb', line 212

def new_object
  return if object_class.nil?

  @new_object ||= object_class.new.from_hash(params)
end

#params(body = request.body.to_s) ⇒ Hash

params

Given a string in JSON format, returns the hash representation of that object.

If the input is invalid JSON, an empty hash is returned.

If no argument is given, ‘request.body` is used as the JSON input.

Examples:

Parse valid JSON request

resource.params('{ "hello": "world" }') # => { 'hello' => 'world' }

Parse invalid JSON request

resource.params('invalid') #=> {}

Parameters:

  • body (String) (defaults to: request.body.to_s)

    JSON provided as a string

Returns:

  • (Hash)

    JSON string, converted to a hash



159
160
161
162
163
# File 'lib/eipiai/webmachine/resources/base.rb', line 159

def params(body = request.body.to_s)
  @params ||= JSON.parse(body)
rescue JSON::ParserError
  {}
end

#post_is_create?Boolean

Returns:

  • (Boolean)


137
138
139
# File 'lib/eipiai/webmachine/resources/base.rb', line 137

def post_is_create?
  true
end

#query_keysArray<String>

query_keys

Returns an array of optional query component keys this resource accepts. The keys are added to the link relation as templated variables.

Defaults to an empty array, not exposing any optional query keys.

Returns:

  • (Array<String>)

    array of query keys



74
75
76
# File 'lib/eipiai/webmachine/resources/base.rb', line 74

def query_keys
  []
end

#resource_relationString

resource_relation

The name to be used when linking to this resource as a relation.

The string returned from this method will become the name of the link in the HAL+JSON representation of this resource.

Examples:

resource.resource_relation # => 'items'

Returns:

  • (String)

    resource relation name



46
47
48
# File 'lib/eipiai/webmachine/resources/base.rb', line 46

def resource_relation
  resource_name.demodulize.underscore
end

#service_available?Boolean

Returns:

  • (Boolean)


78
79
80
81
82
83
# File 'lib/eipiai/webmachine/resources/base.rb', line 78

def service_available?
  return true if Eipiai::HealthCheck.new.healthy?

  response.headers['Retry-After'] = '60'
  false
end

#to_h(obj = object) ⇒ Hash

to_h

Given an object, calls ‘#to_h` on that object,

If the object’s ‘to_h` implementation accepts any arguments, the hash `{ request: request }` is sent as its first argument.

In practice, this method is used without any parameters, causing the method to call ‘represented`, which represents a Roar representer. This in turn converts the represented object to a HAL/JSON compatible hash representation.

Examples:

item = Item.new(uid: 'hello')
get('/item/hello')
resource.to_h(item)['uid'] # => 'hello'

Parameters:

  • obj (Object) (defaults to: object)

    to call #to_h on

Returns:

  • (Hash)

    hash representation of the object



185
186
187
188
189
# File 'lib/eipiai/webmachine/resources/base.rb', line 185

def to_h(obj = object)
  obj.method(:to_h).arity.zero? ? obj.to_h : obj.to_h(request: request)
rescue
  obj
end

#to_hal_jsonObject



195
196
197
# File 'lib/eipiai/webmachine/resources/base.rb', line 195

def to_hal_json
  to_h.to_json
end

#to_jsonObject



191
192
193
# File 'lib/eipiai/webmachine/resources/base.rb', line 191

def to_json
  to_h.to_json
end

#top_level_relation?true, false

top_level_relation?

return ‘true` if the link relation to this resource should be added to the top-level API entrypoint.

It is good practice to keep most link relations out of the top-level entrypoint, instead opting for nesting the resource within its parent resource links relations.

Returns:

  • (true, false)


61
62
63
# File 'lib/eipiai/webmachine/resources/base.rb', line 61

def top_level_relation?
  false
end

#unprocessable_entity?true, false

unprocessable_entity?

return ‘true` if content_type is of type `application/json`, and the newly generated object reports back as “invalid”.

Examples:

invalid object

post('/users', '{}', 'Content-Type': 'application/json')
resource.unprocessable_entity? # => true

valid object

post('/users', '{ "uid": "bartsimpson" }', 'Content-Type': 'application/json')
resource.unprocessable_entity? # => false

Returns:

  • (true, false)


123
124
125
126
127
# File 'lib/eipiai/webmachine/resources/base.rb', line 123

def unprocessable_entity?
  return false unless request.json? && new_object.respond_to?(:invalid?)

  new_object.invalid? && json_error_body(*new_object.errors)
end