Class: Grape::Entity
- Inherits:
-
Object
- Object
- Grape::Entity
- Defined in:
- lib/grape_entity/entity.rb,
lib/grape_entity/delegator.rb,
lib/grape_entity/delegator/base.rb,
lib/grape_entity/delegator/hash_object.rb,
lib/grape_entity/delegator/plain_object.rb,
lib/grape_entity/delegator/fetchable_object.rb,
lib/grape_entity/delegator/openstruct_object.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:
Defined Under Namespace
Class Attribute Summary collapse
-
.exposures ⇒ Hash
Returns exposures that have been declared for this Entity or ancestors.
-
.formatters ⇒ Hash
Returns all formatters that are registered for this and it's ancestors.
-
.nested_attribute_names ⇒ Object
Returns the value of attribute nested_attribute_names.
-
.nested_exposures ⇒ Object
Returns the value of attribute nested_exposures.
-
.root_exposures ⇒ Object
Returns the value of attribute root_exposures.
Instance Attribute Summary collapse
-
#delegator ⇒ Object
readonly
Returns the value of attribute delegator.
-
#object ⇒ Object
readonly
Returns the value of attribute object.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Class Method Summary collapse
-
.documentation ⇒ Object
Returns a hash, the keys are symbolized references to fields in the entity, the values are document keys in the entity's documentation key.
-
.expose(*args, &block) ⇒ Object
This method is the primary means by which you will declare what attributes should be exposed by the entity.
-
.format_with(name, &block) ⇒ Object
This allows you to declare a Proc in which exposures can be formatted with.
- .inherited(subclass) ⇒ Object
-
.present_collection(present_collection = false, collection_name = :items) ⇒ Object
This allows you to present a collection of objects.
-
.represent(objects, options = {}) ⇒ Object
This convenience method allows you to instantiate one or more entities by passing either a singular or collection of objects.
-
.root(plural, singular = nil) ⇒ Object
This allows you to set a root element name for your representation.
-
.root_element(root_type) ⇒ Object
This method returns the entity's root or collection root node, or its parent's.
- .unexpose(attribute) ⇒ Object
-
.with_options(options) ⇒ Object
Set options that will be applied to any exposures declared inside the block.
Instance Method Summary collapse
- #documentation ⇒ Object
- #except_fields(options, for_attribute = nil) ⇒ Object
- #exposures ⇒ Object
- #formatters ⇒ Object
-
#initialize(object, options = {}) ⇒ Entity
constructor
A new instance of Entity.
- #only_fields(options, for_attribute = nil) ⇒ Object
- #presented ⇒ Object
- #root_exposures ⇒ Object
-
#serializable_hash(runtime_options = {}) ⇒ Object
(also: #as_json)
The serializable hash is the Entity's primary output.
- #should_return_attribute?(attribute, options) ⇒ Boolean
- #to_json(options = {}) ⇒ Object
- #to_xml(options = {}) ⇒ Object
Constructor Details
Class Attribute Details
.exposures ⇒ Hash
Returns 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.
106 107 108 |
# File 'lib/grape_entity/entity.rb', line 106 def exposures @exposures end |
.formatters ⇒ Hash
Returns all formatters that are registered for this and it's ancestors
110 111 112 |
# File 'lib/grape_entity/entity.rb', line 110 def formatters @formatters end |
.nested_attribute_names ⇒ Object
Returns the value of attribute nested_attribute_names.
111 112 113 |
# File 'lib/grape_entity/entity.rb', line 111 def nested_attribute_names @nested_attribute_names end |
.nested_exposures ⇒ Object
Returns the value of attribute nested_exposures.
112 113 114 |
# File 'lib/grape_entity/entity.rb', line 112 def nested_exposures @nested_exposures end |
.root_exposures ⇒ Object
Returns the value of attribute root_exposures.
107 108 109 |
# File 'lib/grape_entity/entity.rb', line 107 def root_exposures @root_exposures end |
Instance Attribute Details
#delegator ⇒ Object (readonly)
Returns the value of attribute delegator.
45 46 47 |
# File 'lib/grape_entity/entity.rb', line 45 def delegator @delegator end |
#object ⇒ Object (readonly)
Returns the value of attribute object.
45 46 47 |
# File 'lib/grape_entity/entity.rb', line 45 def object @object end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
45 46 47 |
# File 'lib/grape_entity/entity.rb', line 45 def @options end |
Class Method Details
.documentation ⇒ Object
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.
207 208 209 210 211 212 213 |
# File 'lib/grape_entity/entity.rb', line 207 def self.documentation @documentation ||= exposures.each_with_object({}) do |(attribute, ), memo| if [:documentation].present? memo[key_for(attribute)] = [:documentation] end end end |
.expose(*args, &block) ⇒ Object
This method is the primary means by which you will declare what attributes should be exposed by the entity.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/grape_entity/entity.rb', line 148 def self.expose(*args, &block) = (args.last.is_a?(Hash) ? args.pop : {}) if args.size > 1 fail ArgumentError, 'You may not use the :as option on multi-attribute exposures.' if [:as] fail ArgumentError, 'You may not use block-setting on multi-attribute exposures.' if block_given? end fail ArgumentError, 'You may not use block-setting when also using format_with' if block_given? && [:format_with].respond_to?(:call) [:proc] = block if block_given? && block.parameters.any? @nested_attributes ||= [] # rubocop:disable Style/Next args.each do |attribute| if @nested_attributes.empty? root_exposures[attribute] = else orig_attribute = attribute.to_sym attribute = "#{@nested_attributes.last}__#{attribute}".to_sym nested_attribute_names[attribute] = orig_attribute [:nested] = true nested_exposures.deep_merge!(@nested_attributes.last.to_sym => { attribute => }) end exposures[attribute] = # Nested exposures are given in a block with no parameters. if block_given? && block.parameters.empty? @nested_attributes << attribute block.call @nested_attributes.pop end end 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.
241 242 243 244 |
# File 'lib/grape_entity/entity.rb', line 241 def self.format_with(name, &block) fail ArgumentError, 'You must pass a block for formatters' unless block_given? formatters[name.to_sym] = block end |
.inherited(subclass) ⇒ Object
115 116 117 118 119 120 121 |
# File 'lib/grape_entity/entity.rb', line 115 def self.inherited(subclass) subclass.exposures = exposures.try(:dup) || {} subclass.root_exposures = root_exposures.try(:dup) || {} subclass.nested_exposures = nested_exposures.try(:dup) || {} subclass.nested_attribute_names = nested_attribute_names.try(:dup) || {} subclass.formatters = formatters.try(:dup) || {} end |
.present_collection(present_collection = false, collection_name = :items) ⇒ Object
This allows you to present a collection of objects.
When false (default) every object in a collection to present will be wrapped separately into an instance of your presenter.
341 342 343 344 |
# File 'lib/grape_entity/entity.rb', line 341 def self.present_collection(present_collection = false, collection_name = :items) @present_collection = present_collection @collection_name = collection_name 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.
363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/grape_entity/entity.rb', line 363 def self.represent(objects, = {}) if objects.respond_to?(:to_ary) && ! @present_collection root_element = root_element(:collection_root) inner = objects.to_ary.map { |object| new(object, { collection: true }.merge()).presented } else objects = { @collection_name => objects } if @present_collection root_element = root_element(:root) inner = new(objects, ).presented end root_element = [:root] if .key?(:root) root_element ? { root_element => inner } : inner end |
.root(plural, singular = nil) ⇒ Object
This allows you to set a root element name for your representation.
285 286 287 288 |
# File 'lib/grape_entity/entity.rb', line 285 def self.root(plural, singular = nil) @collection_root = plural @root = singular end |
.root_element(root_type) ⇒ Object
This method returns the entity's root or collection root node, or its parent's
380 381 382 383 384 385 386 |
# File 'lib/grape_entity/entity.rb', line 380 def self.root_element(root_type) if instance_variable_get("@#{root_type}") instance_variable_get("@#{root_type}") elsif superclass.respond_to? :root_element superclass.root_element(root_type) end end |
.unexpose(attribute) ⇒ Object
185 186 187 |
# File 'lib/grape_entity/entity.rb', line 185 def self.unexpose(attribute) exposures.delete(attribute) end |
.with_options(options) ⇒ Object
Set options that will be applied to any exposures declared inside the block.
198 199 200 201 202 |
# File 'lib/grape_entity/entity.rb', line 198 def self.() (@block_options ||= []).push(()) yield @block_options.pop end |
Instance Method Details
#documentation ⇒ Object
410 411 412 |
# File 'lib/grape_entity/entity.rb', line 410 def documentation self.class.documentation end |
#except_fields(options, for_attribute = nil) ⇒ Object
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 |
# File 'lib/grape_entity/entity.rb', line 480 def except_fields(, for_attribute = nil) return nil unless [:except] @except_fields ||= [:except].each_with_object({}) do |attribute, allowed_fields| if attribute.is_a?(Hash) attribute.each do |attr, nested_attrs| allowed_fields[attr] ||= [] allowed_fields[attr] += nested_attrs end else allowed_fields[attribute] = true end end.symbolize_keys if for_attribute && @except_fields[for_attribute].is_a?(Array) @except_fields[for_attribute] elsif for_attribute.nil? @except_fields end end |
#exposures ⇒ Object
402 403 404 |
# File 'lib/grape_entity/entity.rb', line 402 def exposures self.class.exposures end |
#formatters ⇒ Object
414 415 416 |
# File 'lib/grape_entity/entity.rb', line 414 def formatters self.class.formatters end |
#only_fields(options, for_attribute = nil) ⇒ Object
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 |
# File 'lib/grape_entity/entity.rb', line 459 def only_fields(, for_attribute = nil) return nil unless [:only] @only_fields ||= [:only].each_with_object({}) do |attribute, allowed_fields| if attribute.is_a?(Hash) attribute.each do |attr, nested_attrs| allowed_fields[attr] ||= [] allowed_fields[attr] += nested_attrs end else allowed_fields[attribute] = true end end.symbolize_keys if for_attribute && @only_fields[for_attribute].is_a?(Array) @only_fields[for_attribute] elsif for_attribute.nil? @only_fields end end |
#presented ⇒ Object
388 389 390 391 392 393 394 |
# File 'lib/grape_entity/entity.rb', line 388 def presented if [:serializable] serializable_hash else self end end |
#root_exposures ⇒ Object
406 407 408 |
# File 'lib/grape_entity/entity.rb', line 406 def root_exposures self.class.root_exposures 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.
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/grape_entity/entity.rb', line 425 def serializable_hash( = {}) return nil if object.nil? opts = .merge( || {}) root_exposures.each_with_object({}) do |(attribute, ), output| next unless should_return_attribute?(attribute, opts) && conditions_met?(, opts) partial_output = value_for(attribute, opts) output[self.class.key_for(attribute)] = if partial_output.respond_to?(:serializable_hash) partial_output.serializable_hash() elsif partial_output.is_a?(Array) && partial_output.all? { |o| o.respond_to?(:serializable_hash) } partial_output.map(&:serializable_hash) elsif partial_output.is_a?(Hash) partial_output.each do |key, value| partial_output[key] = value.serializable_hash if value.respond_to?(:serializable_hash) end else partial_output end end end |
#should_return_attribute?(attribute, options) ⇒ Boolean
450 451 452 453 454 455 456 457 |
# File 'lib/grape_entity/entity.rb', line 450 def should_return_attribute?(attribute, ) key = self.class.key_for(attribute) only = only_fields().nil? || only_fields().include?(key) except = except_fields() && except_fields().include?(key) && except_fields()[key] == true only && !except end |
#to_json(options = {}) ⇒ Object
503 504 505 506 |
# File 'lib/grape_entity/entity.rb', line 503 def to_json( = {}) = .to_h if && .respond_to?(:to_h) MultiJson.dump(serializable_hash()) end |
#to_xml(options = {}) ⇒ Object
508 509 510 511 |
# File 'lib/grape_entity/entity.rb', line 508 def to_xml( = {}) = .to_h if && .respond_to?(:to_h) serializable_hash().to_xml() end |