Class: Grape::Entity

Inherits:
Object
  • Object
show all
Defined in:
lib/grape_entity/entity.rb

Overview

An Entity is a lightweight structure that allows you to easily represent data from your application in a consistent and abstracted way in your API. Entities can also provide documentation for the fields exposed.

Entities are not independent structures, rather, they create representations of other Ruby objects using a number of methods that are convenient for use in an API. Once you've defined an Entity, you can use it in your API like this:

Examples:

Entity Definition


module API
  module Entities
    class User < Grape::Entity
      expose :first_name, :last_name, :screen_name, :location
      expose :field, :documentation => {:type => "string", :desc => "describe the field"}
      expose :latest_status, :using => API::Status, :as => :status, :unless => {:collection => true}
      expose :email, :if => {:type => :full}
      expose :new_attribute, :if => {:version => 'v2'}
      expose(:name){|model,options| [model.first_name, model.last_name].join(' ')}
    end
  end
end

Usage in the API Layer


module API
  class Users < Grape::API
    version 'v2'

    desc 'User index', { :object_fields => API::Entities::User.documentation }
    get '/users' do
      @users = User.all
      type = current_user.admin? ? :full : :default
      present @users, :with => API::Entities::User, :type => type
    end
  end
end

Defined Under Namespace

Modules: DSL

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(object, options = {}) ⇒ Entity

Returns a new instance of Entity.



305
306
307
# File 'lib/grape_entity/entity.rb', line 305

def initialize(object, options = {})
  @object, @options = object, options
end

Instance Attribute Details

#objectObject (readonly)

Returns the value of attribute object.



44
45
46
# File 'lib/grape_entity/entity.rb', line 44

def object
  @object
end

#optionsObject (readonly)

Returns the value of attribute options.



44
45
46
# File 'lib/grape_entity/entity.rb', line 44

def options
  @options
end

Class Method Details

.documentationObject

Returns a hash, the keys are symbolized references to fields in the entity, the values are document keys in the entity's documentation key. When calling

docmentation, any exposure without a documentation key will be ignored.



174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/grape_entity/entity.rb', line 174

def self.documentation
  @documentation ||= exposures.inject({}) do |memo, value|
                       unless value[1][:documentation].nil? || value[1][:documentation].empty?
                         memo[value[0]] = value[1][:documentation]
                       end
                       memo
                     end

  if superclass.respond_to? :documentation
    @documentation = superclass.documentation.merge(@documentation)
  end

  @documentation
end

.expose(*args, &block) ⇒ Object

This method is the primary means by which you will declare what attributes should be exposed by the entity.

Parameters:

  • options (Hash)

    a customizable set of options

Raises:

  • (ArgumentError)


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/grape_entity/entity.rb', line 125

def self.expose(*args, &block)
  options = args.last.is_a?(Hash) ? args.pop : {}
  options = (@block_options ||= []).inject({}){|final, step| final.merge!(step)}.merge(options)

  if args.size > 1
    raise ArgumentError, "You may not use the :as option on multi-attribute exposures." if options[:as]
    raise ArgumentError, "You may not use block-setting on multi-attribute exposures." if block_given?
  end

  raise ArgumentError, "You may not use block-setting when also using format_with" if block_given? && options[:format_with].respond_to?(:call)

  options[:proc] = block if block_given?

  args.each do |attribute|
    exposures[attribute.to_sym] = options
  end
end

.exposuresObject

Returns a hash of exposures that have been declared for this Entity or ancestors. The keys are symbolized references to methods on the containing object, the values are the options that were passed into expose.



161
162
163
164
165
166
167
168
169
# File 'lib/grape_entity/entity.rb', line 161

def self.exposures
  @exposures ||= {}

  if superclass.respond_to? :exposures
    @exposures = superclass.exposures.merge(@exposures)
  end

  @exposures
end

.format_with(name, &block) ⇒ Object

This allows you to declare a Proc in which exposures can be formatted with. It take a block with an arity of 1 which is passed as the value of the exposed attribute.

Examples:

Formatter declaration


module API
  module Entities
    class User < Grape::Entity
      format_with :timestamp do |date|
        date.strftime('%m/%d/%Y')
      end

      expose :birthday, :last_signed_in, :format_with => :timestamp
    end
  end
end

Formatters are available to all decendants


Grape::Entity.format_with :timestamp do |date|
  date.strftime('%m/%d/%Y')
end

Parameters:

  • name (Symbol)

    the name of the formatter

  • block (Proc)

    the block that will interpret the exposed attribute

Raises:

  • (ArgumentError)


217
218
219
220
# File 'lib/grape_entity/entity.rb', line 217

def self.format_with(name, &block)
  raise ArgumentError, "You must pass a block for formatters" unless block_given?
  formatters[name.to_sym] = block
end

.formattersObject

Returns a hash of all formatters that are registered for this and it's ancestors.



