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
134
135
136
137
138
|
# File 'lib/ar_serializer/serializer.rb', line 57
def self._serialize(mixed_value_outputs, attributes, context, include_id, only = nil, except = nil)
mixed_value_outputs.group_by { |v, _o| v.class }.each do |klass, value_outputs|
next unless klass.respond_to? :_serializer_field_info
models = value_outputs.map(&:first)
value_outputs.each { |value, output| output[:id] = value.id } if include_id && klass.method_defined?(:id)
if attributes[:*]
all_keys = klass._serializer_field_keys.map(&:to_sym)
all_keys &= only.map(&:to_sym) if only
all_keys -= except.map(&:to_sym) if except
attributes = all_keys.map { |k| [k, {}] }.to_h.merge attributes
attributes.delete :*
end
attributes.each_key do |name|
field = klass._serializer_field_info name
raise ArSerializer::InvalidQuery, "No serializer field `#{name}`#{" namespaces: #{current_namespaces}" if current_namespaces} for #{klass}" unless field
ActiveRecord::Associations::Preloader.new.preload models, field.includes if field.includes.present?
end
preloader_params = attributes.flat_map do |name, sub_args|
klass._serializer_field_info(name).preloaders.map do |p|
[p, sub_args[:params]]
end
end
defaults = klass._serializer_field_info(:defaults)
if defaults
preloader_params += defaults.preloaders.map { |p| [p] }
end
preloader_values = preloader_params.compact.uniq.map do |key|
preloader, params = key
if preloader.arity < 0
[key, preloader.call(models, context, params || {})]
else
[key, preloader.call(*[models, context, params || {}].take(preloader.arity))]
end
end.to_h
if defaults
preloadeds = defaults.preloaders.map { |p| preloader_values[[p]] } || []
value_outputs.each do |value, output|
data = value.instance_exec(*preloadeds, context, {}, &defaults.data_block)
output.update data
end
end
attributes.each do |name, sub_arg|
params = sub_arg[:params]
sub_calls = []
column_name = sub_arg[:column_name] || name
info = klass._serializer_field_info name
preloadeds = info.preloaders.map { |p| preloader_values[[p, params]] } || []
data_block = info.data_block
value_outputs.each do |value, output|
child = value.instance_exec(*preloadeds, context, params || {}, &data_block)
if child.is_a?(Array) && child.all? { |el| el.is_a? ArSerializer::Serializable }
output[column_name] = child.map do |record|
data = {}
sub_calls << [record, data]
data
end
elsif child.respond_to? :ar_serializer_build_sub_calls
sub_output, record_elements = child.ar_serializer_build_sub_calls
record_elements.each { |o| sub_calls << o }
output[column_name] = sub_output
elsif child.is_a? ArSerializer::CompositeValue
sub_output, record_elements = child.build
record_elements.each { |o| sub_calls << o }
output[column_name] = sub_output
elsif child.is_a? ArSerializer::Serializable
data = {}
sub_calls << [child, data]
output[column_name] = data
else
output[column_name] = child
end
end
next if sub_calls.empty?
sub_attributes = sub_arg[:attributes] || {}
info.validate_attributes sub_attributes
_serialize sub_calls, sub_attributes, context, include_id, info.only, info.except
end
end
end
|