1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
# frozen_string_literal: true
module Grape
class Entity
module Exposure
class NestingExposure < Base
attr_reader :nested_exposures
def setup(nested_exposures = [])
@nested_exposures = NestedExposures.new(nested_exposures)
end
def dup_args
[*super, @nested_exposures.map(&:dup)]
end
def ==(other)
super && @nested_exposures == other.nested_exposures
end
def nesting?
true
end
def find_nested_exposure(attribute)
nested_exposures.find_by(attribute)
end
def valid?(entity)
nested_exposures.all? { |e| e.valid?(entity) }
end
def value(entity, options)
map_entity_exposures(entity, options) do |exposure, nested_options|
exposure.value(entity, nested_options)
end
end
def serializable_value(entity, options)
map_entity_exposures(entity, options) do |exposure, nested_options|
exposure.serializable_value(entity, nested_options)
end
end
def valid_value_for(key, entity, options)
new_options = nesting_options_for(options)
key_exposures = normalized_exposures(entity, new_options).select { |e| e.key(entity) == key }
key_exposures.map do |exposure|
exposure.with_attr_path(entity, new_options) do
exposure.valid_value(entity, new_options)
end
end.last
end
# if we have any nesting exposures with the same name.
# delegate :deep_complex_nesting?(entity), to: :nested_exposures
def deep_complex_nesting?(entity)
nested_exposures.deep_complex_nesting?(entity)
end
private
def nesting_options_for(options)
if @key
options.for_nesting(@key)
else
options
end
end
def easy_normalized_exposures(entity, options)
nested_exposures.select do |exposure|
exposure.with_attr_path(entity, options) do
exposure.should_expose?(entity, options)
end
end
end
# This method 'merges' subsequent nesting exposures with the same name if it's needed
def normalized_exposures(entity, options)
return easy_normalized_exposures(entity, options) unless deep_complex_nesting?(entity) # optimization
table = nested_exposures.each_with_object({}) do |exposure, output|
should_expose = exposure.with_attr_path(entity, options) do
exposure.should_expose?(entity, options)
end
next unless should_expose
output[exposure.key(entity)] ||= []
output[exposure.key(entity)] << exposure
end
table.map do |key, exposures|
last_exposure = exposures.last
if last_exposure.nesting?
# For the given key if the last candidates for exposing are nesting then combine them.
nesting_tail = []
exposures.reverse_each do |exposure|
nesting_tail.unshift exposure if exposure.nesting?
end
new_nested_exposures = nesting_tail.flat_map(&:nested_exposures)
NestingExposure.new(key, {}, [], new_nested_exposures).tap do |new_exposure|
if nesting_tail.any? { |exposure| exposure.deep_complex_nesting?(entity) }
new_exposure.instance_variable_set(:@deep_complex_nesting, true)
end
end
else
last_exposure
end
end
end
def map_entity_exposures(entity, options)
new_options = nesting_options_for(options)
output = OutputBuilder.new(entity)
normalized_exposures(entity, new_options).each_with_object(output) do |exposure, out|
exposure.with_attr_path(entity, new_options) do
result = yield(exposure, new_options)
out.add(exposure, result)
end
end
end
end
end
end
end
require 'grape_entity/exposure/nesting_exposure/nested_exposures'
require 'grape_entity/exposure/nesting_exposure/output_builder'
|