Suppose we have to use ArticleSerializer to serve article data on multiple endpoints.
Let's assume these attributes to the serializer:
attributes :title, :slug, :description, :is_popular, :content, :collaborators, :tags
and some more...
Since we don't use all of those attributes in all actions, eg. in #index, we will not query for tags or serve the content unnecessarily.
similarly, is_popular will be something that is calculated in a query and we would want that to work that endpoint only.
So, to resolve this currently, we majorly have 2 solutions.
1. Inherited serializers for specific attributes.
BasicArticleSerializer -> attributes :title, :slug, :description
ArticleContentSerializer < BasicArticleSerializer -> attributes :content, :collaborators, :tags
PopularArticlesSerializer < BasicArticleSerializer -> attribute :is_popular
2. Use if conditions
ArticleSerializer
attributes :title, :slug, :description
attribute :popular, if: proc { |_, opt| opt[:with_popular?].present? } do |obj|
...
end
attribute :content, if: proc { |_, opt| opt[:with_content?].present? } do |obj|
...
end
attribute :collaborators, if: proc { |_, opt| opt[:with_collaborators?].present? } do |obj|
...
end
attribute :tags, if: proc { |_, opt| opt[:with_tags?].present? } do |obj|
...
end
And now, send these with_? tags from the controller in params hash cherry-picking the attributes.
Personally, I feel this approach is better than the previous ones because we have not categorized the serializers and so, one attribute can co-exist with another independently. also, no need to create many serializer classes which are hard to maintain since there can be any combination of attributes that are needed.
Problem
The second approach is actually good but I think if we add native support for optional attributes in the serializer, the code will be much cleaner and others can also follow the same.
Proposed solution
CURRENT:
ArticlesSerializer.new(articles, { params: { with_tags?: true, with_popular?: true } })
attribute :tags, if: proc { |_, opt| opt[:with_tags?].present? } do |obj|
...
end
SOLUTION (BETTER WAY):
ArticlesSerializer.new(articles, optional: %i[tags popular])
attribute :tags, optional: true do |obj|
...
end
attribute :content, optional: true do |obj|
...
end
What do you guys think about this?
Suppose we have to use
ArticleSerializerto serve article data on multiple endpoints.Let's assume these attributes to the serializer:
attributes :title, :slug, :description, :is_popular, :content, :collaborators, :tagsand some more...
Since we don't use all of those attributes in all actions, eg. in #index, we will not query for tags or serve the content unnecessarily.
similarly, is_popular will be something that is calculated in a query and we would want that to work that endpoint only.
So, to resolve this currently, we majorly have 2 solutions.
1. Inherited serializers for specific attributes.
2. Use if conditions
ArticleSerializer
And now, send these
with_?tags from the controller in params hash cherry-picking the attributes.Personally, I feel this approach is better than the previous ones because we have not categorized the serializers and so, one attribute can co-exist with another independently. also, no need to create many serializer classes which are hard to maintain since there can be any combination of attributes that are needed.
Problem
The second approach is actually good but I think if we add native support for optional attributes in the serializer, the code will be much cleaner and others can also follow the same.
Proposed solution
CURRENT:
ArticlesSerializer.new(articles, { params: { with_tags?: true, with_popular?: true } })SOLUTION (BETTER WAY):
ArticlesSerializer.new(articles, optional: %i[tags popular])What do you guys think about this?