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
#initialize(object, options = {}) ⇒ Entity
405 406 407 408 409 |
# File 'lib/grape_entity/entity.rb', line 405 def initialize(object, = {}) @object = object @delegator = Delegator.new object = end |
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 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.
216 217 218 219 220 221 222 |
# File 'lib/grape_entity/entity.rb', line 216 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.
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 184 185 186 187 188 189 |
# File 'lib/grape_entity/entity.rb', line 154 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.
250 251 252 253 |
# File 'lib/grape_entity/entity.rb', line 250 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
121 122 123 124 125 126 127 |
# File 'lib/grape_entity/entity.rb', line 121 def self.inherited(subclass) subclass.exposures = exposures.dup subclass.root_exposures = root_exposures.dup subclass.nested_exposures = nested_exposures.dup subclass.nested_attribute_names = nested_attribute_names.dup subclass.formatters = formatters.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.
350 351 352 353 |
# File 'lib/grape_entity/entity.rb', line 350 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.
372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/grape_entity/entity.rb', line 372 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.
294 295 296 297 |
# File 'lib/grape_entity/entity.rb', line 294 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
389 390 391 392 393 394 395 |
# File 'lib/grape_entity/entity.rb', line 389 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
191 192 193 194 195 196 |
# File 'lib/grape_entity/entity.rb', line 191 def self.unexpose(attribute) root_exposures.delete(attribute) exposures.delete(attribute) nested_exposures.delete(attribute) nested_attribute_names.delete(attribute) end |
.with_options(options) ⇒ Object
Set options that will be applied to any exposures declared inside the block.
207 208 209 210 211 |
# File 'lib/grape_entity/entity.rb', line 207 def self.() ( ||= []).push(()) yield .pop end |
Instance Method Details
#documentation ⇒ Object
419 420 421 |
# File 'lib/grape_entity/entity.rb', line 419 def documentation self.class.documentation end |
#except_fields(options, for_attribute = nil) ⇒ Object
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 |
# File 'lib/grape_entity/entity.rb', line 489 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
411 412 413 |
# File 'lib/grape_entity/entity.rb', line 411 def exposures self.class.exposures end |
#formatters ⇒ Object
423 424 425 |
# File 'lib/grape_entity/entity.rb', line 423 def formatters self.class.formatters end |
#only_fields(options, for_attribute = nil) ⇒ Object
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/grape_entity/entity.rb', line 468 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
397 398 399 400 401 402 403 |
# File 'lib/grape_entity/entity.rb', line 397 def presented if [:serializable] serializable_hash else self end end |
#root_exposures ⇒ Object
415 416 417 |
# File 'lib/grape_entity/entity.rb', line 415 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.
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
# File 'lib/grape_entity/entity.rb', line 434 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
459 460 461 462 463 464 465 466 |
# File 'lib/grape_entity/entity.rb', line 459 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
512 513 514 515 |
# File 'lib/grape_entity/entity.rb', line 512 def to_json( = {}) = .to_h if && .respond_to?(:to_h) MultiJson.dump(serializable_hash()) end |
#to_xml(options = {}) ⇒ Object
517 518 519 520 |
# File 'lib/grape_entity/entity.rb', line 517 def to_xml( = {}) = .to_h if && .respond_to?(:to_h) serializable_hash().to_xml() end |