223
224
225
226
227
228
229
230
231
# File 'lib/grape_entity/entity.rb', line 223

def self.formatters
  @formatters ||= {}

  if superclass.respond_to? :formatters
    @formatters = superclass.formatters.merge(@formatters)
  end

  @formatters
end

.represent(objects, options = {}) ⇒ Object

This convenience method allows you to instantiate one or more entities by passing either a singular or collection of objects. Each object will be initialized with the same options. If an array of objects is passed in, an array of entities will be returned. If a single object is passed in, a single entity will be returned.

  entity. Pass nil or false to represent the object or objects with no root name even if one is defined for the entity.

Parameters:

  • objects (Object or Array)

    One or more objects to be represented.

  • options (Hash) (defaults to: {})

    Options that will be passed through to each entity representation.

Options Hash (options):

  • :root (String)

    override the default root name set for the



290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/grape_entity/entity.rb', line 290

def self.represent(objects, options = {})
  inner = if objects.respond_to?(:to_ary)
    objects.to_ary().map{|o| self.new(o, {:collection => true}.merge(options))}
  else
    self.new(objects, options)
  end

  root_element = if options.has_key?(:root)
    options[:root]
  else
    objects.respond_to?(:to_ary) ? @collection_root : @root
  end
  root_element ? { root_element => inner } : inner
end

.root(plural, singular = nil) ⇒ Object

This allows you to set a root element name for your representation.

Examples:

Entity Definition


module API
  module Entities
    class User < Grape::Entity
      root 'users', 'user'
      expose :id
    end
  end
end

Usage in the API Layer


module API
  class Users < Grape::API
    version 'v2'

    # this will render { "users": [ {"id":"1"}, {"id":"2"} ] }
    get '/users' do
      @users = User.all
      present @users, :with => API::Entities::User
    end

    # this will render { "user": {"id":"1"} }
    get '/users/:id' do
      @user = User.find(params[:id])
      present @user, :with => API::Entities::User
    end
  end
end

Parameters:

  • plural (String)

    the root key to use when representing a collection of objects. If missing or nil, no root key will be used when representing collections of objects.

  • singular (String) (defaults to: nil)

    the root key to use when representing a single object. If missing or nil, no root key will be used when representing an individual object.



272
273
274
275
# File 'lib/grape_entity/entity.rb', line 272

def self.root(plural, singular=nil)
  @collection_root = plural
  @root = singular
end

.with_options(options) ⇒ Object

Set options that will be applied to any exposures declared inside the block.

Examples:

Multi-exposure if


class MyEntity < Grape::Entity
  with_options :if => {:awesome => true} do
    expose :awesome, :sweet
  end
end


152
153
154
155
156
# File 'lib/grape_entity/entity.rb', line 152

def self.with_options(options)
  (@block_options ||= []).push(options)
  yield
  @block_options.pop
end

Instance Method Details

#documentationObject



313
314
315
# File 'lib/grape_entity/entity.rb', line 313

def documentation
  self.class.documentation
end

#exposuresObject



309
310
311
# File 'lib/grape_entity/entity.rb', line 309

def exposures
  self.class.exposures
end

#formattersObject



317
318
319
# File 'lib/grape_entity/entity.rb', line 317

def formatters
  self.class.formatters
end

#serializable_hash(runtime_options = {}) ⇒ Object Also known as: as_json

The serializable hash is the Entity's primary output. It is the transformed hash for the given data model and is used as the basis for serialization to JSON and other formats.

Parameters:

  • runtime_options (Hash) (defaults to: {})

    Any options you pass in here will be known to the entity representation, this is where you can trigger things from conditional options etc.



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/grape_entity/entity.rb', line 328

def serializable_hash(runtime_options = {})
  return nil if object.nil?
  opts = options.merge(runtime_options || {})
  exposures.inject({}) do |output, (attribute, exposure_options)|
    if (exposure_options.has_key?(:proc) || object.respond_to?(attribute)) && conditions_met?(exposure_options, opts)
      partial_output = value_for(attribute, opts)
      output[key_for(attribute)] =
        if partial_output.respond_to? :serializable_hash
          partial_output.serializable_hash(runtime_options)
        elsif partial_output.kind_of?(Array) && !partial_output.map {|o| o.respond_to? :serializable_hash}.include?(false)
          partial_output.map {|o| o.serializable_hash}
        else
          partial_output
        end
    end
    output
  end
end

#to_json(options = {}) ⇒ Object



349
350
351
352
# File 'lib/grape_entity/entity.rb', line 349

def to_json(options = {})
  options = options.to_h if options && options.respond_to?(:to_h)
  MultiJson.dump(serializable_hash(options))
end

#to_xml(options = {}) ⇒ Object



354
355
356
357
# File 'lib/grape_entity/entity.rb', line 354

def to_xml(options = {})
  options = options.to_h if options && options.respond_to?(:to_h)
  serializable_hash(options).to_xml(options)
end