Serega

Serialize exactly what was asked. Load exactly what is needed.

Gem Version {!GitHub Actions}[github.com/andreyruby/serega/actions/workflows/main.yml] Coverage 100% Zero dependencies Ruby >= 3.3

Serega is a Ruby serializer that turns your objects into Hashes ready for JSON. Clients choose the fields they need, and Serega loads only the data those fields use โ€” in a few queries, with no N+1.

Show me

class AppSerializer < Serega
  plugin :activerecord_preloads   # preloads associations used by requested fields
  config.auto_preload = true
  config.hide_by_default = :auto  # fields that need extra queries are sent on request
end

class UserSerializer < AppSerializer
  attribute :name
  attribute :posts, serializer: "PostSerializer"
end

class PostSerializer < AppSerializer
  batch :comments_count, ->(posts) { Comment.where(post: posts).group(:post_id).count }

  attribute :title
  attribute :comments_count, batch: true
end

UserSerializer.to_h(User.all)
# => [{name: "Bruce Wayne"}, ...]                                    1 query

UserSerializer.to_h(User.all, only: "name,posts(title,comments_count)")
# => [{name: "Bruce Wayne", posts: [{title: "Gotham at night", comments_count: 12}, ...]}, ...]
#                                                                    3 queries, for any number of users

Installation

bundle add serega

Quick start

Define a base serializer with the setup your app uses. Every serializer inherits it.

# app/serializers/app_serializer.rb
class AppSerializer < Serega
  plugin :activerecord_preloads
  config.auto_preload = true
end

# app/serializers/user_serializer.rb
class UserSerializer < AppSerializer
  attribute :id
  attribute :name
  attribute :email, value: proc { |user, ctx| user.email if user == ctx[:current_user] }
  attribute :avatar_url, hide: true
end

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    users = UserSerializer.to_json(
      User.all,
      only: params[:fields],              # "id,name" from GET /users?fields=id,name
      context: {current_user: current_user}
    )

    render json: users
  end
end

Benchmarks

Serega is on par with the fastest Ruby serializers in speed and allocations, and in some cases beats them. Results, the details of each case and how to run them: benchmarks/README.md.

Features

Select fields

only, except and with accept Symbols, Arrays, Hashes or a string like "name,posts(title)". Unknown fields raise Serega::AttributeNotExist.

UserSerializer.to_h(user, only: [:name, {posts: [:title]}])
UserSerializer.to_h(user, except: :email)
UserSerializer.to_h(user, with: :avatar_url)  # adds hidden fields

โ†’ Selecting fields guide

Batch loading

Load data for all serialized objects with one call.

class UserSerializer < AppSerializer
  batch :followers_count, ->(users) { Follow.where(user: users).group(:user_id).count }

  attribute :followers_count, batch: true
end

โ†’ Batch loading guide ยทPreloads guide

Formatters

Format dates, money and other values the same way everywhere.

class AppSerializer < Serega
  formatter :iso8601, [:iso8601, 3], allow_nil: true
end

class UserSerializer < AppSerializer
  attribute :created_at, format: :iso8601
end

โ†’ Formatters guide

Hashes, Structs and Data

Serialize Hash records, and get results as Struct or Data objects.

class EventSerializer < Serega
  attribute :title, hash_access: true  # reads event[:title]
end

UserSerializer.to_h(user)      # => {name: "Bruce Wayne"}
UserSerializer.to_struct(user) # => #<struct name="Bruce Wayne">
UserSerializer.to_data(user)   # => #<data name="Bruce Wayne">

โ†’ Hash records guide ยทSerializing guide

Ractors

Serialize in parallel Ractors.

class UserSerializer < Serega
  attribute :name
  make_shareable!
end

Ractor.new(user) { |user| UserSerializer.to_h(user) }.value

โ†’ Ractors guide

And more

Documentation

Development

bundle install
bundle exec rake  # tests and linter
bin/console       # irb with serega loaded

Releasing: RELEASE.md.

Contributing

Bug reports, pull requests and ideas are welcome!

License

The gem is available as open source under the terms of the MIT License.