I'm building an API using ActiveModel::Serializers. What is the best way to sideload data conditionally using params?
So I can make requests like GET /api/customers
:
"customers": {
"first_name": "Bill",
"last_name": "Gates"
}
And GET /api/customers?embed=address,note
"customers": {
"first_name": "Bill",
"last_name": "Gates"
},
"address: {
"street": "abc"
},
"note": {
"body": "Banned"
}
Something like that depending on the params. I know ActiveModel::Serializers has the include_[ASSOCIATION]?
syntax but how can I use it efficiently from my controllers?
This is my current solution, but it's not neat:
customer_serializer.rb:
def include_address?
!options[:embed].nil? && options[:embed].include?(:address)
end
application_controller.rb:
def embed_resources(resources = [])
params[:embed].split(',').map { |x| resources << x.to_sym } if params[:embed]
resources
end
customers_controller.rb:
def show
respond_with @customer, embed: embed_resources
end
Must be an easier way?
I'm also looking for an effective and clean way to do this.
I found a solution but it's not pretty.
In my BaseController/ApplicationController I added this method:
serialization_scope :params
So the scope is now the params Hash and I can use it in the include_[ASSOCIATION]?
methods of my serializers.
def include_associations?
if scope[:embed]
embed = scope[:embed].split(',')
return true if embed.include?('associations')
end
end
I don't like this method because if I need to use the scope for something else like the current_user
to conditionally return data if it's an admin for instance.
But this solution can work in some cases.
UPDATE
You can pass view_context
instead of directly pass the params
.
And you can delegate in your Serializer to keep the params
name instead of scope
.
in your ApplicationController:
serialization_scope :view_context
in your serializer:
delegate :params, to: :scope
And voila you can use params[:embed] in the include_[ASSOCIATION]?
methods of your serializers.
I have yet another solution based off of your answers because I wanted similar functionality. According to the documentation if one wants lower level control of association serialization, they can override include_associations!
.
For example:
def include_associations!
if scope[:embed]
include! :addresses, {embed: :ids, include: true}
else
include! :addresses, {embed: :ids}
end
end
Very helpful to know about include_associations!, thanks! Noting that with the active_model_serializers gem (version 0.8.3) you can use @options
to set context in the controller. For example, if in the controller you call
render json: customer, include_addresses: true
then in CustomerSerializer:
has_many :addresses
def include_associations!
if @options[:include_addresses]
include! :addresses
end
end
then the addresses will be serialized. If you render with include_addresses
set to false
, they won't be.
With newer versions of active_model_serializers, use serialization_options
rather than @options
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With