Class: Grape::Entity
- Inherits:
-
Object
- Object
- Grape::Entity
- Defined in:
- lib/grape_entity/entity.rb,
lib/grape_entity/options.rb,
lib/grape_entity/exposure.rb,
lib/grape_entity/condition.rb,
lib/grape_entity/delegator.rb,
lib/grape_entity/exposure/base.rb,
lib/grape_entity/condition/base.rb,
lib/grape_entity/delegator/base.rb,
lib/grape_entity/delegator/hash_object.rb,
lib/grape_entity/delegator/plain_object.rb,
lib/grape_entity/exposure/block_exposure.rb,
lib/grape_entity/condition/hash_condition.rb,
lib/grape_entity/condition/block_condition.rb,
lib/grape_entity/exposure/nesting_exposure.rb,
lib/grape_entity/condition/symbol_condition.rb,
lib/grape_entity/delegator/fetchable_object.rb,
lib/grape_entity/delegator/openstruct_object.rb,
lib/grape_entity/exposure/delegator_exposure.rb,
lib/grape_entity/exposure/formatter_exposure.rb,
lib/grape_entity/exposure/represent_exposure.rb,
lib/grape_entity/exposure/formatter_block_exposure.rb,
lib/grape_entity/exposure/nesting_exposure/output_builder.rb,
lib/grape_entity/exposure/nesting_exposure/nested_exposures.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
Modules: Condition, DSL, Delegator, Exposure Classes: Options
Constant Summary collapse
- OPTIONS =
All supported options.
[ :rewrite, :as, :if, :unless, :using, :with, :proc, :documentation, :format_with, :safe, :attr_path, :if_extras, :unless_extras, :merge ].to_set.freeze
Class Attribute Summary collapse
-
.formatters ⇒ Hash
Returns all formatters that are registered for this and it's ancestors.
- .root_exposure ⇒ Object
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
- .can_unexpose? ⇒ Boolean
- .cannot_unexpose! ⇒ Object
-
.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.
- .find_exposure(attribute) ⇒ Object
-
.format_with(name, &block) ⇒ Object
This allows you to declare a Proc in which exposures can be formatted with.
- .inherited(subclass) ⇒ Object
-
.merge_options(options) ⇒ Object
Merges the given options with current block options.
-
.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.
-
.root_exposures ⇒ Array
Returns exposures that have been declared for this Entity on the top level.
- .unexpose(*attributes) ⇒ Object
- .unexpose_all ⇒ Object
-
.valid_options(options) ⇒ Object
Raises an error if the given options include unknown keys.
-
.with_options(options) ⇒ Object
Set options that will be applied to any exposures declared inside the block.
Instance Method Summary collapse
- #delegate_attribute(attribute) ⇒ Object
- #documentation ⇒ Object
- #exec_with_attribute(attribute, &block) ⇒ Object
- #exec_with_object(options, &block) ⇒ Object
- #formatters ⇒ Object
-
#initialize(object, options = {}) ⇒ Entity
constructor
A new instance of Entity.
-
#inspect ⇒ Object
Prevent default serialization of :options or :delegator.
- #presented ⇒ Object
- #root_exposure ⇒ Object
- #root_exposures ⇒ Object
-
#serializable_hash(runtime_options = {}) ⇒ Object
(also: #as_json)
The serializable hash is the Entity's primary output.
- #to_json(options = {}) ⇒ Object
- #to_xml(options = {}) ⇒ Object
- #value_for(key, options = Options.new) ⇒ Object
Constructor Details
Class Attribute Details
.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 |
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
.can_unexpose? ⇒ Boolean
212 213 214 |
# File 'lib/grape_entity/entity.rb', line 212 def self.can_unexpose? (@nesting_stack ||= []).empty? end |
.cannot_unexpose! ⇒ Object
216 217 218 |
# File 'lib/grape_entity/entity.rb', line 216 def self.cannot_unexpose! raise "You cannot call 'unexpose` inside of nesting exposure!" end |
.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.
238 239 240 241 242 243 244 |
# File 'lib/grape_entity/entity.rb', line 238 def self.documentation @documentation ||= root_exposures.each_with_object({}) do |exposure, memo| if exposure.documentation && !exposure.documentation.empty? memo[exposure.key] = exposure.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.
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 184 185 186 187 188 |
# File 'lib/grape_entity/entity.rb', line 150 def self.expose(*args, &block) = (args.last.is_a?(Hash) ? args.pop : {}) if args.size > 1 raise ArgumentError, 'You may not use the :as option on multi-attribute exposures.' if [: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? && [:format_with].respond_to?(:call) if block_given? if block.parameters.any? [:proc] = block else [:nesting] = true end end @documentation = nil @nesting_stack ||= [] # rubocop:disable Style/Next args.each do |attribute| exposure = Exposure.new(attribute, ) if @nesting_stack.empty? root_exposures << exposure else @nesting_stack.last.nested_exposures << exposure end # Nested exposures are given in a block with no parameters. if exposure.nesting? @nesting_stack << exposure block.call @nesting_stack.pop end end end |
.find_exposure(attribute) ⇒ Object
196 197 198 |
# File 'lib/grape_entity/entity.rb', line 196 def self.find_exposure(attribute) root_exposures.find_by(attribute) 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.
272 273 274 275 |
# File 'lib/grape_entity/entity.rb', line 272 def self.format_with(name, &block) raise ArgumentError, 'You must pass a block for formatters' unless block_given? formatters[name.to_sym] = block end |
.inherited(subclass) ⇒ Object
119 120 121 122 |
# File 'lib/grape_entity/entity.rb', line 119 def self.inherited(subclass) subclass.root_exposure = root_exposure.dup subclass.formatters = formatters.dup end |
.merge_options(options) ⇒ Object
Merges the given options with current block options.
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
# File 'lib/grape_entity/entity.rb', line 514 def self.() opts = {} merge_logic = proc do |key, existing_val, new_val| if [:if, :unless].include?(key) if existing_val.is_a?(Hash) && new_val.is_a?(Hash) existing_val.merge(new_val) elsif new_val.is_a?(Hash) (opts["#{key}_extras".to_sym] ||= []) << existing_val new_val else (opts["#{key}_extras".to_sym] ||= []) << new_val existing_val end else new_val end end @block_options ||= [] opts.merge @block_options.inject({}) { |final, step| final.merge(step, &merge_logic) }.merge((), &merge_logic) 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.
372 373 374 375 |
# File 'lib/grape_entity/entity.rb', line 372 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.
394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'lib/grape_entity/entity.rb', line 394 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, .reverse_merge(collection: true)).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.
316 317 318 319 |
# File 'lib/grape_entity/entity.rb', line 316 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
411 412 413 414 415 416 417 |
# File 'lib/grape_entity/entity.rb', line 411 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 |
.root_exposures ⇒ Array
Returns exposures that have been declared for this Entity on the top level.
192 193 194 |
# File 'lib/grape_entity/entity.rb', line 192 def self.root_exposures root_exposure.nested_exposures end |
.unexpose(*attributes) ⇒ Object
200 201 202 203 204 |
# File 'lib/grape_entity/entity.rb', line 200 def self.unexpose(*attributes) cannot_unexpose! unless can_unexpose? @documentation = nil root_exposures.delete_by(*attributes) end |
.unexpose_all ⇒ Object
206 207 208 209 210 |
# File 'lib/grape_entity/entity.rb', line 206 def self.unexpose_all cannot_unexpose! unless can_unexpose? @documentation = nil root_exposures.clear end |
.valid_options(options) ⇒ Object
Raises an error if the given options include unknown keys. Renames aliased options.
543 544 545 546 547 548 549 550 |
# File 'lib/grape_entity/entity.rb', line 543 def self.() .keys.each do |key| raise ArgumentError, "#{key.inspect} is not a valid option." unless OPTIONS.include?(key) end [:using] = .delete(:with) if .key?(:with) end |
.with_options(options) ⇒ Object
Set options that will be applied to any exposures declared inside the block.
229 230 231 232 233 |
# File 'lib/grape_entity/entity.rb', line 229 def self.() (@block_options ||= []).push(()) yield @block_options.pop end |
Instance Method Details
#delegate_attribute(attribute) ⇒ Object
486 487 488 489 490 491 492 |
# File 'lib/grape_entity/entity.rb', line 486 def delegate_attribute(attribute) if respond_to?(attribute, true) && Grape::Entity > method(attribute).owner send(attribute) else delegator.delegate(attribute) end end |
#documentation ⇒ Object
451 452 453 |
# File 'lib/grape_entity/entity.rb', line 451 def documentation self.class.documentation end |
#exec_with_attribute(attribute, &block) ⇒ Object
478 479 480 |
# File 'lib/grape_entity/entity.rb', line 478 def exec_with_attribute(attribute, &block) instance_exec(delegate_attribute(attribute), &block) end |
#exec_with_object(options, &block) ⇒ Object
474 475 476 |
# File 'lib/grape_entity/entity.rb', line 474 def exec_with_object(, &block) instance_exec(object, , &block) end |
#formatters ⇒ Object
455 456 457 |
# File 'lib/grape_entity/entity.rb', line 455 def formatters self.class.formatters end |
#inspect ⇒ Object
Prevent default serialization of :options or :delegator.
428 429 430 431 |
# File 'lib/grape_entity/entity.rb', line 428 def inspect fields = serializable_hash.map { |k, v| "#{k}=#{v}" } "#<#{self.class.name}:#{object_id} #{fields.join(' ')}>" end |
#presented ⇒ Object
419 420 421 422 423 424 425 |
# File 'lib/grape_entity/entity.rb', line 419 def presented if [:serializable] serializable_hash else self end end |
#root_exposure ⇒ Object
447 448 449 |
# File 'lib/grape_entity/entity.rb', line 447 def root_exposure self.class.root_exposure end |
#root_exposures ⇒ Object
443 444 445 |
# File 'lib/grape_entity/entity.rb', line 443 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.
466 467 468 469 470 471 472 |
# File 'lib/grape_entity/entity.rb', line 466 def serializable_hash( = {}) return nil if object.nil? opts = .merge( || {}) root_exposure.serializable_value(self, opts) end |
#to_json(options = {}) ⇒ Object
496 497 498 499 |
# File 'lib/grape_entity/entity.rb', line 496 def to_json( = {}) = .to_h if && .respond_to?(:to_h) MultiJson.dump(serializable_hash()) end |
#to_xml(options = {}) ⇒ Object
501 502 503 504 |
# File 'lib/grape_entity/entity.rb', line 501 def to_xml( = {}) = .to_h if && .respond_to?(:to_h) serializable_hash().to_xml() end |