Selecting Fields

All attributes are serialized by default, except attributes defined with hide: true. Three options change that:

class UserSerializer < Serega
  attribute :username
  attribute :first_name
  attribute :email, hide: true
  attribute :friends, serializer: UserSerializer, hide: true
end

UserSerializer.to_h(bruce)
# => {username: "batman", first_name: "Bruce"}

UserSerializer.to_h(bruce, only: :username)
# => {username: "batman"}

UserSerializer.to_h(bruce, except: :first_name)
# => {username: "batman"}

UserSerializer.to_h(bruce, with: :email)
# => {username: "batman", first_name: "Bruce", email: "bruce@wayne.com"}

Nested fields

Select fields of relations with a Hash. Fields are Symbols, Strings, Arrays, Hashes or their combinations.

UserSerializer.to_h(bruce, only: [:username, {friends: :email}])
# => {username: "batman", friends: [{email: "alfred@wayne.com"}]}

UserSerializer.to_h(bruce, with: {friends: :email})
# => {
#   username: "batman", first_name: "Bruce",
#   friends: [{username: "alfred", first_name: "Alfred", email: "alfred@wayne.com"}]
# }

UserSerializer.to_h(bruce, with: :friends, except: {friends: :first_name})
# => {username: "batman", first_name: "Bruce", friends: [{username: "alfred"}]}

A relation listed in :only without nested fields serializes its default fields.

Combining options

The options combine at each level: the root serializer and every relation read their own part of :only, :except and :with.

When options name the same field, :except wins. :only and :with both show the fields they name, hidden fields too:

UserSerializer.to_h(bruce, only: [:username, :email], except: :email)
# => {username: "batman"}

UserSerializer.to_h(bruce, only: :username, with: :email)
# => {username: "batman", email: "bruce@wayne.com"}

:only hides the other fields of a level when it names a field of that level without nested fields, or when :except and :with name no fields of that level. Otherwise the level starts with its default fields, and a relation in :only only selects the fields of that relation. Thus :only and :with on one level equal one :only with the fields of both, when :only names a field of that level without nested fields.

Each level can use its own option. The Result column lists the serialized fields:

Root Friends Fields Result
only except only: [:username, :friends], except: {friends: :first_name} username, friends(username)
only with only: [:email, :friends], with: {friends: :email} email, friends(username, first_name, email)
except only except: :first_name, only: {friends: :email} username, friends(email)
except with except: :first_name, with: {friends: :email} username, friends(username, first_name, email)
with only with: :email, only: {friends: :username} username, first_name, email, friends(username)
with except with: :friends, except: {friends: :first_name} username, first_name, friends(username)
only only only: {friends: :username} friends(username)
default only with: :friends, only: {friends: :username} username, first_name, friends(username)

Fields from a request

Fields can be one String, with nested fields in parentheses. This fits query parameters like GET /users?fields=username,friends(username,email).

UserSerializer.to_h(bruce, only: "username,friends(username,email)")
# => {username: "batman", friends: [{username: "alfred", email: "alfred@wayne.com"}]}

UserSerializer.to_h(users, only: params[:fields]) # all default fields when params[:fields] is nil

Unknown fields

An unknown field raises Serega::AttributeNotExist. Its #serializer and #attributes methods return the serializer and the unknown fields.

UserSerializer.to_h(bruce, only: [:nickname, {friends: :age}])
# => Serega::AttributeNotExist: Not existing attributes: nickname, friends(age)

check_initiate_params: false skips unknown fields:

UserSerializer.to_h(bruce, only: [:username, :nickname], check_initiate_params: false)
# => {username: "batman"}

Hiding by default

config.hide_by_default = true hides every attribute that has no hide: false option. config.hide_by_default = :auto hides attributes that need preloads or batch loaders, so clients request expensive fields explicitly.

class UserSerializer < Serega
  config.hide_by_default = true

  attribute :username
  attribute :first_name, hide: false
end

UserSerializer.to_h(bruce) # => {first_name: "Bruce"}